diff --git a/mozilla/cmd/macfe/MailNews/AddressBook/AddressBook.cnst b/mozilla/cmd/macfe/MailNews/AddressBook/AddressBook.cnst new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mozilla/cmd/macfe/MailNews/AddressBook/CABContainerDialogs.cp b/mozilla/cmd/macfe/MailNews/AddressBook/CABContainerDialogs.cp new file mode 100644 index 00000000000..30fcf2669b7 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/AddressBook/CABContainerDialogs.cp @@ -0,0 +1,247 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "rosetta.h" +#include "CABContainerDialogs.h" +#include "ABcom.h" +#ifdef MOZ_NEWADDR +#include "xp_mcom.h" +#include "msgnet.h" +#include "prefapi.h" +#include "dirprefs.h" + +#include "CValidEditField.h" + +#include "RandomFrontEndCrap.h" + +#include "pascalString.h" + + +#include "CTSMEditField.h" +#include "LGAPushButton.h" +#include "LGACheckbox.h" +#include "LGADialogBox.h" +#include "UmodalDialogs.h" +#include "PP_Messages.h" +#include "macgui.h" + +CLDAPPropertyDialogManager::CLDAPPropertyDialogManager( DIR_Server* server, MSG_Pane* inPane ) +{ + StDialogHandler dialogHandler( eLDAPServerPropertiesDialogResID, NULL ); + LGADialogBox* dialog = dynamic_cast( dialogHandler.GetDialog() ) ; + Assert_( dialog ); + Setup( dialog, server ); + + if( dialog ) + { + dialog->Show(); + MessageT theMessage = msg_Nothing; + while ( (theMessage != msg_Cancel) && (theMessage != msg_OK) ) + { + theMessage = dialogHandler.DoDialog(); + switch( theMessage ) + { + case cmd_HelpButton: // help button + // ShowHelp(mHelpTopic); + break; + + // toggle the port numbers in the port field + case eGarbledBox: + Int32 value = eLDAPStandardPort; + if (mGarbledBox->GetValue()) + value = eLDAPGarbledPort; + mPortNumber->SetValue(eLDAPGarbledPort); + break; + + case eUpdateButton: + UpdateDirServerToUI (); + // The Net_ReplicationDirectory call requires that this flag be set + DIR_ForceFlag( mServer, DIR_REPLICATION_ENABLED, true ); + NET_ReplicateDirectory( NULL, mServer ); + break; + case msg_OK: + + if( !UpdateDirServerToUI() ) + theMessage = msg_Nothing; + break; + default: + break; + } + } + + if ( theMessage == msg_OK ) + { + // I think I am supposed to go context digging for the pane? + AB_UpdateDIRServerForContainerPane( inPane, mServer ); + } + } +} + +Boolean CLDAPPropertyDialogManager::UpdateDirServerToUI() +{ + // first check to make sure that the two validated edit fields are ok. If not, + // then break out of here and don't accept the OK, forcing the user to + // fix them. + Boolean rtnValue = true; + if ( !MaxHitsValidationFunc(mPortNumber) || !PortNumberValidationFunc(mMaxHits) ) + rtnValue = false; + + XP_FREEIF(mServer->searchBase); + XP_FREEIF(mServer->serverName); + XP_FREEIF(mServer->description); + + CStr255 pBuffer; + mDescription->GetDescriptor(pBuffer); + mServer->description = XP_STRDUP(CStr255( pBuffer ) ); + mLdapServer->GetDescriptor(pBuffer); + mServer->serverName = XP_STRDUP(CStr255( pBuffer )); + mSearchRoot->GetDescriptor(pBuffer); + mServer->searchBase = XP_STRDUP(CStr255( pBuffer )); + + DIR_ForceFlag( mServer, DIR_REPLICATION_ENABLED, mDownloadCheckBox->GetValue( ) ); + mServer->port = mPortNumber->GetValue(); + mServer->maxHits = mMaxHits->GetValue(); + + HG51388 + mServer->savePassword = mSavePasswordBox->GetValue()? true: false; + DIR_SetServerFileName(mServer, mServer->serverName); + return rtnValue; +} + + +void CLDAPPropertyDialogManager::Setup( LGADialogBox* inDialog , DIR_Server *inServer ) +{ + mServer = inServer; + + mDescription = (CTSMEditField *) inDialog->FindPaneByID(eDescriptionEditField); + XP_ASSERT(mDescription); + mLdapServer = (LEditField *) inDialog->FindPaneByID(eLDAPServerEditField); + XP_ASSERT(mLdapServer); + mSearchRoot = (LEditField *) inDialog->FindPaneByID(eSearchRootEditField); + XP_ASSERT(mSearchRoot); + mPortNumber = (CValidEditField *) inDialog->FindPaneByID(ePortNumberEditField); + XP_ASSERT(mPortNumber); + mMaxHits = (CValidEditField *) inDialog->FindPaneByID(eMaxHitsEditField); + XP_ASSERT(mMaxHits); + mGarbledBox = (LGACheckbox *) inDialog->FindPaneByID(eGarbledBox); + XP_ASSERT(mGarbledBox); + mSavePasswordBox = dynamic_cast(inDialog->FindPaneByID ( eSaveLDAPServerPasswordBox) ); + Assert_( mSavePasswordBox ); + mDownloadCheckBox = dynamic_cast( inDialog->FindPaneByID ( eDownloadCheckBox ) ); + Assert_( mDownloadCheckBox ); + + UReanimator::LinkListenerToControls( inDialog, inDialog, eLDAPServerPropertiesDialogResID ); + + mPortNumber->SetValidationFunction(PortNumberValidationFunc); + mMaxHits->SetValidationFunction(MaxHitsValidationFunc); + + mDescription->SetDescriptor(CStr255( mServer->description) ); + mLdapServer->SetDescriptor(CStr255 (mServer->serverName )); + mSearchRoot->SetDescriptor(CStr255( mServer->searchBase )); + mDownloadCheckBox->SetValue( DIR_TestFlag( mServer, DIR_REPLICATION_ENABLED ) ); + mPortNumber->SetValue(mServer->port); + mMaxHits->SetValue(mServer->maxHits); + + HG51389 + mSavePasswordBox->SetValue(mServer->savePassword ? 1: 0); + + // If the directories are locked, disable everything but cancel so the user can't make any changes. This + // allows them to view the information but not edit it. + XP_Bool serversLocked = PREF_PrefIsLocked("ldap_1.number_of_directories"); + if ( serversLocked ) + { + inDialog->Disable(); + LGAPushButton *canelButton = (LGAPushButton *)(inDialog->FindPaneByID('CnBt') ); + XP_ASSERT(canelButton); + canelButton->Enable(); + } +} + + +Boolean CLDAPPropertyDialogManager::PortNumberValidationFunc(CValidEditField *portNumber) +// Makes sure the port number field of the dialog is between 0 and 32767, but sets +// a reasonable default if the field is left blank. +{ + Boolean result; + Str255 currentValue; + portNumber->GetDescriptor(currentValue); + if (!currentValue[0]) + { + portNumber->SetValue(eLDAPStandardPort); + HG51387 + portNumber->SelectAll(); + result = false; + } + else + { + result = ConstrainEditField(portNumber, 0, 32767); + } + if (!result) + { + StPrepareForDialog prepare; + ::StopAlert(1068, NULL); + } + return result; +} + + +Boolean CLDAPPropertyDialogManager::MaxHitsValidationFunc(CValidEditField *maxHits) +// Makes sure the max hits field of the dialog is between 1 and 65535. +{ + Boolean result; + result = ConstrainEditField(maxHits, 1, 65535); + if (!result) + { + // If it was constrained to 1 then make it 100 instead. + if (1 == maxHits->GetValue() ) + { + maxHits->SetValue(100); + maxHits->SelectAll(); + } + StPrepareForDialog prepare; + ::StopAlert(1069, NULL); + } + return result; +} + +#pragma mark --CPABPropertyDialogManager-- +CPABPropertyDialogManager::CPABPropertyDialogManager( DIR_Server *inServer, MSG_Pane* inPane ) +{ + StDialogHandler dialogHandler( ePABPropertyWindowID, NULL ); + LGADialogBox* dialog = dynamic_cast( dialogHandler.GetDialog() ) ; + Assert_( dialog ); + + LGAEditField* nameField = dynamic_cast( dialog->FindPaneByID( eNamePaneID ) ); + nameField->SetDescriptor( CStr255 ( inServer->description ) ); + + MessageT theMessage = msg_Nothing; + while ( (theMessage != msg_Cancel) && (theMessage != msg_OK) ) + { + theMessage = dialogHandler.DoDialog(); + } + + if ( theMessage == msg_OK ) + { + XP_FREEIF( inServer->description ); + CStr255 pBuffer; + nameField->GetDescriptor(pBuffer); + inServer->description = XP_STRDUP(CStr255( pBuffer ) ); + AB_UpdateDIRServerForContainerPane( inPane, inServer ); + } +} +#endif diff --git a/mozilla/cmd/macfe/MailNews/AddressBook/CABContainerDialogs.h b/mozilla/cmd/macfe/MailNews/AddressBook/CABContainerDialogs.h new file mode 100644 index 00000000000..00340eeae8d --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/AddressBook/CABContainerDialogs.h @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +typedef struct MSG_Pane MSG_Pane; +typedef struct DIR_Server DIR_Server; +class CValidEditField; +class LEditField; +class LGACheckbox; +class LGADialogBox; +class CTSMEditField; + + +//------------------------------------------------------------------------------ +// ¥ CLDAPPropertyDialogManager +//------------------------------------------------------------------------------ + +class CLDAPPropertyDialogManager +{ +public: + CLDAPPropertyDialogManager( DIR_Server* ioServer, MSG_Pane* inPane ); +private: + Boolean UpdateDirServerToUI(); + void Setup( LGADialogBox* inDialog , DIR_Server *inServer ); + + +public: + // PUBLIC CONSTANTS + enum { + class_ID = 'LDSP', + eLDAPServerPropertiesDialogResID = 12002, + cmd_HelpButton = 3 + }; + + // Port ID's + enum { + eLDAPStandardPort = 389, + eLDAPGarbledPort = 636 + }; + + // Pane ID's for dialog + enum { + eDescriptionEditField = 10, + eLDAPServerEditField, + eSearchRootEditField, + ePortNumberEditField, + eMaxHitsEditField, + eGarbledBox = 20, + eSaveLDAPServerPasswordBox = 21, + eUpdateButton = 22, + eDownloadCheckBox = 23 + }; + + static Boolean MaxHitsValidationFunc(CValidEditField *maxHits) ; + static Boolean PortNumberValidationFunc(CValidEditField *portNumber) ; +private: + const char* mHelpTopic; // help string for NetHelp + DIR_Server* mServer; // the LDAP server + + CTSMEditField* mDescription; // Items in dialog + LEditField* mLdapServer; + LEditField* mSearchRoot; + CValidEditField* mPortNumber; + CValidEditField* mMaxHits; + LGACheckbox* mGarbledBox; + LGACheckbox* mSavePasswordBox; + LGACheckbox* mDownloadCheckBox; +}; + +//------------------------------------------------------------------------------ +// ¥ CPABPropertyDialogManager +//------------------------------------------------------------------------------ + +class CPABPropertyDialogManager +{ + enum { eNamePaneID = 'NmEd' }; + enum { ePABPropertyWindowID = 8995 }; +public: + CPABPropertyDialogManager( DIR_Server *inServer, MSG_Pane* inPane ); +}; \ No newline at end of file diff --git a/mozilla/cmd/macfe/MailNews/AddressBook/CAddressBookViews.cp b/mozilla/cmd/macfe/MailNews/AddressBook/CAddressBookViews.cp new file mode 100644 index 00000000000..439ddfeae7e --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/AddressBook/CAddressBookViews.cp @@ -0,0 +1,1481 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +// CAddressBookViews.cp +#include "CAddressBookViews.h" +#include "CAddressBookWindows.h" +#ifdef MOZ_NEWADDR + + +#include "SearchHelpers.h" +#include "MailNewsgroupWindow_Defines.h" +#include "Netscape_Constants.h" +#include "resgui.h" +#include "CMailNewsWindow.h" +#include "CMailNewsContext.h" +#include "UGraphicGizmos.h" +#include "ufilemgr.h" +#include "uerrmgr.h" +#include "UStdDialogs.h" +#include "macutil.h" +#include "UStClasses.h" +#include +#include +#include "divview.h" +#include "CTargetFramer.h" +#include "msgcom.h" +// get string constants +#define WANT_ENUM_STRING_IDS +#include "allxpstr.h" +#undef WANT_ENUM_STRING_IDS + +#include "UProcessUtils.h" +#include "CAppleEventHandler.h" +#include "secnav.h" +#include "CTableKeyAttachment.h" +#include "intl_csi.h" +#include "xp_help.h" +#include "CLDAPQueryDialog.h" +#include "StSetBroadcasting.h" +#include "UAddressBookUtilities.h" +#include "NetscapeDragFlavors.h" +#include "CKeyStealingAttachment.h" +#include "UMailSelection.h" +#pragma mark - + +#define AssertFail_(test) ThrowIfNot_(test) +struct SAddressDelayedDragInfo +{ + CMailSelection mDragSelection; + AB_ContainerInfo* mDragContainer; + AB_DragEffect mDragRequest; +}; + +//------------------------------------------------------------------------------ +// ¥ CAddressBookPane +//------------------------------------------------------------------------------ +// Is the base class for tables which use the addressbook API's +// + +//------------------------------------------------------------------------------ +// ¥ CAddressBookPane +//------------------------------------------------------------------------------ +// +CAddressBookPane::CAddressBookPane(LStream *inStream) : Inherited(inStream), mContainer ( NULL ), mDelayedDragInfo ( false ) +{ + SetRefreshAllWhenResized(false); + mColumnSortCommand [ 0 ]= AB_SortByColumnID0; mColumnAttribID[ 0 ] = AB_attribEntryType; + mColumnSortCommand [ 1 ]= AB_SortByColumnID1; mColumnAttribID[ 1 ] = AB_attribDisplayName; + mColumnSortCommand [ 2 ]= AB_SortByColumnID2; mColumnAttribID[ 2 ] = AB_attribEmailAddress; + mColumnSortCommand [ 3 ]= AB_SortByColumnID3; mColumnAttribID[ 3 ] = AB_attribCompanyName; + mColumnSortCommand [ 4 ]= AB_SortByColumnID4; mColumnAttribID[ 4 ] = AB_attribNickName; + mColumnSortCommand [ 5 ]= AB_SortByColumnID5; mColumnAttribID[ 5 ] = AB_attribLocality; +} + +//------------------------------------------------------------------------------ +// ¥ GetFullAddress +//------------------------------------------------------------------------------ +// Gets an email address for a given row by combining fullname and email address +// MSG_MakeFullAddress should be replaced by the address book equivalent when +// available +// +char* CAddressBookTableView::GetFullAddress( TableIndexT inRow ) +{ + StABEntryAttribute fullName( GetMessagePane(), inRow, AB_attribFullName ); + StABEntryAttribute email( GetMessagePane(), inRow, AB_attribEmailAddress); + return MSG_MakeFullAddress( fullName.GetChar(), email.GetChar() ); +} + + +//------------------------------------------------------------------------------ +// ¥ CurrentBookIsPersonalBook +//------------------------------------------------------------------------------ +// Checks to see if the currenlty loaded container is a PAB +// +Boolean CAddressBookPane::CurrentBookIsPersonalBook() +{ + + Boolean returnResult = false; + if ( mContainer ) + { + AB_ContainerAttribValue * value; + AB_GetContainerAttribute( mContainer, attribContainerType ,& value); + if( value->u.containerType == AB_PABContainer ) + returnResult = true; + AB_FreeContainerAttribValue( value ); + } + return returnResult; +} + + +//------------------------------------------------------------------------------ +// ¥ DrawCellContents +//------------------------------------------------------------------------------ +// Over ride CStandardFlexTable. If the cell is AB_attribEntryType an icon is +// drawn. Otherwise a string is drawn +// +void CAddressBookPane::DrawCellContents(const STableCell &inCell, const Rect &inLocalRect) +{ + ResIDT icon = 0; + CStr255 displayString; + GetCellDisplayData ( inCell, icon, displayString ); + if ( icon ) + { + ResIDT iconID = GetIconID(inCell.row); + IconTransformType transformType = kTransformNone; + + if (iconID) + DrawIconFamily(iconID, 16, 16, transformType, inLocalRect); + } + else + { + + DrawTextString( displayString , &mTextFontInfo, 0, inLocalRect); + } + if (inCell.row == mDropRow) + ::InvertRect(&inLocalRect); +} + +void CAddressBookPane::GetCellDisplayData(const STableCell &inCell, ResIDT& ioIcon, CStr255 &ioDisplayString ) +{ + + AB_AttribID attrib = GetAttribForColumn( inCell.col ); + if ( attrib == AB_attribEntryType ) + { + ioIcon = GetIconID( inCell.row ); + } + else + { + + StABEntryAttribute value ( GetMessagePane(), inCell.row , attrib ); + ioDisplayString = value.GetChar(); + } +} + +//------------------------------------------------------------------------------ +// ¥ GetIconID +//------------------------------------------------------------------------------ +// Over ride CStandardFlexTable. +// +ResIDT CAddressBookPane::GetIconID(TableIndexT inRow) const +{ + ResIDT iconID = 0; + + StABEntryAttribute value ( GetMessagePane(), inRow , AB_attribEntryType ); + + switch( value.GetEntryType() ) + { + case AB_Person: + iconID = ics8_Person; + break; + + case AB_MailingList: + iconID = ics8_List; + break; + + default: + Assert_( 0 ); // Shouldn't be happening + break; + }; + + return iconID; +} + +//------------------------------------------------------------------------------ +// ¥ DrawCellText +//------------------------------------------------------------------------------ +// Gets the given attribute string for the cell and draws it in the passed in the passed in Rect +// +void CAddressBookPane::DrawCellText( const STableCell& inCell, const Rect& inLocalRect, AB_AttribID inAttrib ) +{ + + StABEntryAttribute value ( GetMessagePane(), inCell.row , inAttrib ); + + DrawTextString( value.GetChar(), &mTextFontInfo, 0, inLocalRect); + if (inCell.row == mDropRow) + ::InvertRect(&inLocalRect); +} + + + +//------------------------------------------------------------------------------ +// ¥ AddRowDataToDrag +//------------------------------------------------------------------------------ +// Adds the type text to the drag. The text data is the full email address (ie "John Doe " ) +// +void CAddressBookTableView::AddRowDataToDrag(TableIndexT inRow, DragReference inDragRef) +{ +#if 0 + char* fullName = GetFullAddress( inRow ); + if( fullName ) + { + Size size = XP_STRLEN ( fullName ); + OSErr err = ::AddDragItemFlavor(inDragRef, inRow, 'TEXT', + fullName, size, 0 ); + XP_FREEIF( fullName ); + FailOSErr_(err); + } +#else +#pragma unused(inRow) +#pragma unused(inDragRef) +#endif +} + + +//------------------------------------------------------------------------------ +// ¥ SortCommandFromColumnType +//------------------------------------------------------------------------------ +// Convert a FE column type into an AB_CommandType +// +AB_CommandType CAddressBookPane::SortCommandFromColumnType(EColType inColType) +{ + Int32 index = inColType - eTableHeaderBase; + return mColumnSortCommand[ index ]; +} + + +//------------------------------------------------------------------------------ +// ¥ GetAttribForColumn +//------------------------------------------------------------------------------ +// Converts a FE column enumeration into a AB_AttribID +// +AB_AttribID CAddressBookPane::GetAttribForColumn( TableIndexT col ) +{ + STableCell cell (1, col ); + PaneIDT id = GetCellDataType(cell) ; + Int32 index = id - eTableHeaderBase; + return mColumnAttribID[ index ]; +} + + +//------------------------------------------------------------------------------ +// ¥ FindCommandStatus +//------------------------------------------------------------------------------ +// Overrides LCommander +// +void CAddressBookPane:: FindCommandStatus(CommandT inCommand, Boolean &outEnabled, + Boolean &outUsesMark, Char16 &outMark, + Str255 outName) +{ + + CStandardFlexTable::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); + +} + + +//------------------------------------------------------------------------------ +// ¥ ObeyCommand +//------------------------------------------------------------------------------ +// Overrides LCommander +// +Boolean CAddressBookPane::ObeyCommand(CommandT inCommand, void *ioParam) +{ + Boolean rtnVal = true; + + switch ( inCommand ) { + + case CAddressBookPane::eCol0: + case CAddressBookPane::eCol1: + case CAddressBookPane::eCol2: + case CAddressBookPane::eCol3: + case CAddressBookPane::eCol4: + case CAddressBookPane::eCol5: + GetTableHeader()->SimulateClick(inCommand); + rtnVal = true; + break; + + case CAddressBookPane::cmd_SortAscending: + case CAddressBookPane::cmd_SortDescending: + GetTableHeader()->SetSortOrder(inCommand == CAddressBookPane::cmd_SortDescending); + break; + + default: + AB_CommandType abCommand = UAddressBookUtilites::GetABCommand( inCommand); + rtnVal = UAddressBookUtilites::ABCommand( this, abCommand) == AB_SUCCESS ; + if ( !rtnVal ) + rtnVal = Inherited::ObeyCommand(inCommand, ioParam); + break; + } + + return rtnVal; +} + +//------------------------------------------------------------------------------ +// ¥ OpenRow +//------------------------------------------------------------------------------ +// Overrides CStandardFlexTable +// Delegates to ObeyCommand +// +void CAddressBookPane::OpenRow(TableIndexT inRow) +{ + ObeyCommand( UAddressBookUtilites::cmd_EditProperties, (void*)inRow ); +} + +//------------------------------------------------------------------------------ +// ¥ DeleteSelection +//------------------------------------------------------------------------------ +// Overrides CStandardFlexTable +// Delegates to ObeyCommand +// +void CAddressBookPane::DeleteSelection() +{ + ObeyCommand( UAddressBookUtilites::cmd_DeleteEntry, nil ); +} + + +//------------------------------------------------------------------------------ +// ¥ DestroyMessagePane +//------------------------------------------------------------------------------ +// +void CAddressBookPane::DestroyMessagePane(MSG_Pane* inPane) +{ + if ( inPane != nil ) + { + ::SetCursor(*::GetCursor(watchCursor)); // Could take forever + AB_ClosePane( inPane ); + } +} + + +//----------------------------------- +Boolean CAddressBookPane::ItemIsAcceptable( + DragReference inDragRef, + ItemReference inItemRef ) +//----------------------------------- +{ + FlavorFlags flavorFlags; + if (::GetFlavorFlags(inDragRef, inItemRef, mDragFlavor, &flavorFlags) == noErr) + { + CMailSelection selection; + if (!mIsInternalDrop && GetSelectionFromDrag(inDragRef, selection)) + mIsInternalDrop = (selection.xpPane == GetMessagePane()); + return true; + } + return false; +} // CMessageFolderView::ItemIsAcceptable + +//------------------------------------------------------------------------------ +// ¥ RowCanAcceptDrop +//------------------------------------------------------------------------------ +// +Boolean CAddressBookPane::RowCanAcceptDrop( DragReference inDragRef, TableIndexT inDropRow) +{ + Boolean dropOK = false; + + SInt16 modifiers; + ::GetDragModifiers(inDragRef, NULL, &modifiers, NULL); + Boolean doCopy = ((modifiers & optionKey) != 0); + CMailSelection selection; + AB_DragEffect effect; + AB_DragEffect desiredAction = AB_Default_Drag; + if ( doCopy ) + desiredAction = AB_Require_Copy; + if (inDropRow >= 1 && inDropRow <= mRows) + { + if (!GetSelectionFromDrag(inDragRef, selection )) + return false; // Should handle text drags + AB_ContainerInfo* container = GetContainer( inDropRow ); + effect = AB_DragEntriesIntoContainerStatus( + GetMessagePane(), selection.GetSelectionList(), selection.selectionSize, container, desiredAction ); + } + + if ( effect == AB_Require_Copy) + doCopy = true; + + if ( effect > 0 ) + dropOK = true; + + if (dropOK && doCopy) + { + CursHandle copyDragCursor = ::GetCursor(6608); // finder's copy-drag cursor + if (copyDragCursor != nil) + ::SetCursor(*copyDragCursor); + } + else + ::SetCursor(&qd.arrow); + + return dropOK; +} + + +//------------------------------------------------------------------------------ +// ¥ RowCanAcceptDropBetweenAbove +//------------------------------------------------------------------------------ +// +Boolean CAddressBookPane::RowCanAcceptDropBetweenAbove( + DragReference inDragRef, + TableIndexT inDropRow) +{ + if (inDropRow >= 1 && inDropRow <= mRows) + { + AB_DragEffect effect; + AB_DragEffect desiredAction = AB_Default_Drag; + CMailSelection selection; + if (GetSelectionFromDrag(inDragRef, selection)) + { + AB_ContainerInfo* container = GetContainer( inDropRow ); + + effect = AB_DragEntriesIntoContainerStatus( + GetMessagePane(), selection.GetSelectionList(), selection.selectionSize, container, desiredAction ); + if( effect > 0 ) + return true; + } + } + return false; +} + + +//------------------------------------------------------------------------------ +// ¥ ReceiveDragItem +//------------------------------------------------------------------------------ +// Drags can potentially bring up dialogs which will hang the machine. So instead of +// doing the drag right away do it during SpendTime +// +void CAddressBookPane::ReceiveDragItem( + DragReference inDragRef, + DragAttributes /* inDragAttrs */, + ItemReference /* inItemRef */, + Rect& /* inItemBounds */) +{ + mDelayedDragInfo = new SAddressDelayedDragInfo; + if ( GetSelectionFromDrag(inDragRef, mDelayedDragInfo->mDragSelection) ) + { + mDelayedDragInfo->mDragContainer = GetContainer( mDropRow ); + mDelayedDragInfo->mDragRequest = AB_Default_Drag; + + StartIdling(); + } + // Should handle text drag +}; + + +//------------------------------------------------------------------------------ +// ¥ SpendTime +//------------------------------------------------------------------------------ +// See ReceiveDragItem +// +void CAddressBookPane::SpendTime(const EventRecord &/* inMacEvent*/) +{ + if ( mDelayedDragInfo ) + { + + AB_DragEntriesIntoContainer( + mDelayedDragInfo->mDragSelection.xpPane, + mDelayedDragInfo->mDragSelection.GetSelectionList(), + mDelayedDragInfo->mDragSelection.selectionSize, + mDelayedDragInfo->mDragContainer, + mDelayedDragInfo->mDragRequest ) ; + delete mDelayedDragInfo; + mDelayedDragInfo = NULL; + + } + + StopIdling(); +} + + +//------------------------------------------------------------------------------ +// ¥ PaneChanged +//------------------------------------------------------------------------------ +// +void CAddressBookPane::PaneChanged(MSG_Pane *inPane, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value) +{ + switch (inNotifyCode) + { + case MSG_PaneNotifyStartSearching: + EnableStopButton(true); + break; + + case MSG_PaneNotifyStopSearching: + EnableStopButton(false); + break; + + case MSG_PaneNotifyTypeDownCompleted: + if( value != MSG_VIEWINDEXNONE ) + { + UnselectAllCells(); + STableCell cell(value + 1, 1); + SelectCell( cell ); + ScrollCellIntoFrame( cell ); + } + break; + + // Not sure if I will still get this message + case MSG_PaneDirectoriesChanged: + BroadcastMessage( MSG_PaneDirectoriesChanged, NULL ); + break; + default: + Inherited::PaneChanged(inPane, inNotifyCode, value); + break; + } +} + +//------------------------------------------------------------------------------ +// ¥ SetCellExpansion +//------------------------------------------------------------------------------ +// +void CAddressBookPane::SetCellExpansion( const STableCell& inCell, Boolean inExpanded) +{ + Boolean currentlyExpanded; + if (!CellHasDropFlag(inCell, currentlyExpanded) || (inExpanded == currentlyExpanded)) + return; + ToggleExpandAction(inCell.row); +} + +#pragma mark - +//------------------------------------------------------------------------------ +// ¥ CAddressBookTableView +//------------------------------------------------------------------------------ +// Table for showing an addressbook contents + +//------------------------------------------------------------------------------ +// ¥ ~CAddressBookTableView +//------------------------------------------------------------------------------ +// +CAddressBookTableView::~CAddressBookTableView() +{ + SetMessagePane( NULL ); +} + + +//------------------------------------------------------------------------------ +// ¥ LoadAddressBook +//------------------------------------------------------------------------------ +// Loads the given container into the table +// Will create initialize the MSG_Pane if necessary +// +Boolean CAddressBookTableView::LoadAddressBook(AB_ContainerInfo *inContainer, MWContext* inContext ) +{ + + Try_ { + ::SetCursor(*::GetCursor(watchCursor)); // Could take forever + if ( GetMessagePane() != nil ) + { + CAddressBookManager::FailAddressError( AB_ChangeABContainer( GetMessagePane(), inContainer) ); + mContainer = inContainer; + mTableHeader->SetSortOrder(false); + } + else + { + + + MSG_Pane* pane; + CAddressBookManager::FailAddressError( + AB_CreateABPane( &pane, inContext, CMailNewsContext::GetMailMaster() ) + ); + SetMessagePane( pane ); + + uint32 pageSize = 25;// Fix this. Should be calculated based on window size and recomputed when the window resizes + AB_SetFEPageSizeForPane( pane, pageSize); + MSG_SetFEData((MSG_Pane *) GetMessagePane(), CMailCallbackManager::Get()); + mContainer = inContainer; + CAddressBookManager::FailAddressError( AB_InitializeABPane( GetMessagePane(), inContainer ) ); + SetColumnHeaders( ); + + // Register callbacks + CAddressBookManager::FailAddressError( + AB_SetShowPropertySheetForEntryFunc ( GetMessagePane(), MacFe_ShowModelessPropertySheetForAB2 )); + } + } + Catch_(inErr) { + SetMessagePane( NULL ); + mContainer = NULL; + Throw_(inErr); + } + EndCatch_ + + return true; +} + + +//------------------------------------------------------------------------------ +// ¥ ConferenceCall +//------------------------------------------------------------------------------ +// Needs to be update for new addressbook API's +// +void CAddressBookTableView::ConferenceCall( ) +{ +#if 0 + TableIndexT selectedRow = 0; + GetNextSelectedRow( selectedRow ); + if ( !CurrentBookIsPersonalBook() ) + return; + + ABID id = GetEntryID( selectedRow ); + char emailAddress[kMaxEmailAddressLength]; + char ipAddress[kMaxCoolAddress]; + short serverType; + AB_GetUseServer( sCurrentBook, CAddressBookManager::GetAddressBook(), id, &serverType ); + AB_GetEmailAddress( sCurrentBook, CAddressBookManager::GetAddressBook(), id, emailAddress ); + AB_GetCoolAddress( sCurrentBook, CAddressBookManager::GetAddressBook(), id, ipAddress ); + + // Check to see if we have all the data we need + if ( (serverType==kSpecificDLS || serverType==kHostOrIPAddress ) && !XP_STRLEN( ipAddress )) + { + FE_Alert( CAddressBookWindow::GetMailContext(), XP_GetString(MK_MSG_CALL_NEEDS_IPADDRESS)); + return; + } + + if ( (serverType==kSpecificDLS || serverType==kDefaultDLS) && !XP_STRLEN( emailAddress )) + { + FE_Alert( CAddressBookWindow::GetMailContext(), XP_GetString(MK_MSG_CALL_NEEDS_EMAILADDRESS)); + return; + } + + char *ipAddressDirect = NULL; + char *serverAddress = NULL; + char *address = NULL; + switch( serverType) + { + case kDefaultDLS: + address = &emailAddress[0]; + break; + case kSpecificDLS: + address = &emailAddress[0]; + serverAddress = &ipAddress[0]; + break; + case kHostOrIPAddress: + ipAddressDirect = &ipAddress[0]; + break; + default: + break; + } + // And now the AE fun begins + AppleEvent event,reply; + OSErr error; + ProcessSerialNumber targetPSN; + Boolean isAppRunning; + static const OSType kConferenceAppSig = 'Ncq¹'; // This needs to be in a header file + // so that uapp and us share it + isAppRunning = UProcessUtils::ApplicationRunning( kConferenceAppSig, targetPSN ); + if( !isAppRunning) + { + ObeyCommand(cmd_LaunchConference, NULL); + isAppRunning = UProcessUtils::ApplicationRunning( kConferenceAppSig, targetPSN ); + } + + if( !isAppRunning ) // for some reason we were unable to open app + return; + Try_ + { + error = AEUtilities::CreateAppleEvent( 'VFON', 'CALL', event, targetPSN ); + ThrowIfOSErr_(error); + + if( ipAddressDirect) + { + error = ::AEPutParamPtr(&event, '----', typeChar, ipAddressDirect, XP_STRLEN(ipAddressDirect) ); + ThrowIfOSErr_(error); + } + + if( address) + { + error = ::AEPutParamPtr(&event, 'MAIL', typeChar, address, XP_STRLEN(address) ); + ThrowIfOSErr_(error); + } + + if( serverAddress ) + { + error = ::AEPutParamPtr(&event, 'SRVR', typeChar, serverAddress, XP_STRLEN(serverAddress) ); + ThrowIfOSErr_(error); + } + + // NULL reply parameter + reply.descriptorType = typeNull; + reply.dataHandle = nil; + + error = AESend(&event,&reply,kAENoReply + kAENeverInteract + + kAECanSwitchLayer+kAEDontRecord,kAENormalPriority,-1,nil,nil); + ThrowIfOSErr_(error); + + UProcessUtils::PullAppToFront( targetPSN ); + + } + Catch_(inError) // in case of errors do nothing + { + } + AEDisposeDesc(&reply); + AEDisposeDesc(&event); +#endif +} + +//------------------------------------------------------------------------------ +// ¥ SetColumnHeaders +//------------------------------------------------------------------------------ +// Updates column headers for a new container +// +void CAddressBookTableView::SetColumnHeaders( ) +{ + + LTableViewHeader* tableHeader = GetTableHeader(); + PaneIDT headerPaneID; + + for (short col = 1; col < tableHeader->CountColumns(); col ++) + { + headerPaneID = tableHeader->GetColumnPaneID(col); + Int32 index = headerPaneID - eTableHeaderBase+1; + AB_ColumnInfo *info = AB_GetColumnInfo( mContainer, AB_ColumnID( index) ); + mColumnAttribID[ col ] = info->attribID; + + LCaption* headerPane = dynamic_cast(FindPaneByID(headerPaneID)); + + if (headerPane) + { + + headerPane->SetDescriptor( CStr255 (info->displayString) ); + } + AB_FreeColumnInfo( info); + } +} + + +//------------------------------------------------------------------------------ +// ¥ ChangeSort +//------------------------------------------------------------------------------ +// called from LTableHeader to change the column that is sorted on +// +void CAddressBookTableView::ChangeSort(const LTableHeader::SortChange *inSortChange) +{ + Assert_(GetMessagePane() != nil); + + AB_CommandType sortCmd = SortCommandFromColumnType((EColType) inSortChange->sortColumnID); + + ::SetCursor(*::GetCursor(watchCursor)); + + // Call BE to sort the table + + CAddressBookManager::FailAddressError(AB_CommandAB2( GetMessagePane(), sortCmd, nil, 0)); +} + + +//------------------------------------------------------------------------------ +// ¥ UpdateToTypedownText +//------------------------------------------------------------------------------ +// +void CAddressBookTableView::UpdateToTypedownText(CStr255 inTypedownText ) +{ + CAddressBookManager::FailAddressError( + AB_TypedownSearch( GetMessagePane(), inTypedownText, MSG_VIEWINDEXNONE) ); +} + + +//------------------------------------------------------------------------------ +// ¥ ChangeFinished +//------------------------------------------------------------------------------ +// If LDAP searching, pass the results to the XP layer before updating +// +void CAddressBookTableView::ChangeFinished(MSG_Pane *inPane, MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, SInt32 inRowCount) { + + // If it's an initial insert call, and we're doing LDAP search, then simply pass + // this info on to the addressbook backend. We'll get a MSG_NotifyAll when + // the backend thinks we should redraw. + + if ( /* (mMysticPlane < (kMysticUpdateThreshHold + 2)) && */ + (inChangeCode == MSG_NotifyInsertOrDelete) && + IsLDAPSearching() ) { + + AB_LDAPSearchResultsAB2( GetMessagePane(), inStartRow - 1, inRowCount); + } + + + if( inChangeCode == MSG_NotifyLDAPTotalContentChanged ) + { + Inherited::ChangeFinished( inPane, MSG_NotifyScramble, 0, 0 ); + ScrollRowIntoFrame( inStartRow -1 ); + } + else + Inherited::ChangeFinished(inPane, inChangeCode, inStartRow, inRowCount); + // Because the search is asynchronous, we + // don't want to cause a redraw every time a row is inserted, because that causes + // drawing of rows in random, unsorted order. + + if (inChangeCode == MSG_NotifyInsertOrDelete && IsLDAPSearching()) + DontRefresh(); + else + Refresh(); + +} + +//------------------------------------------------------------------------------ +// ¥ EnableStopButton +//------------------------------------------------------------------------------ +// +void CAddressBookTableView::EnableStopButton(Boolean inBusy) +{ + SetLDAPSearching( inBusy ); + Inherited::EnableStopButton( inBusy); +} + + + + +#pragma mark - +//------------------------------------------------------------------------------ +// ¥ CMailingListTableView +//------------------------------------------------------------------------------ +// Handle the table in the mailing list window + +//------------------------------------------------------------------------------ +// ¥ ~CMailingListTableView +//------------------------------------------------------------------------------ +// +CMailingListTableView::~CMailingListTableView() +{ + SetMessagePane( NULL ); +} + + +//------------------------------------------------------------------------------ +// ¥ DestroyMessagePane +//------------------------------------------------------------------------------ +// The Window is responsible for disposing the pane since it is the owner +// +void CMailingListTableView::DestroyMessagePane( MSG_Pane* /* inPane */ ) +{ +} + + +//------------------------------------------------------------------------------ +// ¥ LoadMailingList +//------------------------------------------------------------------------------ +// Initializes the MailingList +// +void CMailingListTableView::LoadMailingList(MSG_Pane* inPane) +{ + SetMessagePane( inPane ); + MSG_SetFEData((MSG_Pane *) GetMessagePane(), CMailCallbackManager::Get()); + CAddressBookManager::FailAddressError( AB_InitializeMailingListPaneAB2( GetMessagePane() ) ); +} + +//------------------------------------------------------------------------------ +// ¥ GetContainer +//------------------------------------------------------------------------------ +// +AB_ContainerInfo* CMailingListTableView::GetContainer( TableIndexT /* inRow */ ) +{ + return AB_GetContainerForMailingList( GetMessagePane() ); +} + + +//------------------------------------------------------------------------------ +// ¥ DrawCellContents +//------------------------------------------------------------------------------ +// Over ride CStandardFlexTable. If the cell is AB_attribEntryType an icon is +// drawn. Otherwise a string is drawn +// +void CMailingListTableView::GetCellDisplayData(const STableCell &inCell, ResIDT& ioIcon, CStr255 &ioDisplayString ) +{ + AB_AttribID attrib = GetAttribForColumn( inCell.col ); + ioIcon = 0; + if ( attrib == AB_attribEntryType ) + { + ioIcon = GetIconID(inCell.row); + } + else + { + uint16 numItems = 1; + AB_AttributeValue* value; + CAddressBookManager::FailAddressError( + AB_GetMailingListEntryAttributes(GetMessagePane(), inCell.row-1, &attrib, &value, & numItems) ); + ioDisplayString = value->u.string ; + AB_FreeEntryAttributeValue ( value ); + } +} + +#pragma mark - +//------------------------------------------------------------------------------ +// ¥ CAddressBookContainerView +//------------------------------------------------------------------------------ +// Handles displaying the container table + +//------------------------------------------------------------------------------ +// ¥ CAddressBookContainerView +//------------------------------------------------------------------------------ +CAddressBookContainerView::CAddressBookContainerView(LStream *inStream) : + CAddressBookPane(inStream), mDirectoryRowToLoad( 1 ) +{ +}; + + +//------------------------------------------------------------------------------ +// ¥ ~CAddressBookContainerView +//------------------------------------------------------------------------------ +CAddressBookContainerView::~CAddressBookContainerView() +{ + // This must be done here in order to use the correct DestroyMessagePaneFunction + SetMessagePane( NULL ); +} + + +//------------------------------------------------------------------------------ +// ¥ Setup +//------------------------------------------------------------------------------ +// +void CAddressBookContainerView::Setup( MWContext* inContext ) +{ + MSG_Pane *msgPane; + Assert_( inContext ); + CAddressBookManager::FailAddressError ( + AB_CreateContainerPane( &msgPane, inContext, CMailNewsContext::GetMailMaster() ) ); + SetMessagePane( msgPane ); + // I think this should be done in set pane + MSG_SetFEData( (MSG_Pane *) msgPane, CMailCallbackManager::Get() ); + + CAddressBookManager::FailAddressError ( + AB_InitializeContainerPane( GetMessagePane() ) ); + + CAddressBookManager::FailAddressError( + AB_SetShowPropertySheetForEntryFunc ( GetMessagePane(), MacFe_ShowModelessPropertySheetForAB2 )); + + CAddressBookManager::FailAddressError( + AB_SetShowPropertySheetForDirFunc ( GetMessagePane(), MacFE_ShowPropertySheetForDir )); + SelectRow( mDirectoryRowToLoad ) ; +} + + +//------------------------------------------------------------------------------ +// ¥ CellHasDropFlag +//------------------------------------------------------------------------------ +// +Boolean CAddressBookContainerView::CellHasDropFlag( + const STableCell& inCell, + Boolean& outIsExpanded) const +{ + Int32 msgRow = inCell.row-1; + Boolean returnValue = false; + outIsExpanded = false; + + StABContainerAttribute value ( GetMessagePane(), inCell.row ,attribNumChildren ); + + if ( value.GetNumber() ) + { + returnValue = true; + + Int32 expansionDelta = MSG_ExpansionDelta ( GetMessagePane(), msgRow ); + if ( expansionDelta <= 0 ) + outIsExpanded = true; + } + + return returnValue; +} + +//------------------------------------------------------------------------------ +// ¥ GetNestedLevel +//------------------------------------------------------------------------------ +// +UInt16 CAddressBookContainerView::GetNestedLevel(TableIndexT inRow) const +{ + StABContainerAttribute value ( GetMessagePane(), inRow , attribDepth ); + return value.GetNumber(); +} + + +//------------------------------------------------------------------------------ +// ¥ GetIconID +//------------------------------------------------------------------------------ +// +ResIDT CAddressBookContainerView::GetIconID(TableIndexT inRow) const +{ + ResIDT iconID = 0; + StABContainerAttribute value ( GetMessagePane(), inRow , attribContainerType ); + + switch( value.GetContainerType() ) + { + case AB_LDAPContainer: + iconID = ics8_LDAP; + break; + + case AB_MListContainer: + iconID = ics8_List; + break; + + case AB_PABContainer: + iconID = ics8_PAB; + break; + + default: + Assert_( 0 ); // shouldn't be happening + break; + }; + + return iconID; +} + + +//------------------------------------------------------------------------------ +// ¥ DrawCellText +//------------------------------------------------------------------------------ +// +void CAddressBookContainerView::DrawCellText( const STableCell& inCell, const Rect& inLocalRect ) +{ + StABContainerAttribute value ( GetMessagePane(), inCell.row , attribName ); + DrawTextString( value.GetChar(), &mTextFontInfo, 0, inLocalRect); + // Is this a drop target + if (inCell.row == mDropRow) + ::InvertRect(&inLocalRect); +} + + +//------------------------------------------------------------------------------ +// ¥ DrawCellContents +//------------------------------------------------------------------------------ +// +void CAddressBookContainerView::DrawCellContents( const STableCell& inCell, const Rect& inLocalRect) +{ + // Draw the Container Icon + + SInt16 iconRight = DrawIcons(inCell, inLocalRect); + + // Draw the Container name + Rect textRect = inLocalRect; + textRect.left = iconRight+2; + DrawCellText( inCell, textRect ); +} + +//------------------------------------------------------------------------------ +// ¥ DrawIconsSelf +//------------------------------------------------------------------------------ +// CStandardflexTable applies an undesired transform when the row is selected +// +void CAddressBookContainerView::DrawIconsSelf( + const STableCell& inCell, IconTransformType /*inTransformType*/, const Rect& inIconRect) const +{ + ResIDT iconID = GetIconID(inCell.row); + if (iconID) + DrawIconFamily(iconID, 16, 16, kTransformNone, inIconRect); +} + +//------------------------------------------------------------------------------ +// ¥ GetContainer +//------------------------------------------------------------------------------ +// Returns the currently selected container +// +AB_ContainerInfo* CAddressBookContainerView::GetContainer( TableIndexT inRow ) +{ + StABContainerAttribute value ( GetMessagePane(), inRow , attribContainerInfo ); + return value.GetContainerInfo(); +} + +#pragma mark - +//------------------------------------------------------------------------------ +// ¥ CAddressBookController +//------------------------------------------------------------------------------ +// Handles the interaction between the container pane, addressbook pane, +// the popup menu and the edit field + +//------------------------------------------------------------------------------ +// ¥ CAddressBookController +//------------------------------------------------------------------------------ +// +CAddressBookController::CAddressBookController(LStream* inStream ) + :LView( inStream), + mNextTypedownCheckTime(eDontCheckTypedown), + mTypedownName(nil), mSearchButton(nil),mStopButton(nil) +{ +}; + + +//------------------------------------------------------------------------------ +// ¥ ~CAddressBookController +//------------------------------------------------------------------------------ +// +CAddressBookController::~CAddressBookController() +{ +}; + + +//------------------------------------------------------------------------------ +// ¥ ReadStatus +//------------------------------------------------------------------------------ +// +void CAddressBookController::ReadStatus(LStream *inStatusData) +{ + mDividedView->RestorePlace(inStatusData); + mAddressBookTable->GetTableHeader()->ReadColumnState(inStatusData); + Int32 index; + *inStatusData >> index; + mABContainerView->SetIndexToSelectOnLoad( index ); +} + + +//------------------------------------------------------------------------------ +// ¥ WriteStatus +//------------------------------------------------------------------------------ +// +void CAddressBookController::WriteStatus(LStream *outStatusData) +{ + mDividedView->SavePlace(outStatusData); + mAddressBookTable->GetTableHeader()->WriteColumnState(outStatusData); + STableCell cell; + cell = mABContainerView->GetFirstSelectedCell(); + *outStatusData << Int32( cell.row); +} + +//------------------------------------------------------------------------------ +// ¥ HandleKeyPress +//------------------------------------------------------------------------------ +// Tab groups don't work since the commanders are not at the same level +// +Boolean CAddressBookController::HandleKeyPress( const EventRecord &inKeyEvent) +{ + Boolean keyHandled = true; + Char16 theChar = inKeyEvent.message & charCodeMask; + + // Process Tab or Shift-Tab. Pass up if there are any other + // modifiers keys pressed. + + if ((theChar == char_Tab) && (( inKeyEvent.modifiers & ( cmdKey + optionKey + controlKey ) )== 0 ) ) + { + Boolean backwards = (inKeyEvent.modifiers & shiftKey) != 0; + LCommander * currentCommander = LCommander::GetTarget(); + int32 commanderToSelect = 0; + LCommander* commanders[3]; + + commanders[0] = dynamic_cast(mTypedownName); + commanders[1] = dynamic_cast(mAddressBookTable); + commanders[2] = dynamic_cast(mABContainerView); + Int32 i = 2; + + while( i>=0 ) + { + if ( commanders[i] == currentCommander ) + { + commanderToSelect = i + ( backwards ? -1 : 1 ); + break; + } + i--; + } + commanderToSelect = (commanderToSelect+3)%3; + Assert_( commanderToSelect >=0 && commanderToSelect<= 2 ); + SwitchTarget( commanders[ commanderToSelect ] ); + } + else + keyHandled = LCommander::HandleKeyPress(inKeyEvent); + + return keyHandled; +} + +//------------------------------------------------------------------------------ +// ¥ FinishCreateSelf +//------------------------------------------------------------------------------ +// Initializes a bunch of member variables, listens to the controls, and adds the target framer +// +void CAddressBookController::FinishCreateSelf() +{ + mAddressBookTable = dynamic_cast( FindPaneByID( paneID_AddressBookTable )); + FailNILRes_(mAddressBookTable); + + mABContainerView = dynamic_cast< CAddressBookContainerView*>( FindPaneByID( paneID_ContainerView ) ); + FailNILRes_( mABContainerView ); + mABContainerView->AddListener( this ); + + mDividedView = dynamic_cast(FindPaneByID( paneID_DividedView )); + FailNILRes_(mDividedView); + + mSearchButton = dynamic_cast(FindPaneByID( paneID_Search)); + FailNILRes_(mSearchButton); + + mStopButton = dynamic_cast(FindPaneByID( paneID_Stop)); + FailNILRes_(mStopButton); + + mTypedownName = dynamic_cast(FindPaneByID( paneID_TypedownName) ); + FailNILRes_(mTypedownName); + mTypedownName->AddListener(this); + + mTypedownName->SetSuperCommander( mAddressBookTable ); + mAddressBookTable->SetSuperCommander(this ); + + UReanimator::LinkListenerToControls(this, this, 8920); + // + CKeyStealingAttachment* keyStealer = new CKeyStealingAttachment(mAddressBookTable); + mTypedownName->AddAttachment(keyStealer); + keyStealer->StealKey( char_UpArrow ); + keyStealer->StealKey( char_DownArrow ); + keyStealer->StealKey( char_PageUp ); + keyStealer->StealKey( char_PageDown ); + + USearchHelper::SelectEditField(mTypedownName); + // Frame Highlighting + CTargetFramer* framer = new CTargetFramer(); + mAddressBookTable->AddAttachment(framer); + + framer = new CTargetFramer(); + mTypedownName->AddAttachment(framer); + SetLatentSub( mTypedownName ); + + framer = new CTargetFramer(); + mABContainerView->AddAttachment(framer); +} + + +//------------------------------------------------------------------------------ +// ¥ ListenToMessage +//------------------------------------------------------------------------------ +// +void CAddressBookController::ListenToMessage(MessageT inMessage, void *ioParam) +{ + switch ( inMessage ) + { + case CSearchEditField::msg_UserChangedText: + // User changed the typedeown text + /*if ( mNextTypedownCheckTime == eDontCheckTypedown )*/ { + mNextTypedownCheckTime = LMGetTicks() + eCheckTypedownInterval; + } + break; + + case UAddressBookUtilites::cmd_NewAddressCard: + case UAddressBookUtilites::cmd_NewAddressList: + case UAddressBookUtilites::cmd_EditProperties: + case UAddressBookUtilites::cmd_DeleteEntry: + case UAddressBookUtilites::cmd_ComposeMailMessage: + LCommander::GetTarget()->ProcessCommand( inMessage, NULL ); + break; + + case CStandardFlexTable::msg_SelectionChanged: + // if the message is from the container view, load a new container into mAddressBookTableView + if( ioParam == mABContainerView && mABContainerView->GetSelectedRowCount() == 1) + { + StABContainerAttribute container( mABContainerView->GetMessagePane(), mABContainerView->GetTableSelector()->GetFirstSelectedRow(), attribContainerInfo ); + SelectDirectoryServer( container.GetContainerInfo() ); + } + break; + + case UAddressBookUtilites::cmd_ConferenceCall: + mAddressBookTable->ConferenceCall(); + break; + + // Status messages + case msg_NSCAllConnectionsComplete: + StopSearch( ); + break; + + case paneID_Search: + Search(); + break; + + case paneID_Stop: + StopSearch( ); + break; + + default: + // No superclass method + break; + } + +} + +//------------------------------------------------------------------------------ +// ¥ ObeyCommand +//------------------------------------------------------------------------------ +// +Boolean CAddressBookController::ObeyCommand(CommandT inCommand, void *ioParam) +{ + Boolean cmdHandled = true; + switch ( inCommand ) + { + case paneID_Search: + Search(); + break; + + case paneID_Stop: + StopSearch( ); + break; + + case UAddressBookUtilites::cmd_HTMLDomains: + MSG_DisplayHTMLDomainsDialog( CAddressBookWindow::GetMailContext() ); + break; + + case cmd_SecurityInfo: + MWContext * context = CAddressBookWindow::GetMailContext(); + SECNAV_SecurityAdvisor( context, NULL ); + break; + + default: + CAddressBookPane* pane = mABContainerView; + if ( mABContainerView->IsTarget() ) + pane = mAddressBookTable; + AB_CommandType abCommand = UAddressBookUtilites::GetABCommand( inCommand); + cmdHandled = UAddressBookUtilites::ABCommand( pane, abCommand) == AB_SUCCESS ; + if ( !cmdHandled ) + cmdHandled = LCommander::ObeyCommand(inCommand, ioParam); + break; + } + return cmdHandled; +} + + +//------------------------------------------------------------------------------ +// ¥ SpendTime +//------------------------------------------------------------------------------ +// If enought time has passed either initiate a LDAP search or perform typedown +// +void CAddressBookController::SpendTime(const EventRecord &inMacEvent) +{ + + if ( inMacEvent.when >= mNextTypedownCheckTime ) + { + Assert_(mTypedownName); + Assert_(mAddressBookTable); + + CStr255 typedownText; + mTypedownName->GetDescriptor(typedownText); + if( typedownText.Length() > 0 ) + { + mAddressBookTable->UpdateToTypedownText(typedownText); + } + mNextTypedownCheckTime = eDontCheckTypedown; + } +} + + +//------------------------------------------------------------------------------ +// ¥ SelectDirectoryServer +//------------------------------------------------------------------------------ +// Load the given AB_ContainerInfo into the AddressBookTableView and then update +// the popup menu +// +void CAddressBookController::SelectDirectoryServer( AB_ContainerInfo* inContainer ) +{ + AssertFail_(mAddressBookTable != nil); + SetCursor(*GetCursor(watchCursor)); + + // Load server into address book table + + if ( !mAddressBookTable->LoadAddressBook(inContainer, mContext) ) + return; + DIR_Server* server = AB_GetDirServerForContainer( inContainer ); + + const Boolean isLDAPServer = (server->dirType == LDAPDirectory); + USearchHelper::EnableDisablePane(mSearchButton, isLDAPServer, true); + + USearchHelper::EnableDisablePane(mStopButton, false,true ); + + mAddressBookTable->SetColumnHeaders(); + // Update the name caption + LCaption* nameCaption = dynamic_cast( FindPaneByID( paneID_DirectoryName ) ); + if ( nameCaption ) + { + AB_ContainerAttribValue* value; + CAddressBookManager::FailAddressError( AB_GetContainerAttribute( + inContainer, attribName, &value) ); + nameCaption->SetDescriptor( CStr255(value->u.string ) ); + AB_FreeContainerAttribValue ( value ); + } +} + + +//------------------------------------------------------------------------------ +// ¥ Search +//------------------------------------------------------------------------------ +// handles the Search Button. Brings up a dialog and initiates a search +// +void CAddressBookController::Search() +{ + + if ( GetAddressBookTable()->CurrentBookIsPersonalBook() || + mAddressBookTable->IsLDAPSearching() ) return; + StDialogHandler handler(8980, nil); + + CLDAPQueryDialog* dialog = dynamic_cast< CLDAPQueryDialog*>( handler.GetDialog() ); + Assert_( dialog ); + AB_ContainerInfo* container = mAddressBookTable->GetContainer( 0 ) ; + + DIR_Server* server = AB_GetDirServerForContainer( container ); + dialog->Setup( mAddressBookTable->GetMessagePane(), server ); + + + Boolean doSearch = false; + // Run the dialog + MessageT message; + + do { + message = handler.DoDialog(); + } while (message != paneID_Search && message != msg_Cancel); + + if ( message == paneID_Search ) + { + CAddressBookManager::FailAddressError(AB_SearchDirectoryAB2( mAddressBookTable->GetMessagePane(), NULL)); + + USearchHelper::EnableDisablePane(mSearchButton, false,true); + USearchHelper::EnableDisablePane(mStopButton, true, true); + } +} + + +//------------------------------------------------------------------------------ +// ¥ StopSearch +//------------------------------------------------------------------------------ +// +void CAddressBookController::StopSearch() +{ + + if ( GetAddressBookTable()->CurrentBookIsPersonalBook() || + !mAddressBookTable->IsLDAPSearching() ) return; + AB_FinishSearchAB2( mAddressBookTable->GetMessagePane() ); + + USearchHelper::EnableDisablePane(mStopButton, false,true); + USearchHelper::EnableDisablePane(mSearchButton, true ,true); + USearchHelper::SelectEditField(mTypedownName); + Refresh(); +} + + +void CAddressBookController::FindCommandStatus(CommandT inCommand, Boolean &outEnabled, + Boolean &outUsesMark, Char16 &outMark, + Str255 outName) +{ + AB_CommandType abCommand = UAddressBookUtilites::GetABCommand( inCommand); + if ( abCommand != UAddressBookUtilites::invalid_command ) + { + CAddressBookPane* pane = mABContainerView; + if ( mAddressBookTable->IsTarget() ) + pane = mAddressBookTable; + + UAddressBookUtilites::ABCommandStatus( + pane, abCommand, outEnabled, outUsesMark, outMark, outName); + if( !outEnabled ) + { + CAddressBookPane* pane = mABContainerView; + if ( mABContainerView->IsTarget() ) + pane = mAddressBookTable; + UAddressBookUtilites::ABCommandStatus( + pane, abCommand, outEnabled, outUsesMark, outMark, outName); + } + } + else + { + switch ( inCommand ) + { + case cmd_Stop: + outEnabled = mAddressBookTable->IsLDAPSearching(); + break; + case UAddressBookUtilites::cmd_HTMLDomains: + outEnabled = true; + break; + + default: + LCommander::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); + break; + } + } +} + +#endif // NEWADDR diff --git a/mozilla/cmd/macfe/MailNews/AddressBook/CAddressBookViews.h b/mozilla/cmd/macfe/MailNews/AddressBook/CAddressBookViews.h new file mode 100644 index 00000000000..c0c618b62a7 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/AddressBook/CAddressBookViews.h @@ -0,0 +1,327 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +// CAddressBookViews.h + +#include "CMailFlexTable.h" + +#include "PascalString.h" +#include "abcom.h" +#include "dirprefs.h" + +class CSearchEditField; +class LDividedView; +class LGAPushButton; +class CPatternProgressCaption; + +// These should be replaced as there is a JS pref +enum { eDontCheckTypedown = 0xFFFFFFFF, eCheckTypedownInterval = 30 /* 1/2 sec */ , eNewTarget = 'NwTg'}; + +struct SAddressDelayedDragInfo; + +#pragma mark CAddressBookPane +//------------------------------------------------------------------------------ +// ¥ CAddressBookPane +//------------------------------------------------------------------------------ +// +class CAddressBookPane : public CMailFlexTable, public LPeriodical +{ +private: + typedef CMailFlexTable Inherited; + +public: + enum EColType + { // Sort column header ids + eTableHeaderBase = 'col0' + , eCol0 = eTableHeaderBase + , eCol1 + , eCol2 + , eCol3 + , eCol4 + , eCol5 + , eCol6 + }; + + enum + { // Command sort + cmd_SortAscending = 'Ascd' + , cmd_SortDescending = 'Dscd' + }; + + enum + { + eInvalidCachedRowIDType = 0x7FFFFFFF + , eNewEntryID = 0x7FFFFFFF + , eInvalidEntryID = 0 + }; + + enum + { // Icon resource IDs + ics8_Person = 15260 + , ics8_List = 15263 + }; + + CAddressBookPane(LStream *inStream); + + + UInt32 SortTypeFromColumnType(EColType inColType); + AB_CommandType SortCommandFromColumnType(EColType inColType); + + virtual Boolean ObeyCommand(CommandT inCommand, void *ioParam); + virtual void FindCommandStatus(CommandT inCommand, Boolean &outEnabled, + Boolean &outUsesMark, Char16 &outMark, + Str255 outName); + + virtual void DeleteSelection(); + virtual AB_ContainerInfo* GetContainer( TableIndexT /* inRow */ ) { return mContainer; } + Boolean CurrentBookIsPersonalBook(); + virtual void PaneChanged( MSG_Pane *inPane, MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, int32 value); + virtual Boolean CellInitiatesDrag(const STableCell &/*inCell*/) const { return true; } + virtual Boolean ItemIsAcceptable( DragReference inDragRef, ItemReference inItemRef ); +protected: + virtual void GetCellDisplayData(const STableCell &inCell, ResIDT &ioIcon, CStr255 &ioDisplayString ); + virtual void DestroyMessagePane(MSG_Pane* inPane); + virtual void SpendTime(const EventRecord &inMacEvent); + virtual Boolean CellHasDropFlag(const STableCell &/*inCell*/, Boolean &/*outIsExpanded*/) const { return false; } + virtual void DrawCellContents(const STableCell &inCell, const Rect &inLocalRect); + + + + AB_AttribID GetAttribForColumn( TableIndexT col ); + virtual ResIDT GetIconID(TableIndexT inRow) const; + void DrawCellText( const STableCell& inCell, const Rect& inLocalRect, AB_AttribID inAttrib ); + + + + virtual void OpenRow(TableIndexT inRow); + void SetCellExpansion( const STableCell& inCell, Boolean inExpanded); + + virtual Boolean RowCanAcceptDrop( + DragReference inDragRef, + TableIndexT inDropRow); + virtual Boolean RowCanAcceptDropBetweenAbove( + DragReference inDragRef, + TableIndexT inDropRow); + virtual void ReceiveDragItem( + DragReference inDragRef, + DragAttributes inDragAttrs, + ItemReference inItemRef, + Rect& inItemBounds); +protected: + AB_ContainerInfo* mContainer; + AB_CommandType mColumnSortCommand[7]; + AB_AttribID mColumnAttribID[7]; + // Delayed drag info + SAddressDelayedDragInfo *mDelayedDragInfo; +}; + +#pragma mark CAddressBookTableView +//------------------------------------------------------------------------------ +// ¥ CAddressBookTableView +//------------------------------------------------------------------------------ +// +class CAddressBookTableView : public CAddressBookPane +{ +private: + typedef CAddressBookPane Inherited; + +public: + enum { class_ID = 'AbTb' }; + enum { eTableEditField = 'TbEd' }; + + CAddressBookTableView(LStream *inStream) : + CAddressBookPane(inStream),mIsLDAPSearching ( false ) { + + }; + + virtual ~CAddressBookTableView(); + + Boolean LoadAddressBook( AB_ContainerInfo* inContainer, MWContext* inContext ); + void SetColumnHeaders(); + virtual void ChangeSort(const LTableHeader::SortChange *inSortChange); + void UpdateToTypedownText(CStr255 inTypedownText); + void ConferenceCall( ); + void SetLDAPSearching( Boolean inIsSearching ) { mIsLDAPSearching = inIsSearching; } + char* GetFullAddress( TableIndexT inRow ); + Boolean IsLDAPSearching() const { return mIsLDAPSearching; } +protected: + + virtual void ChangeFinished(MSG_Pane *inPane, MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, SInt32 inRowCount); + virtual void AddRowDataToDrag(TableIndexT inRow, DragReference inDragRef); + virtual void EnableStopButton(Boolean inBusy); +private: + Boolean mIsLDAPSearching; +}; + + +#pragma mark CMailingListTableView +//------------------------------------------------------------------------------ +// ¥ CMailingListTableView +//------------------------------------------------------------------------------ +// +class CMailingListTableView : public CAddressBookPane +{ + +private: + typedef CAddressBookPane Inherited; + +public: + enum { class_ID = 'AlTb' }; + + enum { // Broadcast messages + msg_EntriesAddedToList = 'EnAd' // this + , msg_EntriesRemovedFromList ='EnRe' + }; + + CMailingListTableView(LStream *inStream) : + CAddressBookPane(inStream) { + + }; + + virtual ~CMailingListTableView(); + void LoadMailingList( MSG_Pane* inPane ); + virtual AB_ContainerInfo* GetContainer( TableIndexT inRow ); + +protected: + virtual void GetCellDisplayData(const STableCell &inCell, ResIDT& ioIcon, CStr255 &ioDisplayString ); + virtual void DestroyMessagePane(MSG_Pane* inPane); +}; + +#pragma mark CAddressBookContainerView +//------------------------------------------------------------------------------ +// ¥ CAddressBookContainerView +//------------------------------------------------------------------------------ +// +class CAddressBookContainerView : public CAddressBookPane +{ +private: + typedef CAddressBookPane Inherited; + +public: + enum { class_ID = 'AcTb' }; + + // Place holders while I wait for the final art work + enum { + ics8_LDAP = CAddressBookPane::ics8_Person , + ics8_List = CAddressBookPane::ics8_List , + ics8_PAB = CAddressBookPane::ics8_Person + }; + + CAddressBookContainerView(LStream *inStream); + virtual ~CAddressBookContainerView(); + + void Setup( MWContext* inContext); + virtual AB_ContainerInfo* GetContainer( TableIndexT inRow ); + void SetIndexToSelectOnLoad( Int32 index){ mDirectoryRowToLoad = index; } +protected: + virtual Boolean CellHasDropFlag( const STableCell& inCell, Boolean& outIsExpanded) const; + virtual UInt16 GetNestedLevel(TableIndexT inRow) const; + virtual ResIDT GetIconID(TableIndexT inRow) const; + + void DrawCellText( const STableCell& inCell, const Rect& inLocalRect ); + virtual void DrawCellContents( const STableCell& inCell, const Rect& inLocalRect ); + virtual void DrawIconsSelf( const STableCell& inCell, IconTransformType inTransformType, const Rect& inIconRect) const; +private: + MWContext* mContext; + Int32 mDirectoryRowToLoad; + +}; + + +#pragma mark CAddressBookController +//------------------------------------------------------------------------------ +// ¥ CAddressBookController +//------------------------------------------------------------------------------ +// +class CAddressBookController:public LView, + public LListener, public LPeriodical, public LCommander +{ +private: + typedef LView Inherited; + +public: + enum { class_ID = 'AbCn' }; + enum { + paneID_DirServers = 'DRSR' // CDirServersPopupMenu *, this + , paneID_Search = 'SRCH' // MSG_Pane *, search button + , paneID_Stop = 'STOP' // nil, stop button + , paneID_AddressBookTable = 'Tabl' // Address book table + , paneID_TypedownName = 'TYPE' // Typedown name search edit field in window + , paneID_SearchEnclosure = 'SCHE' // Enclosure for search items + , paneID_DividedView = 'A2Vw' // the divided view + , paneID_ContainerView = 'AcTb', + paneID_DirectoryName = 'DiCp' // Directory caption + }; + + + CAddressBookController(LStream* inStream ); + ~CAddressBookController(); + + virtual void ReadStatus(LStream *inStatusData); + virtual void WriteStatus(LStream *outStatusData); + CAddressBookTableView* GetAddressBookTable() const { return mAddressBookTable; } + CAddressBookContainerView* GetAddressBookContainerView() const { return mABContainerView; } + virtual Boolean HandleKeyPress(const EventRecord &inKeyEvent); + virtual Boolean ObeyCommand(CommandT inCommand, void *ioParam); + virtual void FindCommandStatus(CommandT inCommand, Boolean &outEnabled, + Boolean &outUsesMark, Char16 &outMark, + Str255 outName); +protected: + + virtual void FinishCreateSelf(); + virtual void ListenToMessage(MessageT inMessage, void *ioParam); + + virtual void SpendTime(const EventRecord &inMacEvent); + virtual void ActivateSelf() { + StartRepeating(); + Inherited::ActivateSelf(); + } + + virtual void DeactivateSelf() { + StopRepeating(); + Inherited::DeactivateSelf(); + } +public: + + void SelectDirectoryServer( AB_ContainerInfo* inContainer ); + void SetContext( MWContext* inContext) { mContext = inContext; } + +protected: + void Search(); + void StopSearch( ); + +protected: + MWContext* mContext; + UInt32 mNextTypedownCheckTime; + + CSearchEditField* mTypedownName; + LDividedView* mDividedView; + CAddressBookTableView* mAddressBookTable; + CMailNewsContext* mAddressBookContext; + + CAddressBookContainerView* mABContainerView; + + LGAPushButton* mSearchButton; + LGAPushButton* mStopButton; + + +}; + + diff --git a/mozilla/cmd/macfe/MailNews/AddressBook/CAddressBookWindows.cp b/mozilla/cmd/macfe/MailNews/AddressBook/CAddressBookWindows.cp new file mode 100644 index 00000000000..fba9485e270 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/AddressBook/CAddressBookWindows.cp @@ -0,0 +1,1110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + +// CAddressBookWindows.cp + +/*====================================================================================*/ + #pragma mark INCLUDE FILES +/*====================================================================================*/ +#define DEBUGGER_ASSERTIONS + + + +#include "CAddressBookWindows.h" +#ifdef MOZ_NEWADDR + +#include "SearchHelpers.h" +#include "CSizeBox.h" +#include "MailNewsgroupWindow_Defines.h" +#include "Netscape_Constants.h" +#include "resgui.h" +#include "CMailNewsWindow.h" +#include "UGraphicGizmos.h" +#include "uprefd.h" +#include "ufilemgr.h" +#include "uerrmgr.h" +#include "CGATabBox.h" +#include "URobustCreateWindow.h" +#include "UStdDialogs.h" +#include "macutil.h" +#include "UStClasses.h" +#include +#include +#include +#include "CTargetFramer.h" +// get string constants +#define WANT_ENUM_STRING_IDS +#include "allxpstr.h" +#undef WANT_ENUM_STRING_IDS + +#include "dirprefs.h" +#include "prefapi.h" +#include "UProcessUtils.h" +#include "CAppleEventHandler.h" +#include "secnav.h" +#include "CTableKeyAttachment.h" +#include "intl_csi.h" +#include "xp_help.h" +#include "CAddressBookViews.h" +#include "CLDAPQueryDialog.h" +// #include "CAddressPickerWindow.h" +#include "MailNewsMediators.h" +#include "CABContainerDialogs.h" +class CNamePropertiesWindow; +class CListPropertiesWindow; + +const ResIDT kConferenceExampleStr = 8901; +const ResIDT kAddressbookErrorStrings = 8902; + +// Junk to allow linking. When libaddr is update these should be removed +#include "abdefn.h" +#include "addrbook.h" +typedef struct ABook ABook; +ABook *FE_GetAddressBook(MSG_Pane * /*pane*/) { + + return NULL; +} + + +int FE_ShowPropertySheetFor (MWContext* /* context */, ABID /* entryID */, PersonEntry* /* pPerson */) +{ + + return 0; +} + // End of obsolete + +#pragma mark - + +const Int32 kNumPersonAttributes = 22; +const Int32 kMaxPersonSize = 4096; //Really should recalc this + + +#pragma mark - +//------------------------------------------------------------------------------ +// ¥ XP Callbacks +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// ¥ FE_GetDirServers +//------------------------------------------------------------------------------ +// +XP_List *FE_GetDirServers() { + return CAddressBookManager::GetDirServerList(); +} + + +//------------------------------------------------------------------------------ +// ¥ FE_GetAddressBookContext +//------------------------------------------------------------------------------ +// Returns the address book context, creating it if necessary. A mail pane is passed in, +// on the unlikely chance that it might be useful for the FE. If "viewnow" is TRUE, +// then present the address book window to the user; otherwise, don't (unless it is +// already visible). +// +MWContext *FE_GetAddressBookContext(MSG_Pane * /*pane*/, XP_Bool viewnow) { + + if ( viewnow ) { + CAddressBookManager::ShowAddressBookWindow(); + } + + return CAddressBookWindow::GetMailContext(); +} + +//------------------------------------------------------------------------------ +// ¥ FE_ShowPropertySheeetForAB2 +//------------------------------------------------------------------------------ +// The XP layer is requesting that the FE show a property window +// return AB_SUCCESS if the window was brought up +// return AB_Failure if the window couldn't be brought up +// +int MacFe_ShowModelessPropertySheetForAB2( MSG_Pane * pane, MWContext* /* inContext */) +{ + // Get the Resource Id for the property window + ResID resId = 0; + + MSG_PaneType type = MSG_GetPaneType( pane ); + switch( type ) + { + case AB_MAILINGLISTPANE: + resId = CListPropertiesWindow::res_ID; + break; + + case AB_PERSONENTRYPANE: + resId = CNamePropertiesWindow::res_ID; + break; + + default: + assert( 0 ); + return AB_FAILURE; + } + + // Create the new property windows. + try + { + + CAddressBookWindow *addressWindow = dynamic_cast( + CWindowMediator::GetWindowMediator()->FetchTopWindow(WindowType_Address)); + AssertFail_(addressWindow != nil); // Must be around to create name properties window + + LWindow* window = URobustCreateWindow::CreateWindow( resId, addressWindow->GetSuperCommander() ); + CAddressBookChildWindow* propertiesWindow = dynamic_cast( window ); + + propertiesWindow->UpdateUIToBackend( pane ); + propertiesWindow->Show(); + + return AB_SUCCESS; + } catch( ... ) + { + + } + return AB_FAILURE; +} + +int MacFE_ShowPropertySheetForDir( + DIR_Server* server, MWContext* /* context */, MSG_Pane * srcPane, XP_Bool /* newDirectory */) +{ + Boolean isPAB = PABDirectory == server->dirType; + if( isPAB ) + { + CPABPropertyDialogManager dialogManager( server, srcPane ); + } + else + { + CLDAPPropertyDialogManager dialogManager( server, srcPane ); + } + return AB_SUCCESS; +} + + +#pragma mark - +/*====================================================================================*/ + #pragma mark CLASS IMPLEMENTATIONS +/*====================================================================================*/ + +XP_List *CAddressBookManager::sDirServerList = nil; +Boolean CAddressBookManager::sDirServerListChanged = false; + + +//------------------------------------------------------------------------------ +// ¥ OpenAddressBookManager +//------------------------------------------------------------------------------ +// Open the address book at application startup. This method sets the initial address +// book to the local personal address book for the user (creates one if it doesn't +// exist already). +// I think this whole function is obsolete +// as we are no longer using AB_InitializeAddressBook +void CAddressBookManager::OpenAddressBookManager() { + + AssertFail_(sDirServerList == nil); + + RegisterAddressBookClasses(); + + char fileName[64]; + fileName[63] = '\0'; + char *oldName = nil; + Try_ { + // Get the default address book path + FSSpec spec = CPrefs::GetFilePrototype(CPrefs::MainFolder); + // The real, new way! + CStr255 pfilename; + ::GetIndString(pfilename, 300, addressBookFile); // Get address book name from prefs + strncpy( fileName,(char*)pfilename, 63) ; + // Create directory servers + Int32 error = DIR_GetServerPreferences(&sDirServerList, fileName); + // No listed return error codes, who knows what they are! + FailOSErr_(error); + AssertFail_(sDirServerList != nil); + + // Get the Akbar (v3.0) address book. It's in HTML format (addressbook.html) + spec = CPrefs::GetFilePrototype(CPrefs::MainFolder); + ::GetIndString(spec.name, 300, htmlAddressBook); // Get address book name from prefs + oldName = CFileMgr::GetURLFromFileSpec(spec); + FailNIL_(oldName); + #if 0 + CAddressBookManager::FailAddressError( + AB_InitializeAddressBook(GetPersonalBook(), &sAddressBook, + oldName + XP_STRLEN("file://")) + ); + #endif // OBsolete? + XP_FREE(oldName); + } + Catch_(inErr) { + CAddressBookManager::CloseAddressBookManager(); + XP_FREEIF(oldName); + } + EndCatch_ + + //PREF_RegisterCallback("ldap_1", DirServerListChanged, NULL); +} + + + +//------------------------------------------------------------------------------ +// ¥ CloseAddressBookManager +//------------------------------------------------------------------------------ +// Closes any open resources used by the address book manager. +// +void CAddressBookManager::CloseAddressBookManager() +{ + DIR_ShutDown(); +} + +//------------------------------------------------------------------------------ +// ¥ ImportLDIF +//------------------------------------------------------------------------------ +// We come here in response to an odoc event with an LDIF file. +// Currently no error result. Assuming the back end will put up an alert. +// +void CAddressBookManager::ImportLDIF(const FSSpec& inFileSpec) +{ + char* path = CFileMgr::EncodedPathNameFromFSSpec( + inFileSpec, true /*wantLeafName*/); + if (path) + { + int result = AB_ImportData( + nil, // no container, create new + path, + strlen(path), + AB_Filename // FIX ME: need bit that tells the BE to delete the file. + ); + if (result != AB_SUCCESS) + SysBeep(1); + XP_FREE(path); + } +} + + +//------------------------------------------------------------------------------ +// ¥ ShowAddressBookWindow +//------------------------------------------------------------------------------ +// Show the search dialog by bringing it to the front if it is not already. Create it +// if needed. +// +CAddressBookWindow * CAddressBookManager::ShowAddressBookWindow() +{ + + // Find out if the window is already around + CAddressBookWindow *addressWindow = dynamic_cast( + CWindowMediator::GetWindowMediator()->FetchTopWindow(WindowType_Address)); + + if ( addressWindow == nil ) + { + // Search dialog has not yet been created, so create it here and display it. + addressWindow = dynamic_cast( + URobustCreateWindow::CreateWindow(CAddressBookWindow::res_ID, + LCommander::GetTopCommander())); + AssertFail_(addressWindow != nil); + } + + addressWindow->Show(); + addressWindow->Select(); + return addressWindow; +} + + +//------------------------------------------------------------------------------ +// ¥ GetDirServerList +//------------------------------------------------------------------------------ +// Return the list of directory servers for the application. Why are these +// method and variable here? Can also call the BE method FE_GetDirServers() to return +// the same result. +// +XP_List *CAddressBookManager::GetDirServerList() +{ + return DIR_GetDirServers(); +} + + +//------------------------------------------------------------------------------ +// ¥ SetDirServerList +//------------------------------------------------------------------------------ +// This method should be called to set the current directory servers list. The old list +// is destroyed and is replaced with inList; the caller does NOT dispose of inList, since +// it is managed by this class after calling this method. This method also calls the +// BE method DIR_SaveServerPreferences() if inSavePrefs is true. +// +void CAddressBookManager::SetDirServerList(XP_List *inList, Boolean inSavePrefs) +{ + + AssertFail_(inList != nil); + + XP_List *tempDirServerList = sDirServerList; + sDirServerList = inList; // This needs to be set correctly for the callback to work. + if ( inSavePrefs ) { + Int32 error = DIR_SaveServerPreferences(inList); // No listed return error codes, + // who knows what they are! + if (error) + { + sDirServerList = tempDirServerList; // Put this back if there are problems. + } + FailOSErr_(error); + } + + if ( tempDirServerList != nil ) { + Int32 error = DIR_DeleteServerList(tempDirServerList); + Assert_(!error); + } +} + + +//------------------------------------------------------------------------------ +// ¥ GetPersonalBook +//------------------------------------------------------------------------------ +// Return the local personal address book. +// +DIR_Server *CAddressBookManager::GetPersonalBook() +{ + AssertFail_((sDirServerList != nil)); + DIR_Server *personalBook = nil; + DIR_GetPersonalAddressBook(sDirServerList, &personalBook); + AssertFail_(personalBook != nil); + return personalBook; +} + + + +//------------------------------------------------------------------------------ +// ¥ FailAddressError +//------------------------------------------------------------------------------ +// +void CAddressBookManager::FailAddressError(Int32 inError) +{ + if ( inError == AB_SUCCESS ) + return; + switch ( inError ) + { + case AB_FAILURE: + // Should be throwing + break; + case MK_MSG_NEED_FULLNAME: + case MK_MSG_NEED_GIVENNAME: + Throw_( MK_MSG_NEED_FULLNAME ); + break; + case MK_OUT_OF_MEMORY: + Throw_(memFullErr); + break; + case MK_ADDR_LIST_ALREADY_EXISTS: + case MK_ADDR_ENTRY_ALREADY_EXISTS: + Throw_( inError ); + break; + case MK_UNABLE_TO_OPEN_FILE: + case XP_BKMKS_CANT_WRITE_ADDRESSBOOK: + Throw_(ioErr); + break; + case XP_BKMKS_NICKNAME_ALREADY_EXISTS: + Throw_(XP_BKMKS_INVALID_NICKNAME); + break; + default: + Assert_(false); + Throw_(32000); // Who knows? + break; + } +} + + +//------------------------------------------------------------------------------ +// ¥ RegisterAddressBookClasses +//------------------------------------------------------------------------------ +// Register all classes associated with the address book window. +// +void CAddressBookManager::RegisterAddressBookClasses() { + + RegisterClass_(CAddressBookWindow); + RegisterClass_(CAddressBookTableView); + RegisterClass_(CMailingListTableView); + RegisterClass_(CSearchPopupMenu); + + RegisterClass_(CNamePropertiesWindow); + RegisterClass_(CListPropertiesWindow); + + RegisterClass_(CAddressBookController); + RegisterClass_(CLDAPQueryDialog); + RegisterClass_(CAddressBookContainerView); + CGATabBox::RegisterTabBoxClasses(); +} + + +#pragma mark - + +//------------------------------------------------------------------------------ +// ¥ ~CAddressBookWindow +//------------------------------------------------------------------------------ +// +// +CAddressBookWindow::~CAddressBookWindow() +{ + +} + + +//------------------------------------------------------------------------------ +// ¥ GetMailContext +//------------------------------------------------------------------------------ +// Get the mail context for the address book. We must have an address book window to +// access the context, so this method creates the window (but doesn't show it) if the +// window is not yet around. +// +MWContext *CAddressBookWindow::GetMailContext() +{ + CAddressBookWindow *addressWindow = dynamic_cast( + CWindowMediator::GetWindowMediator()->FetchTopWindow(WindowType_Address)); + + if ( addressWindow == nil ) { + // AddressBookWindow dialog has not yet been created, so create it here + addressWindow = dynamic_cast( + URobustCreateWindow::CreateWindow(CAddressBookWindow::res_ID, + LCommander::GetTopCommander())); + AssertFail_(addressWindow != nil); + } + + return *(addressWindow->GetWindowContext()); +} + + +//------------------------------------------------------------------------------ +// ¥ FinishCreateSelf +//------------------------------------------------------------------------------ +// +void CAddressBookWindow::FinishCreateSelf() +{ + mAddressBookController = + dynamic_cast< CAddressBookController* >(FindPaneByID( paneID_AddressBookController) ) ; + FailNILRes_(mAddressBookController); + + // Create the context + Inherited::FinishCreateSelf(); + mMailNewsContext->SetWinCSID(INTL_DocToWinCharSetID(mMailNewsContext->GetDefaultCSID())); + + // Have the AddressBook Controller listen to the toolbar and context + UReanimator::LinkListenerToControls(mAddressBookController, this, res_ID); + mMailNewsContext->AddListener(mAddressBookController); + mAddressBookController->SetContext( *mMailNewsContext ); + mAddressBookController->GetAddressBookContainerView()->Setup( *mMailNewsContext ); + + // Stop is in the toolbar. Could do this in constructor but then would have to duplicate the container + LGAPushButton* stopButton = dynamic_cast(FindPaneByID( paneID_Stop)); + if ( stopButton ) + USearchHelper::ShowHidePane(stopButton, false); + + // Update the window title + CStr255 windowTitle; + GetUserWindowTitle(4, windowTitle); + SetDescriptor(windowTitle); +} + + +//------------------------------------------------------------------------------ +// ¥ CreateContext +//------------------------------------------------------------------------------ +// +CNSContext* CAddressBookWindow::CreateContext() const +{ + CNSContext* result = new CNSContext( MWContextAddressBook); + FailNIL_(result); + return result; +} + + +//------------------------------------------------------------------------------ +// ¥ GetActiveTable +//------------------------------------------------------------------------------ +// The active table is the one with the current keyboard focus. If the keyboard focus is in +// the editfield, the AddressBookTable has focus +// +CMailFlexTable* CAddressBookWindow::GetActiveTable() +{ + CMailFlexTable* result = nil; + Assert_( mAddressBookController ); + result = dynamic_cast( mAddressBookController->GetAddressBookContainerView() ); + if (! (result && result->IsOnDuty() ) ) + result = dynamic_cast( mAddressBookController->GetAddressBookTable() ); + return result; +} + + +//------------------------------------------------------------------------------ +// ¥ ReadWindowStatus +//------------------------------------------------------------------------------ +// Adjust the window to stored preferences. +// +void CAddressBookWindow::ReadWindowStatus(LStream *inStatusData) +{ + Inherited::ReadWindowStatus(inStatusData); + if ( inStatusData != nil ) + { + mAddressBookController->ReadStatus( inStatusData ); + } + +} + + +//------------------------------------------------------------------------------ +// ¥ WriteWindowStatus +//------------------------------------------------------------------------------ +// Write window stored preferences. +// +void CAddressBookWindow::WriteWindowStatus(LStream *outStatusData) +{ + Inherited::WriteWindowStatus(outStatusData); + mAddressBookController->WriteStatus(outStatusData); +} + + +#pragma mark - +//------------------------------------------------------------------------------ +// ¥ ListenToMessage +//------------------------------------------------------------------------------ +// +void CAddressBookChildWindow::ListenToMessage(MessageT inMessage, void *ioParam) +{ + switch ( inMessage ) { + case CSearchEditField::msg_UserChangedText: + if ( ioParam ) + { + switch( (*( (PaneIDT *) ioParam) ) ) + { + case CListPropertiesWindow::paneID_Name: + case CNamePropertiesWindow::paneID_FirstName: + case CNamePropertiesWindow::paneID_LastName: + UpdateTitle(); + break; + default: + break; + + } + } + break; + case msg_OK: + Try_{ + UpdateBackendToUI( ); + + AB_ClosePane( mMSGPane ); + DoClose(); + } Catch_ ( ioParam ) + { + ResIDT stringID = 0; + switch( ioParam ) + { + case XP_BKMKS_INVALID_NICKNAME: + stringID = 4; + break; + case MK_MSG_NEED_FULLNAME: + stringID = 3; + break; + case MK_ADDR_LIST_ALREADY_EXISTS: + stringID = 2; + break; + case MK_ADDR_ENTRY_ALREADY_EXISTS: + stringID = 1; + break; + default: + Throw_( ioParam ); + } + LStr255 errorString( kAddressbookErrorStrings, stringID ); + UStdDialogs::Alert( errorString, eAlertTypeCaution ); + } + break; + + case msg_Cancel: + AB_ClosePane( mMSGPane ); + DoClose(); + break; + + default: + Inherited::ListenToMessage( inMessage, ioParam ); + break; + } +} + + +#pragma mark - +//------------------------------------------------------------------------------ +// ¥ PaneChanged +//------------------------------------------------------------------------------ +// +void CMailWindowCallbackListener::PaneChanged( MSG_Pane* /* inPane */, MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, int32 /* value */) +{ + switch (inNotifyCode) + { + case MSG_PaneClose: + mWindow->DoClose(); + break; + default: + break; + } +} + +#pragma mark - +//------------------------------------------------------------------------------ +// ¥ CNamePropertiesWindow +//------------------------------------------------------------------------------ +// +// +CNamePropertiesWindow::CNamePropertiesWindow(LStream *inStream) : + CAddressBookChildWindow(inStream), + mFirstNameField(nil), mLastNameField(nil) +{ +} + + +//------------------------------------------------------------------------------ +// ¥ FinishCreateSelf +//------------------------------------------------------------------------------ +// +// +void CNamePropertiesWindow::FinishCreateSelf() +{ + + mFirstNameField = USearchHelper::FindViewEditField(this, paneID_FirstName); + mLastNameField = USearchHelper::FindViewEditField(this, paneID_LastName); + + Inherited::FinishCreateSelf(); + + UReanimator::LinkListenerToControls(this, this, res_ID ); + + // need to listen to first/last name fields to update title + USearchHelper::LinkListenerToBroadcasters(USearchHelper::FindViewSubview(this, paneID_GeneralView), this); + + // Need Cooltalk since we listen to the popup + USearchHelper::LinkListenerToBroadcasters(USearchHelper::FindViewSubview(this, paneID_CooltalkView), this); + +} + + + +//------------------------------------------------------------------------------ +// ¥ ListenToMessage +//------------------------------------------------------------------------------ +// +void CNamePropertiesWindow::ListenToMessage(MessageT inMessage, void *ioParam) +{ + switch ( inMessage ) + { + case paneID_ConferencePopup: + SetConferenceText(); + break; + + default: + Inherited::ListenToMessage(inMessage, ioParam); + break; + } +} + + +//------------------------------------------------------------------------------ +// ¥ SetConferenceText +//------------------------------------------------------------------------------ +// Update Caption text in Conference +// The edit field is disable if Netscape DLS server is selected +// +void CNamePropertiesWindow::SetConferenceText( ) +{ + short serverType = USearchHelper::FindSearchPopup( this, paneID_ConferenceServer )->GetValue(); + LStr255 exampleString(kConferenceExampleStr, serverType ); + LCaption* text = dynamic_cast(this->FindPaneByID('CoES')); + Assert_(text); + if( text ) + text->SetDescriptor( exampleString); + LView *view = (LView*)USearchHelper::FindViewEditField( this, paneID_ConferenceAddress); + if( view ) + { + if( serverType == 1 ) + { + view->SetDescriptor("\p"); + view->Disable(); + } + else + view->Enable(); + } +} + + + + +//------------------------------------------------------------------------------ +// ¥ GetPaneAndAttribID +//------------------------------------------------------------------------------ +// Given a Index return the associated Pane and attribute id's. Also returns the +// number of attributes +// +int32 CNamePropertiesWindow::GetPaneAndAttribID( TableIndexT index, PaneIDT& outPaneID, AB_AttribID &outAttrib ) +{ + Int32 personAttributes[][2] = + { + paneID_Nickname , AB_attribNickName + , paneID_FirstName , AB_attribGivenName + , paneID_LastName , AB_attribFamilyName + , paneID_Company , AB_attribCompanyName + , paneID_State , AB_attribRegion + , paneID_EMail , AB_attribEmailAddress + , paneID_Notes , AB_attribInfo + , paneID_PrefersHTML , AB_attribHTMLMail + , paneID_Title , AB_attribTitle + , paneID_Address , AB_attribStreetAddress + , paneID_ZIP , AB_attribZipCode + , paneID_Country , AB_attribCountry + , paneID_WorkPhone , AB_attribWorkPhone + , paneID_FaxPhone , AB_attribFaxPhone + , paneID_HomePhone , AB_attribHomePhone + , paneID_ConferenceAddress, AB_attribCoolAddress + , paneID_ConferenceServer, AB_attribUseServer + , paneID_PagerPhone , AB_attribPager + , paneID_DisplayName , AB_attribDisplayName + , paneID_CelluarPhone , AB_attribCellularPhone + , paneID_Department, AB_attribDistName + }; + outPaneID = paneID_None; + Int32 numEntries = ( sizeof( personAttributes )/ 8 ); + if ( index < numEntries ) + { + outPaneID = PaneIDT( personAttributes[index][0] ); + outAttrib = AB_AttribID( personAttributes[index][1] ); + } + + return numEntries; +}; + +//------------------------------------------------------------------------------ +// ¥ FindPaneForAttribute +//------------------------------------------------------------------------------ +// Given a Index return the associated Pane and attribute id's. Also returns the +// number of attributes +// +PaneIDT CNamePropertiesWindow::FindPaneForAttribute ( AB_AttribID inAttrib ) +{ + Int32 numEntries = 0; + PaneIDT pane = 0; + AB_AttribID attrib; + numEntries = GetPaneAndAttribID ( 0, pane, attrib ); + for ( int32 i = 0 ; i< numEntries ; i++ ) + { + numEntries = GetPaneAndAttribID ( i, pane, attrib ); + if ( attrib == inAttrib ) + return pane; + } + return paneID_None; +} + + +//------------------------------------------------------------------------------ +// ¥ UpdateBackendToUI +//------------------------------------------------------------------------------ +// Update the name properties to the current values in the dialog fields and other settings. +// +void CNamePropertiesWindow::UpdateBackendToUI( ) +{ + char charData[ kMaxPersonSize ]; + Int32 currentTextOffset = 0; + AB_AttribID attribID ; + PaneIDT paneID = paneID_None; + int32 numControls = kNumPersonAttributes - 1 ; + + AB_AttributeValue* attribute = new AB_AttributeValue[ kNumPersonAttributes]; + + // Convert fill array with control values + for ( Int32 i = 0; i< numControls; i++ ) + { + GetPaneAndAttribID ( i, paneID, attribID ); + + attribute[ i ].attrib = attribID; + + LPane* control = FindPaneByID( paneID ); + if ( control ) + { + CSearchEditField* editField = NULL; + LGAPopup* popup = NULL; + LGACheckbox* checkbox = NULL; + if( ( editField = dynamic_cast< CSearchEditField* >( control ) ) != 0) + { + editField->GetText( &charData[ currentTextOffset] ); + Int32 length = XP_STRLEN( &charData[ currentTextOffset] ); + + Assert_( length + currentTextOffset < kMaxPersonSize ); + attribute[i].u.string = &charData[ currentTextOffset ]; + currentTextOffset+= length+1 ; + + } + else if( ( popup = dynamic_cast< LGAPopup* >( control ) ) != 0) + { + attribute[i].u.shortValue = popup->GetValue(); + } + else if ( ( checkbox = dynamic_cast< LGACheckbox *>(control ) )!= 0 ) + { + attribute[i].u.boolValue = checkbox->GetValue(); + } + } + } + + // WinCSID -- I love special cases. + + MWContext* context= CAddressBookWindow::GetMailContext(); + INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(context); + attribute[ numControls ].attrib = AB_attribWinCSID; + attribute [ numControls ].u.shortValue = INTL_GetCSIWinCSID(c); + + // Commit Changes + CAddressBookManager::FailAddressError( AB_SetPersonEntryAttributes ( mMSGPane, attribute, kNumPersonAttributes ) ); + CAddressBookManager::FailAddressError( AB_CommitChanges( mMSGPane ) ); + + delete [] attribute; +} + + +//------------------------------------------------------------------------------ +// ¥ UpdateUIToBackend +//------------------------------------------------------------------------------ +// Update the dialog fields to the current values of the name properties. +// +void CNamePropertiesWindow::UpdateUIToBackend( MSG_Pane* inPane ) +{ + Assert_( inPane ); + mMSGPane = inPane; + MSG_SetFEData( inPane, CMailCallbackManager::Get()); + + // Get the person Attribute + AB_AttribID personAttribs [ kNumPersonAttributes ]; + int32 numPanes = 0; + AB_AttribID attrib; + PaneIDT paneID = 0; + numPanes = GetPaneAndAttribID( 0, paneID, attrib ); + Assert_( numPanes <= kNumPersonAttributes ); + + for( int32 i = 0; i< numPanes; i++ ) + { + GetPaneAndAttribID( i, paneID, attrib ); + personAttribs[ i ] = attrib; + } + uint16 numberAttributes = numPanes; + + // Allocate storage for the attribute values + AB_AttributeValue* attribute; + CAddressBookManager::FailAddressError (AB_GetPersonEntryAttributes ( mMSGPane, personAttribs, &attribute, &numberAttributes ) ); + + for ( Int32 i = 0; i < numberAttributes; i++ ) + { + PaneIDT pane = FindPaneForAttribute( attribute[i].attrib ); + if( pane != paneID_None ) + { + switch( attribute[i].attrib ) + { + case AB_attribUseServer: + USearchHelper::FindSearchPopup( this, pane )->SetValue( attribute[i].u.shortValue + 1 ); // Popups are 1 based + break; + + case AB_attribHTMLMail: + USearchHelper::FindViewControl( this , pane )->SetValue( attribute[i].u.boolValue); + break; + + case AB_attribWinCSID: + break; + + default: + CSearchEditField* editfield = dynamic_cast( FindPaneByID( pane ) ); + if( editfield ) + editfield->SetText ( attribute[i].u.string ); + break; + }; + } + } + AB_FreeEntryAttributeValues( attribute , numberAttributes); + + SetConferenceText( ); +} + + +//------------------------------------------------------------------------------ +// ¥ UpdateTitle +//------------------------------------------------------------------------------ +// Update the window title. +// +void CNamePropertiesWindow::UpdateTitle() { + + CStr255 title; + { + CStr255 first, last; + mFirstNameField->GetDescriptor(first); + mLastNameField->GetDescriptor(last); + if ( last[0] ) first += " "; + first += last; + + ::GetIndString( title, 8903, 1); + ::StringParamText( title, first); + } + SetDescriptor( title ); +} + + +#pragma mark - + + +//------------------------------------------------------------------------------ +// ¥ CListPropertiesWindow +//------------------------------------------------------------------------------ +// Construct the list window. +// +CListPropertiesWindow::CListPropertiesWindow(LStream *inStream) : + CAddressBookChildWindow(inStream), + mAddressBookListTable(nil), + mTitleField(nil) +{ + SetPaneID(pane_ID); +} + + +//------------------------------------------------------------------------------ +// ¥ ~CListPropertiesWindow +//------------------------------------------------------------------------------ +// Dispose of the list window. +// +inline CListPropertiesWindow::~CListPropertiesWindow() +{ + + mAddressBookListTable = nil; +} + + +//------------------------------------------------------------------------------ +// ¥ UpdateUIToBackend +//------------------------------------------------------------------------------ +// Load and display the specified address book mailing listlist +// +void CListPropertiesWindow::UpdateUIToBackend(MSG_Pane* inPane) +{ + AssertFail_(mAddressBookListTable != nil); + Assert_( inPane ); + mMSGPane = inPane; + + mAddressBookListTable->LoadMailingList( inPane ); + + // Set edit fields + AB_AttribID mailingListAttributes[] = { AB_attribFullName, AB_attribNickName, AB_attribInfo }; + uint16 numItems = 3; + // Can't use New since XP code will be freeing the values + AB_AttributeValue* mailingListValues =( AB_AttributeValue*) XP_ALLOC( sizeof( AB_AttributeValue) * numItems ); + if( !mailingListValues ) + Throw_(memFullErr); + CAddressBookManager::FailAddressError( + AB_GetMailingListAttributes( mMSGPane, mailingListAttributes, &mailingListValues, &numItems ) ); + + mTitleField->SetText( mailingListValues[0].u.string ); + dynamic_cast< CSearchEditField *>( USearchHelper::FindViewEditField(this, paneID_Nickname) )->SetText( mailingListValues[1].u.string ); + dynamic_cast< CSearchEditField *>( USearchHelper::FindViewEditField(this, paneID_Description) )->SetText( mailingListValues[2].u.string ); + CAddressBookManager::FailAddressError(AB_FreeEntryAttributeValues( mailingListValues, 3 ) ); + UpdateTitle(); +} + + +//------------------------------------------------------------------------------ +// ¥ UpdateBackendToUI +//------------------------------------------------------------------------------ +// Update the list properties to the current values in the dialog fields. +// +void CListPropertiesWindow::UpdateBackendToUI( ) +{ + char mValue[ kMaxPersonSize ]; + AB_AttributeValue mailingListValues[ 4 ]; + + mailingListValues[0].attrib = AB_attribFullName; + mailingListValues[0].u.string= mTitleField->GetText( &mValue[0] ); + + mailingListValues[1].attrib = AB_attribNickName; + mailingListValues[1].u.string = ( dynamic_cast< CSearchEditField *> + ( USearchHelper::FindViewEditField(this, paneID_Nickname) ) )->GetText( &mValue[256] ); + + mailingListValues[2].attrib = AB_attribInfo; + mailingListValues[2].u.string = ( dynamic_cast< CSearchEditField *> + (USearchHelper::FindViewEditField(this, paneID_Description) ) )->GetText( &mValue[512] ); + // Need to copy in the win CSID + + MWContext* context= CAddressBookWindow::GetMailContext(); + INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(context); + mailingListValues[3].attrib = AB_attribWinCSID; + mailingListValues[3].u.shortValue= INTL_GetCSIWinCSID(c); + + CAddressBookManager::FailAddressError( AB_SetMailingListAttributes( mMSGPane, mailingListValues, 3 ) ); + CAddressBookManager::FailAddressError( AB_CommitChanges( mMSGPane ) ); +} + + + +//------------------------------------------------------------------------------ +// ¥ FinishCreateSelf +//------------------------------------------------------------------------------ +// Initialize the window. +// +void CListPropertiesWindow::FinishCreateSelf() { + + mAddressBookListTable = + dynamic_cast(USearchHelper::FindViewSubview(this, paneID_AddressBookListTable)); + FailNILRes_(mAddressBookListTable); + mTitleField = dynamic_cast< CSearchEditField *> ( USearchHelper::FindViewEditField(this, paneID_Name) ); + + Inherited::FinishCreateSelf(); // Call CMediatedWindow for now since we need to + // create a different context than CMailNewsWindow + + UReanimator::LinkListenerToControls(this, this, res_ID ); + // Lastly, link to our broadcasters, need to link to allow title update + USearchHelper::LinkListenerToBroadcasters(this, this); + +} + + +//------------------------------------------------------------------------------ +// ¥ DrawSelf +//------------------------------------------------------------------------------ +// Draw the window. +// +void CListPropertiesWindow::DrawSelf() { + + Rect frame; + + if ( mAddressBookListTable->CalcPortFrameRect(frame) && ::RectInRgn(&frame, mUpdateRgnH) ) { + + { + StExcludeVisibleRgn excludeRgn(mAddressBookListTable); + Inherited::DrawSelf(); + } + + StColorPenState::Normalize(); + ::EraseRect(&frame); + } else { + Inherited::DrawSelf(); + } + + USearchHelper::RemoveSizeBoxFromVisRgn(this); +} + + +//------------------------------------------------------------------------------ +// ¥ UpdateTitle +//------------------------------------------------------------------------------ +// Update the window title. +// +void CListPropertiesWindow::UpdateTitle() { + + CStr255 title; + { + CStr255 name; + mTitleField->GetDescriptor(name); + ::GetIndString( title, 8903, 2); + ::StringParamText( title, name); + } + SetDescriptor( title ); +} +#endif + diff --git a/mozilla/cmd/macfe/MailNews/AddressBook/CAddressBookWindows.h b/mozilla/cmd/macfe/MailNews/AddressBook/CAddressBookWindows.h new file mode 100644 index 00000000000..310d1b8528f --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/AddressBook/CAddressBookWindows.h @@ -0,0 +1,299 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + +// CAddressBookWindows.h + +#pragma once +#include "abcom.H" +#ifdef MOZ_NEWADDR +class CComposeAddressTableView; +/*====================================================================================*/ + #pragma mark INCLUDE FILES +/*====================================================================================*/ + +#include "CMailNewsWindow.h" +#include "CMailNewsContext.h" +#include "LGADialogBox.h" +#include "MailNewsCallbacks.h" + + +class CSearchEditField; +class LBroadcasterEditField; +class CNamePropertiesWindow; +class CListPropertiesWindow; +class CMailingListTableView; +class CAddressBookController; +#pragma mark - +/*====================================================================================*/ + #pragma mark TYPEDEFS +/*====================================================================================*/ +typedef struct DIR_Server DIR_Server; +typedef struct _XP_List XP_List; +typedef UInt32 ABID; + + + +#pragma mark - +/*====================================================================================*/ + #pragma mark CONSTANTS +/*====================================================================================*/ + +// Save window status version + +static const UInt16 cAddressSaveWindowStatusVersion = 0x0219; +static const UInt16 cNamePropertiesSaveWindowStatusVersion = 0x0202; +static const UInt16 cListPropertiesSaveWindowStatusVersion = 0x0202; + +#pragma mark - + +extern "C" +{ +int MacFe_ShowModelessPropertySheetForAB2( MSG_Pane * pane, MWContext* inContext); +int MacFE_ShowPropertySheetForDir( + DIR_Server* server, MWContext* context, MSG_Pane * srcPane, XP_Bool newDirectory ); +} +class CAddressBookWindow; + + +class CAddressBookManager +{ +public: + + // Should be called when the application starts up + static void OpenAddressBookManager(void); + // Should be called when the application closes + static void CloseAddressBookManager(void); + + static void ImportLDIF(const FSSpec& inSpec); + + static CAddressBookWindow* ShowAddressBookWindow(void); + + static XP_List *GetDirServerList(void); + static void SetDirServerList(XP_List *inList, Boolean inSavePrefs = true); + static DIR_Server *GetPersonalBook(void); + + static void FailAddressError(Int32 inError); + + +private: + + static void RegisterAddressBookClasses(void); + static int DirServerListChanged(const char*, void*) + { + CAddressBookManager::sDirServerListChanged = true; + return 0; + } + + // Instance variables + + static XP_List *sDirServerList; + static Boolean sDirServerListChanged; + +}; + +class CAddressBookWindow : public CMailNewsWindow +{ +private: + typedef CMailNewsWindow Inherited; + +public: + + enum { class_ID = 'AbWn', pane_ID = class_ID, res_ID = 8900 }; + + // IDs for panes in associated view, also messages that are broadcast to this object + enum { + paneID_DirServers = 'DRSR' // CDirServersPopupMenu *, this + , paneID_Search = 'SRCH' // MSG_Pane *, search button + , paneID_Stop = 'STOP' // nil, stop button + , paneID_AddressBookTable = 'Tabl' // Address book table + , paneID_TypedownName = 'TYPE' // Typedown name search edit field in window + , paneID_SearchEnclosure = 'SCHE' // Enclosure for search items + , paneID_AddressBookController = 'AbCn' + }; + + // Stream creator method + + CAddressBookWindow(LStream *inStream) : + CMailNewsWindow(inStream, WindowType_Address), + mAddressBookController(nil) + { + SetPaneID(pane_ID); + SetRefreshAllWhenResized(false); + } + virtual ~CAddressBookWindow(); + + virtual ResIDT GetStatusResID() const { return res_ID; } + + static MWContext *GetMailContext(); + virtual CNSContext* CreateContext() const; + virtual CMailFlexTable* GetActiveTable(); + + CAddressBookController* GetAddressBookController() const { return mAddressBookController; } +protected: + + // Overriden methods + + virtual void FinishCreateSelf(); + + // Utility methods + + virtual void ReadWindowStatus(LStream *inStatusData); + virtual void WriteWindowStatus(LStream *outStatusData); + virtual UInt16 GetValidStatusVersion() const { return cAddressSaveWindowStatusVersion; } + +protected: + CAddressBookController *mAddressBookController; +}; + + +class CAddressBookChildWindow : public LGADialogBox +{ +private: + typedef LGADialogBox Inherited; + +public: + CAddressBookChildWindow(LStream *inStream) : + Inherited (inStream), mMSGPane( NULL ) + { + SetRefreshAllWhenResized(false); + } + + virtual void UpdateBackendToUI() = 0L; + virtual void UpdateUIToBackend( MSG_Pane* inPane ) = 0L; + +protected: + // Overriden methods + virtual void ListenToMessage(MessageT inMessage, void *ioParam = nil); + virtual void UpdateTitle()=0; + // Instance variables + MSG_Pane* mMSGPane; +}; + +class CListPropertiesWindow : public CAddressBookChildWindow { + +private: + typedef CAddressBookChildWindow Inherited; + +public: + enum { class_ID = 'LpWn', pane_ID = class_ID, res_ID = 8940 }; + + // IDs for panes in associated view, also messages that are broadcast to this object + enum { + paneID_Name = 'NAME' + , paneID_Nickname = 'NICK' + , paneID_Description = 'DESC' + , paneID_AddressBookListTable = 'Tabl' // Address book list table + }; + + CListPropertiesWindow(LStream *inStream); + virtual ~CListPropertiesWindow(); + + virtual void UpdateBackendToUI(); + virtual void UpdateUIToBackend( MSG_Pane* inPane ); + +protected: + virtual void FinishCreateSelf(); + virtual void DrawSelf(); + virtual void UpdateTitle(); + + // Instance variables + CMailingListTableView *mAddressBookListTable; + CSearchEditField *mTitleField; +}; + +class CMailWindowCallbackListener: public CMailCallbackListener +{ + void CMailWindowCallBackListener( LWindow* inWindow ) + { + mWindow = inWindow; + } +private: + virtual void PaneChanged( + MSG_Pane* inPane, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value); + LWindow *mWindow; +}; + +//------------------------------------------------------------------------------ +// ¥ CNamePropertiesWindow +//------------------------------------------------------------------------------ +// +class CNamePropertiesWindow : public CAddressBookChildWindow +{ +private: + typedef CAddressBookChildWindow Inherited; + +public: + + enum { class_ID = 'NpWn', pane_ID = class_ID, res_ID = 8930 }; + + // IDs for panes in associated view, also messages that are broadcast to this object + enum { + paneID_GeneralView = 'GNVW' // General preferences view + , paneID_FirstName = 'FNAM' + , paneID_LastName = 'LNAM' + , paneID_DisplayName = 'DNAM' + , paneID_EMail = 'EMAL' + , paneID_Nickname = 'NICK' + , paneID_Notes = 'NOTE' + , paneID_PrefersHTML = 'HTML' + , paneID_ContactView = 'CNVW' // Contact preferences view + , paneID_Company = 'COMP' + , paneID_Title = 'TITL' + , paneID_Department = 'DPRT' + , paneID_Address = 'ADDR' + , paneID_City = 'CITY' + , paneID_State = 'STAT' + , paneID_ZIP = 'ZIP ' + , paneID_Country = 'Coun' + , paneID_WorkPhone = 'WPHO' + , paneID_FaxPhone = 'FPHO' + , paneID_PagerPhone = 'PPHO' + , paneID_HomePhone = 'HPHO' + , paneID_CelluarPhone = 'CPHO' + , paneID_SecurityView = 'SCVW' // Security preferences view + , paneID_CooltalkView = 'CLVW' // Cooltalk preferences view + , paneID_ConferenceAddress = 'CAED' + , paneID_ConferenceServer = 'CnPu' + , paneID_None = 'NONE' + }; + + enum { // Broadcast messages + paneID_ConferencePopup ='CoPU' // conference pop up button + }; + + CNamePropertiesWindow(LStream *inStream); + virtual void UpdateBackendToUI(); + void UpdateUIToBackend( MSG_Pane *inPane ); + void SetConferenceText( ); + +protected: + virtual void FinishCreateSelf(); + virtual void ListenToMessage(MessageT inMessage, void *ioParam = nil); + virtual void UpdateTitle(); + +private: + int32 GetPaneAndAttribID( TableIndexT index, PaneIDT& outPaneID, AB_AttribID &outAttrib ); + PaneIDT FindPaneForAttribute ( AB_AttribID inAttrib ); +protected: + CMailWindowCallbackListener mCallBackListener; + LBroadcasterEditField *mFirstNameField; + LBroadcasterEditField *mLastNameField; +}; +#endif // NEWADDR \ No newline at end of file diff --git a/mozilla/cmd/macfe/MailNews/AddressBook/CAddressPickerWindow.cp b/mozilla/cmd/macfe/MailNews/AddressBook/CAddressPickerWindow.cp new file mode 100644 index 00000000000..449fed6364e --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/AddressBook/CAddressPickerWindow.cp @@ -0,0 +1,274 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + +// CAddressPickerWindow.cp + +#include "CAddressPickerWindow.h" +// #include "CAddressBookWindows.h" +#ifdef MOZ_NEWADDR +#include "SearchHelpers.h" +#include "CAddressBookViews.h" +#include "xp_Help.h" +#include "UStdDialogs.h" +#include "libi18n.h" +#include "CProgressListener.h" +#include "StBlockingDialogHandler.h" +#include "CKeyStealingAttachment.h" + +// Code in the tables depends on recieving key up events hence this class +class StKeyUpDialogHandler : public StBlockingDialogHandler +{ +public: + StKeyUpDialogHandler( + ResIDT inDialogResID, + LCommander* inSuper): StBlockingDialogHandler( inDialogResID, inSuper) {}; + void EventKeyUp(const EventRecord &inMacEvent) + { + /* if it's a CKeyUpReceiver, send the key up. We used to send keyups to + * targets if a browser view was on duty, which had two disadvantages: it required + * any potential targets in a browser view to handle a key up, and sometimes + * the commander chain would be incorrect so key ups were sent to a target in a + * view that was not on duty. + */ + if (dynamic_cast(sTarget)) + sTarget->ProcessKeyPress(inMacEvent); + + } +}; + +//------------------------------------------------------------------------------ +// ¥ DoPickerDialog +//------------------------------------------------------------------------------ +// Bring up and runs the picker dialog +// +void CAddressPickerWindow::DoPickerDialog( CComposeAddressTableView* initialTable ) +{ + StKeyUpDialogHandler handler ( 8990, nil ); + CAddressPickerWindow* dialog = dynamic_cast< CAddressPickerWindow* >( handler.GetDialog() ); + dialog->Setup( initialTable ); + dialog->Show(); + MessageT message; + do + { + message = handler.DoDialog(); + } while ( message != eOkayButton && message != eCancelButton ) ; +} + +//------------------------------------------------------------------------------ +// ¥ CAddressPickerWindow +//------------------------------------------------------------------------------ +// +CAddressPickerWindow::CAddressPickerWindow(LStream *inStream) + : Inherited( inStream ), CSaveWindowStatus( this ), mInitialTable( nil ), mPickerAddresses(nil), + mAddressBookController(nil), mProgressListener( nil ), mMailNewsContext ( nil ), mLastAction ( eToButton ) +{ +} + +//------------------------------------------------------------------------------ +// ¥ ~CAddressPickerWindow +//------------------------------------------------------------------------------ +// +CAddressPickerWindow::~CAddressPickerWindow() +{ + if (mMailNewsContext) + mMailNewsContext->RemoveUser(this); + delete mProgressListener; +} + + +//------------------------------------------------------------------------------ +// ¥ Setup +//------------------------------------------------------------------------------ +// Copies takes in a CComposeAddressTableView and copies its data to the picker bucket +// The passed in table will be updated if necessary when the window is closed +// +void CAddressPickerWindow::Setup( CComposeAddressTableView* initialTable ) +{ + // Copy the old table to the new one + Assert_( initialTable ); + Assert_( mPickerAddresses ); + + TableIndexT numRows; + mInitialTable = initialTable; + initialTable->GetNumRows(numRows); + STableCell cell; + Uint32 size; + char* address = NULL; + for ( int32 i = 1; i <= numRows; i++ ) + { + EAddressType type = initialTable->GetRowAddressType( i ); + cell.row = i; + cell.col = 2; + size = sizeof(address); + initialTable->GetCellData(cell, &address, size); + mPickerAddresses->InsertNewRow( type, address, false ); + } +} + + +//------------------------------------------------------------------------------ +// ¥ FinishCreateSelf +//------------------------------------------------------------------------------ +// +void CAddressPickerWindow::FinishCreateSelf() +{ + Inherited::FinishCreateSelf(); + + mAddressBookController = + dynamic_cast< CAddressBookController* >(FindPaneByID( CAddressBookWindow::paneID_AddressBookController) ) ; + CSaveWindowStatus::FinishCreateWindow(); + + // Context creation + mMailNewsContext = new CMailNewsContext( MWContextAddressBook); + if (!mProgressListener) + mProgressListener = new CProgressListener(this, mMailNewsContext); + ThrowIfNULL_(mProgressListener); + // The progress listener should be "just a bit" lazy during network activity + mProgressListener->SetLaziness(CProgressListener::lazy_NotAtAll); + mMailNewsContext->AddListener(mProgressListener); + mMailNewsContext->AddUser(this); + + mMailNewsContext->SetWinCSID(INTL_DocToWinCharSetID(mMailNewsContext->GetDefaultCSID())); + + // Have the AddressBook Controller listen to the context + mMailNewsContext->AddListener(mAddressBookController); + mAddressBookController->SetContext( *mMailNewsContext ); + mAddressBookController->GetAddressBookContainerView()->Setup( *mMailNewsContext ); + mAddressBookController->GetAddressBookTable()->SetNotifyOnSelectionChange( true ); + mAddressBookController->GetAddressBookTable()->StartBroadcasting(); + + mPickerAddresses = dynamic_cast< CComposeAddressTableView* >( FindPaneByID( 'Addr' ) ); + FailNILRes_( mPickerAddresses ); + + (mAddressBookController->GetAddressBookTable() )->AddListener( this ); + UReanimator::LinkListenerToControls(this, this,CAddressPickerWindow:: res_ID); + +// Setup KeySnatcher for our fancy default button behavior + CKeyStealingAttachment* keyStealer = new CKeyStealingAttachment(this); + mAddressBookController->GetAddressBookTable()->AddAttachment(keyStealer); + mAddressBookController->GetAddressBookContainerView()->AddAttachment(keyStealer ); + keyStealer->StealKey( char_Enter ); + keyStealer->StealKey( char_Return ); + + + // Adjust button state + ListenToMessage ( CStandardFlexTable::msg_SelectionChanged, nil ); +} + + +//------------------------------------------------------------------------------ +// ¥ ListenToMessage +//------------------------------------------------------------------------------ +// +void CAddressPickerWindow::ListenToMessage(MessageT inMessage, void * /* ioParam */ ) +{ + switch( inMessage ) + { + case eOkayButton: + SaveStatusInfo(); + CComposeAddressTableStorage* oldTableStorage =dynamic_cast< CComposeAddressTableStorage*> (mInitialTable->GetTableStorage() ); + mPickerAddresses->EndEditCell(); + mInitialTable->SetTableStorage( mPickerAddresses->GetTableStorage() ); + mInitialTable->Refresh(); + mPickerAddresses->SetTableStorage( oldTableStorage ); + break; + + case eHelp: + ShowHelp( HELP_SELECT_ADDRESSES ); + break; + + case eToButton: + mLastAction = eToButton; + AddSelection ( eToType ); + break; + + case eCcButton: + mLastAction = eCcButton; + AddSelection ( eCcType ); + break; + + case eBccButton: + mLastAction = eBccButton; + AddSelection( eBccType ); + break; + + case ePropertiesButton: + break; + + case CStandardFlexTable::msg_SelectionChanged: + Boolean enable = mAddressBookController->GetAddressBookTable()->GetSelectedRowCount() >0; + SetDefaultButton( mLastAction ); + USearchHelper::EnableDisablePane( USearchHelper::FindViewControl( this ,eToButton ), enable, true ); + USearchHelper::EnableDisablePane( USearchHelper::FindViewControl( this ,eCcButton ), enable, true ); + USearchHelper::EnableDisablePane( USearchHelper::FindViewControl( this ,eBccButton ), enable, true ); + USearchHelper::EnableDisablePane( USearchHelper::FindViewControl( this ,ePropertiesButton ), enable, true ); + break; + + default: + break; + } +} + +//------------------------------------------------------------------------------ +// ¥ AddSelection +//------------------------------------------------------------------------------ +// Adds the table selection to the address bucket. +// +// +void CAddressPickerWindow::AddSelection( EAddressType inAddressType ) +{ + char* address = NULL; + Uint32 size= sizeof(address); + TableIndexT row = 0; + + while ( mAddressBookController->GetAddressBookTable()->GetNextSelectedRow( row ) ) + { + address = mAddressBookController->GetAddressBookTable()->GetFullAddress( row ); + mPickerAddresses->InsertNewRow( inAddressType, address, false ); + XP_FREE( address ); + } + + SetDefaultButton( eOkayButton ); +} + +//------------------------------------------------------------------------------ +// ¥ ReadWindowStatus +//------------------------------------------------------------------------------ +// Adjust the window to stored preferences. +// +void CAddressPickerWindow::ReadWindowStatus(LStream *inStatusData) +{ + if ( inStatusData != nil ) + { + mAddressBookController->ReadStatus( inStatusData ); + } + +} + + +//------------------------------------------------------------------------------ +// ¥ WriteWindowStatus +//------------------------------------------------------------------------------ +// Write window stored preferences. +// +void CAddressPickerWindow::WriteWindowStatus(LStream *outStatusData) +{ + mAddressBookController->WriteStatus(outStatusData); +} + +#endif //NEWADDR \ No newline at end of file diff --git a/mozilla/cmd/macfe/MailNews/AddressBook/CAddressPickerWindow.h b/mozilla/cmd/macfe/MailNews/AddressBook/CAddressPickerWindow.h new file mode 100644 index 00000000000..add96350dd2 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/AddressBook/CAddressPickerWindow.h @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +// CAddressPickerWindow.h + +#pragma once + +#include "abcom.h" +#ifdef MOZ_NEWADDR +#include "LGADialogBox.h" +#include "CSaveWindowStatus.h" +#include "CComposeAddressTableView.h" +class CAddressBookTableView; +class CAddressBookController; +class CMailNewsContext; +static const UInt16 cAddressPickerWindowStatusVersion = 0x006; +//------------------------------------------------------------------------------ +// ¥ CAddressPickerWindow +//------------------------------------------------------------------------------ +// +class CAddressPickerWindow : public LGADialogBox, public CSaveWindowStatus +{ +public: + static void DoPickerDialog( CComposeAddressTableView* initialTable ); +private: + typedef LGADialogBox Inherited; +public: + enum { class_ID = 'ApWn', pane_ID = class_ID, res_ID = 8990 }; + + // Control IDs + enum EButton { + eOkayButton = 'OkBt', + eCancelButton ='CnBt', + eHelp ='help', + eToButton = 'ToBt', + eCcButton = 'CcBt', + eBccButton = 'BcBt', + ePropertiesButton = 'PrBt' + } ; + + CAddressPickerWindow(LStream *inStream); + ~CAddressPickerWindow(); + void Setup( CComposeAddressTableView* initialTable ); + + virtual ResIDT GetStatusResID() const { return res_ID; } +protected: + virtual void FinishCreateSelf(); + virtual void ListenToMessage(MessageT inMessage, void *ioParam); + void AddSelection( EAddressType inAddressType ); + + virtual void ReadWindowStatus(LStream *inStatusData); + virtual void WriteWindowStatus(LStream *outStatusData); + virtual UInt16 GetValidStatusVersion() const { return cAddressPickerWindowStatusVersion; } + +private: + + CComposeAddressTableView* mInitialTable; + CComposeAddressTableView* mPickerAddresses; + + CAddressBookController* mAddressBookController; + CMailNewsContext* mMailNewsContext; + CProgressListener* mProgressListener; + EButton mLastAction; +}; +#endif \ No newline at end of file diff --git a/mozilla/cmd/macfe/MailNews/AddressBook/CLDAPQueryDialog.cp b/mozilla/cmd/macfe/MailNews/AddressBook/CLDAPQueryDialog.cp new file mode 100644 index 00000000000..a24def441a3 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/AddressBook/CLDAPQueryDialog.cp @@ -0,0 +1,387 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +// CLDAPQueryDialog.cp + +#include "CLDAPQueryDialog.h" +#include "pascalstring.h" +#include "LGAEditField.h" +#include "prefapi.h" +#include "SearchHelpers.h" +#include "resgui.h" +#include "xp_help.h" +//------------------------------------------------------------------------------ +// ¥ CLDAPBasicHandler +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// ¥ Setup +//------------------------------------------------------------------------------ +// +void CLDAPBasicHandler::Setup( MSG_Pane *inSearchPane, DIR_Server *inServer, LView *inView ) +{ + Assert_( inSearchPane != NULL ); + Assert_( inServer != NULL ); + Assert_(inView != NULL ); + + mView = inView; + int maxItems = 4; + + // Get Basic Search attributes + MSG_SearchError error = MSG_GetBasicLdapSearchAttributes ( + inServer, &mSearchAttributes[0], &maxItems); + + + // Update the captions to reflect their new attributes + for( int32 captionNumber = 0; captionNumber < maxItems; captionNumber++ ) + { + LCaption* caption = dynamic_cast< LCaption*>( mView->FindPaneByID (eBasicSearchCaption+ captionNumber ) ); + Assert_( caption ); + caption->SetDescriptor ( CStr255(mSearchAttributes[ captionNumber ].name )); + } + +} + + +//------------------------------------------------------------------------------ +// ¥ GetSearchParameters +//------------------------------------------------------------------------------ +// Get the current search parameters. outSearchParams must be able to hold at least +// 4 elements. Strings are output as c-strings. +// +Int32 CLDAPBasicHandler::GetSearchParameters(SearchLevelParamT* levelParam ) +{ + CStr255 valueString; + MSG_SearchOperator op = (MSG_SearchOperator) opContains;; + int maxItems = 4; + Int32 currentLevel = 0; + + for( int32 i = 0; i < maxItems; i++ ) + { + LGAEditField* editField = dynamic_cast< LGAEditField*>( mView->FindPaneByID (eBasicSearchTerm+ i ) ); + Assert_( editField ); + editField->GetDescriptor ( valueString ); + if ( valueString.Length() ) + { + levelParam[ currentLevel ].val.attribute = MSG_SearchAttribute( mSearchAttributes[ i ].attrib ); + XP_STRCPY( levelParam[ currentLevel ].val.u.string, valueString ); + levelParam[ currentLevel ].op = op; + levelParam[ currentLevel ].boolOp = true; + currentLevel++; + } + } + + return currentLevel; +} + + +//------------------------------------------------------------------------------ +// ¥ SetSearchParameters +//------------------------------------------------------------------------------ +// This works by taking the first occurence of an Param whose attribute +// which matches a basic search criteria attribute. +// +void CLDAPBasicHandler::SetSearchParameters ( SearchLevelParamT* levelParam , int32 numLevels ) +{ + int maxItems = 4; + Int32 currentLevel = 0; + MSG_SearchAttribute attribute; + for( int32 basic = 0; basic < maxItems; basic++ ) + { + attribute = MSG_SearchAttribute( mSearchAttributes[ basic ].attrib ); + for ( int32 currentLevel = 0; currentLevel( mView->FindPaneByID (eBasicSearchTerm+ basic ) ); + Assert_( editField ); + editField->SetDescriptor( CStr255( levelParam[ currentLevel ].val.u.string) ); + break; + } + } + } +} + + +//------------------------------------------------------------------------------ +// ¥ Setup +//------------------------------------------------------------------------------ +// Advanced Search Routines +// Most of the work is done by delagation to the Search Manager +// +void CLDAPAdvancedHandler::Setup( MSG_Pane *inSearchPane, DIR_Server *inServer, LView *inView ) +{ + Assert_( inSearchPane != NULL ); + Assert_( inServer != NULL ); + Assert_(inView != NULL ); + + mSearchFolders.InsertItemsAt(1, LArray::index_First, &inServer); + mSearchManager.InitSearchManager( inView, NULL, scopeLdapDirectory, & mSearchFolders ); + +} + + +//------------------------------------------------------------------------------ +// ¥ GetSearchParameters +//------------------------------------------------------------------------------ +// +Int32 CLDAPAdvancedHandler::GetSearchParameters ( SearchLevelParamT* levelParam ) +{ + mSearchManager.GetSearchParameters( levelParam ); + return mSearchManager.GetNumVisibleLevels(); +} + + +//------------------------------------------------------------------------------ +// ¥ SetSearchParameters +//------------------------------------------------------------------------------ +// +void CLDAPAdvancedHandler::SetSearchParameters ( SearchLevelParamT* levelParam , int32 numLevels ) +{ + mSearchManager.SetSearchParameters ( numLevels, levelParam ); +} + +//------------------------------------------------------------------------------ +// ¥ ~CLDAPQueryDialog +//------------------------------------------------------------------------------ +// +CLDAPQueryDialog::~CLDAPQueryDialog() +{ + if( mMSGPane ) // If the search manager isn't initialized don't save the window data + { + PREF_SetBoolPref("mail.addr_book.useAdvancedSearch", mAdvancedSearch); + CSaveWindowStatus::SaveStatusInfo(); + } +} + + +//------------------------------------------------------------------------------ +// ¥ BuildQuery +//------------------------------------------------------------------------------ +// +Boolean CLDAPQueryDialog::BuildQuery() +{ + // Initial Search setup + Assert_( mMSGPane ); + MSG_SearchFree ( mMSGPane ); + MSG_SearchAlloc ( mMSGPane ); + + + if( MSG_AddLdapScope( mMSGPane, mDirServer ) != SearchError_Success ) + return false; + + if( AddParametersToSearch( ) != SearchError_Success) + return false; + + return true; + +}; + + +//------------------------------------------------------------------------------ +// ¥ AddParametersToSearch +//------------------------------------------------------------------------------ +// +MSG_SearchError CLDAPQueryDialog::AddParametersToSearch( ) +{ + Assert_(mMSGPane != nil); + MSG_SearchError error = SearchError_Success; + + // Get the search parameters + StSearchDataBlock searchParams( mMaxLevels, StSearchDataBlock::eAllocateStrings); + SearchLevelParamT *curLevel = searchParams.GetData(); + int32 numLevels = mHandler[ mAdvancedSearch ]->GetSearchParameters( curLevel ); + + // Add parameters to the search + for ( Int32 i=0; i< numLevels; i++ ) + { + #ifdef FE_IMPLEMENTS_BOOLEAN_OR + error = MSG_AddSearchTerm(mMSGPane, curLevel[i].val.attribute, curLevel[i].op, + &curLevel[i].val, curLevel[i].boolOp, NULL ) ; + #else + error = MSG_AddSearchTerm(mMSGPane, curLevel[i].val.attribute, curLevel[i].op, + &curLevel[i].val) ; + #endif + if ( error != SearchError_Success ) + break; + } + return error; +} + + +//------------------------------------------------------------------------------ +// ¥ Setup +//------------------------------------------------------------------------------ +// +void CLDAPQueryDialog::Setup( MSG_Pane* inPane, DIR_Server* inServer ) +{ + mMSGPane = inPane; + mDirServer = inServer; + + // Set the name of the group box + LView* box = dynamic_cast( FindPaneByID('ScBx') ); + Assert_( box); + CStr255 name; + box->GetDescriptor( name ); + name+= mDirServer->description; + box->SetDescriptor( name ); + + // Setup the Handlers + mBasicView = dynamic_cast( box->FindPaneByID( eBasicEnclosure ) ); + Assert_( mBasicView ); + mHandler[ eBasic ]->Setup( inPane, inServer, mBasicView ); + USearchHelper::LinkListenerToBroadcasters( mBasicView, this ); + + mAdvancedView = dynamic_cast( box->FindPaneByID( eAdvancedEnclosure ) ); + Assert_( mAdvancedView ); + mHandler[ eAdvanced ]->Setup( inPane, inServer, mAdvancedView ); + + CLDAPAdvancedHandler* advancedLDAP = dynamic_cast(mHandler[ eAdvanced ]); + Assert_( advancedLDAP ); + mSearchManager = advancedLDAP->GetSearchManager(); + mSearchManager->AddListener( this ); + + XP_Bool isAdvanced = false; + if (PREF_GetBoolPref("mail.addr_book.useAdvancedSearch", &isAdvanced)== PREF_NOERROR ) + mAdvancedSearch = isAdvanced; + CSaveWindowStatus::FinishCreateWindow(); + + mIsModified = false; + + ShowHandler(); + Show(); + +} + + +//------------------------------------------------------------------------------ +// ¥ ListenToMessage +//------------------------------------------------------------------------------ +// +void CLDAPQueryDialog::ListenToMessage(MessageT inMessage, void *ioParam) +{ + switch( inMessage ) + { + case paneID_Search: + BuildQuery(); + break; + + case CSearchManager::msg_SearchParametersResized: + ResizeWindowBy( 0, *((Int16 *) ioParam) ); + break; + + case CSearchEditField::msg_UserChangedText: + mIsModified = true; + break; + + case msg_Help: + if ( mAdvancedSearch ) + ShowHelp( HELP_SEARCH_LDAP_ADVANCED ); + else + ShowHelp( HELP_SEARCH_LDAP_BASIC ); + break; + + case paneID_Advanced: + case paneID_Basic: + if ( !PREF_PrefIsLocked( "mail.addr_book.useAdvancedSearch") ) + mAdvancedSearch = !mAdvancedSearch; + ShowHandler(); + break; + }; + +} + + +//------------------------------------------------------------------------------ +// ¥ FinishCreateSelf +//------------------------------------------------------------------------------ +// +void CLDAPQueryDialog::FinishCreateSelf() +{ + mHandler[ eBasic ] = new CLDAPBasicHandler; + mHandler[ eAdvanced ] = new CLDAPAdvancedHandler; + UReanimator::LinkListenerToControls( this, this, CLDAPQueryDialog::res_ID ); + Inherited::FinishCreateSelf(); +} + + +//------------------------------------------------------------------------------ +// ¥ ShowHandler +//------------------------------------------------------------------------------ +// +void CLDAPQueryDialog::ShowHandler() +{ + Assert_ ( mSearchManager ); + + Int32 windowHeight = 130; + if ( mAdvancedSearch ) + { + Int16 deltaLevels = 5 - mSearchManager->GetNumVisibleLevels(); + windowHeight = 220 - deltaLevels * 23 ; + } + + ResizeWindowTo ( 500, windowHeight ); + // modify contents, always do when going from advanced to basic + // Only do when going from basic to advanced if there has been a modification + if ( mIsModified || !mAdvancedSearch) + { + StSearchDataBlock searchParams( mMaxLevels, StSearchDataBlock::eAllocateStrings); + SearchLevelParamT *curLevel = searchParams.GetData(); + int32 numLevels = mHandler[ !mAdvancedSearch ]->GetSearchParameters( curLevel ); + mHandler[ mAdvancedSearch ]->SetSearchParameters( curLevel, numLevels ); + mIsModified = false; + } + + // Swap the Buttons + USearchHelper::ShowHidePane( FindPaneByID(paneID_Advanced), !mAdvancedSearch); + USearchHelper::ShowHidePane( FindPaneByID( paneID_Basic ), mAdvancedSearch); + + // Swap the panes + USearchHelper::ShowHidePane( mBasicView, !mAdvancedSearch); + USearchHelper::ShowHidePane( mAdvancedView, mAdvancedSearch); +} + + +//------------------------------------------------------------------------------ +// ¥ ReadWindowStatus +//------------------------------------------------------------------------------ +// +void CLDAPQueryDialog::ReadWindowStatus(LStream *inStatusData) +{ + CSaveWindowStatus::ReadWindowStatus(inStatusData); + + mSearchManager->ReadSavedSearchStatus(inStatusData); +} + + +//------------------------------------------------------------------------------ +// ¥ WriteWindowStatus +//------------------------------------------------------------------------------ +// +void CLDAPQueryDialog::WriteWindowStatus(LStream *outStatusData) +{ + CSaveWindowStatus::WriteWindowStatus(outStatusData); + if( mAdvancedSearch == 0 ) + { + mAdvancedSearch = true; + ShowHandler(); + } + mSearchManager->WriteSavedSearchStatus(outStatusData); + +} diff --git a/mozilla/cmd/macfe/MailNews/AddressBook/CLDAPQueryDialog.h b/mozilla/cmd/macfe/MailNews/AddressBook/CLDAPQueryDialog.h new file mode 100644 index 00000000000..7961b596f4a --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/AddressBook/CLDAPQueryDialog.h @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +// CLDAPQueryDialog.h +#pragma once + +#include "LGADialogBox.h" +#include "CSearchManager.h" +#include "CSaveWindowStatus.h" +//------------------------------------------------------------------------------ +// ¥ CLDAPHandlerInterface +//------------------------------------------------------------------------------ +// I am defining a simple interface for getting Search Parameters +// an alternative would have been to subclass CSearchManager +// but that class does a lot more than just building queries +// +class CLDAPHandlerInterface +{ +public: + virtual void Setup( MSG_Pane *inSearchPane, DIR_Server *inServer, LView *inView ) = 0; + virtual Int32 GetSearchParameters ( SearchLevelParamT* levelParam ) = 0; + virtual void SetSearchParameters ( SearchLevelParamT* levelParam , int32 numLevels ) = 0; +protected: + LView * mView; +}; + +//------------------------------------------------------------------------------ +// ¥ CLDAPBasicHandler +//------------------------------------------------------------------------------ +// Handles building queries from basic search pane +// +class CLDAPBasicHandler: public CLDAPHandlerInterface +{ +public: + enum { eBasicSearchTerm = 8965, eBasicSearchCaption = 8961 }; + virtual void Setup( MSG_Pane *inSearchPane, DIR_Server *inServer, LView *inView ); + virtual Int32 GetSearchParameters ( SearchLevelParamT* levelParam ); + virtual void SetSearchParameters ( SearchLevelParamT* levelParam , int32 numLevels ); + +private: + MSG_SearchMenuItem mSearchAttributes[4]; // The names and attributes of the 4 editfields +}; + +//------------------------------------------------------------------------------ +// ¥ CLDAPAdvancedHandler +//------------------------------------------------------------------------------ +// Handles building queries from the advanced pane +// +class CLDAPAdvancedHandler: public CLDAPHandlerInterface +{ +public: + virtual void Setup( MSG_Pane *inSearchPane, DIR_Server *inServer, LView *inView ) ; + virtual Int32 GetSearchParameters ( SearchLevelParamT* levelParam ) ; + virtual void SetSearchParameters ( SearchLevelParamT* levelParam , int32 numLevels ) ; + CSearchManager* GetSearchManager() { return &mSearchManager; } +private: + CSearchManager mSearchManager; + LArray mSearchFolders; +}; + + +static const UInt16 cLDAPSaveWindowStatusVersion = 0x0214; +//------------------------------------------------------------------------------ +// ¥ CLDAPQueryDialog +//------------------------------------------------------------------------------ +// The query building dialog box +// if the user hits okay a query will be build for the given message pane +// +class CLDAPQueryDialog: public LGADialogBox, public CSaveWindowStatus +{ +private: + typedef LGADialogBox Inherited; +public: + enum { class_ID = 'LdDb', res_ID = 8980 }; + enum { eBasic = 0, eAdvanced = 1 }; + enum { eBasicEnclosure = 'BaVw', eAdvancedEnclosure = 'PENC'}; + enum { paneID_Search = 'SRCH', paneID_Advanced = 'AdBt', paneID_Basic = 'BaBt'}; + + CLDAPQueryDialog( LStream* inStream ): LGADialogBox( inStream ), CSaveWindowStatus ( this ) + ,mMaxLevels(5), mAdvancedSearch( 0 ),mSearchManager( NULL ), + mMSGPane(NULL ), mDirServer(NULL ), mIsModified( false) {}; + + ~CLDAPQueryDialog(); + + void Setup( MSG_Pane* inPane, DIR_Server* inServer ); + Boolean BuildQuery(); + + +protected: + virtual void FinishCreateSelf(); + virtual void ListenToMessage(MessageT inMessage, void *ioParam); +// Status window + virtual ResIDT GetStatusResID() const { return res_ID; } + virtual UInt16 GetValidStatusVersion() const { return cLDAPSaveWindowStatusVersion; } + virtual void ReadWindowStatus(LStream *inStatusData); + virtual void WriteWindowStatus(LStream *outStatusData); +private: + void ShowHandler(); + MSG_SearchError AddParametersToSearch(); + + CLDAPHandlerInterface* mHandler[2]; + LView* mBasicView; + LView* mAdvancedView; + + Int32 mAdvancedSearch; + Boolean mIsModified; + + MSG_Pane* mMSGPane; + DIR_Server* mDirServer; + CSearchManager* mSearchManager; + Int32 mMaxLevels; +}; diff --git a/mozilla/cmd/macfe/MailNews/AddressBook/CNameCompletionPicker.cp b/mozilla/cmd/macfe/MailNews/AddressBook/CNameCompletionPicker.cp new file mode 100644 index 00000000000..d176c6f211c --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/AddressBook/CNameCompletionPicker.cp @@ -0,0 +1,414 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +// CNameCompletionPicker.cp + +#include "abcom.H" +#ifdef MOZ_NEWADDR +#include "CNameCompletionPicker.h" +#include "UStdDialogs.h" +#include "SearchHelpers.h" +#include "LGAPushButton.h" + +LEditField* CNameCompletionPicker::mLastReceivedEditField = nil; +MSG_Pane* CNameCompletionPicker::mLastReceivedPickerPane = nil; +CMailNewsContext* CNameCompletionPicker::mLastReceivedContext = nil; +int CNameCompletionPicker::mLastReceivedNumResults = 0; + + +//---------------------------------------------------------------------------- +// ¥ CNameCompletionTable class [static] +//---------------------------------------------------------------------------- +// +CNameCompletionTable::CNameCompletionTable(LStream* inStream ) + : CMailFlexTable(inStream) +{ +} + + +CNameCompletionTable::~CNameCompletionTable() +{ + SetMessagePane(NULL); + // Do it here so that our DestroyMessagePane() is called. + // If we let the inherited CMailFlexTable do it, it will call + // its own DestroyMessagePane() because ours is already destroyed + // and the PickerPane will be deleted (which is something we + // don't want because it belongs to the CMailAddressEditField). +} + + +//---------------------------------------------------------------------------- +// ¥ CNameCompletionTable::DestroyMessagePane +//---------------------------------------------------------------------------- +// Don't delete the MSG_PickerPane: it belongs to the CMailAddressEditField +// +void CNameCompletionTable::DestroyMessagePane(MSG_Pane* /*inPane*/) +{ +} + + +//---------------------------------------------------------------------------- +// ¥ CNameCompletionTable::SetColumnHeaders +//---------------------------------------------------------------------------- +// +void CNameCompletionTable::SetColumnHeaders() +{ + LTableViewHeader* tableHeader = GetTableHeader(); + PaneIDT headerPaneID; + + // Column #1 = 'Type' is the container type (Address Book / LDAP server). + // Column #2 = 'Col0' is the entry type (User / Mailing list). + // The other columns have configurable headers. + for (short col = 2; col <= tableHeader->CountColumns(); col ++) + { + headerPaneID = tableHeader->GetColumnPaneID(col); + Int32 index = headerPaneID - eTableHeaderBase; + AB_ColumnInfo *info = AB_GetColumnInfoForPane(GetMessagePane(), AB_ColumnID(index)); + + LCaption* headerPane = dynamic_cast(GetSuperView()->FindPaneByID(headerPaneID)); + if (headerPane) headerPane->SetDescriptor(CStr255(info->displayString)); + + AB_FreeColumnInfo(info); + } +} + + +//---------------------------------------------------------------------------- +// ¥ CNameCompletionTable::SelectionChanged +//---------------------------------------------------------------------------- +// +void CNameCompletionTable::SelectionChanged() +{ + Inherited::SelectionChanged(); + StartBroadcasting(); + // Long story: the CTableKeyAttachment from CStandardFlexTable + // calls StopBroadcasting() on keyDown and StartBroadcasting() + // on keyUp. Problem: we are in a dialog and we never get keyUp's. +} + + +//---------------------------------------------------------------------------- +// ¥ CNameCompletionTable::OpenRow +//---------------------------------------------------------------------------- +// +void CNameCompletionTable::OpenRow(TableIndexT inRow) +{ + if (IsValidRow(inRow)) + { + BroadcastMessage(msg_OK, nil); + } +} + + +//---------------------------------------------------------------------------- +// ¥ CNameCompletionTable::GetCellDisplayData +//---------------------------------------------------------------------------- +// Entirely copied from CMailingListTableView +// +void CNameCompletionTable::GetCellDisplayData(const STableCell &inCell, ResIDT& ioIcon, CStr255 &ioDisplayString) +{ + ioIcon = 0; + ioDisplayString = ""; + + // Get the attribute corresponding to the current column + LTableViewHeader* tableHeader = GetTableHeader(); + PaneIDT headerPaneID = tableHeader->GetColumnPaneID(inCell.col); + Int32 index = headerPaneID - eTableHeaderBase; + + AB_ColumnInfo *info = AB_GetColumnInfoForPane(GetMessagePane(), AB_ColumnID(index)); + AB_AttribID attrib = info->attribID; + AB_FreeColumnInfo(info); + + // Get the data + uint16 numItems = 1; + AB_AttributeValue* value; + if (AB_GetEntryAttributesForPane(GetMessagePane(), inCell.row-1, &attrib, &value, &numItems) == AB_SUCCESS) + { + if (attrib == AB_attribEntryType) + { + ioIcon = (value->u.entryType == AB_MailingList ? 15263 : 15260); + } + else + { + ioDisplayString = value->u.string ; + } + AB_FreeEntryAttributeValue(value); + } +} + + +//---------------------------------------------------------------------------- +// ¥ CNameCompletionTable::DrawCellContents +//---------------------------------------------------------------------------- +// Mostly copied from CMailingListTableView +// +void CNameCompletionTable::DrawCellContents(const STableCell &inCell, const Rect &inLocalRect) +{ + ResIDT iconID = 0; + PaneIDT cellType = GetCellDataType(inCell); + switch (cellType) + { + case 'Type': + AB_ContainerType container = AB_GetEntryContainerType(GetMessagePane(), inCell.row-1); + switch (container) + { + case AB_LDAPContainer: iconID = 15250; break; // remote folder + case AB_MListContainer: iconID = 15258; break; // address book + case AB_PABContainer: iconID = 15258; break; // address book + case AB_UnknownContainer: iconID = 0; break; + } + if (iconID) + DrawIconFamily(iconID, 16, 16, 0, inLocalRect); + break; +// case 'Col0': +// ... +// case 'Col6': + default: + CStr255 displayString; + GetCellDisplayData(inCell, iconID, displayString); + if (iconID) + DrawIconFamily(iconID, 16, 16, kTransformNone, inLocalRect); + else + DrawTextString(displayString, &mTextFontInfo, 0, inLocalRect); + break; + } +} + + +#pragma mark - +//---------------------------------------------------------------------------- +// ¥ CNameCompletionPicker::DisplayDialog [static] +//---------------------------------------------------------------------------- +// Show the name completion dialog +// +int CNameCompletionPicker::DisplayDialog(LEditField* inEditField, MSG_Pane* inPane, CMailNewsContext* inContext, int inNumResults) +{ + // put up dialog + mLastReceivedEditField = inEditField; + mLastReceivedPickerPane = inPane; + mLastReceivedContext = inContext; + mLastReceivedNumResults = inNumResults; + + RegisterClass_(CNameCompletionPicker); + RegisterClass_(CNameCompletionTable); + + StStdDialogHandler handler(CNameCompletionPicker::res_ID, NULL); + CNameCompletionPicker* dlog = (CNameCompletionPicker*)handler.GetDialog(); + + // run the dialog + MessageT message; + do + { + message = handler.DoDialog(); + } while (message != msg_OK && message != msg_Cancel); + + // return the result + STableCell aCell(0,0); + if (message == msg_OK) + aCell = dlog->GetActiveTable()->GetFirstSelectedCell(); + + // explicitly close the dialog to save its status + dlog->DoClose(); + + return (aCell.row); +} + + +#pragma mark - +//---------------------------------------------------------------------------- +// ¥ CNameCompletionPicker class +//---------------------------------------------------------------------------- +// +CNameCompletionPicker::CNameCompletionPicker(LStream *inStream) + : CMailNewsWindow(inStream, WindowType_NameCompletion) +{ +} + +CNameCompletionPicker::~CNameCompletionPicker() +{ +} + + +//---------------------------------------------------------------------------- +// ¥ CNameCompletionPicker::FinishCreateSelf +//---------------------------------------------------------------------------- +// +void CNameCompletionPicker::FinishCreateSelf() +{ + // transmit parameters from the Compose window to the list + ReceiveComposeWindowParameters(); + + mNameCompletionTable = dynamic_cast(USearchHelper::FindViewSubview(this, paneID_NameCompletionTable)); + FailNILRes_(mNameCompletionTable); + mNameCompletionTable->ReceiveMessagePane(mPickerPane); + + // finish create stuff + Inherited::FinishCreateSelf(); + CSaveWindowStatus::FinishCreateWindow(); + + // prepare list + mNameCompletionTable->SetColumnHeaders(); + mNameCompletionTable->SetRowCount(); + STableCell cellToSelect(2, 1); + mNameCompletionTable->SelectCell(cellToSelect); + + // default button + LGAPushButton * defaultBtn = dynamic_cast(FindPaneByID(paneID_OkButton)); + if (defaultBtn) defaultBtn->SetDefaultButton(true, true); + mNameCompletionTable->AddListener(this); + + // show window + this->Show(); + this->Select(); +} + + +//---------------------------------------------------------------------------- +// ¥ CNameCompletionPicker::DoClose +//---------------------------------------------------------------------------- +// +void CNameCompletionPicker::DoClose() +{ + // Save table data and window position + SaveStatusInfo(); + + // Close window + Inherited::DoClose(); +} + +//---------------------------------------------------------------------------- +// ¥ CNameCompletionPicker::CalcStandardBoundsForScreen +//---------------------------------------------------------------------------- +// Zoom in the vertical direction only. +// +void CNameCompletionPicker::CalcStandardBoundsForScreen( + const Rect &inScreenBounds, + Rect &outStdBounds) const +{ + LWindow::CalcStandardBoundsForScreen(inScreenBounds, outStdBounds); + Rect contRect = UWindows::GetWindowContentRect(mMacWindowP); + + outStdBounds.left = contRect.left; + outStdBounds.right = contRect.right; +} + + +//---------------------------------------------------------------------------- +// ¥ CNameCompletionPicker::ListenToMessage +//---------------------------------------------------------------------------- +// +void CNameCompletionPicker::ListenToMessage(MessageT inMessage, void */*ioParam*/) +{ + if (inMessage == msg_OK) + { + LControl* keyButton = (LControl*)FindPaneByID(paneID_OkButton); + keyButton->SimulateHotSpotClick(kControlButtonPart); + } +} + + +//---------------------------------------------------------------------------- +// ¥ CNameCompletionPicker::HandleKeyPress +//---------------------------------------------------------------------------- +// Handle Escape and Cmd-Period (copied from LDialogBox) +// +Boolean CNameCompletionPicker::HandleKeyPress(const EventRecord &inKeyEvent) +{ + Boolean keyHandled = false; + PaneIDT keyButtonID = 0; + + switch (inKeyEvent.message & charCodeMask) + { + case char_Enter: + case char_Return: + keyButtonID = paneID_OkButton; + break; + + case char_Escape: + if ((inKeyEvent.message & keyCodeMask) == vkey_Escape) + keyButtonID = paneID_CancelButton; + break; + + default: + if (UKeyFilters::IsCmdPeriod(inKeyEvent)) + keyButtonID = paneID_CancelButton; + else + keyHandled = LWindow::HandleKeyPress(inKeyEvent); + break; + } + + if (keyButtonID != 0) + { + LControl* keyButton = (LControl*)FindPaneByID(keyButtonID); + keyButton->SimulateHotSpotClick(kControlButtonPart); + keyHandled = true; + } + + return keyHandled; +} + + +//---------------------------------------------------------------------------- +// ¥ CNameCompletionPicker::ReadWindowStatus +//---------------------------------------------------------------------------- +// Put the window next to the edit field and try to restore its last size. +// +void CNameCompletionPicker::ReadWindowStatus(LStream *inStatusData) +{ + // get last window size + Rect savedBounds; + if (inStatusData != nil) + *inStatusData >> savedBounds; + else + { + GetPaneGlobalBounds(this, &savedBounds); + savedBounds.right = savedBounds.left + 320; //¥ TODO: remove these hard-coded values + } + + // put the window at the right of the caret position in the edit field + TEHandle teH = mEditField->GetMacTEH(); + short caretPos = (*teH)->selStart; + + Rect actualBounds; + mEditField->CalcPortFrameRect(actualBounds); + mEditField->PortToGlobalPoint(topLeft(actualBounds)); + actualBounds.top -= 44; //¥ TODO: remove these hard-coded values + actualBounds.left += (caretPos + 3) * 7; //¥ TODO: + actualBounds.bottom = actualBounds.top + (savedBounds.bottom - savedBounds.top); + actualBounds.right = actualBounds.left + (savedBounds.right - savedBounds.left); + + VerifyWindowBounds(this, &actualBounds); + DoSetBounds(actualBounds); + + // restore table data + if (inStatusData != nil) + mNameCompletionTable->ReadSavedTableStatus(inStatusData); +} + +//---------------------------------------------------------------------------- +// ¥ CNameCompletionPicker::WriteWindowStatus +//---------------------------------------------------------------------------- +// Save window size and table data. +// +void CNameCompletionPicker::WriteWindowStatus(LStream *outStatusData) +{ + CSaveWindowStatus::WriteWindowStatus(outStatusData); + mNameCompletionTable->WriteSavedTableStatus(outStatusData); +} +#endif //MOZ_NEWADDR diff --git a/mozilla/cmd/macfe/MailNews/AddressBook/CNameCompletionPicker.h b/mozilla/cmd/macfe/MailNews/AddressBook/CNameCompletionPicker.h new file mode 100644 index 00000000000..565bfc2a798 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/AddressBook/CNameCompletionPicker.h @@ -0,0 +1,135 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +// CNameCompletionPicker.h + +#pragma once + +#include "abcom.H" +#ifdef MOZ_NEWADDR +#include "CMailNewsWindow.h" +//¥#include "CAddressBookViews.h" +#include "CMailFlexTable.h" +#include "msgcom.h" +#include "PascalString.h" + +//---------------------------------------------------------------------------- +// ¥ CNameCompletionTable class +// +//---------------------------------------------------------------------------- +// +class CNameCompletionTable : public CMailFlexTable +{ +public: + typedef CMailFlexTable Inherited; + enum { class_ID = 'NcVw', pane_ID = 'Tabl' }; + + enum EColType { + eTableHeaderBase = 'Col0' // Columns are called 'Col0', 'Col1', 'Col2' etc... + }; // but 'Col0' is not used (it used to contain the person/list icon) + +public: + CNameCompletionTable(LStream *inStream); + virtual ~CNameCompletionTable(); + void SetColumnHeaders(); + + virtual void DeleteSelection() {}; + virtual void SelectionChanged(); + virtual void OpenRow(TableIndexT inRow); + virtual void ReceiveMessagePane(MSG_Pane* inMsgPane) {SetMessagePane(inMsgPane);}; + virtual void DestroyMessagePane( MSG_Pane* inPane ); + +protected: + virtual void GetCellDisplayData(const STableCell &inCell, ResIDT &ioIcon, CStr255 &ioDisplayString ); + virtual void DrawCellContents( + const STableCell &inCell, + const Rect &inLocalRect); +}; + + +//---------------------------------------------------------------------------- +// ¥ CNameCompletionPicker class +// +//---------------------------------------------------------------------------- +// +static const UInt16 cNameCompletionSaveWindowStatusVersion = 0x0200; + +class CNameCompletionPicker : public CMailNewsWindow + , public LListener +{ +public: + typedef CMailNewsWindow Inherited; + + enum { class_ID = 'NcWn', pane_ID = class_ID, res_ID = 8970 }; + + enum { + paneID_OkButton = 'BtOk', + paneID_CancelButton = 'Canc', + paneID_NameCompletionTable = 'Tabl' + }; + + // Dialog handler + static int DisplayDialog(LEditField* inEditField, MSG_Pane* inPane, CMailNewsContext* inContext, int inNumResults); + + // Stream creator method + CNameCompletionPicker(LStream *inStream); + virtual ~CNameCompletionPicker(); + virtual ResIDT GetStatusResID() const { return res_ID; } + virtual CNSContext* CreateContext() const { return (CNSContext*)mLastReceivedContext; } + virtual void CalcStandardBoundsForScreen( + const Rect &inScreenBounds, + Rect &outStdBounds) const; + virtual void DoClose(); + virtual void ListenToMessage( + MessageT inMessage, + void *ioParam); + + +protected: + + // Overriden methods + virtual void FinishCreateSelf(); + virtual Boolean HandleKeyPress(const EventRecord &inKeyEvent); + + // Utility methods + virtual CMailFlexTable* GetActiveTable() { return mNameCompletionTable; } + virtual void ReadWindowStatus(LStream *inStatusData); + virtual void WriteWindowStatus(LStream *outStatusData); + virtual UInt16 GetValidStatusVersion() const { return cNameCompletionSaveWindowStatusVersion;} + virtual void ReceiveComposeWindowParameters() { + mEditField = mLastReceivedEditField; + mPickerPane = mLastReceivedPickerPane; + mContext = mLastReceivedContext; + mNumResults = mLastReceivedNumResults; + } + + // Instance variables + CNameCompletionTable *mNameCompletionTable; + LEditField* mEditField; + MSG_Pane* mPickerPane; + CMailNewsContext* mContext; + int mNumResults; + +private: + static LEditField* mLastReceivedEditField; + static MSG_Pane* mLastReceivedPickerPane; + static CMailNewsContext* mLastReceivedContext; + static int mLastReceivedNumResults; +}; +#endif //MOZ_NEWADDR diff --git a/mozilla/cmd/macfe/MailNews/AddressBook/UAddressBookUtilities.cp b/mozilla/cmd/macfe/MailNews/AddressBook/UAddressBookUtilities.cp new file mode 100644 index 00000000000..a7c4710a3c4 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/AddressBook/UAddressBookUtilities.cp @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +// UAddressBookUtilities.cp + +#include "UAddressBookUtilities.h" +#ifdef MOZ_NEWADDR +#include "MailNewsgroupWindow_Defines.h" +#include "resgui.h" +#include "CMailFlexTable.h" +#include "UMailSelection.h" +#include "CAddressBookViews.h" // Need for pane ID's that should be fixed + +//------------------------------------------------------------------------------ +// ¥ GetABCommand +//------------------------------------------------------------------------------ +// Convert a powerplant command into a AB_CommandType +// +AB_CommandType UAddressBookUtilites::GetABCommand( CommandT inCommand ) +{ + switch ( inCommand ) + { + + case cmd_NewMailMessage: + case cmd_ComposeMailMessage: return AB_NewMessageCmd; + case cmd_ImportAddressBook: return AB_ImportCmd; + case cmd_SaveAs: return AB_SaveCmd; +// case cmd_Close: return AB_CloseCmd; + + case cmd_NewAddressBook: return AB_NewAddressBook; + case cmd_NewDirectory: return AB_NewLDAPDirectory; + + case cmd_Undo: return AB_UndoCmd; + case cmd_Redo: return AB_RedoCmd; + + case UAddressBookUtilites::cmd_DeleteEntry: return AB_DeleteCmd; + +// case return AB_LDAPSearchCmd, + + case CAddressBookPane::eCol0: return AB_SortByColumnID0; + case CAddressBookPane::eCol1: return AB_SortByColumnID1; + case CAddressBookPane::eCol2: return AB_SortByColumnID2; + case CAddressBookPane::eCol3: return AB_SortByColumnID3; + case CAddressBookPane::eCol4: return AB_SortByColumnID4; + case CAddressBookPane::eCol5: return AB_SortByColumnID5; + case CAddressBookPane::cmd_SortAscending: return AB_SortAscending; + case CAddressBookPane::cmd_SortDescending: return AB_SortDescending; + + case cmd_NewAddressCard: return AB_AddUserCmd; + case cmd_NewAddressList: return AB_AddMailingListCmd; + case cmd_EditProperties: return AB_PropertiesCmd; + case cmd_ConferenceCall: return AB_CallCmd; + // case return AB_ImportLdapEntriesCmd + default: return AB_CommandType( invalid_command ); + } +} + +//------------------------------------------------------------------------------ +// ¥ ABCommandStatus +//------------------------------------------------------------------------------ +// Determine if a command should be enabled. +// +// +void UAddressBookUtilites::ABCommandStatus( + CMailFlexTable* inTable, + AB_CommandType inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +{ + if( inCommand != UAddressBookUtilites::invalid_command ) + { + XP_Bool selectable; + MSG_COMMAND_CHECK_STATE checkedState; + const char* display_string = nil; + XP_Bool plural; + + // Get table selection + CMailSelection selection; + inTable->GetSelection(selection); + + // Check command status + if ( AB_CommandStatusAB2( + inTable->GetMessagePane(), + inCommand, + (MSG_ViewIndex*)selection.GetSelectionList(), + selection.selectionSize, + &selectable, + &checkedState, + &display_string, + &plural) + >= 0) + { + outEnabled = (Boolean)selectable; + outUsesMark = true; + if (checkedState == MSG_Checked) + outMark = checkMark; + else + outMark = 0; + + if (display_string && *display_string) + *(CStr255*)outName = display_string; + } + } +} + +//------------------------------------------------------------------------------ +// ¥ ABCommand +//------------------------------------------------------------------------------ +// Execute a menu command +// NOTE: this takes a PowerPlant Command +// +Boolean UAddressBookUtilites::ABCommand( CMailFlexTable* inTable, AB_CommandType inCommand) +{ + Assert_( inTable ); + Boolean enabled, usesMark; + Char16 mark; + Str255 name; + if ( inCommand == invalid_command ) + return AB_INVALID_COMMAND; + // Check to see if the command was enabled for this table + UAddressBookUtilites::ABCommandStatus( inTable, inCommand, enabled, usesMark, mark, name ); + if ( !enabled ) + return AB_INVALID_COMMAND; + + // Get table Selection + CMailSelection selection; + inTable->GetSelection(selection); + + // Execute the command + return AB_CommandAB2( inTable->GetMessagePane(), inCommand, + (MSG_ViewIndex*)selection.GetSelectionList(), selection.selectionSize ); +} +#endif diff --git a/mozilla/cmd/macfe/MailNews/AddressBook/UAddressBookUtilities.h b/mozilla/cmd/macfe/MailNews/AddressBook/UAddressBookUtilities.h new file mode 100644 index 00000000000..bee459e1992 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/AddressBook/UAddressBookUtilities.h @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +// UAddressBookUtilities.h + +#pragma once +#include "MailNewsAddressBook.h" +#include "abcom.h" +#ifdef MOZ_NEWADDR + + +class CMailFlexTable; +//------------------------------------------------------------------------------ +// ¥ UAddressBookUtilites +//------------------------------------------------------------------------------ +// +class UAddressBookUtilites +{ +public: + enum { // Command IDs + cmd_NewAddressCard = 'NwCd' // New Card + , cmd_NewAddressList = 'NwLs' // New List + , cmd_NewAddressBook = 'NwAb' + , cmd_NewDirectory = 'NwDr' + , cmd_HTMLDomains = 'HtDm' // domains dialog. + , cmd_EditProperties = 'EdPr' // Edit properties for a list or card + , cmd_DeleteEntry = 'DeLe' // Delete list or card + , cmd_ComposeMailMessage = 'Cmps' // Compose a new mail message + , cmd_ConferenceCall = 'CaLL' + , cmd_ViewMyCard = 'vmyC' + , invalid_command = 0xFFFFFFFF + }; + + static AB_CommandType GetABCommand( CommandT inCommand ); + static void ABCommandStatus( + CMailFlexTable* inTable, AB_CommandType inCommand, + Boolean &outEnabled, Boolean &outUsesMark, + Char16 &outMark, Str255 outName + ); + static Boolean ABCommand( CMailFlexTable* inTable, AB_CommandType inCommand); +}; + + +// The following stack based classes are designed to help with the memory managment +// of attributes return from the backend for the Continer and Entry attributes + +//------------------------------------------------------------------------------ +// ¥ StABEntryAttribute +//------------------------------------------------------------------------------ +// Handles memory management for ABEntryAttributes +// +class StABEntryAttribute +{ +public: + StABEntryAttribute ( MSG_Pane* inPane, MSG_ViewIndex inIndex, AB_AttribID inAttrib) + { + if(AB_GetEntryAttributeForPane ( inPane, inIndex-1, inAttrib , &mValue ) != AB_SUCCESS ) + mValue = NULL; + } + + StABEntryAttribute() + { + if ( mValue ) + AB_FreeEntryAttributeValue ( mValue ); + } + + char* GetChar() const { return ( mValue ? mValue->u.string : NULL ); } + Int16 GetShort() const { return ( mValue ? mValue->u.shortValue : 0 ) ;} + XP_Bool GetBoolean() const { return ( mValue ? mValue->u.boolValue : false ); } + AB_EntryType GetEntryType() const { return( mValue ? mValue->u.entryType : AB_Person); } + +private: + AB_AttributeValue *mValue ; +}; + +//------------------------------------------------------------------------------ +// ¥ StABContainerAttribute +//------------------------------------------------------------------------------ +// Handles memory management for ABEContainerAttributes +// +class StABContainerAttribute +{ +public: + StABContainerAttribute ( MSG_Pane* inPane, MSG_ViewIndex inIndex, AB_ContainerAttribute inAttrib) + { + + if( AB_GetContainerAttributeForPane ( inPane, inIndex-1, inAttrib , &mValue ) != AB_SUCCESS ) + mValue = NULL; + } + + StABContainerAttribute() + { + if ( mValue ) + AB_FreeContainerAttribValue ( mValue ); + } + + char* GetChar() const {return( mValue ? mValue->u.string : NULL ); } + Int32 GetNumber() const { return( mValue ? mValue->u.number: 0 ) ;} + AB_ContainerInfo* GetContainerInfo() const { return( mValue ? mValue->u.container : NULL ); } + AB_ContainerType GetContainerType() const { return( mValue ? mValue->u.containerType : AB_PABContainer ); } + +private: + AB_ContainerAttribValue *mValue ; +}; + +#endif diff --git a/mozilla/cmd/macfe/MailNews/AddressBook/temp.txt b/mozilla/cmd/macfe/MailNews/AddressBook/temp.txt new file mode 100644 index 00000000000..8ec30810548 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/AddressBook/temp.txt @@ -0,0 +1,3 @@ +total 8 +drwxr-xr-x 2 sfraser nuucp 4096 May 18 20:47 CVS +-rw-r--r-- 1 sfraser nuucp 0 May 18 20:48 temp.txt diff --git a/mozilla/cmd/macfe/MailNews/CBiffButtonAttachment.cp b/mozilla/cmd/macfe/MailNews/CBiffButtonAttachment.cp new file mode 100644 index 00000000000..429aca804d9 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CBiffButtonAttachment.cp @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CBiffButtonAttachment.cp + +#include "CBiffButtonAttachment.h" + +#include "LSharable.h" +#include "CButton.h" +#include "CCheckMailContext.h" + +#include + +void SetIconState(LControl* inControl, ResIDT inID); + +//====================================== +#pragma mark --------- class CBiffButtonAttachment +//====================================== + +//----------------------------------- +CBiffButtonAttachment::CBiffButtonAttachment(LStream* inStream) +//----------------------------------- +: LAttachment(inStream) +, mButton(nil) +{ + for (int i = MSG_BIFF_NewMail; i <= MSG_BIFF_Unknown; i++) + *inStream >> mResIDList[i]; + SetMessage(msg_Nothing); // Don't bother calling me, ever. + LAttachable *host = LAttachable::GetDefaultAttachable(); + mButton = dynamic_cast(host); + Assert_(mButton); + if (!mButton) return; +#ifndef MOZ_LITE + CCheckMailContext* theContext = CCheckMailContext::Get(); + theContext->AddUser(this); + theContext->AddListener(this); + // Initialize the icon according to the current state! + SetIconState(mButton, mResIDList[theContext->GetState()]); +#endif // MOZ_LITE +} // CBiffButtonAttachment::CBiffButtonAttachment + +//----------------------------------- +CBiffButtonAttachment::~CBiffButtonAttachment() +//----------------------------------- +{ +#ifndef MOZ_LITE + CCheckMailContext* theContext = CCheckMailContext::Get(); + theContext->RemoveListener(this); + theContext->RemoveUser(this); +#endif // MOZ_LITE +} // CBiffButtonAttachment::~CBiffButtonAttachment + +//----------------------------------- +void SetIconState(LControl* inControl, ResIDT inID) +// sets the icon id of the control according to this. +//----------------------------------- +{ + if (!inID) + return; + CButton* button = dynamic_cast(inControl); + if (button) + { + // CButton case + button->SetGraphicID(inID); + } + else + { + // LGAIconSuiteControl case (may need to support others later...) + LGAIconSuiteControl* lgaButton = dynamic_cast(inControl); + if (lgaButton) + lgaButton->SetIconResourceID(inID); + } + inControl->Refresh(); +} + +//----------------------------------- +void CBiffButtonAttachment::ListenToMessage(MessageT inMessage, void* ioParam) +//----------------------------------- +{ + if (inMessage != CCheckMailContext::msg_MailNotificationState) + return; + SetIconState(mButton, mResIDList[*(MSG_BIFF_STATE*)ioParam]); +} // CBiffButtonAttachment::ListenToMessage + +//----------------------------------- +void CBiffButtonAttachment::ExecuteSelf( + MessageT /* inMessage */, + void* /* ioParam */) +//----------------------------------- +{ + // We have nothing to do, and because we set our message to msg_Nothing, we'll never + // be called. However, I don't want to export this empty method from PP just to satisfy + // the linker. +} diff --git a/mozilla/cmd/macfe/MailNews/CBiffButtonAttachment.h b/mozilla/cmd/macfe/MailNews/CBiffButtonAttachment.h new file mode 100644 index 00000000000..eb7873c8c37 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CBiffButtonAttachment.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CBiffButtonAttachment.h + +#pragma once + +#include "LAttachment.h" + +class LControl; + +//====================================== +class CBiffButtonAttachment : public LAttachment, public LListener +// Just attach this to a control. It will listen for biff broadcasts and +// set the icon id of its host button accordingly. There is no ExecuteSelf method, +// we are only using this attachment so we can add it in Constructor, and it +// will figure out which button it is attached to. +//====================================== +{ +public: + enum { class_ID = 'BfAt' }; + CBiffButtonAttachment(LStream* inStream); + virtual ~CBiffButtonAttachment(); + virtual void ListenToMessage(MessageT inMeesage, void* ioParam); +protected: + virtual void ExecuteSelf( + MessageT inMessage, + void *ioParam); +protected: + LControl* mButton; + ResIDT mResIDList[3]; // indexed by MSG_BIFF_STATE +}; // class CBiffButtonAttachment + diff --git a/mozilla/cmd/macfe/MailNews/CCaption.cp b/mozilla/cmd/macfe/MailNews/CCaption.cp new file mode 100644 index 00000000000..71a7f24960f --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CCaption.cp @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CCaption.cp + +#include "CCaption.h" +#include +#include "PascalString.h" +#include +#include + +/////////////////////////////////////////////////////////////////// +// constants +const short booleanStringResID = 901; // resource ID of the boolean strings in macfe.r + +/////////////////////////////////////////////////////////////////// +// CCaption + +void CCaption::DrawSelf() +{ + Rect frame; + CalcLocalFrameRect(frame); + + StColorPenState theColorPenState; + StColorPenState::Normalize (); + StTextState theTextState; + + Int16 just = UTextTraits::SetPortTextTraits(mTxtrID); + + // ¥ Save off the text color as setup by the TextTrait + RGBColor textColor; + ::GetForeColor(&textColor); + ApplyForeAndBackColors(); + ::RGBForeColor(&textColor); + + // the following code adapted from LGARadioButton.cp + + // ¥ Loop over any devices we might be spanning and handle the drawing + // appropriately for each devices screen depth + StDeviceLoop theLoop ( frame ); + Int16 depth; + while ( theLoop.NextDepth ( depth )) + { + if ( depth < 4 ) // ¥ BLACK & WHITE + { + if ( !IsEnabled() ) + { + // ¥ If the caption is dimmed then we use the grayishTextOr + // transfer mode to draw the text + ::TextMode ( grayishTextOr ); + } + } + else if ( depth >= 4 ) // ¥ COLOR + { + if ( !IsEnabled() ) + { + // ¥ If the control is dimmed then we have to do our own version of the + // grayishTextOr as it does not appear to work correctly across + // multiple devices + RGBColor textColor2 = UGraphicsUtilities::Lighten( &textColor ); + ::TextMode ( srcOr ); + ::RGBForeColor ( &textColor2 ); + } + } + + UTextDrawing::DrawWithJustification((Ptr)&mText[1], mText[0], frame, just); + } +} + +void CCaption::EnableSelf() +{ + Draw( nil ); +} + +void CCaption::DisableSelf() +{ + Draw( nil ); +} + +/////////////////////////////////////////////////////////////////// +// CListenerCaption + +// Default constructor +CListenerCaption::CListenerCaption( LStream *inStream ) : labelNum( default_menu_item ), + CCaption ( inStream ) +{ +} + +// Default destructor +CListenerCaption::~CListenerCaption() +{ +} + +// Change label +void +CListenerCaption::ChangeText( const LabelNum& newLabelNum ) +{ + Str255 string; + ::GetIndString( string, resourceID, newLabelNum ); + // needs check and exception + SetDescriptor( string ); + labelNum = newLabelNum; +} + +// Return the label num +LabelNum +CListenerCaption::GetLabelNum() const +{ + return labelNum; +} + +// Return the label num +void +CListenerCaption::SetLabelNum( const LabelNum& newLabelNum ) +{ + labelNum = newLabelNum; +} + +// Override of the ListenToMessage method +// +// *** Needs exceptions +// +void +CListenerCaption::ListenToMessage( MessageT inMessage, void *ioParam) +{ + if( mMsg_changeText == inMessage ) + { + LabelNum menuItem = *( static_cast< LabelNum* >( ioParam ) ); + ChangeText( menuItem ); + } +} + +// Needs to be called before using this class +// +// *** Needs exceptions +// +void +CListenerCaption::Init( const short strResID, const MessageT& getNew_msg ) +{ + if( getNew_msg ) + mMsg_changeText = getNew_msg; + + if( strResID ) + resourceID = strResID; +} diff --git a/mozilla/cmd/macfe/MailNews/CCaption.h b/mozilla/cmd/macfe/MailNews/CCaption.h new file mode 100644 index 00000000000..ad0064ae9ab --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CCaption.h @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CCaption.h + +#pragma once + +#include +#include + +class LStream; + +typedef Int32 LabelNum; + +// CCaption +// +// Moved from meditdlg.cp to be used as base class for the Listener/Lcaption derived +// class ( CListenerCaption ). +// +// ---- +// +// this class is draw a caption in a disabled state if appropriate +// + +class CCaption : public LCaption +{ + public: + enum { class_ID = 'CapD' }; + + CCaption( LStream* inStream ) : LCaption ( inStream ) {}; + + virtual void DrawSelf(); + virtual void EnableSelf(); + virtual void DisableSelf(); +}; + +// CListenerCaption +// +// A simple label class that is also a listener. It listens to +// messages and changes its content (i.e. text ). +// +// *** The Init method needs to be called before this class can be used. +// +// *** Needs exceptions support + +class CListenerCaption : public CCaption, public LListener +{ + public: + enum { class_ID = 'CCap' }; // class id + enum { default_menu_item = 1 }; // initial defualt menu item + + CListenerCaption( LStream *inStream ); + virtual ~CListenerCaption(); + + virtual void Init( const short strResID, const MessageT& getNew_msg ); + + virtual void ChangeText( const LabelNum& newLabelNum ); + virtual void ListenToMessage( MessageT inMessage, void *ioParam ); + + LabelNum GetLabelNum() const; + void SetLabelNum( const LabelNum& newLabelNum ); + + protected: + + private: + LabelNum labelNum; + short resourceID; + MessageT mMsg_changeText; +}; diff --git a/mozilla/cmd/macfe/MailNews/CCheckMailContext.cp b/mozilla/cmd/macfe/MailNews/CCheckMailContext.cp new file mode 100644 index 00000000000..760aa06d75c --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CCheckMailContext.cp @@ -0,0 +1,248 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CCheckMailContext.cp + +#include "CCheckMailContext.h" + +// FE +#include "PascalString.h" + +// XP +#include "prefapi.h" + +extern "C" int GetBiffPrefs(const char* prefnode, void* instanceData); + +//====================================== +// class CCheckMailContext +// +// Context for biff, regular checking for mail. +// Should only be instantiated once. +//====================================== + +CCheckMailContext* CCheckMailContext::sCheckMailContext = nil; + +//----------------------------------- +CCheckMailContext* CCheckMailContext::Get() +//----------------------------------- +{ + if (!sCheckMailContext) + new CCheckMailContext(); + return sCheckMailContext; +} + +#if 0 // seems to be obsolete. +//----------------------------------- +extern "C" int GetBiffPrefs(const char* prefnode, void* /*instanceData*/) +//----------------------------------- +{ + return 0; +} +#endif // 0 + +//----------------------------------- +CCheckMailContext::CCheckMailContext() +//----------------------------------- +: CMailNewsContext(MWContextBiff) +, mCheckMailState(MSG_BIFF_Unknown) +, mOutstandingNotification(false) +, mMailNotifyIcon(nil) +{ + // should only create one + Assert_(sCheckMailContext == nil); + sCheckMailContext = this; + memset(&mNotification, 0, sizeof(NMRec)); +} + +//----------------------------------- +/* static */ void CCheckMailContext::Initialize(void* inUser) +//----------------------------------- +{ + Boolean firstUser = (sCheckMailContext == nil); + CCheckMailContext* biff = CCheckMailContext::Get(); + biff->AddUser(inUser); // must do before initialize call; + Assert_(sCheckMailContext); + if (firstUser) + MSG_BiffInit(*sCheckMailContext, MSG_GetPrefsForMaster(GetMailMaster())); +#if 0 // seems to be obsolete. + GetBiffPrefs(nil, nil); + PREF_RegisterCallback("mail.check_new_mail", GetBiffPrefs, this); + PREF_RegisterCallback("mail.check_time", GetBiffPrefs, this); +#endif // 0 +} // CCheckMailContext::Initialize + +//----------------------------------- +/* static */ void CCheckMailContext::Release(void* inUser) +//----------------------------------- +{ + if (sCheckMailContext) + sCheckMailContext->RemoveUser(inUser); // and delete; +} // CCheckMailContext::Release + +//----------------------------------- +CCheckMailContext::~CCheckMailContext() +//----------------------------------- +{ + Assert_(sCheckMailContext == this); + MSG_BiffCleanupContext(*this); + sCheckMailContext = nil; + RemoveNotification(); +} + +#if 0 +//----------------------------------- +void CCheckMailContext::SetState(MSG_BIFF_STATE state) +// Sets biff state and does all the ui fun to reflect the change +//----------------------------------- +{ + Assert_(sCheckMailContext != nil); + sCheckMailContext->DoSetState(state); +} +#endif + +//----------------------------------- +void CCheckMailContext::SuspendResume() +// End Mac notification of new mail, called on suspend/resume +// +// Actually "resets" notification in for the case +// where we've become the front process having +// posted a notification when not the front process. +// +// This allows us to move the blinking icon from the +// process menu to the apple menu when we become frontmost. + +// NOT ANY MORE!!! +// This logic breaks when you want Dogbert to beep on new mail when +// it's in the background _or_ foreground. This logic also breaks +// when trying to move the blinking icon to the process menu on a suspend +// since we are still the foreground app. +//----------------------------------- +{ +#if 0 + if (sCheckMailContext) + sCheckMailContext->DoSuspendResume(); +#endif +} + +#if 0 +//----------------------------------- +void CCheckMailContext::DoSuspendResume() +//----------------------------------- +{ + RemoveNotification(); + if (GetState() == MSG_BIFF_NewMail) + InstallNotification(); +} +#endif + +//----------------------------------- +void CCheckMailContext::SetState(MSG_BIFF_STATE state) +// Sets biff state and does all the ui fun to reflect the change +//----------------------------------- +{ + if (state != GetState()) + { + mCheckMailState = state; + BroadcastMessage(msg_MailNotificationState, &state); + + // Flash notification icon + if (state == MSG_BIFF_NewMail) + InstallNotification(); + else + RemoveNotification(); + } +} + +//----------------------------------- +void CCheckMailContext::RemoveNotification() +//----------------------------------- +{ + if (mOutstandingNotification) + { + ::NMRemove(&mNotification); + mOutstandingNotification = false; + } + if (mNotification.nmSound) + { + ::DisposeHandle(mNotification.nmSound); + mNotification.nmSound = nil; + } +} + +//----------------------------------- +void CCheckMailContext::InstallNotification() +// If we're not the front process, blink our icon in the process menu and beep. +// If we are the front process, blink our icon in the apple menu (no beep) +//----------------------------------- +{ + if (!mOutstandingNotification) + { + ProcessSerialNumber front, current; + ::GetCurrentProcess(¤t); + ::GetFrontProcess(&front); + Boolean mozillaIsInFront; + OSErr err = ::SameProcess(¤t, &front, &mozillaIsInFront); + + if (err == noErr) + { + if (mMailNotifyIcon == nil) + err = ::GetIconSuite(&mMailNotifyIcon, BIFF_NOTIFY_ICONSUITE, kSelectorSmall8Bit | kSelectorSmall1Bit); + + if (err == noErr && mMailNotifyIcon != nil) + { + Handle soundToPlay = nil; // using nil will play no sound. +// if (!mozillaIsInFront) Always play sound if one specified + { + CStr31 soundName; // constructor initializes to empty. +#define USING_PREF_SOUND 1 +#if USING_PREF_SOUND + char buffer[256]; + int charsReturned = sizeof(buffer); + PREF_GetCharPref("mail.notification.sound", buffer, &charsReturned); + if (strlen(buffer) < sizeof(soundName)) + soundName = buffer; // (PascalString takes care of conversion) +#else + soundName = "Quack"; +#endif + soundToPlay = ::GetNamedResource('snd ', soundName); + if (soundToPlay) + { + ::HNoPurge(soundToPlay); + ::DetachResource(soundToPlay); + } + } + // set up the notification manager record + mNotification.qType = nmType; + mNotification.nmMark = (mozillaIsInFront) ? 0 : 1; // if we're the front process, blink in apple menu, else in process menu + mNotification.nmIcon = mMailNotifyIcon; + mNotification.nmSound = soundToPlay; // can be nil + mNotification.nmStr = nil; // no dialog + mNotification.nmResp = nil; // no callback + + if ( ::NMInstall(&mNotification) == noErr) + mOutstandingNotification = true; + } + } + } +} + + + + diff --git a/mozilla/cmd/macfe/MailNews/CCheckMailContext.h b/mozilla/cmd/macfe/MailNews/CCheckMailContext.h new file mode 100644 index 00000000000..78b81fe42f7 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CCheckMailContext.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CCheckMailContext.h + +#pragma once + +#include "CMailNewsContext.h" + +//====================================== +class CCheckMailContext : public CMailNewsContext +//====================================== +{ +public: + enum + { + BIFF_NOTIFY_ICONSUITE = 15242, + msg_MailNotificationState = 'malS' + }; +private: // must call GetCheckMailContext + CCheckMailContext(); +public: + + static CCheckMailContext* Get(); + static void SuspendResume(); + static void Initialize(void* inUser); + static void Release(void* inUser); + void SetState(MSG_BIFF_STATE state); + MSG_BIFF_STATE GetState() const { return mCheckMailState; } + +protected: +#if 0 + void DoSuspendResume(); +#endif + void RemoveNotification(); + void InstallNotification(); // do fun mac stuff + virtual ~CCheckMailContext(); + +// -- Data -- +protected: + static CCheckMailContext *sCheckMailContext; + MSG_BIFF_STATE mCheckMailState; + + Boolean mOutstandingNotification; + NMRec mNotification; + Handle mMailNotifyIcon; +}; // class CCheckMailContext diff --git a/mozilla/cmd/macfe/MailNews/CComposeAddressTableView.cp b/mozilla/cmd/macfe/MailNews/CComposeAddressTableView.cp new file mode 100644 index 00000000000..dab7259e64b --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CComposeAddressTableView.cp @@ -0,0 +1,2094 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CComposeAddressTableView.cp + +#include "abcom.h" +#include "addrbook.h" +#include "CComposeAddressTableView.h" + +#include +#include +#include "NetscapeDragFlavors.h" +#include "msgcom.h" +#include "uprefd.h" +#include "UGraphicGizmos.h" +#include "CMailComposeWindow.h" +#include "CMailFlexTable.h" +#include "dirprefs.h" +#include "CComposeSession.h" +#include "CDrawingState.h" +#include "CPasteSnooper.h" +#include "resgui.h" +#include "CStandardFlexTable.h" +#include "LTableRowSelector.h" +#include "CTSMEditField.h" +#include "libi18n.h" +#include "prefapi.h" + +#include + +const Int16 kIconWidth = 16; +const Int16 kIconMargin = 1; + +const Int16 gsPopup_ArrowHeight = 5; // Actual height of the arrow +const Int16 gsPopup_ArrowWidth = 9; // Actual width of the arrow at widest + +const Int16 kLeftPaddingOfAddressTypeLabel = 14; +const Int16 kLeftPaddingOfPopupArrow = 7; +const Int16 kRightPaddingOfPopupArrow = 3; + +const Int16 kTextDistFromTop = 2; + +#define kAddressTypeTextTraitsID 10009 +#define kAddressTypePopupTextTraitsID 130 + +#ifdef MOZ_NEWADDR +#include "CNameCompletionPicker.h" + + + +//---------------------------------------------------------------------------- +// ¥ CMailAddressEditField class +// +//---------------------------------------------------------------------------- +// +CMailAddressEditField::CMailAddressEditField(LStream* inStream) + : CTSMEditField(inStream) + , mTimeLastCall(0) + , mIsCompletedName(false) + , mNeedToAutoCompleteAtIdleTime(false) + , mMailNewsContext(nil) + , mPickerPane(nil) + , mDisplayString(nil) + , mHeaderString(nil) + , mExpandHeaderString(nil) +{ +} + + +CMailAddressEditField::~CMailAddressEditField() +{ + if (mMailNewsContext) + mMailNewsContext->RemoveUser(this); + + if (mPickerPane) + AB_ClosePane(mPickerPane); + + XP_FREE(mDisplayString); + XP_FREE(mHeaderString); + XP_FREE(mExpandHeaderString); +} + + +void CMailAddressEditField::Init() +{ + mNeedToAutoCompleteAtIdleTime = false; +} + + +//---------------------------------------------------------------------------- +// ¥ CMailAddressEditField::FinishCreateSelf +// +//---------------------------------------------------------------------------- +// +void CMailAddressEditField::FinishCreateSelf() +{ + LEditField::FinishCreateSelf(); + + AddAttachment(new CPasteSnooper("\r\t ", ",,\a")); + +//¥ CMailComposeWindow* window = dynamic_cast(LWindow::FetchWindowObject(GetMacPort())); +//¥ ThrowIfNil_(window); + + // Can't reuse the Composition context: we need an Address Book context + mMailNewsContext = new CMailNewsContext(MWContextAddressBook); + mMailNewsContext->SetWinCSID(INTL_DocToWinCharSetID(mMailNewsContext->GetDefaultCSID())); + mMailNewsContext->AddListener(this); + mMailNewsContext->AddUser(this); + StartListening(); + + (void)AB_CreateABPickerPane(&mPickerPane, *mMailNewsContext, CMailNewsContext::GetMailMaster(), 10); + if (mPickerPane) + CMailCallbackListener::SetPane(mPickerPane); +} + + +//---------------------------------------------------------------------------- +// ¥ CMailAddressEditField::HandleKeyPress +// +//---------------------------------------------------------------------------- +// +Boolean CMailAddressEditField::HandleKeyPress(const EventRecord& inKeyEvent) +{ + Boolean keyHandled = true; + EKeyStatus theKeyStatus = keyStatus_Input; + Int16 theKey = inKeyEvent.message & charCodeMask; + + if (inKeyEvent.modifiers & cmdKey) + theKeyStatus = keyStatus_PassUp; // Always pass up when Cmd-Key down + else + if (mKeyFilter != nil) + { + Int16 theChar = inKeyEvent.message & charCodeMask; + + theKeyStatus = (*mKeyFilter)(mTextEditH, inKeyEvent.message, + theChar, inKeyEvent.modifiers); + } + + if (mIsCompletedName) + { + if (mNumResults >= 2) + { + switch (theKey) + { + case char_Enter: + case char_Tab: + case char_Return: + XP_Bool showPicker; + PREF_GetBoolPref("ldap_1.autoComplete.showDialogForMultipleMatches", &showPicker); + if (showPicker) + { + // save location of the edited cell + LWindow* window = LWindow::FetchWindowObject(GetMacPort()); + CComposeAddressTableView* addressTable = dynamic_cast(window->FindPaneByID('Addr')); + ThrowIfNil_(addressTable); + STableCell editCell = addressTable->GetEditCell(); + + // save caret pos & selection + TEHandle teH = GetMacTEH(); + short selStart = (*teH)->selStart; + short selEnd = (*teH)->selEnd; + + // display the name completion picker + int selectedRow = CNameCompletionPicker::DisplayDialog(this, mPickerPane, mMailNewsContext, mNumResults); + + // restore the edited cell + window->Activate(); + window->Select(); + addressTable->StartEditCell(editCell); + + if (selectedRow > 0) + { + // if we have a result, put it in the cell + AB_NameCompletionCookie* cookie = AB_GetNameCompletionCookieForIndex(mPickerPane, selectedRow-1); + NameCompletionBECallbackFunction(cookie, 1, this); + } + else + { + // otherwise restore the previous selection + StFocusAndClipIfHidden focus(this); + ::TESetSelect(selStart, selEnd, teH); + return true; // key was handled + } + } + break; + } + } + + StFocusAndClipIfHidden focus(this); + switch (theKeyStatus) + { + // If the user deletes a char, we need to do 2 deletes. + // The first deletes the nickname completion. + // The second actually erases a character. + case keyStatus_TEDelete: + if ((**mTextEditH).selEnd > 0) + { + if (mTypingAction == nil) + { + mTypingAction = new LTETypingAction(mTextEditH, this, this); + PostAction(mTypingAction); + } + + if (mTypingAction != nil) + mTypingAction->BackwardErase(); + else + ::TEKey(char_Backspace, mTextEditH); + // UserChangedText(); + } + break; + + // If the user moves the cursor, we need to + // get rid of the "multiple names found". + case keyStatus_TECursor: + if (mNumResults >= 2) + { + ::TEKey(char_Backspace, mTextEditH); + mIsCompletedName = false; + switch (theKey) + { + case char_UpArrow: + case char_DownArrow: + return LCommander::HandleKeyPress(inKeyEvent); + + } + } + break; + } + } + else + { + if (theKeyStatus == keyStatus_TEDelete) + { + CStr255 currentText; + GetDescriptor(currentText); + if (currentText.Length() == 0) + return LCommander::HandleKeyPress(inKeyEvent); + } + switch (theKey) + { + case char_UpArrow: + case char_DownArrow: + return LCommander::HandleKeyPress(inKeyEvent); + + } + } + keyHandled = Inherited::HandleKeyPress(inKeyEvent); + return keyHandled; +} + + +//---------------------------------------------------------------------------- +// ¥ CMailAddressEditField::SpendTime +// +//---------------------------------------------------------------------------- +// +void CMailAddressEditField::SpendTime(const EventRecord& inMacEvent) +{ + if (mNeedToAutoCompleteAtIdleTime) + { + if ((::TickCount() - mTimeLastCall) > ::LMGetKeyThresh() / 2) + StartNameCompletion(); + } + Inherited::SpendTime(inMacEvent); +} + + +//---------------------------------------------------------------------------- +// ¥ CMailAddressEditField::UserChangedText +// +//---------------------------------------------------------------------------- +// +void CMailAddressEditField::UserChangedText() +{ + mIsCompletedName = false; + mNeedToAutoCompleteAtIdleTime = true; + + if ((::TickCount() - mTimeLastCall) > ::LMGetKeyThresh() / 2) + StartNameCompletion(); + + mTimeLastCall = ::TickCount(); +} + + +//---------------------------------------------------------------------------- +// ¥ CMailAddressEditField::NameCompletionBECallbackFunction +// +//---------------------------------------------------------------------------- +// +int CMailAddressEditField::NameCompletionBECallbackFunction(AB_NameCompletionCookie* cookie, int numResults, void* FECookie) +{ + CMailAddressEditField* editField = (CMailAddressEditField*)FECookie; + if (cookie != NULL) + { + char* displayString = AB_GetNameCompletionDisplayString(cookie); + char* headerString = AB_GetHeaderString(cookie); + char* expandHeaderString = AB_GetExpandedHeaderString(cookie); + + editField->SetNameCompletionResults(numResults, displayString, headerString, expandHeaderString); + + AB_FreeNameCompletionCookie(cookie); + } + return(0); +} + + +//---------------------------------------------------------------------------- +// ¥ CMailAddressEditField::SetNameCompletionResults +// +//---------------------------------------------------------------------------- +// +void CMailAddressEditField::SetNameCompletionResults(int numResults, char* displayString, char* headerString, char* expandHeaderString) +{ + XP_FREE(mDisplayString); + XP_FREE(mHeaderString); + XP_FREE(mExpandHeaderString); + + TEHandle teH = GetMacTEH(); + short caretPos = (*teH)->selStart; + + mNumResults = numResults; + if (numResults == 1) + { + mDisplayString = displayString; + mHeaderString = headerString; + mExpandHeaderString = expandHeaderString; + + SetDescriptor(CStr255(mDisplayString)); + } + else // we got 2 or more results + { + CStr255 userEntry; + GetDescriptor(userEntry); + userEntry[0] = caretPos; + + mDisplayString = XP_STRDUP(userEntry); + mHeaderString = XP_STRDUP(userEntry); + mExpandHeaderString = XP_STRDUP(userEntry); + + CStr255 multipleHitsStr; + CStr255 resultStr; + ::GetIndString(multipleHitsStr, 10610, 2); + resultStr = mDisplayString + multipleHitsStr; + SetDescriptor(resultStr); + } + StFocusAndClipIfHidden focus(this); + ::TESetSelect(caretPos, 1000, teH); + + mIsCompletedName = true; +} + + +//---------------------------------------------------------------------------- +// ¥ CMailAddressEditField::StartNameCompletion +// +//---------------------------------------------------------------------------- +// +void CMailAddressEditField::StartNameCompletion() +{ + mNeedToAutoCompleteAtIdleTime = false; + + if (!mPickerPane) + return; + + CStr255 currentText; + GetDescriptor(currentText); + (void)AB_NameCompletionSearch(mPickerPane, (char*)currentText, NameCompletionBECallbackFunction, (void*)this); +} + + +//---------------------------------------------------------------------------- +// ¥ CMailAddressEditField::PaneChanged +// +//---------------------------------------------------------------------------- +// + void CMailAddressEditField::PaneChanged( + MSG_Pane* /*inPane*/, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 /*value*/) +{ + switch (inNotifyCode) + { + } +} + + +//---------------------------------------------------------------------------- +// ¥ CMailAddressEditField::FinalizeEdit +// +//---------------------------------------------------------------------------- +// +const char* CMailAddressEditField::FinalizeEdit() +{ + if (mIsCompletedName) + { + mIsCompletedName = false; + + //¥¥¥ should also return mExpandHeaderString + return XP_STRDUP(mHeaderString); + } + else + { + CStr255 currentText; + GetDescriptor(currentText); + return XP_STRDUP(currentText); + } +} + + + + +#else //MOZ_NEWADDR +#pragma mark - +CMailAddressEditField::CMailAddressEditField( LStream* inStream ) + : CTSMEditField( inStream ) + , mTimeLastCall(0) + , mDirServerList(nil) + , mAddressBook(nil) + , mIsCompletedName( false ) + , mCheckAddress( false ) + , mEntryID( MSG_MESSAGEIDNONE ) +{ +} + +void CMailAddressEditField::Init() +{ + mCheckAddress = false; + mEntryID = MSG_MESSAGEIDNONE; +} + +//----------------------------------- +void CMailAddressEditField::FinishCreateSelf() +// override to do address-book name completion. +//----------------------------------- +{ + LEditField::FinishCreateSelf(); + Try_ + { + AddAttachment(new CPasteSnooper("\r\t ", ",,\a")); + XP_List *list = FE_GetDirServers(); + ThrowIf_( DIR_GetComposeNameCompletionAddressBook(list, &mDirServerList) != 0); + #if 0 // Currently FE_GetAddressbook ignores the message pane. + // Since we would like to use this control else where( AddressPicker, and Mailing list window + // its not a good idea for this class to be dependant on being created from a CMailComposeWindow + CMailComposeWindow* window + = dynamic_cast( + LWindow::FetchWindowObject(GetMacPort()) + ); + ThrowIfNil_(window); + + mAddressBook = FE_GetAddressBook( + window->GetComposeSession()->GetMSG_Pane()); + #else if + mAddressBook = FE_GetAddressBook( NULL); + #endif + ThrowIfNil_(mAddressBook); + } + Catch_(inErr) + { + mDirServerList = nil; + mAddressBook = nil; + } + EndCatch_ +} + +void +CMailAddressEditField::SpendTime( + const EventRecord& inMacEvent ) +{ + + if( mCheckAddress ) + { + UInt32 currentTime = ::TickCount(); + if (currentTime - mTimeLastCall > ::LMGetKeyThresh() / 2) + UserChangedText(); + } + LEditField::SpendTime( inMacEvent ); +} +//----------------------------------- +void CMailAddressEditField::UserChangedText() +// override to do address-book name completion. +//----------------------------------- +{ + UInt32 currentTime = ::TickCount(); + mIsCompletedName = false; + mEntryID = MSG_MESSAGEIDNONE; + mCheckAddress = true; + if (currentTime - mTimeLastCall > ::LMGetKeyThresh() / 2) + { + ABID field; + if (mAddressBook) + { + mCheckAddress = false; + // Got a personal address book + if (mDirServerList) + { + // Got a DIR_Server entry for the address book + CStr255 currentText; + GetDescriptor(currentText); + if (::AB_GetIDForNameCompletion( + mAddressBook, + mDirServerList, + &mEntryID, + &field, + currentText) == 0 && mEntryID != MSG_MESSAGEIDNONE) + { + char newText[256]; + newText[0] = '\0'; + if (field == ABNickname) + AB_GetNickname(mDirServerList, mAddressBook, mEntryID, newText); + else + AB_GetFullName(mDirServerList, mAddressBook, mEntryID, newText); + if (newText[0]) + { + // whew! + TEHandle teH = GetMacTEH(); + short selStart = (*teH)->selStart; + SetDescriptor(CStr255(newText)); + FocusDraw(); + ::TESetSelect(selStart, 1000, teH); + mIsCompletedName = true; + } + } + } + } + } + mTimeLastCall = ::TickCount(); +} // CMailAddressEditField::UserChangedText + +Boolean CMailAddressEditField::HandleKeyPress( const EventRecord& inKeyEvent) +{ + Boolean keyHandled = true; + EKeyStatus theKeyStatus = keyStatus_Input; + Int16 theKey = inKeyEvent.message & charCodeMask; + + if (inKeyEvent.modifiers & cmdKey) + { // Always pass up when the command + theKeyStatus = keyStatus_PassUp; // key is down + } + else if (mKeyFilter != nil) + { + Int16 theChar = inKeyEvent.message & charCodeMask; + + theKeyStatus = (*mKeyFilter)(mTextEditH, inKeyEvent.message, + theChar, inKeyEvent.modifiers); + } + + if( mIsCompletedName ) + { + + StFocusAndClipIfHidden focus(this); + // If the user deletes a char we need to do 2 deletes + // The first deletes the nickname completion + // the second actually erases a character + + switch (theKeyStatus) + + case keyStatus_TEDelete:{ + if ((**mTextEditH).selEnd > 0) { + if (mTypingAction == nil) { + mTypingAction = new LTETypingAction(mTextEditH, this, this); + PostAction(mTypingAction); + } + if (mTypingAction != nil) { + mTypingAction->BackwardErase(); + } else { + ::TEKey(char_Backspace, mTextEditH); + } + //UserChangedText(); + } + break; + } + } + else + { + if( theKeyStatus == keyStatus_TEDelete ) + { + CStr255 currentText; + GetDescriptor(currentText); + if( currentText.Length() == 0 ) + return LCommander::HandleKeyPress(inKeyEvent); + } + switch (theKey) + { + case char_UpArrow: + case char_DownArrow: + return LCommander::HandleKeyPress(inKeyEvent); + + } + } + keyHandled = LEditField::HandleKeyPress(inKeyEvent); + return keyHandled; +} +//----------------------------------- +const char* CMailAddressEditField::FinalizeEdit() +//----------------------------------- +{ + char* fullName = nil; + CStr255 currentText; + GetDescriptor(currentText); + if (mDirServerList) + { + if ( mEntryID == MSG_MESSAGEIDNONE ) + { + ABID field; + ::AB_GetIDForNameCompletion( + mAddressBook, + mDirServerList, + &mEntryID, + &field, + currentText); + } + if ( mEntryID != MSG_MESSAGEIDNONE) + { + ::AB_GetExpandedName(mDirServerList, mAddressBook, mEntryID, &fullName); + } + } + if (!fullName) + return XP_STRDUP(currentText); + return fullName; +} // CMailAddressEditField::FinalizeEdit() + +#endif //MOZ_NEWADDR + +#pragma mark - + +CComposeAddressTableView::CComposeAddressTableView(LStream* inStream) : + LTableView(inStream), mInputField(NULL) +, LDragAndDrop(GetMacPort(), this) +, mCurrentlyAddedToDropList(true) // because LDragAndDrop constructor adds us. +, mDirty(false) +, mTextTraits( 10610 ) +, mEditCell( 0, 0) +, mAddressTypeHasFocus(false) +, mTypedownTable(nil) +{ +} + +CComposeAddressTableView::~CComposeAddressTableView() +{ + XP_FREEIF(mTypedownTable); +} + +void CComposeAddressTableView::SetUpTableHelpers() +{ + SetTableGeometry(new LTableMultiGeometry(this, CalculateAddressTypeColumnWidth(), 16)); + SetTableSelector(new LTableRowSelector(this)); + SetTableStorage(new CComposeAddressTableStorage(this)); +} + +void CComposeAddressTableView::FinishCreateSelf() +{ + SetUpTableHelpers(); + + Assert_(mTableStorage != NULL); + + InsertCols(2, 1, NULL, 0, false); + AdjustColumnWidths(); + // Get the Editfield + mInputField = dynamic_cast< CMailAddressEditField*>( FindPaneByID ('Aedt') ); + // highlight colors + RGBColor ignore; + UGraphicGizmos::CalcWindowTingeColors(GetMacPort(), ignore, mDropColor ); + + // Set up table for type-ahead + MenuHandle popupMenu = (MenuHandle)::GetResource('MENU', kAddressTypeMenuID ); + ThrowIfNil_(popupMenu); + + Int16 numItems = CountMItems(popupMenu); + + // this will hold lower case of first letters, with trailing null + mTypedownTable = (char *)XP_ALLOC(numItems + 1); + + for (Int16 i = 1; i <= numItems; i ++) // menu items are 1-based + { + CStr255 itemName; + GetMenuItemText(popupMenu, i, itemName); + mTypedownTable[i - 1] = tolower(itemName[1]); + } + mTypedownTable[numItems] = '\0'; + + ReleaseResource((Handle)popupMenu); +} + +// adjust address column to be width of pane minus width of +// first column +void CComposeAddressTableView::AdjustColumnWidths() +{ + SDimension16 frameSize; + UInt16 firstColWidth = GetColWidth(1); + GetFrameSize(frameSize); + SetColWidth(frameSize.width - firstColWidth, 2, 2); +} + +void CComposeAddressTableView::ResizeFrameBy( + Int16 inWidthDelta, + Int16 inHeightDelta, + Boolean inRefresh) +{ + LTableView::ResizeFrameBy( inWidthDelta, inHeightDelta, inRefresh); + AdjustColumnWidths(); + // May have to adjust edit field + if( mEditCell.row!=0 ) + { + Rect cellRect; + if( GetLocalCellRect( mEditCell, cellRect)) + { + // put edit field over cell + cellRect.left += kIconWidth + 3; + cellRect.top +=1; + mInputField->ResizeFrameTo(cellRect.right - cellRect.left, + cellRect.bottom - cellRect.top, + false); + mInputField->PlaceInSuperImageAt(cellRect.left, cellRect.top, false); + } + } +} + + +void CComposeAddressTableView::ClickCell(const STableCell &inCell, const SMouseDownEvent &inMouseDown) +{ + StopInputToAddressColumn(); // on any click + + if (inCell.col == 1) + { +#if 0 + LCommander::SwitchTarget(this); + LTableView::ClickCell(inCell, inMouseDown); //Does nothing +#endif //0 + + EAddressType oldAddressType = GetRowAddressType( inCell.row ); + + // load and install the menu + MenuHandle popupMenu = ::GetMenu( kAddressTypeMenuID ); + if (popupMenu) + { + Int16 result; + + FocusDraw(); + + { + StSysFontState theSysFontState; + + // ¥ We also detach the resource so we don't run into problems + // for cases where there are multiple popups referencing the same + // menu resource. + ::DetachResource( (Handle) popupMenu); + ::InsertMenu( popupMenu, hierMenu); + + Point where = inMouseDown.wherePort; + PortToGlobalPoint( where ); + Rect cellRect; + if (GetLocalCellRect( inCell, cellRect )) + { + where = topLeft( cellRect ); + ::LocalToGlobal( &where ); + } + + // Adjust text traits to be the appropriate text traits for the popup + + theSysFontState.SetTextTraits(/* kAddressTypePopupTextTraitsID */ ); + + Int16 defaultChoice = oldAddressType; + result = ::PopUpMenuSelect( popupMenu, where.v, where.h, defaultChoice ); + } + + // ¥ Clean up the menu + ::DeleteMenu( kAddressTypeMenuID ); + ::DisposeHandle( (Handle) popupMenu); // OK because we detached it. + + STableCell addressCell( inCell.row, 2); + + if (result) + { + if( CellIsSelected( addressCell) ) + SetSelectionAddressType( EAddressType( result ) ); + else + { + SetRowAddressType( inCell.row, EAddressType( result ) ); + RefreshCell( addressCell ); + BroadcastMessage( msg_AddressChanged, NULL ); + } + } + RefreshCell( inCell ); + } + } + else + { // in address cell + // was the click on the icon? + Rect iconRect; + GetLocalCellRect( inCell, iconRect ); + iconRect.right = iconRect.left + kIconWidth+kIconMargin; + if (!(::PtInRect(inMouseDown.whereLocal, &iconRect)) ) + StartEditCell( inCell ); + } +} + +void CComposeAddressTableView::DrawSelf() +{ + Rect r; + if ( CalcLocalFrameRect( r ) ) + EraseRect( &r ); + Int16 bottom = r.bottom; + Int16 right = r.right; + // What Color should these lines be? + RGBColor lightTinge,darkTinge; + UGraphicGizmos::CalcWindowTingeColors(GetMacPort(), lightTinge, darkTinge); + ::RGBForeColor(&lightTinge); + // vertical seperator + short width=mTableGeometry->GetColWidth(1); + ::MoveTo(width,r.top); + ::LineTo(width,r.bottom); + // Horizontal seperators + STableCell topLeftCell, botRightCell; + FetchIntersectingCells(r, topLeftCell, botRightCell); + GetLocalCellRect(topLeftCell,r); + + short height = mTableGeometry->GetRowHeight(1); // All Rows are the same size + short currentHeight = r.top - height; + if (height > 0) + { + + while (currentHeightmenuWidth + kLeftPaddingOfPopupArrow + gsPopup_ArrowWidth + kRightPaddingOfPopupArrow; + + // Release the menu + + ::ReleaseResource((Handle) menu); + + return width; +} + +void CComposeAddressTableView::DrawCell(const STableCell &inCell, const Rect &inLocalRect) +{ + EAddressType type = GetRowAddressType( inCell.row ); + + if ( inCell.col == 1 ) + { + // Draw the hilite region if we are taking keyboard input + if (mAddressTypeHasFocus) + { + STableCell addressCell(mEditCell.row, 1); + Rect cellRect; + + if (GetLocalCellRect(addressCell, cellRect)) + { + StColorPenState stateSaver; + StRegion hiliteRgn; + + cellRect.top ++; + ::RectRgn(hiliteRgn, &cellRect); + + { + StRegion tempRgn(hiliteRgn); + + ::InsetRgn(tempRgn, 2, 2); + ::DiffRgn(hiliteRgn, tempRgn, hiliteRgn); + } + + UDrawingUtils::SetHiliteModeOn(); + ::InvertRgn(hiliteRgn); + } + } + + // Draw the address type string + + UTextTraits::SetPortTextTraits(kAddressTypeTextTraitsID); + + MoveTo(inLocalRect.left + kLeftPaddingOfAddressTypeLabel, inLocalRect.bottom - 4); + + MenuHandle menu = ::GetMenu( kAddressTypeMenuID ); + if ( menu ) + { + // ¥ We also detach the resource so we don't run into problems + // for cases where there are multiple popups referencing the same + // menu resource. + ::DetachResource( (Handle) menu); + Str255 typeText; + :: GetMenuItemText( menu, type, typeText); + ::DrawString( typeText ); + ::DisposeHandle( (Handle) menu); // OK because we detach + } + + // Draw the drop flag + + Rect iconRect; + + UInt32 vDiff = (inLocalRect.bottom - inLocalRect.top - (gsPopup_ArrowHeight - 1)) >> 1; + iconRect.top = inLocalRect.top + vDiff; + iconRect.bottom = iconRect.top + gsPopup_ArrowHeight - 1; + iconRect.left = inLocalRect.right - gsPopup_ArrowWidth - 1 - kRightPaddingOfPopupArrow; + iconRect.right = iconRect.left + gsPopup_ArrowWidth - 1; + + UGraphicGizmos::DrawPopupArrow(iconRect, true, true, false); + } + else + { + // Draw the icon + Rect iconRect = inLocalRect; + iconRect.left += kIconMargin; + iconRect.right = iconRect.left + kIconWidth; + + ResIDT iconResIDT = 15260; // Default is a person id + if( type == eNewsgroupType ) + iconResIDT = 15231 ; + CStandardFlexTable::DrawIconFamily(iconResIDT , 16, 16, 0, iconRect); + + if (inCell.row != mEditCell.row) + { + // we're trying to draw an address string cell that + // isn't the one with the LEditField over it. + char* addr = NULL; + Uint32 size = sizeof(addr); + GetCellData(inCell, &addr, size); + + if (addr) + { + Uint8 stringLen = LString::CStringLength(addr); + UTextTraits::SetPortTextTraits( mTextTraits ); + + Rect textRect = inLocalRect; + + textRect.left = iconRect.right + 3; + textRect.right = inLocalRect.right; + textRect.top += kTextDistFromTop; + + UGraphicGizmos::PlaceTextInRect( addr, stringLen, textRect, teFlushLeft, teFlushTop ); //flushtop to match the TEUpdate used by LEditField + } + } + } +} + + +//----------------------------------- +void CComposeAddressTableView::HiliteRow( TableIndexT inRow, Boolean inUnderline ) +//----------------------------------- +{ + STableCell cell; + Rect r; + + cell.row = inRow; + cell.col = 1; + + GetLocalCellRect(cell, r); + + r.left = 0; + r.right = mFrameSize.width; + + if (inUnderline) + { + r.top = r.bottom-1; + r.bottom += 1; + } + + //RGBColor savedHiliteColor; + UDrawingUtils::SetHiliteModeOn(); + //LMGetHiliteRGB(&savedHiliteColor); + //::HiliteColor(&mDropColor); + ::InvertRect(&r); + //::HiliteColor(&savedHiliteColor); +} + +void CComposeAddressTableView::DirectInputToAddressColumn() +{ + if (mAddressTypeHasFocus) return; + + mAddressTypeHasFocus = true; + SwitchTarget(this); + + STableCell theCell(mEditCell.row, 1); + RefreshCell(theCell); + theCell.col = 2; + RefreshCell(theCell); +} + + +void CComposeAddressTableView::StopInputToAddressColumn() +{ + if (!mAddressTypeHasFocus) return; + + mAddressTypeHasFocus = false; + SwitchTarget(mInputField); + + STableCell theCell(mEditCell.row, 1); + RefreshCell(theCell); + theCell.col = 2; + RefreshCell(theCell); +} + + +Boolean CComposeAddressTableView::HandleKeyPress(const EventRecord &inKeyEvent) +{ + Boolean keyHandled = true; + Boolean cmdKeyDown = (inKeyEvent.modifiers & cmdKey) != 0; + Char16 c = inKeyEvent.message & charCodeMask; + + if (mAddressTypeHasFocus) + { + EAddressType newAddressType = eNoAddressType; + char *thisOne = mTypedownTable; + + for (Int16 i = 0; *thisOne; i++, thisOne ++) + { + if (tolower(c) == *thisOne) + { + newAddressType = (EAddressType)(i + 1); // enum is 1-based + break; + } + } + + if (newAddressType != eNoAddressType) + { + STableCell addressCell(mEditCell.row, 1); + SetRowAddressType(mEditCell.row, newAddressType); + RefreshCell(addressCell); + BroadcastMessage( msg_AddressChanged, NULL ); + return keyHandled; + } + } + + switch (c) + { + case char_Backspace: + case char_FwdDelete: + DeleteSelection(); + return true; + break; + + case char_UpArrow: + EndEditCell(); + STableCell previousCell( GetFirstSelectedCell().row, 2 ); + if( previousCell.row != 1 ) + { + previousCell.row--; + StartEditCell( previousCell ); + } + keyHandled = true; + break; + + + /*EndEditCell(); + STableCell nextCell( GetFirstSelectedCell().row, 2 ); + nextCell.row++; + StartEditCell( nextCell ); + keyHandled = true; + break;*/ + case char_Enter: + if( !mEditCell.IsNullCell() ) + { + EndEditCell(); + STableCell cellToSelect( GetFirstSelectedCell().row, 2); + SelectCell( cellToSelect ); + } + else + { + STableCell cellToSelect( GetFirstSelectedCell().row, 2); + StartEditCell( cellToSelect ); + } + break; + + case char_LeftArrow: + if (!mAddressTypeHasFocus && cmdKeyDown) + DirectInputToAddressColumn(); + else + keyHandled = LCommander::HandleKeyPress(inKeyEvent); + break; + case char_RightArrow: + if (mAddressTypeHasFocus && cmdKeyDown) + StopInputToAddressColumn(); + else + keyHandled = LCommander::HandleKeyPress(inKeyEvent); + break; + case char_DownArrow: + case char_Return: + case char_Tab: + EventRecord tabKeyEvent(inKeyEvent); + tabKeyEvent.message &= ~charCodeMask; // clear char code bits + tabKeyEvent.message |= char_Tab; + Boolean backward = ((inKeyEvent.modifiers & shiftKey) != 0); + TableIndexT nRows, nCols; + GetTableSize(nRows, nCols); + EndEditCell(); + StopInputToAddressColumn(); + STableCell cell( GetFirstSelectedCell().row, 2); + if (backward && cell.row <= 1) + { + keyHandled = LCommander::HandleKeyPress(tabKeyEvent); // tab out of here + break; + } + else if (!backward && cell.row >= nRows) + { + // If we were editing, and we tabbed out... + if (cell.row != 0) + { + // Tabbing out of a non-empty addressee field adds another new addressee + char* addr = NULL; + Uint32 size = sizeof(addr); + GetCellData( cell, &addr, size); + if (addr && *addr && (c != char_Tab) ) + { + EAddressType addressType = GetRowAddressType( nRows ); + char emptyString[1]="\0"; + InsertNewRow( addressType, emptyString, true ); + keyHandled = true; + } + else + { + keyHandled = LCommander::HandleKeyPress(tabKeyEvent); // tab out of here + } + } + else if (nRows==0) + { + InsertNewRow(true, true); + keyHandled = true; + } + break; + } + else if (backward) + cell.row--; + else + cell.row++; + StartEditCell( cell ); + break; + + default: + keyHandled = LCommander::HandleKeyPress(inKeyEvent); + } // switch + return keyHandled; +} + +Boolean CComposeAddressTableView::ClickSelect(const STableCell &inCell, + const SMouseDownEvent &inMouseDown) +{ + + if( inCell.col == 1 ) + { + return true; + } + else + { + SwitchTarget( this ); + EndEditCell(); + if( !mEditCell.IsNullCell() ) + UnselectCell( mEditCell ); + return LTableView::ClickSelect( inCell, inMouseDown ); + } +} + +void +CComposeAddressTableView::ClickSelf( + const SMouseDownEvent &inMouseDown) +{ + STableCell hitCell; + SPoint32 imagePt; + + LocalToImagePoint(inMouseDown.whereLocal, imagePt); + + if (GetCellHitBy(imagePt, hitCell)) { + if (ClickSelect(hitCell, inMouseDown)) { + ClickCell(hitCell, inMouseDown); + } + + } else { // Click is outside of any Cell + InsertNewRow( true, true); // so create a new cell + } +} + +void CComposeAddressTableView::FillInRow( Int32 row, EAddressType inAddressType, const char* inAddress) +{ + STableCell cell; + cell.col = 1; + cell.row = row; + SetCellData( cell, &inAddressType, sizeof(inAddressType)); + RefreshCell( cell ); + cell.col = 2; + SetCellData( cell, inAddress, sizeof(inAddress)); + RefreshCell( cell ); +} + +// Takes in a string explodes it, and inserts it into the table +Int32 CComposeAddressTableView::CommitRow( const char* inString, STableCell cell) +{ + EAddressType originAddressType = GetRowAddressType( cell.row ); + Boolean dirty = false; + // Update and do name replacement + // Explode string to 1 entry per line + MSG_HeaderEntry *returnList = nil; + Int32 numberItems = MSG_ExplodeHeaderField( originAddressType, inString, &returnList ); + Int32 numberRowsAdded = 0; + + if ( numberItems > 1) + { + InsertRows( (numberItems - 1), cell.row, NULL, NULL, false ); + dirty = true; + } + // We will get 0 if we passed in a Null string, this occurs when the user + // deletes an exsiting email address. Should the row be removed? + if (numberItems == 0) + { + if( !dirty ) + { + char *addr = NULL; + Int32 size = sizeof(addr); + GetCellData( cell, &addr, size); + if( addr !=NULL ) + dirty = true; + } + SetCellData( cell, inString, sizeof(inString) ); + } + else if ( numberItems != -1 ) + { + for (Int32 currentItem = 0; currentItem < numberItems; currentItem ++ ) + { + if ( !dirty ) + { + char *addr = NULL; + Int32 size = sizeof(addr); + GetCellData( cell, &addr, size); + if( addr == NULL ) + dirty = true; + else + dirty = XP_STRCMP( addr, returnList[currentItem].header_value); + } + // Use input field to do name expansion on each entry + char* unexpandedName = returnList[currentItem].header_value; + char* actualName = unexpandedName; + EAddressType addressType = (EAddressType)returnList[currentItem].header_type; // this is just what we passed in above + + if ( XP_STRNCASECMP(unexpandedName, "mailto:", 7) == 0 ) + { + actualName = unexpandedName + 7; + //addressType = eToType; + } + else if ( (XP_STRNCASECMP(unexpandedName, "news://", 7) == 0) || + (XP_STRNCASECMP(unexpandedName, "snews://", 8) == 0) ) + { + // test whether this URL points to the current server being used for this message. + // if not, and there is no other news recipient so far, set the news post url + // in the backend, and just enter the group name + //MSG_SetCompHeader( , MSG_NEWSPOSTURL_HEADER_MASK, ); etc + + // no backend support yet, so just put in the whole URL + actualName = unexpandedName; + addressType = eNewsgroupType; + } + else if ( (XP_STRNCASECMP(unexpandedName, "news:", 5) == 0) || + (XP_STRNCASECMP(unexpandedName, "snews:", 6) == 0) ) + { + actualName = unexpandedName + 5; + addressType = eNewsgroupType; + } + + mInputField->Init(); + mInputField->SetDescriptor( CStr255( actualName ) ); + const char* expandedName = mInputField->FinalizeEdit(); + + FillInRow( cell.row, addressType, expandedName ); + + XP_FREE( unexpandedName ); + XP_FREE( (char*)expandedName ); + cell.row++; + } + XP_FREE ( returnList ); + numberRowsAdded = numberItems-1; + } + + if ( dirty ) + BroadcastMessage( msg_AddressChanged, NULL ); + + Refresh(); + + return numberRowsAdded; +} + +Int32 CComposeAddressTableView::FinalizeAddrCellEdit() +{ + int32 numRowsAdded; + const char* fullName = nil; + Try_ + { + fullName = mInputField->FinalizeEdit(); + if (fullName) + { + numRowsAdded = CommitRow( fullName, mEditCell ); + XP_FREE((char*)fullName); + } + } + Catch_(inErr) + { + XP_FREEIF((char*)fullName); + } + EndCatch_ + return numRowsAdded; +} + +Boolean CComposeAddressTableView::ObeyCommand(CommandT inCommand, void *ioParam) +{ + switch (inCommand) + { + case msg_TabSelect: + STableCell cell(1, 2); + StartEditCell( cell ); + return true; + } + return LCommander::ObeyCommand(inCommand, ioParam); +} + +void CComposeAddressTableView::HideEditField() +{ + mInputField->Hide(); + LView::OutOfFocus(NULL); + RefreshCell(mEditCell); +} + +#if 0 +void CComposeAddressTableView::ListenToMessage(MessageT inMessage, void* ioParam) +{ +#if 0 // NO longer have buttons to listen to + switch(inMessage) + { + case msg_AddAddressee: + InsertNewRow(true, true); + break; + case msg_RemoveAddressee: + DeleteSelection(); + break; + } +#endif //0 +} +#endif + +void CComposeAddressTableView::InsertNewRow(Boolean inRefresh, Boolean inEdit) +{ + char* dummy = NULL; + EndEditCell(); + TableIndexT numRows; + GetNumRows(numRows); + InsertRows(1, numRows++, &dummy, sizeof(dummy), inRefresh); + STableCell cell(numRows, 2); + if (inEdit) + StartEditCell(cell); +} + +void CComposeAddressTableView::InsertNewRow(EAddressType inAddressType, const char* inAddress, Boolean inEdit) +{ + InsertNewRow(false, false); + STableCell cell; + cell.col = 1; + GetNumRows(cell.row); + SetCellData(cell, &inAddressType, sizeof(inAddressType)); + RefreshCell(cell); + cell.col = 2; + SetCellData(cell, inAddress, sizeof(inAddress)); // this makes a copy of the data, getting the size from strlen + RefreshCell(cell); + if ( inEdit ) + StartEditCell( cell ); +} + + +void CComposeAddressTableView::StartEditCell(const STableCell &inCell) +{ + UnselectAllCells(); + Assert_( inCell.col == 2 ); + Rect cellRect; + + // Make sure the edit filed is fully visible + ScrollCellIntoFrame(inCell); + // now move edit field over cell + + if (GetLocalCellRect(inCell, cellRect)) + { + // put edit field over cell + mInputField->PutInside(this); + + Rect textRect = cellRect; + + textRect.left += kIconWidth + 3; + textRect.top += kTextDistFromTop; // text is drawn flush top + + mInputField->ResizeFrameTo(textRect.right - textRect.left, + textRect.bottom - textRect.top, + false); + mInputField->PlaceInSuperImageAt(textRect.left, cellRect.bottom - (textRect.bottom - textRect.top), false); + mInputField->Init(); + // set text of edit field + char *addr = NULL; + Uint32 size = sizeof(addr); + GetCellData(inCell, &addr, size); + if (addr) + { + LStr255 pstr(addr); + mInputField->SetDescriptor(pstr); + mInputField->SelectAll(); + } + else + mInputField->SetDescriptor("\p"); + mInputField->Show(); + SwitchTarget(mInputField); + mEditCell.row = inCell.row; + mEditCell.col = inCell.col; + } +} + + +void CComposeAddressTableView::EndEditCell() +{ + if (!mEditCell.IsNullCell()) + { // Copy text out of LEditField into corresponding cell if + // user was editing an address + Int32 numRowsAdded = FinalizeAddrCellEdit(); + HideEditField(); + RefreshCell( mEditCell ); + mEditCell.row += numRowsAdded; + SelectCell( mEditCell ); + mEditCell.row = 0; + mEditCell.col = 0; + } +} + +void CComposeAddressTableView::TakeOffDuty() +{ + // This causes unwanted behaviour -- CommitRow when window is deactivated, + // and causes deactivated windows to lose keyboard focus when reactivated + + + // Unfortunately, we have to do it so that rows are committed when the user + // clicks in the subject or the body without tabbing out of the field first + EndEditCell(); + UnselectAllCells(); +} + +// Commands +void CComposeAddressTableView::DeleteSelection() +{ + EndEditCell(); + TableIndexT currentRow; + TableIndexT nextRow; + nextRow = mTableSelector->GetFirstSelectedRow(); + if( nextRow == 0 ) + return; + while ( nextRow != 0) + { + currentRow = nextRow; + RemoveRows(1, currentRow, true); + nextRow = mTableSelector->GetFirstSelectedRow(); + } + // Set the focus + STableCell currentCell( currentRow, 2 ); + currentCell.col = 2 ; + if( currentCell.row != 1 ) + { + currentCell.row -= 1; + } + else if (mRows == 0 ) + { + InsertNewRow(true, true); + currentCell.row = 1; + } + StartEditCell( currentCell ); +} + +void CComposeAddressTableView::SetSelectionAddressType( EAddressType inAddressType ) +{ + STableCell currentCell; + STableCell currentAddressTypeCell(0,1); + + while ( GetNextSelectedCell( currentCell )) + { + currentAddressTypeCell.row = currentCell.row; + SetRowAddressType( currentAddressTypeCell.row, inAddressType ); + RefreshCell( currentCell ); + RefreshCell( currentAddressTypeCell ); + } + BroadcastMessage( msg_AddressChanged, NULL ); +} // CComposeAddressTableView::SetSelectionAddressType + +// utility functions +void CComposeAddressTableView::GetNumRows(TableIndexT &inRowCount) +{ + TableIndexT colCount; + GetTableSize(inRowCount, colCount); +} + +EAddressType CComposeAddressTableView::GetRowAddressType( TableIndexT inRow ) +{ + EAddressType addressType; + Uint32 size = sizeof( addressType ); + STableCell cell( inRow,1 ); + GetCellData( cell, &addressType, size ); + return addressType; +} // CComposeAddressTableView::GetRowAddressType + + +void CComposeAddressTableView::SetRowAddressType( TableIndexT inRow, EAddressType inAddressType ) +{ + STableCell cell( inRow,1 ); + SetCellData(cell, &inAddressType, sizeof(EAddressType)); + +} // CComposeAddressTableView::GetRowAddressType + + +// utility function to put a comma seperated list of +// addressees of type inAddressType in the table view +// into an LHandleStream +void CComposeAddressTableView::CreateCompHeader(EAddressType inAddressType, LHandleStream& inStream) +{ + TableIndexT rowCount, i = 1; + EAddressType cellType; + STableCell cell; + Uint32 size; + char* addr = NULL; + Boolean first = true; + + GetNumRows(rowCount); + try { + do { + cell.col = 1; + cell.row = i; + size = sizeof(cellType); + GetCellData(cell, &cellType, size); + if (cellType == inAddressType) + { + cell.col = 2; + size = sizeof(addr); + GetCellData(cell, &addr, size); + if (addr) + { + if( strlen( addr ) > 0 ) // ignore cells with no data + { + if (!first) + inStream << (unsigned char) ','; + else + first = false; + inStream.WriteBlock(addr, strlen(addr)); + } + } + } + ++i; + } while (i <= rowCount); + if (inStream.GetMarker() > 0) + // write null terminator if we have data + inStream << (unsigned char) '\0'; + } catch (...) { + // not enough memory to make header! + } +} + +// I hate this. I need to override the default method, because we want +// to ignore the item in drags from CMailFlexTable and descendents that +// contains the selection +Boolean CComposeAddressTableView::DragIsAcceptable(DragReference inDragRef) +{ + Boolean isAcceptable = true; + Boolean gotOneOrMore = false; + + Uint16 itemCount; + ::CountDragItems(inDragRef, &itemCount); + + for (Uint16 item = 1; item <= itemCount; item++) + { + ItemReference itemRef; + ::GetDragItemReferenceNumber(inDragRef, item, &itemRef); + + // ignore flex table selection data + if ( itemRef == CMailFlexTable::eMailNewsSelectionDragItemRefNum ) + { + FlavorFlags flavorFlags; + + if (::GetFlavorFlags( inDragRef, itemRef, kMailNewsSelectionDragFlavor, &flavorFlags) == noErr ) + continue; + } + + isAcceptable = ItemIsAcceptable(inDragRef, itemRef); + if (!isAcceptable) { + break; // Stop looping upon finding an + } // unaccepatable item + + gotOneOrMore = true; + } + + return isAcceptable && gotOneOrMore; +} + +/* + A drag flavor being promoted by Apple for text drags containing mailto + information. Will contain a comma-separated list of email addresses. + One characteristic of this flavor that we don't yet support is that + quotes within the real name part will be escaped, e.g. + + "Joe \"MacHead\" Bloggs" + + Note that the TEXT flavor for this drag contains a tab-separated list. +*/ +#define kAppleMailAddressDragFlavor 'a822' + +// Drag and Drop Support +Boolean CComposeAddressTableView::ItemIsAcceptable( DragReference inDragRef, + ItemReference inItemRef ) +{ + FlavorFlags flavorFlags; + Boolean acceptable = false; + + /* This is no longer used + if (::GetFlavorFlags( inDragRef, inItemRef, kMailAddresseeFlavor, &flavorFlags) == noErr) + acceptable = true ; + */ + + if (::GetFlavorFlags( inDragRef, inItemRef, kAppleMailAddressDragFlavor, &flavorFlags) == noErr) + acceptable = true ; + + if (::GetFlavorFlags( inDragRef, inItemRef, 'TEXT', &flavorFlags) == noErr) + acceptable |= true; + + return acceptable; +} + +void CComposeAddressTableView::ReceiveDragItem( DragReference inDragRef, + DragAttributes /*flags*/, + ItemReference inItemRef, + Rect& itemBounds) +{ + +/* + // Check that correct flavor is present, check to see if the entry is not in the list yet + if ( ::GetFlavorFlags(inDragRef, inItemRef, kMailAddresseeFlavor, &flavorFlags) == noErr ) + { + // Get the ABID of the item being dragged, either person or list + dataSize = sizeof( SAddressDragInfo ); + if ( ::GetFlavorData(inDragRef, inItemRef, kMailAddresseeFlavor, &info, &dataSize, 0) == noErr ) + { + Assert_(dataSize == sizeof( SAddressDragInfo )); + ABook* pABook; + // CMailComposeWindow* window = dynamic_cast(LWindow::FetchWindowObject(GetMacPort())); + pABook = FE_GetAddressBook( NULL ); + pABook->GetFullAddress( info.dir, info.id, &fullName ); + } + } + else + { +*/ + + OSType dragFlavor = 0; + FlavorFlags flavorFlags = 0; + OSErr err = noErr; + + if ( ::GetFlavorFlags (inDragRef, inItemRef, emBookmarkDrag, &flavorFlags) == noErr ) + dragFlavor = emBookmarkDrag; + else if ( ::GetFlavorFlags (inDragRef, inItemRef, kAppleMailAddressDragFlavor, &flavorFlags) == noErr ) + dragFlavor = kAppleMailAddressDragFlavor; + else if ( ::GetFlavorFlags (inDragRef, inItemRef, 'TEXT', &flavorFlags) == noErr ) + dragFlavor = 'TEXT'; + + if ( dragFlavor == 0) + return; + + Size dataSize = 0; + char *buffer = NULL; + + err = ::GetFlavorDataSize (inDragRef, inItemRef, dragFlavor, &dataSize); + ThrowIfOSErr_ (err); // caught by PP handler + + if (dataSize == 0 || dataSize > 4096) + return; + + DragAttributes theDragAttributes; + err = ::GetDragAttributes(inDragRef, &theDragAttributes); + ThrowIfOSErr_ (err); + + buffer = (char *)XP_ALLOC(dataSize + 1); + ThrowIfNil_(buffer); + + err = ::GetFlavorData (inDragRef, inItemRef, dragFlavor, buffer, &dataSize, 0); + if (err != noErr) { + XP_FREE(buffer); + ThrowIfOSErr_(err); + } + + buffer[dataSize] = '\0'; // Terminate the string + + // we need to munge the text to take out white space etc. + // URLs originating in Communicator are like: /r, and we need to distinguish + // that from plain text containing /r from outside. + + char *title = NULL; + + if ( (theDragAttributes & kDragInsideSenderApplication) == 0) // if an external drag + { + // it would be nice to user the CPasteActionSnooper here to clean stuff up, but we don't + // have a TEHandle to work with. + + char *lastChar = &buffer[dataSize - 1]; + while (lastChar > buffer && (*lastChar == '\r' || *lastChar == '\n' || *lastChar == '\t' || *lastChar == ' ') ) + { + lastChar --; + dataSize --; + } + buffer[dataSize] = '\0'; // Re-terminate the string + + //convert \r or \t to comma so subsequent items get separated + lastChar = buffer; + while (*lastChar) { + if (*lastChar == '\r' || *lastChar == '\t') + *lastChar = ','; + lastChar ++; + } + } + else { + // internal drag + title = strchr(buffer, '\r'); // may return NULL + } + + char *addressText = NULL; + + if ( ( XP_STRNCASECMP( buffer, "news:", 5) == 0 ) || + ( XP_STRNCASECMP( buffer, "snews:", 6) == 0 ) || + ( XP_STRNCASECMP( buffer, "mailto:", 7) == 0 ) ) + { + if (title) + *title = '\0'; // use the URL part of the text + + addressText = XP_STRDUP(buffer); + } + else + { + if (title == NULL) + title = buffer; + else + title ++; // skip the /r and use the text part + + addressText = XP_STRDUP( title ); + } + + XP_FREE(buffer); + buffer = NULL; + + if ( addressText != NULL ) + { + // Find Cell which intersects itemBounds + STableCell topLeftCell, bottomRightCell; + FetchIntersectingCells( itemBounds, topLeftCell, bottomRightCell); + + TableIndexT numRows; + GetNumRows( numRows ); + + // Dropped in a cell which doesn't exist + if ( topLeftCell.row > numRows ) + { + InsertNewRow(false, false); + STableCell cell(0, 1); + + GetNumRows(cell.row); + + CommitRow( addressText, cell ); + } + else + { + if( topLeftCell.row == mEditCell.row) + EndEditCell(); + + topLeftCell.col = 2; + char *addr = NULL; + Int32 size = sizeof(addr); + GetCellData(topLeftCell, &addr, size); + Int32 stringSize = strlen( addr) + strlen(addressText) + 2; + char *newCellStr = new char[ stringSize ]; + + if( XP_STRCMP(addr,"\0") ) + { + XP_STRCPY( newCellStr, addr ); + XP_STRCAT( newCellStr,"," ); + XP_STRCAT( newCellStr, addressText ); + } + else + { + XP_STRCPY( newCellStr, addressText ); + } + //SetRowAddressType(topLeftCell.row, addressType); + CommitRow( newCellStr, topLeftCell ); + delete newCellStr; + } + + XP_FREE( addressText ); + } +} + + + +void CComposeAddressTableView::AddDropAreaToWindow(LWindow* inWindow) +{ + if (!mCurrentlyAddedToDropList) + LDropArea::AddDropArea(this, inWindow->GetMacPort()); + mCurrentlyAddedToDropList = true; +} + +void CComposeAddressTableView::RemoveDropAreaFromWindow(LWindow* inWindow) +{ + if (mCurrentlyAddedToDropList) + LDropArea::RemoveDropArea(this, inWindow->GetMacPort()); + mCurrentlyAddedToDropList = false; +} + +void CComposeAddressTableView::InsideDropArea(DragReference inDragRef) +{ + + Point mouseLoc; + SPoint32 imagePt; + TableIndexT newDropRow; + Boolean newIsDropBetweenFolders; + + FocusDraw(); + + ::GetDragMouse(inDragRef, &mouseLoc, NULL); + ::GlobalToLocal(&mouseLoc); + LocalToImagePoint(mouseLoc, imagePt); + + newDropRow = mTableGeometry->GetRowHitBy(imagePt); + + imagePt.v += 3; + newIsDropBetweenFolders = false; + // (newDropRow != mTableGeometry->GetRowHitBy(imagePt)); + + + if (newDropRow != mDropRow || newIsDropBetweenFolders != mIsDropBetweenFolders) + { + TableIndexT nRows, nCols; + + HiliteRow(mDropRow, mIsDropBetweenFolders); + mIsDropBetweenFolders = newIsDropBetweenFolders; + + GetTableSize(nRows, nCols); + if (newDropRow > 0 && newDropRow <= nRows) + { + HiliteRow(newDropRow, mIsDropBetweenFolders); + mDropRow = newDropRow; + } + else { + mDropRow =0; + } + } +} + +void CComposeAddressTableView::LeaveDropArea(DragReference inDragRef) +{ + + FocusDraw(); + if (mDropRow != 0) { + HiliteRow( mDropRow, false ); + } + mDropRow = 0; + LDragAndDrop::LeaveDropArea( inDragRef); +} + +void CComposeAddressTableView::SetTextTraits( ResIDT textTraits ) +{ + mTextTraits = textTraits; + mInputField->SetTextTraitsID(textTraits); + Refresh(); +} + + +#pragma mark - + +CComposeAddressTableStorage::CComposeAddressTableStorage(LTableView* inTableView) : +LTableStorage(inTableView) +{ + try { + mAddrTypeArray = new LArray(sizeof(EAddressType)); + } catch (...) { + mAddrTypeArray = NULL; + } + try { + mAddrStrArray = new LArray(sizeof(char*)); + } catch (...) { + mAddrStrArray = NULL; + } +} + +CComposeAddressTableStorage::~CComposeAddressTableStorage() +{ + delete mAddrTypeArray; + // iterate over mAddrStrArray, deleting address strings + LArrayIterator iter(*mAddrStrArray, LArrayIterator::from_End); + char* str; + while (iter.Previous(&str)) + delete [] str; + // now we can delete address string array + delete mAddrStrArray; +} + +// --------------------------------------------------------------------------- +// ¥ SetCellData +// --------------------------------------------------------------------------- +// Store data for a particular Cell + +void +CComposeAddressTableStorage::SetCellData( + const STableCell &inCell, + const void *inDataPtr, + Uint32 inDataSize) +{ + if ( inCell.col == 1 ) + mAddrTypeArray->AssignItemsAt(1, inCell.row, inDataPtr, inDataSize); + else + { // copy address string and store string in mAddrStrArray + const char* inStr = (const char*)inDataPtr; + size_t strLength = strlen(inStr); + char* str = new char[strLength + 1]; + ::BlockMoveData(inStr, str, strLength); + str[strLength] = '\0'; + char* current; + // if there's already a string in mAddrStrArray, delete it + if (mAddrStrArray->FetchItemAt(inCell.row, ¤t)) + delete [] current; + mAddrStrArray->AssignItemsAt(1, inCell.row, &str, sizeof(str)); + } +} + +// --------------------------------------------------------------------------- +// ¥ GetCellData +// --------------------------------------------------------------------------- +// Retrieve data for a particular Cell +// +// If outDataPtr is nil, pass back the size of the Cell data +// +// If outDataPtr is not nil, it must point to a buffer of at least +// ioDataSize bytes. On output, ioDataSize is set to the minimum +// of the Cell data size and the input value of ioDataSize and that +// many bytes are copied to outDataPtr. + +void +CComposeAddressTableStorage::GetCellData( + const STableCell &inCell, + void *outDataPtr, + Uint32 &ioDataSize) const +{ + LArray* array = (inCell.col == 1) ? mAddrTypeArray : mAddrStrArray; + if (outDataPtr == nil) + { + ioDataSize = array->GetItemSize(inCell.row); + } + else + { + array->FetchItemAt(inCell.row, outDataPtr, ioDataSize); + } +} + + +// --------------------------------------------------------------------------- +// ¥ FindCellData +// --------------------------------------------------------------------------- +// Pass back the Cell containing the specified data. Returns whether +// or not such a Cell was found. +// For a CComposeAddressTableView, I assume the address string column is the only +// interesting data to find + +Boolean +CComposeAddressTableStorage::FindCellData( + STableCell &outCell, + const void *inDataPtr, + Uint32 inDataSize) const +{ + Boolean found = false; + + Int32 dataIndex = mAddrStrArray->FetchIndexOf(inDataPtr, inDataSize); + + if (dataIndex != LArray::index_Bad) { + outCell.col = 2; + outCell.row = dataIndex; + found = true; + } + + return found; +} + + +// --------------------------------------------------------------------------- +// ¥ InsertRows +// --------------------------------------------------------------------------- +// Insert rows into an ArrayStorage. +// +// inDataPtr points to the data for the new cells. Each new cell will +// have the same data. + +void +CComposeAddressTableStorage::InsertRows( + Uint32 inHowMany, + TableIndexT inAfterRow, + const void* /*inDataPtr*/, + Uint32 /*inDataSize*/) +{ + EAddressType type = eToType; + mAddrTypeArray->InsertItemsAt(inHowMany, inAfterRow + 1, &type, sizeof(type)); + char *nothing = NULL; + mAddrStrArray->InsertItemsAt(inHowMany, inAfterRow + 1, ¬hing, sizeof(nothing)); +} + + +// --------------------------------------------------------------------------- +// ¥ RemoveRows +// --------------------------------------------------------------------------- +// Removes rows from an ArrayStorage + +void +CComposeAddressTableStorage::RemoveRows( + Uint32 inHowMany, + TableIndexT inFromRow) +{ + mAddrTypeArray->RemoveItemsAt(inHowMany, inFromRow); + // deallocate strings in mAddrStrArray + char *string = NULL; + Uint32 itemSize = sizeof(string); + for (TableIndexT i = 0; i < inHowMany; i++) + { + mAddrStrArray->FetchItemAt(inFromRow + i, &string, itemSize); + if (string) + delete [] string; + mAddrStrArray->RemoveItemsAt(inHowMany, inFromRow); + } +} + + +// --------------------------------------------------------------------------- +// ¥ GetStorageSize +// --------------------------------------------------------------------------- +// Pass back the number of rows and columns represented by the data +// in an ArrayStorage + +void +CComposeAddressTableStorage::GetStorageSize( + TableIndexT &outRows, + TableIndexT &outCols) +{ + // An Array is one-dimensional. By default, we assume a + // single column with each element being a sepate row. + + outRows = mAddrStrArray->GetCount(); + outCols = 2; + if (outRows == 0) { + outCols = 0; + } +} diff --git a/mozilla/cmd/macfe/MailNews/CComposeAddressTableView.h b/mozilla/cmd/macfe/MailNews/CComposeAddressTableView.h new file mode 100644 index 00000000000..37add1986ee --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CComposeAddressTableView.h @@ -0,0 +1,298 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CComposeAddressTableView.h + +#pragma once + +#include "abcom.h" +#include +#include +#include +#include +#include "MailNewsAddressBook.h" +#include "CTSMEditField.h" +#include "MailNewsCallbacks.h" +#include "CMailNewsContext.h" + +#define kAddressTypeMenuID 10611 + +typedef enum { + eNoAddressType = 0, + eToType, + eCcType, + eBccType, + eReplyType, + eNewsgroupType, + eFollowupType +} EAddressType; + +class ABook; +typedef struct DIR_Server DIR_Server; + +#ifdef MOZ_NEWADDR +//====================================== +class CMailAddressEditField : public CTSMEditField + , public CMailCallbackListener +//====================================== +{ +private: + typedef CTSMEditField Inherited; + +public: + enum { class_ID = 'Aedt' }; + + CMailAddressEditField( LStream* inStream ); + virtual ~CMailAddressEditField(); + + void Init(); + virtual void FinishCreateSelf(); + virtual Boolean HandleKeyPress(const EventRecord &inKeyEvent); + + virtual void SpendTime(const EventRecord& inMacEvent); + virtual void UserChangedText(); // override to do address-book name completion. + virtual void StartNameCompletion(); + virtual void SetNameCompletionResults( + int numResults, + char* displayString, + char* headerString, + char* expandHeaderString); + virtual void PaneChanged( + MSG_Pane* inPane, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value); + + const char* FinalizeEdit(); // user has tabbed out, etc. Returns Mallocked string. + +protected: + static int NameCompletionBECallbackFunction( + AB_NameCompletionCookie* cookie, + int numResults, + void* FECookie); + + +//----- +// Data +//----- +protected: + UInt32 mTimeLastCall; + Boolean mIsCompletedName; + Boolean mNeedToAutoCompleteAtIdleTime; + CMailNewsContext* mMailNewsContext; + MSG_Pane* mPickerPane; + + int mNumResults; + char* mDisplayString; + char* mHeaderString; + char* mExpandHeaderString; +}; + +#else //MOZ_NEWADDR + +//====================================== +class CMailAddressEditField : public CTSMEditField + // ,public LBroadcaster +//====================================== +{ +public: + enum { class_ID = 'Aedt' }; + CMailAddressEditField( LStream* inStream ); + virtual void FinishCreateSelf(); + virtual void UserChangedText(); // override to do address-book name completion. + const char* FinalizeEdit(); // user has tabbed out, etc. Returns Mallocked string. + virtual Boolean HandleKeyPress( const EventRecord &inKeyEvent ); + virtual void SpendTime( const EventRecord& inMacEvent ); + void Init(); +//protected: +// virtual void TakeOffDuty(); + +//----- +// Data +//----- +protected: + UInt32 mTimeLastCall; + ABook* mAddressBook; + DIR_Server* mDirServerList; + Boolean mIsCompletedName; + Boolean mCheckAddress; + ABID mEntryID; + +}; +#endif //MOZ_NEWADDR + + +const MessageT msg_AddressChanged ='AdCh'; + +//====================================== +class CComposeAddressTableView : public LTableView, + public LCommander, + //public LListener, + public LDragAndDrop, + public LBroadcaster +//====================================== +{ +private: + typedef LTableView Inherited; + +public: + enum { class_ID = 'AdTV' }; + CComposeAddressTableView(LStream* inStream); + + virtual ~CComposeAddressTableView() ; + + virtual void FinishCreateSelf(); + virtual Boolean ObeyCommand(CommandT inCommand, void *ioParam = nil); + + void SetUpTableHelpers(); + + void AdjustColumnWidths(); + virtual void ResizeFrameBy( Int16 inWidthDelta, Int16 inHeightDelta, + Boolean inRefresh); + + virtual Boolean HandleKeyPress(const EventRecord &inKeyEvent); + + virtual Boolean ClickSelect( const STableCell &inCell, + const SMouseDownEvent &inMouseDown ); + virtual void ClickSelf( const SMouseDownEvent &inMouseDown ); + // virtual void ListenToMessage(MessageT inMessage, void* ioParam ); + + void InsertNewRow(Boolean inRefresh, Boolean inEdit ); + void InsertNewRow(EAddressType inAddressType, + const char* inAddress, Boolean inEdit = false); + + void FillInRow( Int32 row, EAddressType inAddressType, const char* inAddress); + void StartEditCell(const STableCell &inCell); + void EndEditCell(); + STableCell GetEditCell() {return mEditCell;} + + void CreateCompHeader(EAddressType inAddressType, LHandleStream& inStream); + // Drag and Drop + virtual void InsideDropArea( DragReference inDragRef); + virtual void LeaveDropArea(DragReference inDragRef); + virtual Boolean DragIsAcceptable(DragReference inDragRef); + virtual Boolean ItemIsAcceptable(DragReference inDragRef, ItemReference inItemRef ); + + virtual void ReceiveDragItem( DragReference inDragRef, + DragAttributes flags, + ItemReference inItemRef, + Rect& itemBounds); + void HiliteRow( TableIndexT inRow, Boolean inUnderline ); + + // utility functions for new compose window because attach view + // is inside a tab switcher + void AddDropAreaToWindow(LWindow* inWindow); + void RemoveDropAreaFromWindow(LWindow* inWindow); + // Commands + void DeleteSelection(); + void SetSelectionAddressType( EAddressType inAddressType ); + // utility functions + void GetNumRows(TableIndexT &inRowCount); + EAddressType GetRowAddressType( TableIndexT inRow ); + void SetRowAddressType( TableIndexT inRow, EAddressType inAddressType ); + + void SetTextTraits( ResIDT textTraits ); + +protected: + + void DirectInputToAddressColumn(); + void StopInputToAddressColumn(); + + virtual void ClickCell(const STableCell &inCell, + const SMouseDownEvent &inMouseDown); + + virtual void DrawCell(const STableCell &inCell, + const Rect &inLocalRect); + + virtual void DrawSelf(); + + + + Int32 FinalizeAddrCellEdit(); + Int32 CommitRow( const char* inString, STableCell cell); + void HideEditField(); + + virtual void TakeOffDuty(); + + virtual Uint16 CalculateAddressTypeColumnWidth(); + +//------ +// data +//------ +protected: + + CMailAddressEditField* mInputField; + STableCell mEditCell; + Boolean mCurrentlyAddedToDropList; + Boolean mAddressTypeHasFocus; + char *mTypedownTable; + RGBColor mDropColor; + TableIndexT mDropRow; + Boolean mIsDropBetweenFolders; // changing order + Boolean mDirty; + ResIDT mTextTraits; +}; + + +//====================================== +class CComposeAddressTableStorage : public LTableStorage +//====================================== +{ +public: + CComposeAddressTableStorage(LTableView* inTableView); + virtual ~CComposeAddressTableStorage(); + + virtual void SetCellData( + const STableCell &inCell, + const void *inDataPtr, + Uint32 inDataSize); + virtual void GetCellData( + const STableCell &inCell, + void *outDataPtr, + Uint32 &ioDataSize) const; + virtual Boolean FindCellData( + STableCell &outCell, + const void *inDataPtr, + Uint32 inDataSize) const; + virtual void InsertRows( + Uint32 inHowMany, + TableIndexT inAfterRow, + const void *inDataPtr, + Uint32 inDataSize); + virtual void InsertCols( + + Uint32 /* inHowMany */, + TableIndexT /* inAfterCol */, + const void* /* inDataPtr */, + Uint32 /* inDataSize */) { }; + virtual void RemoveRows( + Uint32 inHowMany, + TableIndexT inFromRow); + virtual void RemoveCols( + Uint32 /* inHowMany */, + TableIndexT /* inFromCol */) { }; + virtual void GetStorageSize( + TableIndexT &outRows, + TableIndexT &outCols); + +protected: + LArray* mAddrTypeArray; + LArray* mAddrStrArray; +}; + diff --git a/mozilla/cmd/macfe/MailNews/CComposeSession.cp b/mozilla/cmd/macfe/MailNews/CComposeSession.cp new file mode 100644 index 00000000000..3e03f6f0aff --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CComposeSession.cp @@ -0,0 +1,452 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CComposeSession.cp + +#include "CComposeSession.h" +#include "CMailNewsContext.h" +#include "CEditView.h" + +// PowerPlant +#include + +// XP +#include "proto.h" // for XP_InterruptContext +#include "addrbook.h" +//#include "aberror.h" +#include "edt.h" +#include "mime.h" +#include "libi18n.h" + +#include "msg_srch.h" // needed for priority +// Mac FE +#include "uerrmgr.h" // ErrorManager +#include "resgui.h" // error string constants +#include "mailNewsgroupWindow_Defines.h" // Tmessages +int FE_QuoteCallback(void* closure, const char* data) +{ + if (data) + { + CComposeSession* session = (CComposeSession*)closure; + // this causes a broadcast from session to compose window + // how do I return an error? + session->InsertQuoteText(data); + } + else + { + CComposeSession* session = (CComposeSession*)closure; + session->AutoQuoteDone(); + } + return 0; +} + +//----------------------------------- +CComposeSession::CComposeSession(Boolean inOpeningAsDraft) +//----------------------------------- +: mCompositionContext(nil), + mXPCompositionPane(nil), + mDontIgnoreAllConnectionsComplete(false), + mDownloadingAttachments(false), + mCloseWindowAfterSavingDraft(false), + mClosing(false), + mAutoQuoting( false ), + mOpeningAsDraft(inOpeningAsDraft) +{ +} + +//----------------------------------- +CComposeSession::~CComposeSession() +//----------------------------------- +{ + if (mClosing) + return; + mClosing = true; + + Stop(); + + if (mXPCompositionPane) + MSG_DestroyPane(mXPCompositionPane); + + mXPCompositionPane = nil; + mCompositionContext->RemoveUser(this); + mCompositionContext->RemoveListener(this); +} + +// --------------------------------------------------------------------------- +MSG_Pane* CComposeSession::CreateBackendData(MWContext* inOldContext, + MSG_CompositionFields* inCompositionFields) +// Create backend data associated with a compose session. This data consists +// of the MWContext and the MSG_CompositionPane object. +// --------------------------------------------------------------------------- +{ + Try_ { + // if we can't allocate CMailCompositionContext, this object does us no good + mCompositionContext = new CMailCompositionContext(); + mCompositionContext->AddListener(this); + mCompositionContext->CreateContextProgress(); + mCompositionContext->AddUser(this); + mCompositionContext->WaitWhileBusy(); + mXPCompositionPane = MSG_CreateCompositionPane( + *mCompositionContext, + inOldContext, + MSG_GetPrefsForMaster(CMailNewsContext::GetMailMaster()), + inCompositionFields, + CMailNewsContext::GetMailMaster()); + // while we're here, set from field using preference +#if 0 // FIX: no need to: 'From' field is already set + if (mXPCompositionPane) + { + char * fromField = MIME_MakeFromField(); + // FIX ME -- what do we do if there is an error? + int err = MSG_SetCompHeader(mXPCompositionPane, + MSG_FROM_HEADER_MASK, + fromField); + XP_FREE(fromField); + } +#endif + } + Catch_(e) + { + // There's a leak here: mCompositionPane will not be cleaned up. + // I couldn't work out how to do it, unless it's to call: + // MSG_MailCompositionAllConnectionsComplete(mXPCompositionPane); + + Throw_(e); + } + EndCatch_ + // I assume that we call this method shortly after creating + // the object itself, so mXPCompositionPane should be NULL before we + // enter this method + return mXPCompositionPane; +} + +void CComposeSession::SetCompositionPaneFEData( void *data ) +{ + if (mXPCompositionPane) + { + // let's set the window here so we can send it a close message after sending the mail + MSG_SetFEData(mXPCompositionPane, data); + } +} + +void CComposeSession::ListenToMessage(MessageT inMessage, void* ioParam) +{ + if (mClosing) + return; + + switch(inMessage) + { + case msg_NSCAllConnectionsComplete: + if (mDownloadingAttachments) + { + // we're done downloading attachments, now do send command + mDownloadingAttachments = false; + switch (mMessage){ + case cmd_SaveDraft: + SaveDraftOrTemplate(inMessage, mCloseWindowAfterSavingDraft ); + break; + case cmd_SaveTemplate: + SaveDraftOrTemplate(inMessage, false ); + break; + default: // Handles SendNow and SendLater + SendMessage(mSendNow); + break; + } + + } + else if (mDontIgnoreAllConnectionsComplete) // MOAN! + { + if (mXPCompositionPane) + { + // We need to call this to let backend know to delete mXPCompositionPane + // Backend will destroy mXPCompositionPane when ready, so we don't need to. + // We'll come here several times for HTML parts messages, so don't do + // anything final. The backend will call FE_DestroyCompositionContext when + // everything is REALLY complete. We'll close the window then. + MSG_MailCompositionAllConnectionsComplete(mXPCompositionPane); + } + } + break; + // pass on broadcast to listeners + default: + BroadcastMessage(inMessage, ioParam); + break; + } +} + +void CComposeSession::SetMessageHeaderData(MSG_HEADER_SET inMessageHeaderType, + LHandleStream& inDataStream) +{ + if (mXPCompositionPane) + { + Handle data = inDataStream.GetDataHandle(); + StHandleLocker lock(data); + int err = MSG_SetCompHeader(mXPCompositionPane,inMessageHeaderType, *data); + // what do we do if there is an error? + } +} + +const char* CComposeSession::GetMessageHeaderData(MSG_HEADER_SET inMessageHeaderType) +{ + return MSG_GetCompHeader(mXPCompositionPane, inMessageHeaderType); +} + +void CComposeSession::SetMessageBody(LHandleStream& inDataStream) +{ + if (mXPCompositionPane) + { + Handle data = inDataStream.GetDataHandle(); + StHandleLocker lock(data); + MSG_SetHTMLMarkup(mXPCompositionPane, false); + int err = MSG_SetCompBody(mXPCompositionPane, *data); + } +} + +void CComposeSession::SetHTMLMessageBody() +{ + if (mXPCompositionPane) + { + XP_HUGE_CHAR_PTR textp = nil; + EDT_SaveToBuffer(*mCompositionContext, &textp); + MSG_SetHTMLMarkup(mXPCompositionPane, true); + int err = MSG_SetCompBody(mXPCompositionPane, textp); + if (textp) + delete textp; + } +} + +void CComposeSession::SendMessage(Boolean inSendNow) +{ + mDontIgnoreAllConnectionsComplete = true; + mSendNow = inSendNow; + MSG_Command(mXPCompositionPane, inSendNow ? MSG_SendMessage : MSG_SendMessageLater, nil, nil); +} + +const char* CComposeSession::GetSubject() +{ + return MSG_GetCompHeader(mXPCompositionPane, MSG_SUBJECT_HEADER_MASK); +} + +MSG_PRIORITY CComposeSession::GetPriority() +{ + // Oh, no! msglib gives us a hard-coded string. What sort of consciousness + // can lead to this type of code. Let's hope that the linker can pool strings for us, + // so that this will only take up 20 extra bytes. + const char *priority = MSG_GetCompHeader( GetMSG_Pane(), MSG_PRIORITY_HEADER_MASK ); + if( priority ) + { + if (strcasestr(priority, "Normal") != NULL) + return MSG_NormalPriority; + else if (strcasestr(priority, "Lowest") != NULL) + return MSG_LowestPriority; + else if (strcasestr(priority, "Highest") != NULL) + return MSG_HighestPriority; + else if (strcasestr(priority, "High") != NULL || + strcasestr(priority, "Urgent") != NULL) + return MSG_HighPriority; + else if (strcasestr(priority, "Low") != NULL || + strcasestr(priority, "Non-urgent") != NULL) + return MSG_LowPriority; + else + return MSG_NoPriority; + } + return MSG_NoPriority; +} + +const struct MSG_AttachmentData* +CComposeSession::GetAttachmentData() +{ + return MSG_GetAttachmentList(mXPCompositionPane); +} + +void CComposeSession::SetAttachmentList(MSG_AttachmentData* inAttachmentList) +{ + try + { + mDownloadingAttachments = true; + int status = MSG_SetAttachmentList(mXPCompositionPane, inAttachmentList); + if (status) throw status; + } + catch (...) + { + mDownloadingAttachments = false; + throw; + } +} + + +void CComposeSession::InsertQuoteText(const char* text) +{ + BroadcastMessage(msg_InsertQuoteText, (void*)text); +} + +void CComposeSession::AutoQuoteDone() +{ + if( mAutoQuoting ) + BroadcastMessage(msg_AutoQuoteDone, NULL); + mAutoQuoting = false; +} + + +void CComposeSession::CheckForAutoQuote() +{ + if (ShouldAutoQuote()) + { + mAutoQuoting = true; + QuoteMessage(); + } +} + +void CComposeSession::QuoteMessage() +{ + MSG_QuoteMessage(mXPCompositionPane, FE_QuoteCallback, this); +} + +#if 0 +// As far as I can tell, this never gets called. +void CComposeSession::QuoteInHTMLMessage( const char *text ) +{ + MWContext* context = (MWContext*)*mCompositionContext; + EDT_BeginOfDocument( context, false ); // before signature, if any. + // insert a blank line between message and quote + EDT_ReturnKey( context ); + EDT_PasteQuote( context, (char *)text ); + // move the insertion caret to the beginning (where it should be) + EDT_BeginOfDocument( context, false ); +} +#endif + +void CComposeSession::SetPriority(MSG_PRIORITY inPriority) +{ + char priorityString[50]; + MSG_GetUntranslatedPriorityName ( inPriority, priorityString, sizeof(priorityString)); + MSG_SetCompHeader( + mXPCompositionPane, + MSG_PRIORITY_HEADER_MASK, + priorityString ); +} + +void CComposeSession::Stop() +{ + // ignore all connections complete message from backend + mDontIgnoreAllConnectionsComplete = false; + XP_InterruptContext(*mCompositionContext); + mDownloadingAttachments = false; +} + +void CComposeSession::SaveDraftOrTemplate(CommandT inCommand, Boolean inCloseWindow ) +{ + MSG_CommandType cmd; + if ( inCloseWindow ) + { + Assert_(inCommand == cmd_SaveDraft); + cmd = MSG_SaveDraftThenClose; + mDontIgnoreAllConnectionsComplete = true; + } + else if (inCommand == cmd_SaveTemplate) + cmd = MSG_SaveTemplate; + else + cmd = MSG_SaveDraft; + + MSG_Command(mXPCompositionPane, cmd , nil, nil); +} + +int CComposeSession::SetCompBoolHeader(MSG_BOOL_HEADER_SET header,XP_Bool value) +{ + return MSG_SetCompBoolHeader(mXPCompositionPane, header,value); +} + + +Uint32 GetAttachmentListLength(const MSG_AttachmentData* inAttachList); +Uint32 GetAttachmentListLength(const MSG_AttachmentData* inAttachList) +{ + UInt32 result = 0; + if (inAttachList) + { + MSG_AttachmentData* iter = const_cast(inAttachList); + while (iter->url != nil) + { + ++result; + ++iter; + } + } + return result; +} + +Int16 CComposeSession::GetDefaultCSID() +{ // Delegate to mCompositionContext + if (mCompositionContext) + return mCompositionContext->GetDefaultCSID(); + else + return 0; +} +void CComposeSession::SetDefaultCSID(Int16 default_csid) +{ // Delegate to mCompositionContext + Assert_(mCompositionContext); + if (mCompositionContext) + { + mCompositionContext->SetDefaultCSID(default_csid); + mCompositionContext->SetDocCSID(default_csid); + mCompositionContext->SetWinCSID(INTL_DocToWinCharSetID(default_csid)); + } +} +int16 CComposeSession::GetWinCSID() +{ + Assert_(mCompositionContext); + if (mCompositionContext) + return mCompositionContext->GetWinCSID(); + else + return 0; +} + +Boolean CComposeSession::NeedToSyncAttachmentList(MSG_AttachmentData* inAttachList) +{ + Boolean result = false; + const MSG_AttachmentData* backendList = MSG_GetAttachmentList(mXPCompositionPane); + if ( (inAttachList != nil && backendList == nil) || + (inAttachList == nil && backendList != nil) ) + { // easy case, one list is empty the other is not + result = true; + } else if (inAttachList != nil && backendList != nil) + { // both lists are non-empty + // first check list lengths + Uint32 inLength = GetAttachmentListLength(inAttachList), + backendLength = GetAttachmentListLength(backendList); + if (inLength != backendLength) + // lengths are different + result = true; + else + { // both same length, now check to see if both lists contain same data + MSG_AttachmentData *inIter = inAttachList, + *backendIter = const_cast(backendList); + while (inIter->url != nil) + { + if (XP_STRCMP(inIter->url, backendIter->url)) + { // URL's are different, need to sync up lists + result = true; + break; + } + ++inIter; + ++backendIter; + } + } + } + return result; +} diff --git a/mozilla/cmd/macfe/MailNews/CComposeSession.h b/mozilla/cmd/macfe/MailNews/CComposeSession.h new file mode 100644 index 00000000000..58bb42f1816 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CComposeSession.h @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CComposeSession.h + +#pragma once + +// PowerPlant +#include + +// XP +#include "msgcom.h" +#include "structs.h" // mother of all structs (i.e. MWContext) + +// MacFE +#include "CProgressBroadcaster.h" +#include "CMailComposeWindow.h" // for CMailCompositionContext + +// quote function passed into MSG_QuoteMessage +extern "C" +{ +int FE_QuoteCallback(void* closeure, const char* data); +} + +class CComposeSession : public CProgressBroadcaster, + public LListener +{ +public: + enum { msg_AllConnectionsComplete = 'ACnC', + msg_InsertQuoteText = 'InsQ', + msg_AutoQuoteDone = 'AQDn' }; + + CComposeSession(Boolean inOpeningAsDraft); + virtual ~CComposeSession(); + + virtual void ListenToMessage(MessageT inMessage,void *ioParam); + + MSG_Pane* CreateBackendData(MWContext* inOldContext, + MSG_CompositionFields* inCompositionFields); + +// get data + const char* GetSubject(); + MSG_PRIORITY GetPriority(); + const char* GetMessageHeaderData(MSG_HEADER_SET inMessageHeaderType); + const struct MSG_AttachmentData* + GetAttachmentData(); + Boolean GetDownloadingAttachments() const { return mDownloadingAttachments; } + Boolean GetDeliveryInProgress() const { if ( mXPCompositionPane ) + return MSG_DeliveryInProgress( mXPCompositionPane ); + else + return false; + } + MSG_Pane* GetMSG_Pane() {return mXPCompositionPane;} + void MarkMSGPaneDestroyed() {mXPCompositionPane = nil;} + CMailCompositionContext* GetCompositionContext() {return mCompositionContext;} + Boolean GetAutoQuoting() { return mAutoQuoting; } +// set data + void SetMessageHeaderData(MSG_HEADER_SET inMessageHeaderType, + LHandleStream& inDataStream); + void SetAttachmentList(MSG_AttachmentData* inAttachmentList); + void SetPriority(MSG_PRIORITY inPriority); + void SetMessageBody(LHandleStream& inHandleStream); + void SetHTMLMessageBody(); + void SetCompositionPaneFEData(void *data); + void SetMessage(MessageT command) {mMessage=command;}; + int SetCompBoolHeader(MSG_BOOL_HEADER_SET header,XP_Bool value); + XP_Bool GetCompBoolHeader(MSG_BOOL_HEADER_SET header) + { return MSG_GetCompBoolHeader(mXPCompositionPane, header);} +// quoting + void InsertQuoteText(const char* text); + void AutoQuoteDone(); + Boolean ShouldAutoQuote() const + { return !mOpeningAsDraft + && ::MSG_ShouldAutoQuote(mXPCompositionPane); + } + void CheckForAutoQuote(); + void QuoteInHTMLMessage(const char* text); + +// actions + void WaitWhileContextBusy(); + void SendMessage(Boolean inSendNow); + void SetSendNow(Boolean inSendNow) { mSendNow = inSendNow; } + void SetCloseWindow( Boolean inClose) { mCloseWindowAfterSavingDraft = inClose; } + void QuoteMessage(); + void Stop(); + void SaveDraftOrTemplate(CommandT inCommand, Boolean inCloseWindow); + +// potpourri + Boolean NeedToSyncAttachmentList(MSG_AttachmentData* inAttachList); + + // I18N stuff + virtual Int16 GetDefaultCSID(void); + virtual void SetDefaultCSID(Int16 defaultcsid); + int16 GetWinCSID(); +protected: + + CMailCompositionContext* mCompositionContext; + MSG_Pane* mXPCompositionPane; + // I must say, the reason why I have to do this... + Boolean mDontIgnoreAllConnectionsComplete; + // Double SIGH! + Boolean mDownloadingAttachments; + Boolean mSendNow; + MessageT mMessage; + Boolean mCloseWindowAfterSavingDraft; + Boolean mClosing; + Boolean mAutoQuoting; + Boolean mOpeningAsDraft; +}; + + diff --git a/mozilla/cmd/macfe/MailNews/CFolderThreadController.cp b/mozilla/cmd/macfe/MailNews/CFolderThreadController.cp new file mode 100644 index 00000000000..d45468b6e19 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CFolderThreadController.cp @@ -0,0 +1,236 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CFolderThreadController.cp + +#include "CFolderThreadController.h" + +#include "CMessageFolderView.h" +#include "CThreadView.h" +#include "MailNewsgroupWindow_Defines.h" +#include "divview.h" +#include "CTargetFramer.h" +#include "CNetscapeWindow.h" + +#include "resgui.h" // for cmd_ShowLocationBar + +#include "prefapi.h" + +const char* Pref_MailShowLocationBarFolders = "mailnews.chrome.show_url_bar.folders"; +const char* Pref_MailShowLocationBarNoFolders = "mailnews.chrome.show_url_bar.nofolders"; + +//---------------------------------------------------------------------------------------- +CFolderThreadController::CFolderThreadController( + LDividedView* inDividedView +, CNSContext* inThreadContext +, CMessageFolderView* inFolderView +, CThreadView* inThreadView +) +//---------------------------------------------------------------------------------------- +: mDividedView(inDividedView) +, mThreadContext(inThreadContext) +, mFolderView(inFolderView) +, mThreadView(inThreadView) +{ + Assert_(mDividedView); + Assert_(mThreadContext); + Assert_(mThreadView); + Assert_(mFolderView); + inDividedView->SetCollapseByDragging(true, true); + mDividedView->AddListener(this); // for msg_DividerChangedPosition + mFolderView->AddListener(this); + mFolderView->SetRightmostVisibleColumn(1); // hide the count columns +} // CFolderThreadController::CFolderThreadController + +//---------------------------------------------------------------------------------------- +CFolderThreadController::~CFolderThreadController() +//---------------------------------------------------------------------------------------- +{ + // See comment in FinishCreateSelf. Destroy the folder and thread views explicitly + // here, so that it's done in the right order. Because of the tab order requirement, + // LCommander::~LCommander would otherwise be deleting these in the opposite order + // to the tab order, namely message/thread/folder. Boom. + delete mFolderView; + mFolderView = nil; + delete mThreadView; + mThreadView = nil; + // The message view remains a subcommander, so will be deleted in the base class + // destructor. +} // CFolderThreadController::~CFolderThreadController + +//---------------------------------------------------------------------------------------- +void CFolderThreadController::FinishCreateSelf() +//---------------------------------------------------------------------------------------- +{ + // It's critical the order we do this. These are added to the end of the + // supercommander's subcommander list, and destroyed in the opposite order. + // Since we have to destroy in the order folder/thread/message, we would like to add + // here in the order message/thread/folder. But unfortunately, the order we add them + // also affects the tab order, which we would like to be folder/thread/message. So + // the order here is for the benefit of the tab order. See the destructor code above. + mFolderView->SetSuperCommander(this); + mThreadView->SetSuperCommander(this); + CTargetFramer* framer = new CTargetFramer(); + mThreadView->AddAttachment(framer); + framer = new CTargetFramer(); + mFolderView->AddAttachment(framer); + SetLatentSub(mFolderView); + + mFolderView->SetFancyDoubleClick(true); +} // CFolderThreadController::FinishCreateSelf + +//---------------------------------------------------------------------------------------- +void CFolderThreadController::ListenToMessage(MessageT inMessage, void *ioParam) +//---------------------------------------------------------------------------------------- +{ + switch (inMessage) + { + case msg_DividerChangedPosition: + { + // Don't take any action during FinishCreate(): assume that the panes + // will be constructed in the same correct positions that they were saved in. + if (mDividedView->IsVisible() && (LDividedView*)ioParam == mDividedView) + NoteDividerChanged(); + break; + } + case CStandardFlexTable::msg_SelectionChanged: + { + Assert_(ioParam == mFolderView); + MSG_FolderInfo* info = nil; + if (mFolderView->GetSelectedRowCount() == 1) + { + // See also CMessageFolderView::OpenRow + info = mFolderView->GetSelectedFolder(); + CMessageFolder folder(info); + if (folder.IsMailServer() + || folder.IsNewsHost() + || !folder.CanContainThreads()) + { + info = nil; + } + } + mThreadView->LoadMessageFolder(mThreadContext, info, false /* delay: don't load now */); + break; + } + default: + break; + } +} // CFolderThreadController::ListenToMessage + +//---------------------------------------------------------------------------------------- +void CFolderThreadController::FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +//---------------------------------------------------------------------------------------- +{ + switch(inCommand) + { + case cmd_ToggleFolderPane: + outEnabled = (mDividedView != nil); + outUsesMark = false; + if (outEnabled && mDividedView->IsFirstPaneCollapsed()) + ::GetIndString(outName, BROWSER_MENU_TOGGLE_STRINGS_ID, SHOW_FOLDERPANE_STRING); + else + ::GetIndString(outName, BROWSER_MENU_TOGGLE_STRINGS_ID, HIDE_FOLDERPANE_STRING); + break; + default: + LTabGroup::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); + break; + } +} // CFolderThreadController::FindCommandStatus + +//---------------------------------------------------------------------------------------- +Boolean CFolderThreadController::ObeyCommand( + CommandT inCommand, + void *ioParam) +//---------------------------------------------------------------------------------------- +{ + switch (inCommand) + { + case cmd_ToggleFolderPane: + if (mDividedView) + mDividedView->ToggleFirstPane(); + // force menu items to update show "Show" and "Hide" string changes are reflected + LCommander::SetUpdateCommandStatus(true); + return true; + case cmd_RelocateViewToFolder: + if (mFolderView) + mFolderView->SelectFolder((MSG_FolderInfo*)ioParam); + else + mThreadView->RelocateViewToFolder((MSG_FolderInfo*)ioParam); + return true; + case msg_TabSelect: + // Subcommanders (thread/folder/message) will kick this upstairs here. + return true; + } + return LTabGroup::ObeyCommand(inCommand, ioParam); +} // CFolderThreadController::ObeyCommand + +//---------------------------------------------------------------------------------------- +void CFolderThreadController::ReadStatus(LStream *inStatusData) +//---------------------------------------------------------------------------------------- +{ + mDividedView->RestorePlace(inStatusData); +} // CFolderThreadController::ReadWindowStatus + +//---------------------------------------------------------------------------------------- +void CFolderThreadController::WriteStatus(LStream *outStatusData) +//---------------------------------------------------------------------------------------- +{ + mDividedView->SavePlace(outStatusData); +} // CFolderThreadController::WriteWindowStatus + +//---------------------------------------------------------------------------------------- +void CFolderThreadController::NoteDividerChanged() +//---------------------------------------------------------------------------------------- +{ + Boolean foldersCollapsed = mDividedView->IsFirstPaneCollapsed(); + const char* prefName = foldersCollapsed ? + Pref_MailShowLocationBarNoFolders + : Pref_MailShowLocationBarFolders; + XP_Bool doShow; + if (PREF_GetBoolPref(prefName, &doShow) != PREF_NOERROR) + { + // If the preference is not yet set, the default is to show iff folders are collapsed + doShow = foldersCollapsed; + } + // These commands will be handled by CMailNewsWindow. The values will be written out to the + // prefs file as a side effect of ToggleDragBar, using the virtual method + // GetLocationBarPrefName() which we have provided. + if (doShow) + ObeyCommand(cmd_ShowLocationBar, nil); + else + ObeyCommand(cmd_HideLocationBar, nil); +} // CFolderThreadController::NoteDividerChanged + +//---------------------------------------------------------------------------------------- +const char* CFolderThreadController::GetLocationBarPrefName() const +//---------------------------------------------------------------------------------------- +{ + if (!mDividedView) + return nil; + if (mDividedView->IsFirstPaneCollapsed()) + return Pref_MailShowLocationBarNoFolders; + return Pref_MailShowLocationBarFolders; +} + diff --git a/mozilla/cmd/macfe/MailNews/CFolderThreadController.h b/mozilla/cmd/macfe/MailNews/CFolderThreadController.h new file mode 100644 index 00000000000..1cbd36d0104 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CFolderThreadController.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CFolderThreadController.h + +#include +#include + +class CMessageFolderView; +class CThreadView; +class CNSContext; +class LDividedView; + +//====================================== +class CFolderThreadController + : public LListener + , public LTabGroup +// This class is here to mediate between the folder pane and the thread pane in a 3-pane +// window. Its function is to allow the thread view class not to know anything about +// the folder view. +//====================================== +{ + public: + CFolderThreadController( + LDividedView* inDividedView + , CNSContext* inThreadContext + , CMessageFolderView* inFolderView + , CThreadView* inThreadView + ); + virtual ~CFolderThreadController(); + // LListener overrides: + protected: + virtual void ListenToMessage(MessageT inMessage, void *ioParam); + + // LCommander overrides: + protected: + virtual void FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName); + virtual Boolean ObeyCommand(CommandT inCommand, void *ioParam); + + // CSaveWindowStatus helpers: + public: + void ReadStatus(LStream *inStatusData); + void WriteStatus(LStream *outStatusData); + const char* GetLocationBarPrefName() const; + + // Specials + public: + void FinishCreateSelf(); + void NoteDividerChanged(); + + // Data + protected: + LDividedView* mDividedView; + CMessageFolderView* mFolderView; + CThreadView* mThreadView; + CNSContext* mThreadContext; +}; // class CFolderThreadController diff --git a/mozilla/cmd/macfe/MailNews/CKeyStealingAttachment.cp b/mozilla/cmd/macfe/MailNews/CKeyStealingAttachment.cp new file mode 100644 index 00000000000..8fd6aa710c7 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CKeyStealingAttachment.cp @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +#include "CKeyStealingAttachment.h" + +//----------------------------------- +void CKeyStealingAttachment::ExecuteSelf(MessageT, void *ioParam) +//----------------------------------- +{ + EventRecord* event = (EventRecord*)ioParam; + if ((event->what != keyDown) && (event->what != autoKey)) + return; + char theKey = (char)(event->message & charCodeMask); + if (mKeysToSteal.FetchIndexOf(&theKey) != LArray::index_Bad) + { + mTarget->ProcessKeyPress(*event); + event->what = nullEvent; // prevent normal handling. + event->message = 0; // prevent normal handling. + } +} // CKeyStealingAttachment::ExecuteSelf \ No newline at end of file diff --git a/mozilla/cmd/macfe/MailNews/CKeyStealingAttachment.h b/mozilla/cmd/macfe/MailNews/CKeyStealingAttachment.h new file mode 100644 index 00000000000..43ca07a6224 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CKeyStealingAttachment.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include +//====================================== +class CKeyStealingAttachment : public LAttachment +// This guy is added at the front of the attachment list in expanded mode, so that +// certain keypresses go to the message view instead of to the table view. +//====================================== +{ +public: + CKeyStealingAttachment(LCommander *inKeyTarget) + : LAttachment(msg_KeyPress) + , mKeysToSteal(sizeof(char)) + , mTarget(inKeyTarget) {} + virtual void ExecuteSelf(MessageT inMessage, void *ioParam); + void StealKey(char keyToSteal) + { + mKeysToSteal.InsertItemsAt( + 1, + LArray::index_Last, + &keyToSteal); + } +protected: + LCommander* mTarget; + LArray mKeysToSteal; +}; + diff --git a/mozilla/cmd/macfe/MailNews/CMailComposeWindow.cp b/mozilla/cmd/macfe/MailNews/CMailComposeWindow.cp new file mode 100644 index 00000000000..406df774649 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMailComposeWindow.cp @@ -0,0 +1,2237 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMailComposeWindow.cp + +#include "CMailComposeWindow.h" +#include "rosetta.h" + +#include "CSimpleTextView.h" +#include "CEditView.h" +#include "CThreadWindow.h" +#include "CBrowserWindow.h" +#include "CMessageWindow.h" + +//#include +//#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "UOffline.h" +#include "macutil.h" +#include "resgui.h" +#include "uapp.h" +#include "MailNewsgroupWindow_Defines.h" +#include "CComposeSession.h" +#include "UStdDialogs.h" +#include "URobustCreateWindow.h" +#include "LGACheckbox.h" +#include "edt.h" +#include "macgui.h" // MakeLOColor +#include "xpgetstr.h" // for XP_GetString +#include "secnav.h" +#define WANT_ENUM_STRING_IDS +#include "allxpstr.h" +#undef WANT_ENUM_STRING_IDS +#include "CTabControl.h" +#include "meditor.h" // HandleModalDialog +#include "UGraphicGizmos.h" +#include "CPatternButtonPopup.h" +#include "CMailNewsContext.h" +#include "CLargeEditField.h" +#include "CPasteSnooper.h" +#include "UDeferredTask.h" + +#include "MailNewsAddressBook.h" +#include "ABcom.h" +#ifdef MOZ_NEWADDR +#include "CAddressPickerWindow.h" +#endif + +#define cmd_CheckSpelling 'ChSp' + +const Uint32 cDefaultLineWrapLength = 78; +const ResIDT COMPOSEDLG_SAVE_BEFORE_QUIT = 10621; +const MessageT msg_NoSave = 3; + +#include "msg_srch.h" // needed for priority +#define UPDATE_WINDOW_TITLE_ON_IDLE 1 + + +#pragma mark -- libmsg callbacks -- + +#include "prefapi.h" // temporarily include this until MacFE prefs for ComposeHTML are added + +//----------------------------------- +MSG_Pane* FE_CreateCompositionPane( + MWContext* old_context, + MSG_CompositionFields* fields, + const char* initialText, + MSG_EditorType editorType) +//----------------------------------- +{ + MSG_Pane* result = nil; + CMailComposeWindow *win = nil; + try + { + CMailNewsContext::ThrowUnlessPrefsSet(MWContextMessageComposition); + XP_Bool useHtmlEditor; + PREF_GetBoolPref( "mail.html_compose", &useHtmlEditor ); + // override if + if (MSG_HTML_EDITOR == editorType) + useHtmlEditor = TRUE; + else if (MSG_PLAINTEXT_EDITOR == editorType) + useHtmlEditor = FALSE; + else if (IsThisKeyDown(kCtlKey)) + useHtmlEditor = !useHtmlEditor; + + ResIDT id = useHtmlEditor ? + CMailComposeWindow::res_ID + : CMailComposeWindow::text_res_ID; + + win = dynamic_cast( + URobustCreateWindow::CreateWindow( + id, + LCommander::GetTopCommander())); + if (win) + { + // Now here's a hidden meaning I bet you didn't know. Opening from the sent or + // drafts folder is indicated by the value editorType != MSG_DEFAULT! + Boolean openingAsDraft = editorType != MSG_DEFAULT; + result = win->CreateSession(old_context, fields, initialText, openingAsDraft); + } + } + catch (...) + { + if (win) + win->DoClose(); + } + return result; +} // FE_CreateCompositionPane + +void FE_UpdateCompToolbar(MSG_Pane* msgpane) // in +{ + if (msgpane) + { + void *fedata = MSG_GetFEData(msgpane); + if (fedata) + { + CMailComposeWindow *window = (CMailComposeWindow *)fedata; + window->HandleUpdateCompToolbar(); + } + } +} + +void FE_DestroyMailCompositionContext(MWContext* context) +{ + MSG_Pane* msgpane; + msgpane = MSG_FindPane(context, MSG_COMPOSITIONPANE); + if (msgpane) + { + void *fedata = MSG_GetFEData(msgpane); + if (fedata) + { + CMailComposeWindow *window = (CMailComposeWindow *)fedata; + if (window->IsVisible()) + { + if (window->GetComposeSession()) + window->GetComposeSession()->MarkMSGPaneDestroyed(); +// window->BroadcastMessage(msg_NSCAllConnectionsComplete, nil); + window->DoCloseLater(); + } + } + } +} + +void FE_SecurityOptionsChanged( MWContext* context) +{ + MSG_Pane* msgpane = MSG_FindPane(context, MSG_COMPOSITIONPANE); + if (msgpane) + { + void *fedata = MSG_GetFEData( msgpane ); + if (fedata) + { + CMailComposeWindow *window = (CMailComposeWindow *)fedata; + USecurityIconHelpers::UpdateComposeButton( window ); + CMailOptionTabContainer *optionTab= dynamic_cast (window->FindPaneByID('OpTC') ); + if ( optionTab ) // if the option tab is visible update it + window-> ListenToMessage( msg_ActivatingOptionTab, optionTab ); + } + } + +} + +#pragma mark - + +void UComposeUtilities::WordWrap(Handle& inText, + Uint32 inTextLength, + LHandleStream& outTextStream) +{ + Int32 lineWrapLength = cDefaultLineWrapLength; + + PREF_GetIntPref("mailnews.wraplength", &lineWrapLength); + + try { + StHandleLocker lock(inText); + char *scanner = *inText, + *startOfLine = *inText, + *lastSpace = *inText, + *end = *inText + inTextLength; + while (startOfLine < end) + { + // if line starts with "> " skip to next line + if (*startOfLine == '>' && *(startOfLine + 1) == ' ') + { // move to next line + while (scanner < end && *scanner != '\r') + ++scanner; + // move past '\r' + ++scanner; + // copy text into stream + outTextStream.WriteBlock(startOfLine, scanner - startOfLine); + // set startOfLine to scanner + startOfLine = scanner; + } + else + { + // find ' ' in text stream + while (scanner < end && *scanner != '\r' && *scanner != ' ') + ++scanner; + // we spit out a line of text if a) we hit the end of the text, + // b) have enough text to fill a line, or c) hit a carriage return + if (scanner - startOfLine > lineWrapLength || + scanner == end || + *scanner == '\r') + { + if (*scanner == '\r' || scanner == end) + outTextStream.WriteBlock(startOfLine, scanner - startOfLine); + else + outTextStream.WriteBlock(startOfLine, lastSpace - startOfLine); + if (scanner < end) + { + outTextStream << (unsigned char) '\r'; + if (*scanner == '\r') + startOfLine = scanner + 1; + else + startOfLine = lastSpace + 1; + } + else + startOfLine = scanner; + } + lastSpace = scanner; + ++scanner; + } + } + } catch (...) { + } +} + +MSG_HEADER_SET UComposeUtilities::GetMsgHeaderMaskFromAddressType(EAddressType inAddressType) +{ + MSG_HEADER_SET result = 0; + switch (inAddressType) + { + case eToType: + result = MSG_TO_HEADER_MASK; + break; + case eCcType: + result = MSG_CC_HEADER_MASK; + break; + case eBccType: + result = MSG_BCC_HEADER_MASK; + break; + case eReplyType: + result = MSG_REPLY_TO_HEADER_MASK; + break; + case eNewsgroupType: + result = MSG_NEWSGROUPS_HEADER_MASK; + break; + case eFollowupType: + result = MSG_FOLLOWUP_TO_HEADER_MASK; + break; + } + return result; +} + +MSG_PRIORITY UComposeUtilities::GetMsgPriorityFromMenuItem(Int32 inMenuItem) +{ + // These are mapped to MSG_LowPriority, MSG_NormalPriority, MSG_HighPriority, + // and MSG_HighestPriority, respectively. + return (MSG_PRIORITY)(inMenuItem - 1 + MSG_LowestPriority); +} + + +#pragma mark - + +//#define REGISTER_(letter,root) \ +// RegisterClass_(letter##root::class_ID, \ +// (ClassCreatorFunc)letter##root::Create##root##Stream); + +#define REGISTER_(letter,root) \ + RegisterClass_(letter##root); + +#define REGISTERC(root) REGISTER_(C,root) +#define REGISTERL(root) REGISTER_(L,root) + +//----------------------------------- +void UComposeUtilities::RegisterComposeClasses() +//----------------------------------- +{ + REGISTERC(MailComposeWindow) + REGISTERC(MailEditView) + REGISTERC(MailComposeTabContainer) + REGISTERC(MailOptionTabContainer) + REGISTERC(MailAttachmentTabContainer) + REGISTERC(ComposeAddressTableView) + REGISTERC(ComposeTabSwitcher) + REGISTERC(MailAddressEditField) + #ifdef MOZ_NEWADDR + REGISTERC(AddressPickerWindow); + #endif +} + +//----------------------------------- +CMailEditView::CMailEditView(LStream * inStream) : CEditView(inStream) +//----------------------------------- +{ + mEditorDoneLoading = false; + mHasAutoQuoted = false; + mHasInsertSignature =false; + mCursorSet = false; + mComposeSession = nil; + mInitialText = nil; + mStartQuoteOffset = 0; + mEndQuoteOffset = 0; +} + +//----------------------------------- +void CMailEditView::InstallBackgroundColor() +// Overridden to use a white background +//----------------------------------- +{ + memset(&mBackgroundColor, 0xFF, sizeof(mBackgroundColor)); // white is default +} + +void CMailEditView::GetDefaultBackgroundColor(LO_Color* outColor) const +{ + // if the editor is not done initializing then set the color to mBackgroundColor (white) + // we do this to keep the window from changing colors (potentially) + if ( !IsDoneLoading() ) + *outColor = UGraphics::MakeLOColor(mBackgroundColor); + // else + // if the editor has completed initialization then we don't want to change outColor +} + +//----------------------------------- +void CMailEditView::SetInitialText( const char *textp ) +//----------------------------------- +{ + if ( mInitialText ) + { + XP_FREE( mInitialText ); + mInitialText = nil; + } + + if ( textp ) + { + mInitialText = XP_STRDUP( textp ); + } +} + + +//----------------------------------- +void CMailEditView::InitMailCompose() +//----------------------------------- +{ + MWContext *context; + if (GetContext()) + context = *(GetContext()); + else + return; + + XP_Bool composeDirty = EDT_DirtyFlag(context); + + MSG_Pane *msgpane = MSG_FindPane( context, MSG_COMPOSITIONPANE ); + + if (!mEditorDoneLoading) + { + mEditorDoneLoading = true; + MSG_SetHTMLMarkup( msgpane, true ); + + // do InstallBackgroundColor again to set the editor background color + // we need to do this one last time to set the backend editor data to + // have the correct (white) background color + InstallBackgroundColor(); + // if mInitalText is nonNull it means that the message is a draft/Edit Message + // and there fore quoting should not be done. + if (mComposeSession->ShouldAutoQuote() && !mInitialText) + { + mHasAutoQuoted = true; + mStartQuoteOffset = EDT_GetInsertPointOffset( context ); + EDT_EndOfDocument( context, false ); + mComposeSession->QuoteMessage(); + // Then eventually CComposeSession::QuoteInHTMLMessage will be called. + } + else + { + EDT_EndOfDocument( context, false ); + mHasInsertSignature = true; + /* insert signature here!!!! and Drafts too*/ + DisplayDefaultTextBody(); + EDT_BeginOfDocument( context, false ); + //mCursorSet = true; + } + + } + else + { + + mEndQuoteOffset = EDT_GetInsertPointOffset( context ); + if ( !mHasInsertSignature ) + { + mHasInsertSignature = true; + EDT_EndOfDocument( context, false ); + + /* insert signature here!!!! and Drafts too*/ + DisplayDefaultTextBody(); + // EDT_BeginOfDocument( context, false ); + // mCursorSet = true; + } + + if ( !mCursorSet ) + { + mCursorSet = true; + if( mHasAutoQuoted ) + { + int32 eReplyOnTop = 1; + if ( PREF_NOERROR == PREF_GetIntPref("mailnews.reply_on_top", &eReplyOnTop) ) + { + switch (eReplyOnTop) + { + case 0: + EDT_SetInsertPointToOffset( context, mEndQuoteOffset, 0 ); + break; + case 1: + default: + EDT_SetInsertPointToOffset( context, mStartQuoteOffset, 0 ); + break; + case 2: + case 3: + EDT_SetInsertPointToOffset( context, mStartQuoteOffset, + mEndQuoteOffset - mStartQuoteOffset ); + break; + } + } + } + else + { + EDT_BeginOfDocument( context, false ); + mCursorSet = true; + } + } + + } + + if (!composeDirty) + EDT_SetDirtyFlag( context , false ); + +} // CMailEditView::InitMailCompose + + +// Taken from X11 +void CMailEditView::InsertMessageCompositionText(const char* text, + XP_Bool leaveCursorBeginning, XP_Bool isHTML) +{ + + Boolean noTagsP = false; + MWContext *context; + if (GetContext()) + context = *(GetContext()); + else + return; + + ED_BufferOffset ins = -1; + + if ( leaveCursorBeginning ){ + ins = EDT_GetInsertPointOffset( context ); + } + + if (isHTML) { + // + // NOTE: let's try to see if they really have any HTML tags + // in the text they've given us... [ poor man's parser ] + // + if (XP_STRCASESTR(text, "") || + XP_STRCASESTR(text, "") || + XP_STRCASESTR(text, "
")  ||
+				XP_STRCASESTR(text, "\n");
+
+			EDT_PasteQuote( context, (char *) text );
+
+			if (noTagsP)  
+				EDT_PasteQuote( context, "
\n" ); + EDT_PasteQuoteEnd( context ); + } + else { + EDT_PasteText( context, (char *) text ); + } + + if ( leaveCursorBeginning && ins != -1 ) { + EDT_SetInsertPointToOffset( context, ins, 0 ); + } +} + +void CMailEditView::DisplayDefaultTextBody() +{ + + const char *pBody = MSG_GetCompBody(mComposeSession->GetMSG_Pane()); + + if ( mInitialText && strlen(mInitialText) ) { + InsertMessageCompositionText( mInitialText, false, true); + InsertMessageCompositionText( "\n", false, true); + + MSG_SetCompBody(mComposeSession->GetMSG_Pane(), ""); + + XP_FREEIF(mInitialText); + } + else if ( pBody && strlen(pBody) ) { + XP_Bool isHTML =XP_STRCASESTR( pBody,"") ? true:false; + // To prevent quoting the SIG. Is there a more correct way to do this? + EDT_ReturnKey( *GetContext() ); + InsertMessageCompositionText( pBody, true, isHTML ); + } +} + +#pragma mark - + +//----------------------------------- +CMailComposeWindow::CMailComposeWindow(LStream* inStream) : +//----------------------------------- + CMailNewsWindow(inStream, WindowType_Compose), + mComposeSession(nil), + mAddressTableView(nil), + mProgressListener(nil), + mAttachmentList(nil), + mAttachmentView(nil), + mHTMLEditView(nil), + mPlainEditView(nil), + mInitializeState(eUninitialized), + mHeadersDirty( false ), + mOnlineLastFindCommandStatus( true ), + mDefaultWebAttachmentURL("\p"), + mCurrentSaveCommand(cmd_SaveDraft) +{ +} // CMailComposeWindow::CMailComposeWindow + +//----------------------------------- +CMailComposeWindow::~CMailComposeWindow() +//----------------------------------- +{ + StopListening(); + if (mPlainEditView && mPlainEditView->IsOnDuty() ) // for a TSM problem, we have deactivate it first + SwitchTarget(this); + + delete mComposeSession; + + if (mAttachmentView) + mAttachmentView->SetAttachList(nil); + + delete mAttachmentList; + + delete mProgressListener; + RemoveAllAttachments(); + // Kludgy, but prevents crash in LUndoer caused by view being destroyed before + // attachments. +} // CMailComposeWindow::~CMailComposeWindow + +//----------------------------------- +ResIDT CMailComposeWindow::GetStatusResID() const +//----------------------------------- +{ + return mHTMLEditView ? + CMailComposeWindow::res_ID + : CMailComposeWindow::text_res_ID; +} // CMailComposeWindow::GetStatusResID + +//----------------------------------- +void CMailComposeWindow::FinishCreateSelf() +//----------------------------------- +{ + mPlainEditView = dynamic_cast(FindPaneByID('text')); + if (mPlainEditView) + { + +// mPlainEditView->BuildTextObjects(this); // sets the super model + +// SetDefaultSubModel(mPlainEditView->GetTextModel()); + +// LTextSelection* theSelection = mPlainEditView->GetTextSelection(); +// if (theSelection) +// theSelection->SetSelectionRange(LTextEngine::sTextStart); + + mPlainEditView->SetSelection( 0, 0 ); + mPlainEditView->SetTabSelectsAll( false ); + } + else + mHTMLEditView = (CMailEditView *)FindPaneByID(CMailEditView::pane_ID); + + try { + // make the window the undoer. typing actions end up there. + LUndoer* theUndoAttachment = new LUndoer; + AddAttachment(theUndoAttachment); // attach to window + // create attachment list + mAttachmentList = new CAttachmentList; + mHaveInitializedAttachmentsFromBE = false; + } catch (...) { + } + + // link up compose window to addressing table view and vice versa + CComposeAddressTableView* tableView = dynamic_cast(FindPaneByID('Addr')); + mAddressTableView = tableView; + mAddressTableView->AddListener( this ); + + // add an attachment to the subject edit field to strip out CRs + LEditField* subjectField = dynamic_cast(FindPaneByID('Subj')); + Assert_( subjectField ); + subjectField->AddAttachment(new CPasteSnooper(MAKE_RETURNS_SPACES)); + + +// // set subject as latent sub +// jrm 97/02/04 removed this, now done more subtly in CreateSession. +// LEditField* subjectField = dynamic_cast(FindPaneByID('Subj')); +// if (subjectField) +// SetLatentSub(subjectField); + + UReanimator::LinkListenerToControls(this, this, COMPOSE_BUTTON_BAR_ID); + + CComposeTabSwitcher* tabSwitcher = + dynamic_cast(FindPaneByID('TbSw')); + if (tabSwitcher) + tabSwitcher->AddListener(this); + + USecurityIconHelpers::AddListenerToSmallButton( + this /*LWindow**/, + this /*LListener**/); + + LGAIconSuiteControl* offlineButton + = dynamic_cast(FindPaneByID(kOfflineButtonPaneID)); + if (offlineButton) + offlineButton->AddListener(CFrontApp::GetApplication()); + +#if UPDATE_WINDOW_TITLE_ON_IDLE + StartIdling(); +#endif + CSaveWindowStatus::FinishCreateWindow(); + // Build the priority Menu + LMenu* priorityMenu = new LMenu(10620,"\pPriority"); + for( MSG_PRIORITY priorityToInsert = MSG_LowestPriority; priorityToInsert <= MSG_HighestPriority; priorityToInsert=MSG_PRIORITY(priorityToInsert+1) ) + { + char priorityString[255]; + MSG_GetPriorityName ( priorityToInsert, priorityString, sizeof( priorityString ) ); + priorityMenu->InsertCommand(CStr255(priorityString ),0, Int16(priorityToInsert)-2 ); + } + CPatternButtonPopup* button = dynamic_cast(FindPaneByID('Prio')); + if( button ) + button->AdoptMenu( priorityMenu ); + + SetDefaultWebAttachmentURL(); + +} // CMailComposeWindow::FinishCreateSelf + +//----------------------------------- +void CMailComposeWindow::SetDefaultWebAttachmentURL (void) +//----------------------------------- +{ + CWindowIterator lookForIt(WindowType_Any); + CMediatedWindow *parentWindow; + do { + lookForIt.Next(parentWindow); // skip over invisible progress dialog and compose window itself + } while ( + parentWindow && + (parentWindow->GetWindowType() == WindowType_Progress) || + (parentWindow->GetWindowType() == WindowType_Compose)); + + cstring defaultURL = "\p"; + + if (parentWindow) { + switch (parentWindow->GetWindowType()) { + case WindowType_MailThread: + { + CThreadWindow *threadParent = dynamic_cast(parentWindow); + if (threadParent) + defaultURL = threadParent->GetCurrentURL(); + } + case WindowType_Message: + { + CMessageWindow *messageParent = dynamic_cast(parentWindow); + if (messageParent) + defaultURL = messageParent->GetCurrentURL(); + } + case WindowType_Browser: + { + CBrowserWindow *browserParent = dynamic_cast(parentWindow); + if (browserParent) + defaultURL = browserParent->GetWindowContext()->GetCurrentURL(); + } + } + } + mDefaultWebAttachmentURL = (char *)defaultURL; +} + +//----------------------------------- +MSG_Pane* CMailComposeWindow::CreateSession( + MWContext* old_context, + MSG_CompositionFields* inCompositionFields, + const char* initialText, + Boolean inOpeningDraft) +//----------------------------------- +{ + MSG_Pane* result = nil; + try { + mComposeSession = new CComposeSession(inOpeningDraft); + mComposeSession->AddListener(this); + mProgressListener = new CProgressListener(this, mComposeSession); + + result = mComposeSession->CreateBackendData(old_context, inCompositionFields); + if (result) + { + + // now pull data out from backend + // mHTMLEditView will be the flag if we are doing HTML mail or plain text + // if we have an editView we have an HTML mail window + // if we have an editor window, we need to init/load a blank page + // we do this after obtaining the subject and addressees so those + // fields are properly updated + SetSensibleTarget(); + if ( !mHTMLEditView ) + { + //----------------------------------- + // JRM: NOTE THE ORDERING. EACH ONE OF THESE CALLS WILL SET ITSELF AS + // THE TARGET IF IT IS EMPTY. SO THE ORDERING SHOULD BE MESSAGE, SUBJECT, + // ADDRESSEES. + //----------------------------------- + // check to see if we need to quote message at startup + mComposeSession->CheckForAutoQuote(); + const char* body =nil; + body = initialText? initialText:MSG_GetCompBody(mComposeSession->GetMSG_Pane()); + InsertMessageCompositionText( body, true ); + } + else + { + mHTMLEditView->SetInitialText(initialText); + // SwitchTarget(mHTMLEditView); + } + + // get subject + GetSubjectFromBackend(); + // get addresses + GetAllCompHeaders(); + + // Get priority + GetPriorityFromBackend(); + // Set up security button + USecurityIconHelpers::UpdateComposeButton( this ); + + // set fe data + mComposeSession->SetCompositionPaneFEData(this); + // make sure there is at least an empty addressee + EnsureAtLeastOneAddressee(); + + // we initialize the editor which will take care of calling the necessary + // functions for quoting, signatures, etc. (once the editor is done loading) + if ( mHTMLEditView ) + this->InitializeHTMLEditor( mHTMLEditView ); + // Set the default encodings + SetDefaultCSID( DefaultCSIDForNewWindow() ); + } + } catch (...) { + // what do we do if we can't create compose session or backend data? + if (mComposeSession) + mComposeSession->Stop(); + } + + // now that everything is set up, show the window + Show(); + + return result; +} // CMailComposeWindow::CreateSession + +//----------------------------------- +void CMailComposeWindow::FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +//----------------------------------- +{ + switch(inCommand) + { + case cmd_Toggle_Paragraph_Toolbar: + if (mHTMLEditView != nil) { + outEnabled = true; + outUsesMark = false; + if (mToolbarShown[CMailComposeWindow::FORMATTING_TOOLBAR]) + ::GetIndString(outName, CEditView::STRPOUND_EDITOR_MENUS, + CEditView::EDITOR_MENU_HIDE_FORMAT_TOOLBAR); + else + ::GetIndString(outName, CEditView::STRPOUND_EDITOR_MENUS, + CEditView:: EDITOR_MENU_SHOW_FORMAT_TOOLBAR); + } else { + outEnabled = false; + outUsesMark = false; + } + break; + case cmd_ToggleToolbar: + outEnabled = true; + outUsesMark = false; + if (mToolbarShown[CMailComposeWindow::MESSAGE_TOOLBAR]) + ::GetIndString(outName, BROWSER_MENU_TOGGLE_STRINGS_ID, HIDE_MESSAGE_TOOLBAR_STRING); + else + ::GetIndString(outName, BROWSER_MENU_TOGGLE_STRINGS_ID, SHOW_MESSAGE_TOOLBAR_STRING); + break; + case cmd_SendMessage: + case cmd_SendMessageLater: + outEnabled = ( + (!mHTMLEditView || mHTMLEditView->IsDoneLoading()) // HTML edit case + && !mComposeSession->GetDeliveryInProgress() + ); + outUsesMark = false; + if ( mOnlineLastFindCommandStatus != UOffline::AreCurrentlyOnline( ) ) + UpdateSendButton(); + break; + case cmd_Save: + if (mCurrentSaveCommand == cmd_SaveDraft) + ::GetIndString(outName, BROWSER_MENU_TOGGLE_STRINGS_ID, + SAVE_DRAFT_STRING); + else + ::GetIndString(outName, BROWSER_MENU_TOGGLE_STRINGS_ID, + SAVE_TEMPLATE_STRING); + // and fall through + case cmd_SaveDraft: + case cmd_SaveTemplate: + case cmd_QuoteMessage: + case cmd_Attach: + case msg_AttachWeb: + case msg_AttachFile: + case cmd_SecurityInfo: + case cmd_AddressBookWindow: + outEnabled = ( + (!mHTMLEditView || mHTMLEditView->IsDoneLoading()) // HTML edit case + && !mComposeSession->GetDeliveryInProgress() + ); + outUsesMark = false; + break; + case cmd_AttachMyAddressBookCard: + outEnabled = ( + (!mHTMLEditView || mHTMLEditView->IsDoneLoading()) // HTML edit case + && !mComposeSession->GetDeliveryInProgress() ); + outUsesMark = true; + outMark = mComposeSession->GetCompBoolHeader( MSG_ATTACH_VCARD_BOOL_HEADER_MASK) + ? checkMark: noMark; + break; + case cmd_PasteQuote: + Int32 offset; + outEnabled = ( + (!mHTMLEditView || mHTMLEditView->IsDoneLoading()) // HTML edit case + && !mComposeSession->GetDeliveryInProgress() + && ::GetScrap(nil, 'TEXT', &offset) > 0); + break; + case cmd_SaveAs: + if( !mHTMLEditView ) + outEnabled = true; + else + Inherited::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); + break; + default: + if(inCommand >= ENCODING_BASE && inCommand < ENCODING_CEILING) + { + outEnabled = true; + outUsesMark = true; + + int16 csid = CPrefs::CmdNumToDocCsid( inCommand ); + outMark = (csid == mComposeSession->GetDefaultCSID()) ? checkMark : ' '; + } else { + Inherited::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); + } + break; + } +} // CMailComposeWindow::FindCommandStatus + +//----------------------------------- +void CMailComposeWindow::HandleUpdateCompToolbar() +// called from FE_UpdateCompToolbar. +//----------------------------------- +{ + if (mInitializeState == eComposeSessionIsSet) + { + CMailCompositionContext* context = mComposeSession->GetCompositionContext(); + if (context) + { + URL_Struct* url = NET_CreateURLStruct( "about:editfilenew", NET_NORMAL_RELOAD ); + mInitializeState = eAboutURLLoading; + context->SwitchLoadURL( url, FO_CACHE_AND_PRESENT ); + } + } +} // CMailComposeWindow::HandleUpdateCompToolbar + +//----------------------------------- +Boolean CMailComposeWindow::HandleTabKey(const EventRecord &inKeyEvent) +//----------------------------------- +{ + Boolean keyHandled = true; + LCommander *body = NULL; + + if ( mHTMLEditView ) + body = dynamic_cast( mHTMLEditView ); + else if ( mPlainEditView ) + body = dynamic_cast( mPlainEditView ); + + Assert_( body != NULL ); + + Boolean tabBack = (inKeyEvent.modifiers & shiftKey) != 0; + + LEditField *subjectBar = dynamic_cast( FindPaneByID('Subj') ); + + Assert_( subjectBar != NULL ); + Assert_( mAddressTableView != NULL ); + + LCommander *curTarget = NULL, *tempTarget = nil; + + // we have to handle the following things here: + // 1. Collapsed drag bars + // 2. Reordering of drag bars + Boolean addressVisible = true, subjectVisible = true; + + // find out ordering in window of subject bar and address panel + SPoint32 subjectLocation, addressLocation; + + subjectBar->GetFrameLocation( subjectLocation ); + mAddressTableView->GetFrameLocation( addressLocation ); + + Int32 curItem = 1; + Int32 curTargetIndex = 1; //assume first item has focus for now + Int32 newTargetIndex, numTargets = 0; + LArray *targetList = new LArray(); + + if (addressVisible ) + { + tempTarget = dynamic_cast( mAddressTableView ); + targetList->InsertItemsAt(1, LArray::index_Last, &tempTarget, sizeof( LCommander *) ); + if (tempTarget->IsOnDuty()) + curTarget = tempTarget; + } + + if ( subjectVisible ) + { + tempTarget = dynamic_cast( subjectBar ); + targetList->InsertItemsAt(1, LArray::index_Last, &tempTarget, sizeof( LCommander *) ); + if (tempTarget->IsOnDuty()) + curTarget = tempTarget; + } + + if (addressVisible && subjectVisible) + { + if ( addressLocation.v >= subjectLocation.v ) // address below subject + targetList->SwapItems(1, 2); + } + + targetList->InsertItemsAt(1, LArray::index_Last, &body, sizeof( LCommander *) ); + if (body->IsOnDuty()) + curTarget = body; + + Assert_(curTarget != NULL); // something should always have focus. + + // now let's work out who is on duty, and get forward and backward + curTargetIndex = targetList->FetchIndexOf(&curTarget, sizeof(LCommander *)); + Assert_( curTargetIndex != LArray::index_Bad); + + // not so stupid as it seems, when we have collapsed bars + numTargets = targetList->GetCount(); + + // choose which one to go to + if ( tabBack ) { + newTargetIndex = curTargetIndex - 1; + if (newTargetIndex < 1) //arrays are 1-based + newTargetIndex += numTargets; + } else { + newTargetIndex = curTargetIndex + 1; + if (newTargetIndex > numTargets) //arrays are 1-based + newTargetIndex -= numTargets; + } + + LCommander *newTarget = NULL; + + if ( targetList->FetchItemAt( newTargetIndex, &newTarget) && (newTarget != curTarget) ) + { + if ( newTarget->ProcessCommand(msg_TabSelect) ) + { + // ProcessCommand sets the correct target for the mAddressTAbleView so Don't do it here + if( newTarget != mAddressTableView ) + SwitchTarget( newTarget ); + } + else + SwitchTarget( body ); + } + + delete targetList; + + return keyHandled; // always true for now +} + +//----------------------------------- +Boolean CMailComposeWindow::HandleKeyPress(const EventRecord &inKeyEvent) +//----------------------------------- +{ + Boolean keyHandled = true; + Char16 theKey = inKeyEvent.message & charCodeMask; + + if ( theKey == char_Tab ) + keyHandled = HandleTabKey( inKeyEvent ); + else + keyHandled = LCommander::HandleKeyPress(inKeyEvent); + + return keyHandled; +} + +//----------------------------------- +void CMailComposeWindow::InitializeHTMLEditor( CMailEditView* inEditorView ) +//----------------------------------- +{ + // initialize editor by loading url: "about:editfilenew" + if ( inEditorView ) + { + + CMailCompositionContext* context = mComposeSession->GetCompositionContext(); + inEditorView->SetContext( context ); + + // we need to set the compose session for auto-quoting (later) + inEditorView->SetComposeSession( mComposeSession ); + mInitializeState = eComposeSessionIsSet; + + if (!XP_IsContextBusy(*context)) + { + // we won't get an FE_UpdateCompToolbar call, so do it ourselves! + if (mInitializeState == eComposeSessionIsSet) + { + HandleUpdateCompToolbar(); + } + } + } + +} + +//----------------------------------- +void CMailComposeWindow::ListenToMessage(MessageT inMessage, void* ioParam) +//----------------------------------- +{ + CAttachmentView* attachView; + MSG_HTMLComposeAction action; + switch (inMessage) + { + // cmd's sent from toolbar buttons + case cmd_SendMessage: + SetSensibleTarget(); + XP_Bool sendNow; + PREF_GetBoolPref("network.online" , &sendNow); + SendMessage( sendNow ); + break; + case cmd_SendMessageLater: + SetSensibleTarget(); + SendMessage(false); + break; + case cmd_Save: + // This does a "save as template/draft according to last command. + inMessage = mCurrentSaveCommand; + // FALL THROUGH + case cmd_SaveDraft: + case cmd_SaveTemplate: + mCurrentSaveCommand = inMessage; + mComposeSession->SetMessage(inMessage); //needed for a relatively clean Drafts imp. + SetSensibleTarget(); + SaveDraftOrTemplate(inMessage); + break; + case cmd_QuoteMessage: + SetSensibleTarget(); + mComposeSession->QuoteMessage(); + break; + case cmd_Stop: + SetSensibleTarget(); + mComposeSession->Stop(); + // EnableButtons(); + break; + // messages from CComposeSession + case CComposeSession::msg_InsertQuoteText: + //SetSensibleTarget(); + InsertMessageCompositionText(reinterpret_cast(ioParam)); + break; + case CComposeSession::msg_AutoQuoteDone: + int32 eReplyOnTop = 1; + +// LTextEngine* theTextEngine = mPlainEditView->GetTextEngine(); +// LTextSelection* theSelection = mPlainEditView->GetTextSelection(); +// TextRangeT theBeforeRange = theSelection->GetSelectionRange(); + + SInt32 selStart = 0; + SInt32 selEnd = 0; + + mPlainEditView->GetSelection( &selStart, &selEnd ); + + if ( PREF_NOERROR ==PREF_GetIntPref("mailnews.reply_on_top", &eReplyOnTop) ) + { + switch (eReplyOnTop) + { + case 0: + // Cursor is in the right spot + break; + case 1: + default: + +// theBeforeRange.SetStart( 0 ); +// theSelection->SetSelectionRange(theBeforeRange); + + selEnd = selEnd - selStart; + selStart = 0; + + mPlainEditView->SetSelection( selStart, selEnd ); + + break; + case 2: + case 3: + +// long end = theBeforeRange.Start(); +// theBeforeRange.SetStart( 0 ); +// theBeforeRange.SetEnd( end ); +// theSelection->SetSelectionRange(theBeforeRange); + + selEnd = selStart; + selStart = 0; + + mPlainEditView->SetSelection( selStart, selEnd ); + + break; + } + } + break; + + case cmd_CheckSpelling: + if( mHTMLEditView ) + mHTMLEditView->ObeyCommand( inMessage, ioParam ); + else + { + if( mPlainEditView ) + mPlainEditView->ObeyCommand( inMessage, ioParam ); + } + break; + // message from CComposeTabSwitcher + // This is the only semi-clean way that I know of to synchronize + // the attachment list with the CAttachmentView. + case msg_ActivatingAttachTab: + SetSensibleTarget(); + attachView = reinterpret_cast(ioParam); + if (mAttachmentList && !attachView->GetAttachList()) + { + GetAttachmentsFromBackend(); + mHaveInitializedAttachmentsFromBE = true; + attachView->SetAttachList(mAttachmentList); + mAttachmentView = attachView; + mAttachmentView->AddListener( this ); + } + attachView->AddDropAreaToWindow(this); + break; + case msg_DeactivatingAttachTab: + SetSensibleTarget(); + attachView = reinterpret_cast(ioParam); + attachView->RemoveDropAreaFromWindow(this); + break; + case msg_ActivatingAddressTab: + SetSensibleTarget(); + mAddressTableView->AddDropAreaToWindow(this); + break; + case msg_DeactivatingAddressTab: + SetSensibleTarget(); + mAddressTableView->RemoveDropAreaFromWindow(this); + break; + case msg_ActivatingOptionTab: + SetSensibleTarget(); + CMailOptionTabContainer* optionView = reinterpret_cast(ioParam); + + LGACheckbox *control=dynamic_cast (optionView-> FindPaneByID( 'OtRe' )); + Assert_(control); + control->SetValue( mComposeSession->GetCompBoolHeader( MSG_RETURN_RECEIPT_BOOL_HEADER_MASK) ); + + HG43287 + + LGAPopup *popup = dynamic_cast( optionView->FindPaneByID('OtHa') ); + Assert_( popup != nil ); + if( mHTMLEditView ) + { + action = MSG_GetHTMLAction( mComposeSession->GetMSG_Pane() ); + Int32 popupValue = 0; + switch( action) + { + case MSG_HTMLAskUser: + popupValue = 1; + break; + case MSG_HTMLConvertToPlaintext: + popupValue = 2; + break; + case MSG_HTMLSendAsHTML: + popupValue = 3; + break; + case MSG_HTMLUseMultipartAlternative: + popupValue = 4; + break; + } + popup->SetValue( popupValue ); + } + else + popup->Disable(); // Not used for plain text editing + break; + case cmd_SecurityInfo: + SetSensibleTarget(); + // Update Header fields + try + { + SyncAddressLists(); + SECNAV_SecurityAdvisor((MWContext*)*(mComposeSession->GetCompositionContext()), + (URL_Struct_*)nil); + } catch (...) { + // couldn't allocate memory for buffer + } + break; + case cmd_AddressBookWindow: + SetSensibleTarget(); + #ifdef MOZ_NEWADDR + CAddressPickerWindow::DoPickerDialog( mAddressTableView ); + #else + CAddressBookManager::ShowAddressBookWindow( ); + #endif + break; + // Option Tab + case msg_ReturnRecipt: + mComposeSession->SetCompBoolHeader( MSG_RETURN_RECEIPT_BOOL_HEADER_MASK, *(Int32*)ioParam); + break; + case msg_Garbled: + HG43288 + break; + case msg_Signed: + mComposeSession->SetCompBoolHeader( MSG_SIGNED_BOOL_HEADER_MASK, *(Int32*)ioParam); + USecurityIconHelpers::UpdateComposeButton( this); + break; + case msg_UUEncode: + mComposeSession->SetCompBoolHeader( MSG_UUENCODE_BINARY_BOOL_HEADER_MASK, *(Int32*)ioParam); + break; + #if 0 + case msg_8BitEncoding: + MIME_ConformToStandard( value ? 0 : 1 ); + break; + #endif //0 + case msg_HTMLAction: + switch( *(Int32*)ioParam ) + { + case 1: + action = MSG_HTMLAskUser; + break; + case 2: + action = MSG_HTMLConvertToPlaintext; + break; + case 3: + action = MSG_HTMLSendAsHTML; + break; + case 4: + action = MSG_HTMLUseMultipartAlternative; + break; + } + MSG_SetHTMLAction( mComposeSession->GetMSG_Pane(), action ); + break; + // Attachments + case msg_AttachFile: + case msg_AttachWeb: + // Create an attachment view by switching to it and then switch back + CComposeTabSwitcher* tabSwitcher = + dynamic_cast(FindPaneByID('TbSw')); + tabSwitcher->ManuallySwitchToTab( 2 ); + mAttachmentView->ListenToMessage( inMessage, (void *)((unsigned char *)mDefaultWebAttachmentURL)); + break; + + case cmd_AttachMyAddressBookCard: + mComposeSession->SetCompBoolHeader(MSG_ATTACH_VCARD_BOOL_HEADER_MASK , + !mComposeSession->GetCompBoolHeader( MSG_ATTACH_VCARD_BOOL_HEADER_MASK)); + break; + // mDirtyHeaders + case msg_AddressChanged: + case CAttachmentView::msg_AttachmentsAdded: + case CAttachmentView::msg_AttachmentsRemoved: + mHeadersDirty = true; + break; + } +} + +void CMailComposeWindow::UpdateSendButton() +{ + mOnlineLastFindCommandStatus = UOffline::AreCurrentlyOnline( ); + + CPatternButton *sendNowButton = dynamic_cast(FindPaneByID(cSendButtonPaneID)); + const ResIDT kSendNowGraphicID = 15319, kSendLaterGraphicID = 15323; + sendNowButton->SetGraphicID( + mOnlineLastFindCommandStatus + ? kSendNowGraphicID + : kSendLaterGraphicID); + sendNowButton->Refresh(); +} + +Boolean CMailComposeWindow::ObeyCommand(CommandT inCommand, void *ioParam) +{ + Boolean cmdHandled = true; + + switch (inCommand) + { + case cmd_ToggleToolbar: + ToggleDragBar(cMessageToolbar, CMailNewsWindow::MESSAGE_TOOLBAR); + cmdHandled = true; + break; + + case cmd_Toggle_Paragraph_Toolbar: + ToggleDragBar(cFormattingToolbar, CMailComposeWindow::FORMATTING_TOOLBAR); + cmdHandled = true; + break; + + case cmd_PasteQuote: + SetSensibleTarget(); + int32 textLen; + textLen = LClipboard::GetClipboard()->GetData( 'TEXT', nil ); + if (textLen > 0) + { + Handle textHandle = ::NewHandle(textLen + 1); + if (textHandle) + { + LClipboard::GetClipboard()->GetData( 'TEXT', textHandle ); + ::HLock(textHandle); + (*textHandle)[textLen] = '\0'; + if( mHTMLEditView ) + MSG_PastePlaintextQuotation(mComposeSession->GetMSG_Pane(), *textHandle); + else + { //plain text case + char *quotedText = MSG_ConvertToQuotation (*textHandle); + if( quotedText ) + { + InsertMessageCompositionText( quotedText ); + XP_FREE( quotedText ); + } + } + ::DisposeHandle(textHandle); + } + } + break; + case cmd_SaveAs: + if( mHTMLEditView ) + cmdHandled =false; + else + { + StandardFileReply reply; + short format; + CStr31 defaultFileName; + GetDescriptor( defaultFileName ); + UStdDialogs::AskSaveAsSource( reply, defaultFileName , format ); + if ( reply.sfGood ) + mPlainEditView->Save( reply.sfFile ); + } + break; + // Commands that have button bar equivalents + case cmd_SendMessage: + case cmd_SendMessageLater: + case cmd_QuoteMessage: + case cmd_SaveDraft: + case cmd_SaveTemplate: + case msg_AttachWeb: + case msg_AttachFile: + case cmd_AttachMyAddressBookCard: + ListenToMessage( inCommand, ioParam); + break; + default: + if(inCommand >= ENCODING_BASE && inCommand < ENCODING_CEILING) + { + SetDefaultCSID(CPrefs::CmdNumToDocCsid(inCommand)); + cmdHandled = true; + } else { + cmdHandled = Inherited::ObeyCommand(inCommand, ioParam); + } + break; + } + return cmdHandled; +} +void CMailComposeWindow::SetDefaultCSID(Int16 defaultcsid) +{ + Assert_(mComposeSession); + if(mComposeSession) + { + + int16 wincsid = INTL_DocToWinCharSetID(defaultcsid); + ResIDT textTraitID = CPrefs::GetTextFieldTextResIDs(wincsid); + + // Set the HTML editor, if present + if (mHTMLEditView) + { + if( !mHTMLEditView->SetDefaultCSID(defaultcsid) ) + return; + } + // Set the plain text editor, if present + if ( mPlainEditView ) { + mPlainEditView->SetInitialTraits(textTraitID); + mPlainEditView->ResetModCount(); // fix for bug # 104805 ask save on close + } + + // Set The Subject Font + if(CTSMEditField* subjectField = dynamic_cast(FindPaneByID('Subj'))) + { + subjectField->SetTextTraitsID(textTraitID); + subjectField->Refresh(); + } + + // Set The Address Font + if( mAddressTableView ) + mAddressTableView->SetTextTraits( textTraitID ); + + // Set the Session + mComposeSession->SetDefaultCSID(defaultcsid); + +#if 0 + // To Work Around Our TSMTE problem + Int32 dummyVer; + if( (defaultcsid & MULTIBYTE ) && + (noErr == ::Gestalt(gestaltTSMTEAttr, &dummyVer)) && + ((dummyVer & (1 << gestaltTSMTEPresent) ) != 0) + ) { + this->SetSensibleTarget(); + } +#endif //0 + } +} + + +void CMailComposeWindow::SyncAddressLists() +{ + + // Clear the comp headers in case entries were deleted from the + // address view + // Save & Restore the (Reply To) and (Followup To) fields + char* replyto = nil; + replyto = XP_STRDUP( + mComposeSession->GetMessageHeaderData( MSG_REPLY_TO_HEADER_MASK ) ); + FailNIL_(replyto); + char* followupto = nil; + followupto = XP_STRDUP( + mComposeSession->GetMessageHeaderData( MSG_FOLLOWUP_TO_HEADER_MASK ) ); + FailNIL_(followupto); + MSG_ClearComposeHeaders ( mComposeSession->GetMSG_Pane() ); + MSG_SetCompHeader( mComposeSession->GetMSG_Pane(), + MSG_REPLY_TO_HEADER_MASK, replyto ); + MSG_SetCompHeader( mComposeSession->GetMSG_Pane(), + MSG_FOLLOWUP_TO_HEADER_MASK, followupto ); + XP_FREE( replyto ); + XP_FREE( followupto ); + + // set composition headers + for (int type = eToType; type <= eFollowupType; type++) + SetCompHeader((EAddressType)type); +} + +Boolean CMailComposeWindow::PrepareMessage( Boolean isDraft) +{ + // disable all toolbar buttons except stop + //DisableAllButtonsExceptStop(); + if (mComposeSession) + { + SyncAddressLists(); + // set subject + char* subject = GetSubject(); + // if (**teHandle).teLength == 0, we should probably ask the user + // if they want to enter a subject right about here + if (subject && XP_STRLEN( subject )) + { + // Copy subject out of TEHandle in edit field and pass it to + // msglib. Don't forget to dispose of buffer. + MSG_SetCompHeader(mComposeSession->GetMSG_Pane(),MSG_SUBJECT_HEADER_MASK, subject); + + XP_FREE( subject ); + } +#if 1 // This is a temporary hack till we really fix bug #42878. + else if( !isDraft ) + { + CTSMEditField* subjectField = dynamic_cast(FindPaneByID('Subj')); // use CTSMEditField to support Asian Inline input + SysBeep(1); + SwitchTarget(subjectField); + CStr255 errorSubject(XP_GetString(MK_MIME_NO_SUBJECT)); + subjectField->SetDescriptor(errorSubject); + subjectField->SelectAll(); + return false; + } +#endif + // set priority + // pkc -- 10/8/96 priority isn't ready in backend + LControl* priorityMenu = dynamic_cast(FindPaneByID('Prio')); + Int32 priority = priorityMenu->GetValue(); + mComposeSession->SetPriority(UComposeUtilities::GetMsgPriorityFromMenuItem(priority)); + + // set body + if ( mPlainEditView ) + { +// LTextEngine* textEngine = dynamic_cast(mPlainEditView->GetTextEngine()); + + Handle msgBody = mPlainEditView->GetTextHandle(); + if (msgBody) + { + try { + LHandleStream handleStream; + // preallocate handle + handleStream.SetLength(1024); + +// TextRangeT textRange = textEngine->GetTotalRange(); + UInt32 textLength = mPlainEditView->GetTextLength(); + + UComposeUtilities::WordWrap(msgBody, textLength, handleStream); + handleStream << (unsigned char) '\0'; + mComposeSession->SetMessageBody(handleStream); + } catch (...) { + // couldn't allocate memory for LHandleStream + } + } + } + else + { + // send HTML text + // all of the HTML is gotten from the MWContext + mComposeSession->SetHTMLMessageBody(); + } + } + if( !mHaveInitializedAttachmentsFromBE ) + { + GetAttachmentsFromBackend(); + mHaveInitializedAttachmentsFromBE = true; + } + + return true; +} // CMailComposeWindow::PrepareMessage + +extern "C" void MSG_ResetUUEncode(MSG_Pane *pane); + +void CMailComposeWindow::SaveDraftOrTemplate(CommandT inCommand, Boolean inCloseWindow) +{ + mComposeSession->SetMessage( inCommand ); + MSG_AttachmentData* attachList = nil; + try { + + // ### mwelch Reset the uuencode UI state so the + // dialog pops up once per send attempt. + MSG_ResetUUEncode(mComposeSession->GetMSG_Pane()); + + PrepareMessage( true ); + // set attachments + attachList = mAttachmentList->NewXPAttachmentList(); + if (mComposeSession->NeedToSyncAttachmentList(attachList)) + { // send attachments + // there's no need to call mComposeSession->SaveDraftOrTemplate() + // because the compose session will catch the FE_AllConnectionsComplete + // callback when loading the attachments and will SaveDraft then + mComposeSession->SetAttachmentList(attachList); + mComposeSession->SetCloseWindow( inCloseWindow ); + XP_FREE(attachList); + } + else + { + // just save the Draft + mComposeSession->SaveDraftOrTemplate(inCommand, inCloseWindow); + } + } catch (...) { + XP_FREEIF(attachList); + // most likely, couldn't allocate memory for attachList + } + mHeadersDirty = false; + if( mPlainEditView ) + { + +// LTextEngine* theTextEngine = mPlainEditView->GetTextEngine(); +// theTextEngine->SetTextChanged( false ); + + mPlainEditView->ResetModCount(); + + } +} + +void CMailComposeWindow::SendMessage(Boolean inSendNow) +{ + // See if we have to spell check + XP_Bool needToSpellCheck = false; + PREF_GetBoolPref("mail.SpellCheckBeforeSend", &needToSpellCheck); + if ( needToSpellCheck ) + { + ListenToMessage( cmd_CheckSpelling, NULL ); + ObeyCommand( cmd_CheckSpelling, NULL ); + } + + MSG_AttachmentData* attachList = nil; + //Boolean needToSave = NeedToSave(); + if( PrepareMessage( false ) ) + { + // set attachments + try { + // ### mwelch. Reset the uuencode UI state so the + // dialog pops up once per send attempt. + MSG_ResetUUEncode(mComposeSession->GetMSG_Pane()); + + attachList = mAttachmentList->NewXPAttachmentList(); + if (mComposeSession->NeedToSyncAttachmentList(attachList)) + { // send attachments + // there's no need to call mComposeSession->SendMessage() + // because the compose session will catch the FE_AllConnectionsComplete + // callback when loading the attachments and will call SendMessage then + mComposeSession->SetAttachmentList(attachList); + XP_FREE(attachList); + mComposeSession->SetSendNow(inSendNow); + } + else + { + // just send message + mComposeSession->SendMessage(inSendNow); + } + } catch (...) { + XP_FREEIF(attachList); + // ### mwelch. We can get exceptions by running out of memory + // or if the user hit "Cancel" to a uuencode confirmation + // dialog. (Yes, the back end throws an exception here.) + // See MSG_DeliverMimeAttachment::SnarfAttachment in + // ns/lib/libmsg/msgsend.cpp. + } + } + //mHeadersDirty = needToSave; // if send fails, dirty flag is still preserved +} + +void CMailComposeWindow::SetCompHeader(EAddressType inAddressType) +{ + if (mComposeSession && mAddressTableView) + { + try { + LHandleStream stream; + // preallocate handle + stream.SetLength(512); + stream.SetMarker(0, streamFrom_Start); + mAddressTableView->CreateCompHeader(inAddressType, stream); + if (stream.GetMarker() > 0) + mComposeSession->SetMessageHeaderData( + UComposeUtilities::GetMsgHeaderMaskFromAddressType(inAddressType), + stream); + } catch (...) { + } + } +} + + +//----------------------------------- +void CMailComposeWindow::GetCompHeader(EAddressType inAddressType) +//----------------------------------- +{ + Assert_(mAddressTableView && mComposeSession); + char* scanner = (char *)mComposeSession->GetMessageHeaderData( + UComposeUtilities::GetMsgHeaderMaskFromAddressType(inAddressType)); + MSG_HeaderEntry *returnList=nil; + Int32 numberItems= MSG_ExplodeHeaderField( inAddressType, scanner, &returnList ); + if ( numberItems!=-1 ) + { + for(Int32 currentItem=0; currentItemInsertNewRow(EAddressType(returnList[currentItem].header_type), + returnList[currentItem].header_value ); + XP_FREE( returnList[currentItem].header_value ); + } + XP_FREE ( returnList ); + } + + #if 0 // MSGComh. has a better function + if (scanner) + { + // now parse comma delimited string + char *addr = scanner; + while (*scanner != nil) + { + // find end of address + while (*scanner != nil && *scanner != ',') + ++scanner; + try { + char* addrStr = new char[scanner - addr + 1]; + ::BlockMoveData(addr, addrStr, scanner - addr); + addrStr[scanner - addr] = '\0'; + mAddressTableView->InsertNewRow(inAddressType, addrStr); + delete [] addrStr; + } catch (...) { + } + while (*scanner != nil && (*scanner == ',' || *scanner == ' ' || *scanner == '\t')) + ++scanner; + addr = scanner; + } + } + #endif //0 +} // CMailComposeWindow::GetCompHeader + +//----------------------------------- +Int16 CMailComposeWindow::DefaultCSIDForNewWindow() +//----------------------------------- +{ + if(mComposeSession) + return mComposeSession->GetDefaultCSID(); // Delgate to mComposeSession + else + return 0; +} // CMailComposeWindow::DefaultCSIDForNewWindow() + +//----------------------------------- +void CMailComposeWindow::GetAllCompHeaders() +//----------------------------------- +{ + for (int i = eToType; i <= eFollowupType; i++) + if (i != eReplyType) + GetCompHeader((EAddressType)i); +} + +//----------------------------------- +char* CMailComposeWindow::GetSubject() +// Caller responsible for freeing return value +//----------------------------------- +{ + char* result = nil; + CLargeEditField* subjectField = dynamic_cast(FindPaneByID('Subj')); // use CTSMEditField to support Asian Inline input + if (subjectField) + result = subjectField->GetLongDescriptor(); + Assert_(result); + return result; +} // CMailComposeWindow::GetSubjectTEHandle + +//----------------------------------- +void CMailComposeWindow::GetSubjectFromBackend() +//----------------------------------- +{ + Assert_(mComposeSession); + const char* subject = mComposeSession->GetSubject(); + + // We need to set up the TextTrait of Subject according to the csid. + CLargeEditField* subjectField = dynamic_cast(FindPaneByID('Subj')); + + if (subject && *subject) + subjectField->SetLongDescriptor( subject ); + else + { + SwitchTarget( subjectField ); + } +} // CMailComposeWindow::GetSubjectFromBackend + + +void CMailComposeWindow::GetPriorityFromBackend() +{ + LControl *priority = dynamic_cast( FindPaneByID('Prio') ); + if( priority ) + { + // Need to do this to ensure that we refresh the menu with new value + priority->SetValue( 1 ); + MSG_PRIORITY backendPriority = mComposeSession->GetPriority(); + if( backendPriority == MSG_NoPriority ) + backendPriority = MSG_NormalPriority; + // MSG_NoPriority isn't a popup item so all numbers off by 1 + Int32 value = Int32( backendPriority) - Int32( MSG_NoPriority ); + priority->SetValue( value ); + } +} +//----------------------------------- +void CMailComposeWindow::GetAttachmentsFromBackend() +//----------------------------------- +{ + Assert_(mComposeSession); + const struct MSG_AttachmentData* attachData = + mComposeSession->GetAttachmentData(); + if (attachData) + { + Assert_(mAttachmentList); + mAttachmentList->InitializeFromXPAttachmentList(attachData); + } +} + +//----------------------------------- +void CMailComposeWindow::EnsureAtLeastOneAddressee() +//----------------------------------- +{ + Assert_(mAddressTableView); + TableIndexT numRows; + mAddressTableView->GetNumRows(numRows); + Boolean insertNewRow = true; + while ( numRows ) + { + EAddressType addressType = mAddressTableView->GetRowAddressType( numRows ); + if( addressType != eBccType) + { + insertNewRow = false; + break; + } + numRows--; + } + + if ( insertNewRow ) + { + mAddressTableView->InsertNewRow( true, true); + } + +} + +//----------------------------------- +void CMailComposeWindow::SetSensibleTarget() +//----------------------------------- +{ + + if (mPlainEditView) + { + SwitchTarget(mPlainEditView); + } + if (mHTMLEditView) + SwitchTarget(mHTMLEditView); +} + +//----------------------------------- +ExceptionCode CMailComposeWindow::InsertMessageCompositionText(const char* text, Boolean leaveCursorinFront) +// this code taken straight from mailmac.cp +// Actually seems to be called only for plain-text quoting. +//----------------------------------- +{ +#define SUCCESS 0 + ExceptionCode err = SUCCESS; +// if (text == nil) // Don't do this. +// Even if text is nil, this function must set the target to the appropriate text view. +// return err; + + StWatchCursor watchCursor; + + try // TextReplaceByPtr can throw + { + + Assert_(mPlainEditView); // seems to be called only for simple text. + if (mPlainEditView) + { + // SwitchTarget(mPlainEditView); + if (!text || !*text) + return SUCCESS; + + Boolean textChanged = (mPlainEditView->GetModCount() > 0); + + + // + // Blast the text into the text field. + // + // We must first ensure that the field is not marked + // read-only (which is likely since we've probably been + // asked by XP to disable toolbar buttons, at which + // point we also disable the body field). + // + // Save the field's attributes, mark it writable, + // blast the text, restore the attributes. + // + + Boolean isReadOnly = mPlainEditView->IsReadOnly(); + + if ( isReadOnly ) + mPlainEditView->SetReadOnly( false ); + + SInt32 oldSelStart = 0; + SInt32 oldSelEnd = 0; + + mPlainEditView->GetSelection( &oldSelStart, &oldSelEnd ); + + SInt32 textLen = text ? XP_STRLEN(text) : 0; + + if (text != NULL) + mPlainEditView->InsertPtr( const_cast(text), textLen, NULL, NULL, false, false ); + + // We need to move the selection range manually + + if ( !leaveCursorinFront ) + { + oldSelStart = oldSelStart + textLen; + + if ( oldSelEnd < oldSelStart ) + oldSelEnd = oldSelStart; + } + + mPlainEditView->SetSelection( oldSelStart, oldSelEnd ); + + if (! textChanged) + mPlainEditView->ResetModCount(); + } + + } + catch (ExceptionCode inErr) + { + err = inErr; + } + + return err; +} // CMailComposeWindow::InsertMessageCompositionText + +Boolean CMailComposeWindow::NeedToSave() +{ + + // attachments and addresses broadcast when changed + SetSensibleTarget(); + if( mHeadersDirty ) + return true; + Boolean dirty = true; + + // Subject + CStr255 subject( mComposeSession->GetSubject() ); + CTSMEditField* subjectField = dynamic_cast(FindPaneByID('Subj')); + CStr255 currentText; + if (subjectField) + subjectField->GetDescriptor(currentText); + dirty = (currentText != subject); + if (dirty) + return dirty; + //Body + + if (mHTMLEditView) + dirty = EDT_DirtyFlag( mComposeSession->GetCompositionContext() ->operator MWContext*() ); + else + { + +// LTextEngine* theTextEngine = mPlainEditView->GetTextEngine(); + + dirty = (mPlainEditView->GetModCount() > 0); + + } + return dirty; + +} // CMailComposeWindow::NeedToSave + +Boolean CMailComposeWindow::AskIfUserWantsToClose() +{ + MessageT itemHit = HandleModalDialog( COMPOSEDLG_SAVE_BEFORE_QUIT, nil, nil ); + if (itemHit == cancel) + return false; + + if (itemHit == ok) + { + SaveDraftOrTemplate(cmd_SaveDraft, true ); + return false ; // Drafts will close the window Clean up for 4.02 + } + return true; +} + +void CMailComposeWindow::AttemptClose() +{ + Select(); // This helps for "close all" + if (mComposeSession->GetDownloadingAttachments() + || XP_IsContextBusy(*mComposeSession->GetCompositionContext())) + { + mComposeSession->Stop(); + return; // do we really want to return here? See bug #73544 + // need this return to cancel close of aborted send + // but it might leave open a window with partially + // loaded attachments, which could cause us to send + // bad data. + // see also bug 107078 that prompted this comment. + } + else + { + if (NeedToSave() && !AskIfUserWantsToClose()) + return; + } + Inherited::AttemptClose(); +} // CMailComposeWindow::AttemptClose() + +//---------------------------------------------------------------------------------------- +Boolean CMailComposeWindow::AttemptQuitSelf(Int32 inSaveOption) +//---------------------------------------------------------------------------------------- +{ + if (mComposeSession->GetDownloadingAttachments() + || XP_IsContextBusy(*mComposeSession->GetCompositionContext())) + { + mComposeSession->Stop(); + return false; + } + + if (NeedToSave() && !AskIfUserWantsToClose()) + return false; + return Inherited::AttemptQuitSelf(inSaveOption); +} // CMailComposeWindow::AttemptQuitSelf + +//---------------------------------------------------------------------------------------- +void CMailComposeWindow::DoCloseLater() +//---------------------------------------------------------------------------------------- +{ + CDeferredCloseTask::DeferredClose(this); +} // CMailComposeWindow::DoCloseLater + +//---------------------------------------------------------------------------------------- +void CMailComposeWindow::SpendTime(const EventRecord &/*inMacEvent*/) +//---------------------------------------------------------------------------------------- +{ + // Update the window title to match the subject + CTSMEditField* subjectField = dynamic_cast(FindPaneByID('Subj')); + CStr255 currentSubject; + if (subjectField) + { + subjectField->GetDescriptor(currentSubject); + // Leave the default window title until they've typed something. + if (currentSubject != CStr255::sEmptyString) + { + CStr255 windowTitle; + this->GetDescriptor(windowTitle); + // Test for equality to avoid flickering. + if (windowTitle != currentSubject) + this->SetDescriptor(currentSubject); + } + } +} // CMailComposeWindow::SpendTime + +#pragma mark - + +CTabContainer::CTabContainer(LStream* inStream) : +CPatternBevelView(inStream) +{ +} + +void CTabContainer::DrawBeveledFrame(void) +{ + // Overridden to draw a partial-rect border + Rect theFrame; + CalcLocalFrameRect(theFrame); + SBooleanRect theBevelSides = { true, false, true, true }; + UGraphicGizmos::BevelTintPartialRect(theFrame, mMainBevel, 0x4000, 0x4000, theBevelSides); +} + +void CTabContainer::DrawSelf() +{ +#if 0 + Inherited::DrawSelf(); + Rect theFrame; + if (CalcLocalFrameRect(theFrame)) + { + StColorPenState theSaver; + theSaver.Normalize(); + + SBevelColorDesc theDesc; + UGraphicGizmos::LoadBevelTraits(5000, theDesc); + + StClipRgnState theClipSaver(theFrame); + StColorState::Normalize(); + + theFrame.left-=5; + ::FrameRect(&theFrame); + + SBooleanRect theBevelSides = { false, true, true, true }; + UGraphicGizmos::BevelPartialRect(theFrame, 1, eStdGrayBlack, eStdGrayBlack, theBevelSides); + ::InsetRect(&theFrame, 1, 1); + UGraphicGizmos::BevelPartialRect(theFrame, 2, theDesc.topBevelColor, theDesc.bottomBevelColor, theBevelSides); + } +#endif + Inherited::DrawSelf(); + Rect theFrame; + if (CalcLocalFrameRect(theFrame)) + { + StColorPenState theSaver; + theSaver.Normalize(); + + StClipRgnState theClipSaver(theFrame); + StColorState::Normalize(); + + theFrame.top -= 5; // to hide the top + ::FrameRect(&theFrame); + } +} + +CMailTabContainer::CMailTabContainer(LStream* inStream) : CTabContainer( inStream ) +{ +} + +void CMailTabContainer::DrawBeveledFrame(void) +{ + // Overridden to draw a partial-rect border + Rect theFrame; + CalcLocalFrameRect(theFrame); + SBooleanRect theBevelSides = { false, true, true, true }; + UGraphicGizmos::BevelTintPartialRect(theFrame, mMainBevel, 0x4000, 0x4000, theBevelSides); +} + +void CMailTabContainer::DrawSelf() +{ + Inherited::DrawSelf(); + Rect theFrame; + if (CalcLocalFrameRect(theFrame)) + { + StColorPenState theSaver; + theSaver.Normalize(); + + StClipRgnState theClipSaver(theFrame); + StColorState::Normalize(); + + theFrame.left -= 5; // to hide the left + ::FrameRect(&theFrame); + } +} + +#pragma mark - + +CMailComposeTabContainer::CMailComposeTabContainer(LStream* inStream) : +CMailTabContainer(inStream) +{ +} + +void CMailComposeTabContainer::FinishCreateSelf() +{ + //UReanimator::LinkListenerToControls(dynamic_cast(FindPaneByID('Addr')),this,10611); +} + + +#pragma mark - + +CMailAttachmentTabContainer::CMailAttachmentTabContainer(LStream* inStream) : +CMailTabContainer(inStream) +{ +} + +void CMailAttachmentTabContainer::FinishCreateSelf() +{ +// CAttachmentView* attachView = dynamic_cast(FindPaneByID('Attv')); +// try { +// attachView->SetAttachList(new CAttachmentList); +// } catch (...) { +// } + UReanimator::LinkListenerToControls(dynamic_cast(FindPaneByID('Attv')),this,10612); +} + +#pragma mark - + +CMailCompositionContext::CMailCompositionContext() : +CBrowserContext(MWContextMessageComposition) +{ + MWContext *mwcontext = operator MWContext*(); + if ( mwcontext ) + { + mwcontext->is_editor = true; + mwcontext->bIsComposeWindow = true; + } +} + +void CMailCompositionContext::AllConnectionsComplete() +{ + StSharer share(this); + CBrowserContext::AllConnectionsComplete(); +} + +void CMailCompositionContext::CreateContextProgress() +{ + try { + mProgress = new CContextProgress; + mProgress->AddUser(this); + } catch (...) { + } +} + +#pragma mark - + +CComposeTabSwitcher::CComposeTabSwitcher(LStream* inStream) : +CTabSwitcher(inStream) +{ +} + +void CComposeTabSwitcher::ManuallySwitchToTab( int32 tabID) +{ + CTabControl* theTabControl = (CTabControl*)FindPaneByID(mTabControlID); + theTabControl->SetValue( tabID ); +} +void CComposeTabSwitcher::DoPostLoad(LView* inLoadedPage, Boolean /*inWillCache*/) +{ + + LView* view = (LView*)inLoadedPage->FindPaneByID('Attv'); + if (view) + { + CAttachmentView* attachView = dynamic_cast(view); + BroadcastMessage(msg_ActivatingAttachTab, attachView); + } + else if ((LView*)inLoadedPage->FindPaneByID('Addr')) + { + BroadcastMessage(msg_ActivatingAddressTab,nil); + } + + view = ( LView*)inLoadedPage->FindPaneByID('OpTC'); + if( view ) + { + CMailOptionTabContainer* optionView = dynamic_cast(view); + BroadcastMessage( msg_ActivatingOptionTab, optionView ); + } + +} + +void CComposeTabSwitcher::DoPreDispose(LView* inLeavingPage, Boolean /*inWillCache*/) +{ + LView* view = (LView*)inLeavingPage->FindPaneByID('Attv'); + if (view) + BroadcastMessage(msg_DeactivatingAttachTab, view); + else if ((LView*)inLeavingPage->FindPaneByID('Addr')) + { + BroadcastMessage(msg_DeactivatingAddressTab, nil); + } +} + +//====================================== +// class CMailOptionTabContainer : public CTabContainer +//====================================== +CMailOptionTabContainer::CMailOptionTabContainer(LStream* inStream):CMailTabContainer(inStream) +{ + +} + +void CMailOptionTabContainer::FinishCreateSelf() +{ + CMailComposeWindow* window + = dynamic_cast(LWindow::FetchWindowObject(GetMacPort())); + if( window ) + { + UReanimator::LinkListenerToControls( window, this, 10617); + } +} diff --git a/mozilla/cmd/macfe/MailNews/CMailComposeWindow.h b/mozilla/cmd/macfe/MailNews/CMailComposeWindow.h new file mode 100644 index 00000000000..d2b944c9690 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMailComposeWindow.h @@ -0,0 +1,305 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMailComposeWindow.h + +#pragma once + +// C/C++ headers +//#include + +// PowerPlant +#include +#include +#include +#include +#include + +// Mail/News MacFE stuff +#include "CBrowserContext.h" +#include "CComposeAddressTableView.h" +//#include "VEditField.h" +#include "CTSMEditField.h" + +// UI elements + +#include "CMailNewsWindow.h" +#include "CPatternBevelView.h" +#include "CTabSwitcher.h" +// #include "CGrayBevelView.h" + +// Netscape stuff +#include "msgcom.h" +#include "mattach.h" +#include "PascalString.h" + +class CSimpleTextView; +extern "C" void FE_SecurityOptionsChanged( MWContext* context); + + +const MessageT msg_DeleteCompositionContext = 'DCmC'; + +const MessageT msg_FinalizeAddrCellEdit = 'FAdC'; + +const MessageT msg_RefreshAddrCellEdit = 'RfAC'; + +const MessageT msg_ActivatingAttachTab = 'AcAT'; +const MessageT msg_DeactivatingAttachTab = 'DeAT'; +const MessageT msg_ActivatingAddressTab = 'AcAD'; +const MessageT msg_DeactivatingAddressTab = 'DeAD'; +const MessageT msg_ActivatingOptionTab = 'AcOT'; +const MessageT msg_DeactivatingOptionTab = 'DeOT'; + +const PaneIDT cSendButtonPaneID = 'Send'; +const PaneIDT cSendLaterButtonPaneID = 'Sl8r'; +const PaneIDT cQuoteButtonPaneID = 'Quot'; +const PaneIDT cStopButtonPaneID = 'Stop'; +const PaneIDT cAddressTab = 10611; +const PaneIDT cAttachTab = 10612; +const PaneIDT cOptionTab = 10617; +const PaneIDT cFormattingToolbar = 'HTbr'; + +const CommandT cmd_Attach = 'Batc'; +const MessageT msg_ReturnRecipt = 'OtRe'; +const MessageT msg_Garbled = 'OtEn'; +// const MessageT msg_8BitEncoding = 'Ot8b'; +const MessageT msg_Signed = 'OtSi'; +const MessageT msg_UUEncode = 'OtUU'; +const MessageT msg_HTMLAction = 'OtHa'; +const CommandT cmd_AttachMyAddressBookCard = 'AtMA'; +class CMailComposeWindow; +class CProgressListener; +class CComposeSession; + +//====================================== +class UComposeUtilities +//====================================== +{ +public: + static void WordWrap(Handle& inText, + Uint32 inTextLength, + LHandleStream& outTextStream); + + static MSG_HEADER_SET GetMsgHeaderMaskFromAddressType(EAddressType inAddressType); + + static MSG_PRIORITY GetMsgPriorityFromMenuItem(Int32 inMenuItem); + + static void RegisterComposeClasses(); +}; + +//====================================== +class CMailCompositionContext : public CBrowserContext +//====================================== +{ +public: + CMailCompositionContext(); + virtual ~CMailCompositionContext() {} + + void Cleanup() { BroadcastMessage(msg_DeleteCompositionContext); } + + virtual void AllConnectionsComplete(); // call this method + + void CreateContextProgress(); +}; + + + +//====================================== +class CTabContainer : public CPatternBevelView +//====================================== +{ +private: typedef CPatternBevelView Inherited; +public: + enum { class_ID = 'C_TC' }; + CTabContainer(LStream* inStream); + + virtual ~CTabContainer() { }; + + virtual void DrawSelf(); + virtual void DrawBeveledFrame(); +}; + +//====================================== +class CMailTabContainer : public CTabContainer +//========================================= +{ +public: + CMailTabContainer(LStream* inStream); + + virtual ~CMailTabContainer() { }; + virtual void DrawBeveledFrame(); + virtual void DrawSelf(); +}; + +//====================================== +class CMailComposeTabContainer : public CMailTabContainer +//====================================== +{ +private: typedef CTabContainer Inherited; + +public: + enum { class_ID = 'CmTC' }; + + CMailComposeTabContainer(LStream* inStream); + virtual ~CMailComposeTabContainer() { }; + virtual void FinishCreateSelf(); +}; + +//====================================== +class CMailAttachmentTabContainer : public CMailTabContainer +//====================================== +{ +private: typedef CTabContainer Inherited; +public: + enum { class_ID = 'AtTC' }; + + CMailAttachmentTabContainer(LStream* inStream); + virtual ~CMailAttachmentTabContainer() { }; + + virtual void FinishCreateSelf(); +}; + + + +typedef struct TERec **TEHandle; +class CMailEditView; + +//====================================== +class CMailComposeWindow : public CMailNewsWindow, + public LBroadcaster, + public LListener, + public LPeriodical +//====================================== +{ +private: + typedef CMailNewsWindow Inherited; +public: + enum { class_ID = 'mail', res_ID = 10610, text_res_ID = 10614 }; // this is same class_ID as old compose window + // Index into mToolbarShown for tracking visibility of toolbars + // Start at 2 because CMailNewsWindow uses 0 and 1 + enum { FORMATTING_TOOLBAR = 2}; + enum { COMPOSE_BUTTON_BAR_ID = 10618}; + CMailComposeWindow(LStream* inStream); + virtual ~CMailComposeWindow(); + virtual void FinishCreateSelf(); + + MSG_Pane* CreateSession(MWContext* old_context, + MSG_CompositionFields* inCompositionFields, + const char* initialText, + Boolean inOpeningAsDraft); + + virtual void ListenToMessage(MessageT inMessage, void* ioParam); + virtual Boolean ObeyCommand(CommandT inCommand, void *ioParam); + virtual void FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName); + + virtual void AttemptClose(); + virtual Boolean AttemptQuitSelf(Int32 inSaveOption); + CComposeSession* GetComposeSession() { return mComposeSession; } + void HandleUpdateCompToolbar(); // from FE_UpdateCompToolbar. + + virtual Boolean HandleTabKey(const EventRecord &inKeyEvent); + virtual Boolean HandleKeyPress(const EventRecord &inKeyEvent); + + void DoCloseLater(); + virtual void SpendTime(const EventRecord &inMacEvent); + + // I18N stuff + virtual Int16 DefaultCSIDForNewWindow(void); + virtual void SetDefaultCSID(Int16 default_csid); + +protected: + void SetDefaultWebAttachmentURL(); + void SendMessage(Boolean inSendNow); + void UpdateSendButton(); + void SaveDraftOrTemplate(CommandT inCommand, Boolean inCloseWindow = false ); + Boolean PrepareMessage(Boolean isDraft = false); + void SyncAddressLists(); + void GetSubjectFromBackend(); + void GetPriorityFromBackend(); + void GetAttachmentsFromBackend(); + void InitializeHTMLEditor(CMailEditView* inEditorView); + void GetAllCompHeaders(); + void SetCompHeader(EAddressType inAddressType); + void GetCompHeader(EAddressType inAddressType); + void EnsureAtLeastOneAddressee(); + void SetSensibleTarget(); + char* GetSubject(); + + ExceptionCode InsertMessageCompositionText(const char* text, Boolean leaveCursorinFront = false); + void TargetMessageCompositionText(); + Boolean NeedToSave(); + Boolean AskIfUserWantsToClose(); + virtual ResIDT GetStatusResID(void) const; + virtual UInt16 GetValidStatusVersion(void) const { return 0x0115; } + + CComposeSession* mComposeSession; + CComposeAddressTableView* mAddressTableView; + CAttachmentList* mAttachmentList; + CAttachmentView* mAttachmentView; + CProgressListener* mProgressListener; + CMailEditView* mHTMLEditView; // nil unless HTML mode! + CSimpleTextView* mPlainEditView; // nil unless in PlainTextMode + Boolean mHeadersDirty; // The address, attachment + Boolean mHaveInitializedAttachmentsFromBE; + enum EInitializeState { + eUninitialized, + eComposeSessionIsSet, + eAboutURLLoading, + eDone }; + EInitializeState mInitializeState; + Boolean mOnlineLastFindCommandStatus; + + CStr255 mDefaultWebAttachmentURL; + CommandT mCurrentSaveCommand; // cmd_SaveDraft, cmd_SaveTemplate +}; // class CMailComposeWindow + +//====================================== +class CComposeTabSwitcher : public CTabSwitcher, + public LBroadcaster +//====================================== +{ +public: + enum { class_ID = 'CmTb' }; + CComposeTabSwitcher(LStream* inStream); + virtual ~CComposeTabSwitcher() { }; + virtual void ManuallySwitchToTab( int32 tabID); + virtual void DoPostLoad(LView* inLoadedPage, Boolean inWillCache); + virtual void DoPreDispose(LView* inLeavingPage, Boolean inWillCache); +}; + +//====================================== +class CMailOptionTabContainer : public CMailTabContainer +//====================================== +{ +private: typedef CTabContainer Inherited; + +public: + enum { class_ID = 'OpTC' }; + + CMailOptionTabContainer(LStream* inStream); + virtual ~CMailOptionTabContainer() { }; + virtual void FinishCreateSelf(); +}; + diff --git a/mozilla/cmd/macfe/MailNews/CMailFlexTable.cp b/mozilla/cmd/macfe/MailNews/CMailFlexTable.cp new file mode 100644 index 00000000000..a1024f23325 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMailFlexTable.cp @@ -0,0 +1,869 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +#include "CMailFlexTable.h" + +// PP +#include +#include + +#include "LFlexTableGeometry.h" +//#include "LTableRowSelector.h" + +// Want a command number? They hide in several places... +#include "resgui.h" +#include "MailNewsgroupWindow_Defines.h" +#include "UMessageLibrary.h" + + +#include "CPrefsDialog.h" +#include "CNSContext.h" +#include "UMailSelection.h" + +#include "uapp.h" // for UpdateMenus(). Ugh. +#include "CPaneEnabler.h" +#include "CMailNewsContext.h" +#include "UOffline.h" +#include "CBookmarksAttachment.h" +#include "CMailProgressWindow.h" +#include "macutil.h" +#include "prefapi.h" + +// XP +#include "shist.h" + +//----------------------------------- +CMailFlexTable::CMailFlexTable(LStream *inStream) +//----------------------------------- +: Inherited(inStream) +, mMsgPane(NULL) +, mMysticPlane(0) +, mStillLoading(false) +, mContext(nil) +, mFirstRowToRefresh(0) +, mLastRowToRefresh(0) +, mClosing(false) +{ + *inStream >> mDragFlavor; +} // CMailFlexTable::CMailFlexTable + +//----------------------------------- +CMailFlexTable::~CMailFlexTable() +//----------------------------------- +{ + mClosing = true; + SetMessagePane(NULL); +} // CMailFlexTable::~CMailFlexTable + +//----------------------------------- +void CMailFlexTable::DrawSelf() +//----------------------------------- +{ + ApplyForeAndBackColors(); + + // This function is similar to what we had when the "Erase On Update" + // LWindow attribute was set in Constructor. This flag has been removed + // because it created a lot of flickers when browsing mails. + // The other objects in the Thread window continued to behave correctly + // but the CThreadView showed some update problems. Instead of fixing + // them as we are supposed to (ie. by invalidating and erasing only what + // needs to be redrawn), I prefered to emulate the way it used to work + // when "Erase On Update" was set. My apologies for this easy solution + // but we have something to ship next week. + + // OK, I made it better by only erasing what was below the last cell (if anything). + // jrm 97/08/18 + + const STableCell bottomCell(mRows, 1); + Int32 cellLeft, cellTop, cellRight, cellBottom; + mTableGeometry->GetImageCellBounds(bottomCell, cellLeft, cellTop, + cellRight, cellBottom); + // Convert from image coordinates to port coordinates + cellBottom += mImageLocation.v; + Int32 frameBottom = mFrameLocation.v + mFrameSize.height; + if (cellBottom < frameBottom) // Note the "=", edge case for deleting the last row. + { + // erase everything + Rect frame; + CalcLocalFrameRect(frame); + frame.top = frame.bottom - (frameBottom - cellBottom); + ::EraseRect(&frame); + } + + // redraw everything + Inherited::DrawSelf(); +} // CMailFlexTable::DrawSelf + +//----------------------------------- +void CMailFlexTable::DestroyMessagePane(MSG_Pane* inPane ) +//----------------------------------- +{ + if (mContext) + XP_InterruptContext((MWContext*)*mContext); + CMailCallbackListener::SetPane(nil); // turn off callbacks + if ( GetMessagePane() != NULL ) + ::MSG_DestroyPane( inPane); +}// CMailFlexTable::DestroyMessagePane + +//----------------------------------- +void CMailFlexTable::SetMessagePane(MSG_Pane* inPane) +//----------------------------------- +{ + DestroyMessagePane( mMsgPane ); + mMsgPane = inPane; + CMailCallbackListener::SetPane(inPane); +} + +//----------------------------------- +void CMailFlexTable::SetRowCount() +// Queries the back end pane and sets the number of rows. +//----------------------------------- +{ + TableIndexT rows, cols; + GetTableSize(rows, cols); + SInt32 diff = mMsgPane ? (::MSG_GetNumLines(mMsgPane) - rows) : -rows; + if (diff > 0) + InsertRows(diff, 1, NULL, 0, false); + else if (diff < 0) + RemoveRows(-diff, 1, false); + +} // CMailFlexTable::SetRowCount() + +#if 0 +//====================================== +class StProgressWindowHandler : public StDialogHandler +//====================================== +{ +private: + typedef StDialogHandler Inherited; +public: +}; +#endif + +static Boolean gCanceled = false; +static Boolean CancelCallback() +{ + gCanceled = true; + return false; +} +//----------------------------------- +void CMailFlexTable::OpenSelection() +// Overrides the base class, in order to show progress. +//----------------------------------- +{ + TableIndexT total = GetSelectedRowCount(); + if (!total) + return; + if (total < 10) + { + Inherited::OpenSelection(); + return; + } + + TableIndexT selectedRow = 0; + StDialogHandler handler(CMailProgressWindow::res_ID_modal, LCommander::GetTopCommander()); + CMailProgressWindow* pw = dynamic_cast(handler.GetDialog()); + if (!pw) + throw memFullErr; + + CStr255 description; + ::GetIndString(description, 7099, 17); + pw->SetCancelCallback(CancelCallback); + CContextProgress progress; + progress.mAction = description; + progress.mTotal = total; + progress.mInitCount = 0; + progress.mPercent = 0; + progress.mRead = 0; + progress.mStartTime = ::TickCount(); + gCanceled = false; + pw->Show(); + pw->UpdatePort(); + pw->ListenToMessage(msg_NSCProgressBegin, &progress); + while (GetNextSelectedRow(selectedRow) && !gCanceled) + { + // Handle plenty of events - activates, updates coming on strong... + for (int i = 1; i <= 20; i++) + { + handler.DoDialog(); + if (gCanceled) + break; + } + OpenRow(selectedRow); + ::GetIndString(description, 7099, 18); + ::StringParamText(description, ++progress.mRead, progress.mTotal, 0, 0); + progress.mPercent = ((Int32)(progress.mRead) * 100/ total); +// progress.mMessage = description; +// pw->ListenToMessage(msg_NSCProgressUpdate, &progress); +// Someone turned off support for msg_NSCProgressUpdate, so: + pw->ListenToMessage(msg_NSCProgressMessageChanged, (char*)description); + pw->ListenToMessage(msg_NSCProgressPercentChanged, &progress.mPercent); + } + // No. Handler will delete! pw->ListenToMessage(msg_NSCAllConnectionsComplete, nil); +} // CMailFlexTable::OpenSelection + +//----------------------------------- +Boolean CMailFlexTable::GetSelection(CMailSelection& selection) +// CStandardFlexTable has one-based lists which are lists of TableIndexT. +// CMailSelection requires zero-based lists which are lists of MSG_ViewIndex +// This routine clones and converts. +//----------------------------------- +{ + selection.xpPane = mMsgPane; + // Assert, cuz we're going to cast and to convert an array in place! + Assert_(sizeof(TableIndexT) == sizeof(MSG_ViewIndex)); + // if we've got a selection list, it's assumed up to date, + // so DON't convert it to a MSG_Index. When the selection changes, we just get + // rid of it (in SelectionChanged()) and set mSelectionList to NULL + Boolean usingCachedSelection = (mSelectionList != NULL); + selection.selectionList + = (MSG_ViewIndex*)Inherited::GetUpdatedSelectionList( + (TableIndexT&)selection.selectionSize); + if (!selection.selectionList) + return false; + if (usingCachedSelection) + return true; + // Have selection, not cached, so convert in place from 1-based to 0-based + MSG_ViewIndex* index = selection.selectionList; + for (TableIndexT i = 0; i < selection.selectionSize; i++, index++) + (*index)--; + return true; +} // CMailFlexTable::GetSelection + +//----------------------------------- +const TableIndexT* CMailFlexTable::GetUpdatedSelectionList(TableIndexT& /*outSelectionSize*/) +// This override is here to stop people calling it. Mail table requires a different type +// for the index and a zero-based one at that! +//----------------------------------- +{ + Assert_(false); // Use GetSelection() instead. + return NULL; +} // CMailFlexTable::GetUpdatedSelectionList + +//---------------------------------------------------------------------------------------- +void CMailFlexTable::AddSelectionToDrag( + DragReference inDragRef, + RgnHandle inDragRgn) +// Adds a single drag item, which is an array of the +// selected row indices. +// Throws drag manager errors. +//---------------------------------------------------------------------------------------- +{ + Inherited::AddSelectionToDrag(inDragRef, inDragRgn); + + // Our drag data is just a pointer to a list of our selected items + // Danger: the list changes when the selection changes, + // so this pointer's lifetime is limited. + CMailSelection selection; + if (GetSelection(selection)) + { + mDragFlavor = kMailNewsSelectionDragFlavor; + OSErr err = ::AddDragItemFlavor(inDragRef, eMailNewsSelectionDragItemRefNum, + mDragFlavor, &selection, sizeof(selection), flavorSenderOnly); + ThrowIfOSErr_(err); + } +} // CMailFlexTable::AddSelectionToDrag + +//---------------------------------------------------------------------------------------- +void CMailFlexTable::AddRowToDrag( + TableIndexT inRow, + DragReference inDragRef, + RgnHandle inDragRgn) +// 98/04/03 added to support dragging of an unselected item. +// Adds a single drag item, which is an array of ONE row index, probably not currently +// selected! +// Throws drag manager errors. +//---------------------------------------------------------------------------------------- +{ + if (inRow == LArray::index_Bad) + return; + Inherited::AddRowToDrag(inRow, inDragRef, inDragRgn); + + // Our drag data is just a pointer to a pseudo selection + // Danger: the list changes when the selection changes, + // so this pointer's lifetime is limited. + CMailSelection selection; + // Subtract 1 to make a MSG_ViewIndex (0-based) from the TableIndexT (1-based) + selection.xpPane = mMsgPane; + selection.SetSingleSelection(inRow - 1); + mDragFlavor = kMailNewsSelectionDragFlavor; + OSErr err = ::AddDragItemFlavor(inDragRef, eMailNewsSelectionDragItemRefNum, + mDragFlavor, &selection, sizeof(selection), flavorSenderOnly); + ThrowIfOSErr_(err); +} // CMailFlexTable::AddSelectionToDrag + +//---------------------------------------------------------------------------------------- +Boolean CMailFlexTable::GetSelectionFromDrag( + DragReference inDragRef, + CMailSelection& outSelection) +// Get the selection back out from the drag data. +// NOTE: this is called by the DESTINATION pane of the drop. +// The only flavor we need is kMailNewsSelectionDragFlavor +//---------------------------------------------------------------------------------------- +{ + Size dataSize; + dataSize = sizeof(CMailSelection); + if (noErr != ::GetFlavorData( + inDragRef, + eMailNewsSelectionDragItemRefNum, + kMailNewsSelectionDragFlavor, + &outSelection, + &dataSize, + 0)) + return false; + Assert_(dataSize == sizeof(CMailSelection)); + outSelection.Normalize(); + Assert_(outSelection.GetSelectionList() != NULL); + return true; +} // CMailFlexTable::GetSelectionFromDrag + +//----------------------------------- +void CMailFlexTable::ToggleExpandAction(TableIndexT row) +//----------------------------------- +{ + // rowDelta tells us how many items are added or removed. We don't + // need it, because we call ChangeFinished in the FE_LIstChangeFinished + // callback. + SInt32 rowDelta; + MSG_ToggleExpansion(mMsgPane, row - 1, &rowDelta); +} // CMailFlexTable::ToggleExpansion + +//----------------------------------- +void CMailFlexTable::ChangeStarting( + MSG_Pane* /* inPane */, + MSG_NOTIFY_CODE /* inChangeCode */, + TableIndexT /* inStartRow */, + SInt32 /* inRowCount */) +//----------------------------------- +{ + ++mMysticPlane; +} // CMailFlexTable::ChangeStarting + +//----------------------------------- +void CMailFlexTable::RowsChanged(TableIndexT inFirst, TableIndexT inCount) +// Accumulate a range of rows to update. We use this to delay refreshing until +// mMysticPlane has reached zero (outer call). +//----------------------------------- +{ + if (inCount == 0 || inFirst == 0) + return; + if (inFirst > mRows) + return; + if (mFirstRowToRefresh == 0 || inFirst < mFirstRowToRefresh) + mFirstRowToRefresh = inFirst; + TableIndexT maxCount = mRows - inFirst + 1; + if (inCount > maxCount) + mLastRowToRefresh = ULONG_MAX; + else + { + TableIndexT last = inFirst + inCount - 1; + if (last > mLastRowToRefresh) + mLastRowToRefresh = last; + } +} // CMailFlexTable::RowsChanged + +//----------------------------------- +void CMailFlexTable::ChangeFinished( + MSG_Pane* /* inPane */, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount) +//----------------------------------- +{ + if (mMysticPlane > 0) + --mMysticPlane; + if (mMsgPane && (mMysticPlane <= (kMysticUpdateThreshHold+1))) switch (inChangeCode) + { + case MSG_NotifyInsertOrDelete: + { + if (inRowCount > 0) + { + if (mRows + inRowCount > ::MSG_GetNumLines(mMsgPane)) + { + // Undo bug. Undo inserts extra rows. + Assert_(FALSE); // congrats! The backend "extra ghost row on undo" bug. + } + else + { + // InsertRows has an "inAfterRow" parameter, but the meaning of + // inStartRow as received from libmsg is that it is the index of + // the first INSERTED row! + InsertRows(inRowCount, inStartRow - 1, NULL, 0, false); // line order... + RowsChanged(inStartRow, ULONG_MAX); // ...does matter + } + } + else if (inRowCount < 0 && mRows > 0) + { + if (inStartRow - inRowCount - 1 <= mRows) + { + RowsChanged(inStartRow, ULONG_MAX); // line order... + RemoveRows(-inRowCount, inStartRow, false); // ...does matter + } + } + break; + } + case MSG_NotifyChanged: + { + RowsChanged(inStartRow, inRowCount); + break; + } + case MSG_NotifyScramble: + case MSG_NotifyAll: + SetRowCount(); +// TableIndexT rows, cols; +// GetTableSize(rows, cols); + mFirstRowToRefresh =1 ; + mLastRowToRefresh = ULONG_MAX; + break; + default: + case MSG_NotifyNone: + break; + } // switch + if (mMysticPlane == 0 && mFirstRowToRefresh != 0) + { + const STableCell topLeftCell(mFirstRowToRefresh, 1); + const STableCell botRightCell( + mLastRowToRefresh > mRows ? mRows : mLastRowToRefresh, mCols); + if (mLastRowToRefresh > mRows) + { + // (note that we're refreshing all the way to the bottom here). + // Because of the complication of "reconcile + // overhang", we really need to refresh all --- but only if part of the + // range is visible. To do this, we need only check if the top of the top cell + // is above the bottom of the frame. + Int32 cellLeft, cellTop, cellRight, cellBottom; + mTableGeometry->GetImageCellBounds(topLeftCell, cellLeft, cellTop, + cellRight, cellBottom); + // Convert from image coordinates to port coordinates + cellTop += mImageLocation.v; + Int32 frameBottom = mFrameLocation.v + mFrameSize.height; + if (cellTop <= frameBottom) // Note the "=", edge case for deleting the last row. + Refresh(); + } + else + RefreshCellRange(topLeftCell, botRightCell); + mFirstRowToRefresh = 0; + mLastRowToRefresh = 0; + } +} // CMailFlexTable::ChangeFinished + +//----------------------------------- +void CMailFlexTable::PaneChanged( + MSG_Pane* /* inPane */, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 /* value */) +//----------------------------------- +{ + switch (inNotifyCode) + { + case MSG_PanePastPasswordCheck: + //EnableStopButton(true); + break; + } +} // CMailFlexTable::PaneChanged + +//----------------------------------- +Boolean CMailFlexTable::FindMessageLibraryCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +// returns false if not a msglib command. +//----------------------------------- +{ + CMailSelection selection; + GetSelection(selection); + return UMessageLibrary::FindMessageLibraryCommandStatus( + GetMessagePane(), + (MSG_ViewIndex*)selection.GetSelectionList(), + selection.selectionSize, + inCommand, + outEnabled, + outUsesMark, + outMark, + outName); +} + +//----------------------------------- +void CMailFlexTable::FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +//----------------------------------- +{ + if (mClosing) // don't respond to BE callbacks when being destroyed + return; + + if (inCommand == cmd_Stop && mStillLoading) + { + outEnabled = true; // stop button on, nothing else. + return; + // ... otherwise, fall through and pass it up to the window + } + + if (!mMsgPane) + { + LCommander::GetTopCommander()->FindCommandStatus( + inCommand, outEnabled, outUsesMark, outMark, outName); + return; + } + switch (inCommand) + { + case cmd_AddToBookmarks: + { + outEnabled = mContext && SHIST_GetCurrent(&((MWContext*)*mContext)->hist); + return; + } + } + if (!FindMessageLibraryCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName) + || !outEnabled) + Inherited::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); +} // CMailFlexTable::FindCommandStatus + +//----------------------------------- +Boolean CMailFlexTable::ObeyMessageLibraryCommand( + CommandT inCommand, + void * /* ioParam*/) +// Commands handled here are enabled/disabled in +// UMessageLibrary::FindMessageLibraryCommandStatus. +//----------------------------------- +{ + CStr255 commandName; + MSG_CommandType cmd; + switch (inCommand) + { + case cmd_CompressAllFolders: + inCommand = cmd_CompressFolder; + cmd = MSG_CompressFolder; // see hack below. + break; + default: + // Callers rely on this to check if it really is a msglib command. + // Do that first. + cmd = UMessageLibrary::GetMSGCommand(inCommand); + break; + } + if (!UMessageLibrary::IsValidCommand(cmd)) + return false; + // For msglib commands, we have to be careful to check whether the command + // can be handled for THIS pane, because (in the case of a thread pane) + // the message pane might have enabled the menu item. Failing to check + // again here leads to a nasty crash. + Boolean enabled; Boolean usesMark; Char16 mark; + if (inCommand == cmd_Undo) + { + inCommand = mUndoCommand; + // set it for next time + mUndoCommand = (inCommand == cmd_Undo) ? cmd_Redo : cmd_Undo; + } + else + mUndoCommand = cmd_Undo; + if (!FindMessageLibraryCommandStatus(inCommand, enabled, usesMark, mark, commandName) + || !enabled) + { + if (inCommand == cmd_CompressFolder) + { + // Hack. + // The logic is: if the selection permits MSG_CompressFolder, then do that. + // Otherwise, try for MSG_CompressAllFolders. We can't change the resources + // now (localization freeze). So the menu may have either of these commands + // in it. + + // I don't think that we will ever get here since FindCommand status does a + // a conversion between CompressFolder and CompressALLFolders --djm + inCommand = cmd_CompressAllFolders; + cmd = MSG_CompressAllFolders; + if (!FindMessageLibraryCommandStatus(inCommand, enabled, usesMark, mark, commandName) + || !enabled) + return false; + } + else + return false; + } + // YAH (Yet another Hack) FindMessageLibraryCommandStatus is going to return true since it internally + // does a conversion between cmd_CompressAllFolders and cmd_CompressFolder. The command then never + // gets switched + if ( inCommand == cmd_CompressFolder ) + { + CMailSelection selection; + GetSelection(selection); + XP_Bool plural; + XP_Bool enabledCommand = false; + MSG_COMMAND_CHECK_STATE checkedState; + const char* display_string = nil; + MSG_CommandStatus( GetMessagePane(), MSG_CompressFolder, + (MSG_ViewIndex*)selection.GetSelectionList(), selection.selectionSize, + &enabledCommand, &checkedState, &display_string, &plural) ; + // If the Compress Folder isn't enabled then Compress all is enabled + if ( !enabledCommand ) + { + inCommand = cmd_CompressAllFolders; + cmd = MSG_CompressAllFolders; + } + + } + + try + { +#define ALLOW_MODELESS_PROGRESS 1 +#define ALLOW_MODAL_PROGRESS 1 + + Boolean cmdHandled = false; + switch (cmd) + { + case MSG_GetNewMail: + case MSG_GetNextChunkMessages: + if (NET_IsOffline()) + { + // Bug #105393. This fails unhelpfully if the user is offline. There + // used to be a test for this here, but for some reason it was + // removed. This being so, the newly agreed-upon fix is that, if + // the user requests new messages while offline, we should instead + // present the "Go Online" dialog. See also CMessageView.cp. + // - 98/02/10 jrm. + PREF_SetBoolPref("offline.download_discussions", true); + PREF_SetBoolPref("offline.download_mail", true); + PREF_SetBoolPref("offline.download_directories", false); + UOffline::ObeySynchronizeCommand(); + cmdHandled = true; + } + else if (ALLOW_MODELESS_PROGRESS) + { + // Modeless window with separate context and pane + CMailProgressWindow::ObeyMessageLibraryCommand( + CMailProgressWindow::res_ID_modeless, + GetMessagePane(), cmd, commandName); + cmdHandled = true; + } + break; + + case MSG_MarkAllRead: + // Can't display a dialog with command(s) which apply to + // all the messages at once in the list because we don't + // get the callbacks from the BE which allow to update + // the progress bar and close the Progress window. + break; + + case MSG_CompressFolder: + case MSG_CompressAllFolders: + // Bug #90378 (BE problem which is much easier to fix in the FE) + // Make these commands run inside their own separate context. + CMailSelection selection; + GetSelection(selection); + CMailProgressWindow::ObeyMessageLibraryCommand( + CMailProgressWindow::res_ID_modeless, + GetMessagePane(), cmd, commandName, + (MSG_ViewIndex*)selection.GetSelectionList(), + selection.selectionSize); + cmdHandled = true; + break; + + default: + if (ALLOW_MODAL_PROGRESS && !NET_IsOffline()) + { + // Modal parasite window with same context and pane + CMailProgressWindow::CreateModalParasite( + CMailProgressWindow::res_ID_modal, + GetMessagePane(), commandName); + } + break; + } + if (! cmdHandled) + { + CMailSelection selection; + GetSelection(selection); + MSG_Command(GetMessagePane(), cmd, + (MSG_ViewIndex*)selection.GetSelectionList(), + selection.selectionSize); + } + } + catch(...) + { + } + return true; +} // CMailFlexTable::ObeyMessageLibraryCommand + +//----------------------------------- +Boolean CMailFlexTable::ObeyCommand( + CommandT inCommand, + void *ioParam) +//----------------------------------- +{ + if (mClosing) // don't respond to BE callbacks when being destroyed + return false; + + if (mStillLoading && inCommand != cmd_Stop) + return false; + + if (!mMsgPane) + return LCommander::GetTopCommander()->ObeyCommand(inCommand, ioParam); + + switch (inCommand) + { + case cmd_Stop: + { + if (mContext) + XP_InterruptContext(*mContext); + EnableStopButton(false); + return true; + } + case cmd_AddToBookmarks: + { + // MSG_GetFolderInfoFromURL() does not work for URLs pointing to Mail & News messages. + // SHIST_GetCurrent(&((MWContext*)*mContext)->hist) + // ); + + // Nova: BM_Entry *entry = SHIST_CreateHotlistStructFromHistoryEntry( + // SHIST_GetCurrent(&((MWContext*)*mContext)->hist) ); + + History_entry *entry = mContext->GetCurrentHistoryEntry(); // Mozilla + + if (entry) + CBookmarksAttachment::AddToBookmarks(entry->address, entry->title); + else + SysBeep(1); + break; + } + case cmd_Preferences: + CPrefsDialog::EditPrefs(CPrefsDialog::eExpandMailNews); + return true; + default: + return ObeyMessageLibraryCommand(inCommand, ioParam) + || Inherited::ObeyCommand(inCommand, ioParam); + } + return false; +} // CMailFlexTable::ObeyCommand + +//----------------------------------- +void CMailFlexTable::EnableStopButton(Boolean inBusy) +//----------------------------------- +{ + if (inBusy == mStillLoading) + return; + mStillLoading = inBusy; + (CFrontApp::GetApplication())->UpdateMenus(); + // done in CFrontApp::UpdateMenus() already. CPaneEnabler::UpdatePanes(); +} + +//----------------------------------- +void CMailFlexTable::DrawCountCell( + Int32 inCount, + const Rect& inLocalRect) +// inCount < 0 indicates unknown value +//----------------------------------- +{ + char sizeString[32]; + if (inCount >= 0) + sprintf(sizeString, "%d", inCount); + else + sprintf(sizeString, "?"); + DrawTextString(sizeString, &mTextFontInfo, 2, inLocalRect, true, truncEnd); +} // CMessageFolderView::DrawCountCell + +//----------------------------------- +void CMailFlexTable::ListenToMessage(MessageT inMessage, void* ioParam) +//----------------------------------- +{ + switch (inMessage) + { + case CMailCallbackManager::msg_PaneChanged: + case CMailCallbackManager::msg_ChangeStarting: + case CMailCallbackManager::msg_ChangeFinished: + if (IsMyPane(ioParam)) + CMailCallbackListener::ListenToMessage(inMessage, ioParam); + break; + case msg_NSCStartLoadURL: + case msg_NSCProgressBegin: + EnableStopButton(true); + break; + case msg_NSCAllConnectionsComplete: + EnableStopButton(false); + break; + default: + if (!IsOnDuty() || !ObeyCommand(inMessage, ioParam)) + ListenToHeaderMessage(inMessage, ioParam); + } +} // CMailFlexTable::ListenToMessage + +//--------------------------------------------------------------------- +char* CMailFlexTable::GetTextFromDrag( + DragReference inDragRef, + ItemReference inItemRef) +// Check if this drag is a URL and returns the URL if it is. +// *** It is the responsibility of the client to delete the returned +// result by calling XP_FREEIF() +//--------------------------------------------------------------------- +{ + // get the drag data size + Size dataSize = 0; + OSErr err = ::GetFlavorDataSize(inDragRef, inItemRef, 'TEXT', &dataSize); + if (err) + return nil; // we can't throw during a drag! Inconvenient in MWDebug. + char* result = (char*)XP_ALLOC(1 + dataSize); + if (!result) + return nil; + unsigned long offset = 0; + // get the data out of the drag and put it into the buffer + err = ::GetFlavorData(inDragRef, inItemRef, 'TEXT', result, &dataSize, offset); + if (!err) + { + // terminate the string with a null char + result[dataSize] = '\0'; + return result; + } + XP_FREEIF(result); + return nil; +} // CMailFlexTable::GetTextFromDrag + +//--------------------------------------------------------------------- +MessageKey CMailFlexTable::MessageKeyFromURLDrag( + DragReference inDragRef, + ItemReference inItemRef) +// Check if this drag is the URL of a message and returns the message key if it is. +//--------------------------------------------------------------------- +{ + MessageKey result = MSG_MESSAGEKEYNONE; + char* url = GetTextFromDrag(inDragRef, inItemRef); + if (!url) + return MSG_MESSAGEKEYNONE; + MSG_MessageLine messageLine; + int status = MSG_GetMessageLineForURL( CMailNewsContext::GetMailMaster(), url, &messageLine ); + if (status >= 0) + result = messageLine.messageKey; + XP_FREEIF(url); + return result; +} // CMailFlexTable::MessageKeyFromURLDrag + +//--------------------------------------------------------------------- +MSG_FolderInfo* CMailFlexTable::GetFolderInfoFromURLDrag( + DragReference inDragRef, + ItemReference inItemRef) +// Check if this drag is the URL of a folder and returns the folderInfo if it is. +//--------------------------------------------------------------------- +{ + char* url = GetTextFromDrag(inDragRef, inItemRef); + if (!url) + return nil; + MSG_FolderInfo* result = MSG_GetFolderInfoFromURL(CMailNewsContext::GetMailMaster(), url, false); + XP_FREEIF(url); + return result; +} // CMailFlexTable::GetFolderInfoFromURLDrag + diff --git a/mozilla/cmd/macfe/MailNews/CMailFlexTable.h b/mozilla/cmd/macfe/MailNews/CMailFlexTable.h new file mode 100644 index 00000000000..a1ee3e3378e --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMailFlexTable.h @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +#pragma once + +#include "msgcom.h" +#include "CStandardFlexTable.h" +#include "MailNewsCallbacks.h" + +const kMysticUpdateThreshHold = 10; + + +class CMailNewsContext; +class CMailProgressWindow; +class CMailSelection; + +//======================================================================================== +class CMailFlexTable + : public CStandardFlexTable + , public CMailCallbackListener +// Like CStandardFlexTable, an abstract class. +//======================================================================================== +{ +private: + typedef CStandardFlexTable Inherited; +public: + enum + { class_ID = 'mfTb' + }; + + enum + { + eMailNewsSelectionDragItemRefNum = 0 + }; + + CMailFlexTable(LStream *inStream); + virtual ~CMailFlexTable(); + virtual void SetRowCount(); + Boolean IsStillLoading() const { return mStillLoading; } + + //----------------------------------- + // Drawing + //----------------------------------- + virtual void DrawSelf(); + + //----------------------------------- + // Selection + //----------------------------------- + virtual Boolean GetSelection(CMailSelection&); + // CStandardFlexTable has one-based lists which are lists of TableIndexT. + // CMailSelection requires zero-based lists which are lists of MSG_ViewIndex + // This routine clones and converts. + virtual void OpenSelection(); + + //----------------------------------- + // Drag/drop + //----------------------------------- + FlavorType GetMessageFlavor(TableIndexT /*inMessageRow*/) const + { return mDragFlavor; } + + virtual void AddSelectionToDrag( + DragReference inDragRef, RgnHandle inDragRgn); + virtual void AddRowToDrag( + TableIndexT inRow, + DragReference inDragRef, + RgnHandle inDragRgn); + + Boolean GetSelectionFromDrag( + DragReference inDragRef, + CMailSelection& outSelection); + + CNSContext* GetContext() const { return mContext; } + + //----------------------------------- + // CMailCallbackListener overrides + //----------------------------------- + virtual void ChangeStarting( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount); + virtual void ChangeFinished( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount); + virtual void PaneChanged( + MSG_Pane* inPane, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value); + MSG_Pane* GetMessagePane(void) const + { return mMsgPane; } + virtual void DestroyMessagePane( MSG_Pane* inPane ); + void ListenToMessage(MessageT inMessage, void* ioParam); + +protected: + + void SetMessagePane(MSG_Pane* inPane); + + static char* GetTextFromDrag( + DragReference inDragRef, + ItemReference inItemRef); + // Caller must XP_FREE the result. + + static MSG_FolderInfo* GetFolderInfoFromURLDrag( + DragReference inDragRef, + ItemReference inItemRef); + // Check if this drag is the URL of a folder + // and returns the folderInfo if it is. + + static MessageKey MessageKeyFromURLDrag( + DragReference inDragRef, + ItemReference inItemRef); + // Check if this drag is the URL of a message + // and returns the message key if it is. + + //----------------------------------- + // Commands + //----------------------------------- +protected: + virtual void FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName); + virtual Boolean ObeyCommand( + CommandT inCommand, + void *ioParam); + Boolean FindMessageLibraryCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName); + // returns false if not a msglib command. + Boolean ObeyMessageLibraryCommand( + CommandT inCommand, + void *ioParam); + void NotifySelfAll() { // Manual notification for BE bugs! + ChangeStarting(mMsgPane, MSG_NotifyAll, 0, 0); + ChangeFinished(mMsgPane, MSG_NotifyAll, 0, 0); + } +public: + void EnableStopButton(Boolean inBusy); + + //----------------------------------- + // Internal helpers + //----------------------------------- +protected: + void DrawCountCell( + Int32 inCount, + const Rect& inLocalRect); + void RowsChanged(TableIndexT inFirst, TableIndexT inCount); + + //----------------------------------- + // Window Saving + //----------------------------------- +private: + virtual const TableIndexT* GetUpdatedSelectionList(TableIndexT& outSelectionSize); + // This does an assert: the idea is to use only GetSelection, above. +protected: + //----------------------------------- + // Discloser support + //----------------------------------- + void ToggleExpandAction(TableIndexT row); +protected: + CNSContext* mContext; // Belongs to the window, not us. + SInt32 mMysticPlane; // for keeping track of callback level + FlavorType mDragFlavor; + Boolean mStillLoading; + TableIndexT mFirstRowToRefresh; + TableIndexT mLastRowToRefresh; + Boolean mClosing; + CommandT mUndoCommand; // cmd_Undo or cmd_Redo + +private: + MSG_Pane* mMsgPane; // MUST be private. Must call Get/Set. +}; // class CMailFlexTable diff --git a/mozilla/cmd/macfe/MailNews/CMailFolderButtonPopup.cp b/mozilla/cmd/macfe/MailNews/CMailFolderButtonPopup.cp new file mode 100644 index 00000000000..d27bdfbdbd0 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMailFolderButtonPopup.cp @@ -0,0 +1,503 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMailFolderButtonPopup.cp + +/*====================================================================================*/ + #pragma mark INCLUDE FILES +/*====================================================================================*/ + +#include "CMailFolderButtonPopup.h" +#include "StSetBroadcasting.h" +#include "MailNewsgroupWindow_Defines.h" +#include "CDrawingState.h" +#include "CMessageFolder.h" + + +#pragma mark - + +/*====================================================================================== + Call UpdateMailFolderMixins() to initialize the menu. +======================================================================================*/ + +void CMailFolderButtonPopup::FinishCreateSelf(void) { + + CPatternButtonPopup::FinishCreateSelf(); + + CMailFolderMixin::UpdateMailFolderMixinsNow(this); +} + + +/*====================================================================================== + Instead of broadcasting the actual value, broadcast the new folder string. +======================================================================================*/ + +void CMailFolderButtonPopup::BroadcastValueMessage(void) { + + if ( mValueMessage != msg_Nothing ) + { + BroadcastMessage(mValueMessage, MGetSelectedFolder().GetFolderInfo()); + } +} + + +/*====================================================================================== + When the user selects a new item, we don't really want to change the currently + selected item in the menu, just broadcast the item that the user selected and + reset the currently selected item. +======================================================================================*/ + +Boolean CMailFolderButtonPopup::TrackHotSpot(Int16 inHotSpot, Point inPoint, Int16 inModifiers) { + + // Store the current value + + Int32 oldValue = mValue; + OSErr thrownErr = noErr; + Boolean result; + + Try_ { + result = CPatternButtonPopup::TrackHotSpot(inHotSpot, inPoint, inModifiers); + } + Catch_(inErr) { + thrownErr = inErr; + } + EndCatch_ + + // Reset the old value + + StSetBroadcasting setBroadcasting(this, false); // Don't broadcast anything here + SetValue(oldValue); + FailOSErr_(thrownErr); + + return result; +} + + +/*====================================================================================== + Get a handle to the Mac menu associated with this object. +======================================================================================*/ + +MenuHandle CMailFolderButtonPopup::MGetSystemMenuHandle(void) +{ + MenuHandle menuH = nil; + + if (GetMenu()) + { + menuH = GetMenu()->GetMacMenuH(); + } + + return menuH; +} + + +/*====================================================================================== + Refresh the menu display. +======================================================================================*/ + +void CMailFolderButtonPopup::MRefreshMenu(void) { + + // Nothing, no user menu displayed unless clicked +} + +#pragma mark - + +//----------------------------------- +CMailFolderPatternTextPopup::CMailFolderPatternTextPopup(LStream *inStream) +: CPatternButtonPopupText(inStream) +//----------------------------------- +{ + mUseFolderIcons = eUseFolderIcons; + // This is for the relocation menu, so add newsgroups. + CMailFolderMixin::mDesiredFolderFlags = + (FolderChoices)(int(mDesiredFolderFlags) | int(eWantNews)); +} + +/*====================================================================================== + Call UpdateMailFolderMixins() to initialize the menu. +======================================================================================*/ + +void CMailFolderPatternTextPopup::FinishCreateSelf(void) +{ + Inherited::FinishCreateSelf(); + CMailFolderMixin::UpdateMailFolderMixinsNow(this); +} + + +/*====================================================================================== + Instead of broadcasting the actual value, broadcast the new folder string. +======================================================================================*/ + +void CMailFolderPatternTextPopup::BroadcastValueMessage(void) { + + if ( mValueMessage != msg_Nothing ) { + BroadcastMessage(mValueMessage, MGetSelectedFolder().GetFolderInfo()); + } +} + + +/*====================================================================================== + Unlike CMailFolderButtonPopup, when the user selects a new item, we DO really want + to change the currently selected item in the menu, not just broadcast the item that + the user selected and reset the currently selected item. +======================================================================================*/ + +Boolean CMailFolderPatternTextPopup::TrackHotSpot(Int16 inHotSpot, Point inPoint, Int16 inModifiers) +{ + return Inherited::TrackHotSpot(inHotSpot, inPoint, inModifiers); +} + +void CMailFolderPatternTextPopup::HandlePopupMenuSelect( Point inPopupLoc, + Int16 inCurrentItem, + Int16 &outMenuID, + Int16 &outMenuItem) +{ + Int16 saveFont = ::LMGetSysFontFam(); + Int16 saveSize = ::LMGetSysFontSize(); + + StMercutioMDEFTextState theMercutioMDEFTextState; + + Inherited::HandlePopupMenuSelect(inPopupLoc, inCurrentItem, outMenuID, outMenuItem); + + // Restore the system font + + ::LMSetSysFontFam(saveFont); + ::LMSetSysFontSize(saveSize); + ::LMSetLastSPExtra(-1L); + +} + +/*====================================================================================== + Get a handle to the Mac menu associated with this object. +======================================================================================*/ + +MenuHandle CMailFolderPatternTextPopup::MGetSystemMenuHandle(void) { + if ( GetMenu() ) + return GetMenu()->GetMacMenuH(); + + return NULL; +} + + +/*====================================================================================== + Refresh the menu display. +======================================================================================*/ + +void CMailFolderPatternTextPopup::MRefreshMenu(void) { + + // Nothing, no user menu displayed unless clicked +} + + +#pragma mark - + +//----------------------------------- +CMailFolderGAPopup::CMailFolderGAPopup(LStream *inStream) +: LGAPopup(inStream) +//----------------------------------- +{ + mUseFolderIcons = eUseFolderIcons; + // This is for the relocation menu, so add newsgroups. + CMailFolderMixin::mDesiredFolderFlags = + (FolderChoices)(int(mDesiredFolderFlags) | int(eWantNews)); +} + +/*====================================================================================== + Call UpdateMailFolderMixins() to initialize the menu. +======================================================================================*/ + +void CMailFolderGAPopup::FinishCreateSelf(void) +{ + LGAPopup::FinishCreateSelf(); + CMailFolderMixin::UpdateMailFolderMixinsNow(this); +} + + +/*====================================================================================== + Instead of broadcasting the actual value, broadcast the new folder string. +======================================================================================*/ + +void CMailFolderGAPopup::BroadcastValueMessage(void) { + + if ( mValueMessage != msg_Nothing ) { + BroadcastMessage(mValueMessage, MGetSelectedFolder().GetFolderInfo()); + } +} + + +/*====================================================================================== + Unlike CMailFolderButtonPopup, when the user selects a new item, we DO really want + to change the currently selected item in the menu, not just broadcast the item that + the user selected and reset the currently selected item. +======================================================================================*/ + +Boolean CMailFolderGAPopup::TrackHotSpot(Int16 inHotSpot, Point inPoint, Int16 inModifiers) +{ + return LGAPopup::TrackHotSpot(inHotSpot, inPoint, inModifiers); +} + +void CMailFolderGAPopup::HandlePopupMenuSelect( Point inPopupLoc, + Int16 inCurrentItem, + Int16 &outMenuID, + Int16 &outMenuItem) +{ + Int16 saveFont = ::LMGetSysFontFam(); + Int16 saveSize = ::LMGetSysFontSize(); + + StMercutioMDEFTextState theMercutioMDEFTextState; + + LGAPopup::HandlePopupMenuSelect(inPopupLoc, inCurrentItem, outMenuID, outMenuItem); + + // Restore the system font + + ::LMSetSysFontFam(saveFont); + ::LMSetSysFontSize(saveSize); + ::LMSetLastSPExtra(-1L); + +} + +/*====================================================================================== + Get a handle to the Mac menu associated with this object. +======================================================================================*/ + +MenuHandle CMailFolderGAPopup::MGetSystemMenuHandle(void) { + return GetMacMenuH(); +} + + +/*====================================================================================== + Refresh the menu display. +======================================================================================*/ + +void CMailFolderGAPopup::MRefreshMenu(void) { + + // Nothing, no user menu displayed unless clicked +} + + +#pragma mark - + +//----------------------------------- +CFolderScopeGAPopup::CFolderScopeGAPopup(LStream* inStream) +//----------------------------------- +: CMailFolderGAPopup(inStream) +{ + // This is for the scope menu in the message search window. + mUseFolderIcons = eUseFolderIcons; + CMailFolderMixin::mDesiredFolderFlags = + (FolderChoices)(int(mDesiredFolderFlags) | int(eWantNews) | int(eWantHosts)); +} + +#pragma mark - + +// Class static members + +CMailFolderSubmenu *CMailFolderSubmenu::sMoveMessageMenu = nil; +CMailFolderSubmenu *CMailFolderSubmenu::sCopyMessageMenu = nil; + +// Helper functions + +static void CreateMailFolderSubmenu(Int16 inMENUid, CMailFolderSubmenu **outMenu) { + + if ( *outMenu == nil ) { + Try_ { + *outMenu = new CMailFolderSubmenu(inMENUid); + if ( *outMenu ) { + CMailFolderMixin::UpdateMailFolderMixinsNow(*outMenu); + } + } + Catch_(inErr) { + if ( *outMenu ) { + delete *outMenu; + *outMenu = nil; + } + } + EndCatch_ + } +} + +static void InstallMailFolderSubmenu(CommandT inCommand, CMailFolderSubmenu *inMenu) { + + if ( inMenu && !inMenu->IsInstalled() ) { + ResIDT menuID; + MenuHandle menuHandle; + Int16 menuItem; + + LMenuBar::GetCurrentMenuBar()->FindMenuItem(inCommand, menuID, + menuHandle, menuItem); + + if ( menuHandle != nil ) { + LMenuBar::GetCurrentMenuBar()->InstallMenu(inMenu, hierMenu); + + // Install the submenu (hierarchical menu) + + ::SetItemCmd(menuHandle, menuItem, hMenuCmd); + ::SetItemMark(menuHandle, menuItem, inMenu->GetMenuID()); + } + } +} + +static void RemoveMailFolderSubmenu(CMailFolderSubmenu *inMenu) { + + if ( inMenu && inMenu->IsInstalled() ) { + LMenuBar::GetCurrentMenuBar()->RemoveMenu(inMenu); + } +} + +/*====================================================================================== + Install the mail folder submenus (file, copy) into the current menu bar. If the + menu bar already contains the submenus, do nothing. This method should be called + when a window using the menus becomes active. + + This method can be called at any time. +======================================================================================*/ + +void CMailFolderSubmenu::InstallMailFolderSubmenus(void) { + + // Create and initialize the menus if they don't exist yet + CreateMenus(); + + // Install the menus + InstallMailFolderSubmenu(cmd_MoveMailMessages, sMoveMessageMenu); + InstallMailFolderSubmenu(cmd_CopyMailMessages, sCopyMessageMenu); +} + + +/*====================================================================================== + Remove the mail folder submenus (file, copy) from the current menu bar. If the + menu bar doesn't contain the submenus, do nothing. This method should be called + when a window using the menus becomes inactive or a new menu bar is installed. + + This method can be called at any time. +======================================================================================*/ + +void CMailFolderSubmenu::RemoveMailFolderSubmenus(void) { + + // Remove the menus + RemoveMailFolderSubmenu(sMoveMessageMenu); + RemoveMailFolderSubmenu(sCopyMessageMenu); +} + +//----------------------------------- +void CMailFolderSubmenu::SetSelectedFolder(const MSG_FolderInfo* inInfo) +// Set the currently selected folder item in the hierarchical menus. If inInfo is nil +// or empty, all items are unselected. +// +// This method can be called at any time. +//----------------------------------- +{ + // Create and initialize the menus if they don't exist yet + CreateMenus(); + + if ( sMoveMessageMenu != nil ) + sMoveMessageMenu->MSetSelectedFolder(inInfo); + if ( sCopyMessageMenu != nil ) + sCopyMessageMenu->MSetSelectedFolder(inInfo); +} + +//----------------------------------- +Boolean CMailFolderSubmenu::IsMailFolderCommand(CommandT *ioMenuCommand, const char** outName) +// Determine if the specified synthetic menu command passed to ObeyCommand() is from +// a mail folder menu. If it is, reassign ioMenuCommand to represent the actual mail +// folder command ID and set outName to the name of the selected mail folder and return +// true. Otherwise, return false and do nothing. outName can be nil, in which case no +// name is assigned. +// +// This method can be called at any time. +//----------------------------------- +{ + ResIDT menuID; + Int16 menuItem; + if ( LCommander::IsSyntheticCommand(*ioMenuCommand, menuID, menuItem) ) + { + if ( menuID == menuID_MoveMessage ) + { + if ( sMoveMessageMenu != nil ) + { + *ioMenuCommand = cmd_MoveMailMessages; + if ( outName != nil ) + *outName = sMoveMessageMenu->MGetFolderName(menuItem); + return true; + } + } + else if ( menuID == menuID_CopyMessage ) + { + if ( sCopyMessageMenu != nil ) + { + *ioMenuCommand = cmd_CopyMailMessages; + if ( outName != nil ) + *outName = sCopyMessageMenu->MGetFolderName(menuItem); + return true; + } + } + } + return false; +} + +/*====================================================================================== + Create the hierarchical menus if they don't already exist. + + This method can be called at any time. +======================================================================================*/ + +void CMailFolderSubmenu::CreateMenus(void) { + + // Create and initialize the menus if they don't exist yet + CreateMailFolderSubmenu(CMailFolderSubmenu::menuID_MoveMessage, &sMoveMessageMenu); + CreateMailFolderSubmenu(CMailFolderSubmenu::menuID_CopyMessage, &sCopyMessageMenu); + +} + +#pragma mark - + +/*====================================================================================== +======================================================================================*/ +CFolderMoveGAPopup::CFolderMoveGAPopup(LStream *inStream) : + CMailFolderGAPopup( inStream ) +{ +} + +CFolderMoveGAPopup::~CFolderMoveGAPopup() +{ +} + +// reset the default menu item to the original value +Boolean +CFolderMoveGAPopup::TrackHotSpot(Int16 inHotSpot, Point inPoint, Int16 inModifiers) +{ + Int32 oldValue = GetValue(); + bool result = false; + + result = LGAPopup::TrackHotSpot(inHotSpot, inPoint, inModifiers); + if (oldValue != GetValue()) + { + BroadcastMessage(mValueMessage, MGetSelectedFolder().GetFolderInfo()); + } + else + { + // we want to prevent a broadcast because we have already + // sent one out when we set the value of the control + StSetBroadcasting broacast(this, false); + SetValue(oldValue); + } + return result; +} + + + diff --git a/mozilla/cmd/macfe/MailNews/CMailFolderButtonPopup.h b/mozilla/cmd/macfe/MailNews/CMailFolderButtonPopup.h new file mode 100644 index 00000000000..c9ee7353df9 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMailFolderButtonPopup.h @@ -0,0 +1,198 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMailFolderButtonPopup.h + +#pragma once + +/*====================================================================================*/ + #pragma mark INCLUDE FILES +/*====================================================================================*/ + +#include "CPatternButtonPopup.h" +#include "CPatternButtonPopupText.h" +#include "UMailFolderMenus.h" + +/*====================================================================================*/ + #pragma mark CLASS DECLARATIONS +/*====================================================================================*/ + +#pragma mark - CMailFolderButtonPopup + +//====================================== +class CMailFolderButtonPopup : public CPatternButtonPopup, + public CMailFolderPopupMixin +// Class for the button popup +//====================================== +{ +public: + + enum { class_ID = 'MfPu' }; + + CMailFolderButtonPopup(LStream *inStream) : + CPatternButtonPopup(inStream) { + } + + +protected: + + virtual void FinishCreateSelf(void); + virtual void BroadcastValueMessage(void); + virtual Boolean TrackHotSpot(Int16 inHotSpot, Point inPoint, Int16 inModifiers); + + virtual MenuHandle MGetSystemMenuHandle(void); + virtual void MRefreshMenu(void); +}; + +#pragma mark - CMailFolderPatternTextPopup + +//====================================== +class CMailFolderPatternTextPopup : public CPatternButtonPopupText, + public CGAPopupFolderMixin +// Class for the relocation popup menu in the thread pane +//====================================== +{ +private: + typedef CPatternButtonPopupText Inherited; + +public: + enum { class_ID = 'MfPT' }; + + CMailFolderPatternTextPopup(LStream *inStream); + +protected: + + virtual void FinishCreateSelf(void); + virtual void BroadcastValueMessage(void); + virtual Boolean TrackHotSpot(Int16 inHotSpot, Point inPoint, Int16 inModifiers); + virtual void HandlePopupMenuSelect( Point inPopupLoc, + Int16 inCurrentItem, + Int16 &outMenuID, + Int16 &outMenuItem); + + virtual MenuHandle MGetSystemMenuHandle(void); + virtual void MRefreshMenu(void); + +}; // class CMailFolderPatternTextPopup + +#pragma mark - CMailFolderGAPopup + +//====================================== +class CMailFolderGAPopup : public LGAPopup, + public CGAPopupFolderMixin +// Class for the relocation popup menu in the thread pane +//====================================== +{ +public: + + enum { class_ID = 'MfPM' }; + + CMailFolderGAPopup(LStream *inStream); + + +protected: + + virtual void FinishCreateSelf(void); + virtual void BroadcastValueMessage(void); + virtual Boolean TrackHotSpot(Int16 inHotSpot, Point inPoint, Int16 inModifiers); + virtual void HandlePopupMenuSelect( Point inPopupLoc, + Int16 inCurrentItem, + Int16 &outMenuID, + Int16 &outMenuItem); + + virtual MenuHandle MGetSystemMenuHandle(void); + virtual void MRefreshMenu(void); +}; // class CMailFolderGAPopup + +#pragma mark - CFolderScopeGAPopup + +//====================================== +class CFolderScopeGAPopup : public CMailFolderGAPopup +// differs from CMailFolderGAPopup only in the setting of mDesiredFolderFlags +//====================================== +{ +public: + + enum { class_ID = 'SsPM' }; + + CFolderScopeGAPopup(LStream *inStream); +}; // class CFolderScopeGAPopup + +#pragma mark - CFolderMoveGAPopup + +//====================================== +class CFolderMoveGAPopup : public CMailFolderGAPopup +//====================================== +{ + typedef CMailFolderGAPopup Inherited; + +public: + + enum { class_ID = 'SsFM' }; + + CFolderMoveGAPopup(LStream *inStream); + virtual ~CFolderMoveGAPopup(); + +protected: + virtual Boolean TrackHotSpot( + Int16 inHotSpot, + Point inPoint, + Int16 inModifiers); + +}; // class CFolderScopeGAPopup + +#pragma mark - CMailFolderSubmenu + +//====================================== +class CMailFolderSubmenu : public LMenu, + public CMenuMailFolderMixin +// Class for the hierarchical menu +//====================================== +{ +public: + + static void InstallMailFolderSubmenus(void); + static void RemoveMailFolderSubmenus(void); + static void SetSelectedFolder(const MSG_FolderInfo* inInfo); + static Boolean IsMailFolderCommand(CommandT *ioMenuCommand, const char** outName = nil); + + CMailFolderSubmenu(Int16 inMENUid, + CommandT inMenuCommand = cmd_UseMenuItem) : + LMenu(inMENUid), + CMenuMailFolderMixin(inMenuCommand) { + } + +protected: + + enum { + menuID_MoveMessage = 44 + , menuID_CopyMessage = 46 + }; + + static void CreateMenus(void); + + // Instance variables + + // Class variables + + static CMailFolderSubmenu *sMoveMessageMenu; + static CMailFolderSubmenu *sCopyMessageMenu; +}; + diff --git a/mozilla/cmd/macfe/MailNews/CMailNewsContext.cp b/mozilla/cmd/macfe/MailNews/CMailNewsContext.cp new file mode 100644 index 00000000000..81b457f155e --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMailNewsContext.cp @@ -0,0 +1,426 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMailNewsContext.cp + +#include "CMailNewsContext.h" + +#include "ufilemgr.h" + +#include "CCheckMailContext.h" +#include "UStdDialogs.h" +#include "CMessageFolderView.h" + +// Command numbers +#include "resgui.h" +#include "MailNewsgroupWindow_Defines.h" + +#include "uprefd.h" +#include "prefapi.h" +#include "shist.h" +#include "macutil.h" +#include "macgui.h" +#include "uerrmgr.h" + +// get string constants +#define WANT_ENUM_STRING_IDS +#include "allxpstr.h" +#undef WANT_ENUM_STRING_IDS + +#include "mimages.h" +#include "layers.h" +#include "CDrawable.h" +#include "CBrowserContext.h" + +#include "CNSContextCallbacks.h" +#include "CMailProgressWindow.h" + +// The message master from which all life springs... +MSG_Master* CMailNewsContext::sMailMaster = NULL; +Int32 CMailNewsContext::sMailMasterRefCount = 0; +MSG_Prefs* CMailNewsContext::sMailPrefs = NULL; + +//---------------------------------------------------------------------------------------- +CMailNewsContext::CMailNewsContext(MWContextType inContextType) +//---------------------------------------------------------------------------------------- +: Inherited(inContextType) +, mWasPromptOkay( false ) +{ + ThrowUnlessPrefsSet(inContextType); + GetMailMaster(); // make sure it's there. + sMailMasterRefCount++; + SHIST_InitSession(&mContext); +#if 0 + // Bug: hang when replying to a thread with attachment icons + // from the collapsed thread window. + + // The following is left here as a caution to young programmers who might need a lesson + // learned the hard way by a foolish forerunner - me. The easy solution + // is to set the output_function pointer in mimemoz.c to a stub, so that no + // HTML is generated. + + // Adding an image context avoids the hang by handling the attachment icon images. + CreateImageContext( &mContext ); + + // Adding a compositor to avoid the HTML dialog complaining about window.document.layers + // having "no properties" mime converter was adding javascript that referred to them. + CRouterDrawable* onscreenDrawable = new CRouterDrawable(); + CL_Drawable * theDrawable = CL_NewDrawable(100, 100, CL_WINDOW, + &mfe_drawable_vtable, (void *) onscreenDrawable); + CL_Compositor* c = CL_NewCompositor(theDrawable, nil, 0, 0, 100, 100, 10); + CSharableCompositor* compositor = new CSharableCompositor(c); + compositor->AddUser(this); // shared by context and view. - NEVER REMOVED/ TEST/ + mContext.compositor = *compositor; + CL_SetCompositorEnabled(*compositor, PR_TRUE); +#endif +} // CMailNewsContext::CMailNewsContext + +//---------------------------------------------------------------------------------------- +CMailNewsContext::~CMailNewsContext() +//---------------------------------------------------------------------------------------- +{ + XP_InterruptContext(*this); // This must happen before the master is deleted! + #if 1 //CNSContext::NoMoreUsers is responsible for disposing the history sessions + // yes, but 97/05/22 CNSContext::NoMoreUsers now has "NoMoreUsers". + LO_DiscardDocument(&mContext); + SHIST_EndSession(&mContext); + MimeDestroyContextData(&mContext); + // No java + // No layers + // No mocha + #endif //0 + if (sMailMasterRefCount == 1) + { + // If we are about to delete the last mail-news context... + // (WARNING: there's a backend bug: MSG_CleanupNeeded will continue to return + // true even after the cleanup is finished. So be sure not to get into an + // infinite loop.) + if (mContext.type != MWContextMailNewsProgress && MSG_CleanupNeeded(sMailMaster)) + { + try + { + CMailProgressWindow::CleanUpFolders(); + } + catch(...) + { + // Don't throw here, we're trying to quit. + } + } + } + Assert_(sMailMaster); + Assert_(sMailMasterRefCount); + sMailMasterRefCount--; + if (sMailMasterRefCount == 0) + { + MSG_DestroyMaster(sMailMaster); + sMailMaster = NULL; + } +} // CMailNewsContext::~CMailNewsContext + +//---------------------------------------------------------------------------------------- +Boolean CMailNewsContext::IsPrefSet(const char* inPrefKey) +//---------------------------------------------------------------------------------------- +{ + char buffer[512]; int bufferLength = sizeof(buffer); + PREF_GetCharPref(inPrefKey, buffer, &bufferLength); + if (*buffer) + return true; + return false; +} // CMailNewsContext::IsPrefSet + +//---------------------------------------------------------------------------------------- +Boolean CMailNewsContext::ThrowUnlessPrefSet( + const char* inPrefKey, + PREF_Enum inPrefPaneSelector) +//---------------------------------------------------------------------------------------- +{ + if (IsPrefSet(inPrefKey)) + return true; + AlertPrefAndThrow(inPrefPaneSelector); + return false; // dead, but the compiler needs this, see? +} // CMailNewsContext::ThrowUnlessPrefSet + +//---------------------------------------------------------------------------------------- +void CMailNewsContext::AlertPrefAndThrow(PREF_Enum inPrefPaneSelector) +//---------------------------------------------------------------------------------------- +{ + // OK. We're going to throw, but first, we prompt and send them to the prefs pane. + // Ask "Would you like to set the preference?" + short strID; + switch (inPrefPaneSelector) + { + case PREF_EmailAddress: strID = 23; break; // I don't know why but all string IDs from + case PREF_Pop3ID: strID = 21; break; // STR# 7099 are hard-coded in other parts + case PREF_SMTPHost: strID = 21; break; // of the code: let's continue! + case PREF_PopHost: strID = 21; break; + case PREF_NewsHost: strID = 22; break; + default: strID = 24; break; + } + CStr255 whereString; + ::GetIndString(whereString, 7099, strID); + + CStr255 alertString; + ::GetIndString(alertString, 7099, 7); + + ::StringParamText(alertString, (char*)whereString, nil, nil, nil); + + if (UStdDialogs::AskOkCancel(alertString, nil, nil)) // if "ok" + FE_EditPreference(inPrefPaneSelector); + throw (OSErr)userCanceledErr; // we already presented the alert. This avoids another. +} // CMailNewsContext::AlertPrefAndThrow + +//---------------------------------------------------------------------------------------- +/* static */ Boolean CMailNewsContext::UserHasNoLocalInbox() +//---------------------------------------------------------------------------------------- +{ + // Check whether there is an inbox in their local mail tree. If not, assume + // first time setup. + FSSpec inboxSpec = CPrefs::GetFilePrototype(CPrefs::MailFolder); +// *(CStr63*)(inboxSpec.name) = XP_GetString(MK_MSG_INBOX_L10N_NAME); +// if (FSMakeFSSpec(inboxSpec.vRefNum, inboxSpec.parID, inboxSpec.name, &spec2) == fnfErr) +// return true; + FSSpec spec2; + *(CStr63*)(inboxSpec.name) = XP_GetString(MK_MSG_TRASH_L10N_NAME); + if (FSMakeFSSpec(inboxSpec.vRefNum, inboxSpec.parID, inboxSpec.name, &spec2) == fnfErr) + return true; + return false; +} // CMailNewsContext::UserHasNoLocalInbox + +//---------------------------------------------------------------------------------------- +/* static */ void CMailNewsContext::ThrowIfNoLocalInbox() +//---------------------------------------------------------------------------------------- +{ + if (UserHasNoLocalInbox()) + AlertPrefAndThrow(PREF_PopHost); +} // CMailNewsContext::ThrowIfNoLocalInbox + +//---------------------------------------------------------------------------------------- +void CMailNewsContext::ThrowUnlessPrefsSet(MWContextType inContextType) +//---------------------------------------------------------------------------------------- +{ + if (inContextType == MWContextBiff + || inContextType == MWContextSearch + || inContextType == MWContextAddressBook + || inContextType == MWContextMailFilters) + return; // no prefs required for biff. + +#ifdef HAVE_SEPARATE_SMTP_USERNAME_PREF + //if (we are using POP) + // ThrowUnlessPrefSet("mail.pop_name",PREF_Pop3ID); + + ThrowUnlessPrefSet("mail.smtp_name", PREF_Pop3ID); +#else + // temporary code while the backend is still using pop_name for smtp_name + if ( !IsPrefSet("mail.pop_name") ) + { + char *smtpName = NULL; + + if (PREF_CopyCharPref("mail.smtp_name", &smtpName) == PREF_NOERROR) + { + int prefError = PREF_SetCharPref("mail.pop_name", smtpName); + Assert_(prefError == PREF_NOERROR || prefError == PREF_VALUECHANGED); + } + + XP_FREEIF(smtpName); + } +#endif + + if (!IsPrefSet("network.hosts.pop_server") && !IsPrefSet("network.hosts.imap_servers")) + AlertPrefAndThrow(PREF_PopHost); + // OK, she has the basic stuff. Now for the fancy stuff. I know it's plain wrong + // to have a switch like this in an object-oriented world, but time is of the essence. + switch (inContextType) + { + case MWContextNews: // A news reader window + case MWContextNewsMsg: // A window to display a news msg + ThrowUnlessPrefSet( + "network.hosts.nntp_server", + PREF_NewsHost); + break; + case MWContextMessageComposition: // A news-or-mail message editing window + ThrowUnlessPrefSet( + "network.hosts.smtp_server", + PREF_SMTPHost); + ThrowUnlessPrefSet( + "mail.identity.useremail", + PREF_EmailAddress); + break; + } +} // CMailNewsContext::ThrowUnlessPrefsSet + +//---------------------------------------------------------------------------------------- +/* static */ MSG_Master* CMailNewsContext::GetMailMaster() +//---------------------------------------------------------------------------------------- +{ + if (!sMailMaster) + { + sMailMaster = MSG_InitializeMail(GetMailPrefs()); + ThrowIfNULL_(sMailMaster); + } + return sMailMaster; +} + +/* +//---------------------------------------------------------------------------------------- +void CMailNewsContext::DoProgress(const char* message, int level) +//---------------------------------------------------------------------------------------- +{ + StatusInfo info; // constructor zeroes fields + info.message = message; + info.level = level; + BroadcastMessage(CProgressBroadcaster::msg_StatusText, &info); +} + +//---------------------------------------------------------------------------------------- +void CMailNewsContext::DoSetProgressBarPercent(int32 percent) +//---------------------------------------------------------------------------------------- +{ + StatusInfo info; // constructor zeroes fields + info.percent = percent; + BroadcastMessage(CProgressBroadcaster::msg_StatusPercent, &info); +} +//---------------------------------------------------------------------------------------- +void CMailNewsContext::AllConnectionsComplete() +//---------------------------------------------------------------------------------------- +{ + CNSContext::AllConnectionsComplete(); + StatusInfo info; // constructor zeroes fields + BroadcastMessage(CProgressBroadcaster::msg_StatusText, &info); + BroadcastMessage(CProgressBroadcaster::msg_StatusPercent, &info); + BroadcastMessage(CProgressBroadcaster::msg_StatusComplete, &info); +} +*/ + +//---------------------------------------------------------------------------------------- +/* static */ MSG_Prefs* CMailNewsContext::GetMailPrefs() +//---------------------------------------------------------------------------------------- +{ + if (!sMailPrefs) + { + sMailPrefs = MSG_CreatePrefs(); + ThrowIfNULL_(sMailPrefs); + FSSpec mailFolder = CPrefs::GetFolderSpec(CPrefs::MailFolder); + char* mailFolderPath = CFileMgr::EncodedPathNameFromFSSpec(mailFolder, true); + + MSG_SetFolderDirectory(sMailPrefs, mailFolderPath); + XP_FREE(mailFolderPath); + } + return sMailPrefs; +} + +//---------------------------------------------------------------------------------------- +char* CMailNewsContext::PromptWithCaption( + const char* inTitleBarText, + const char* inMessage, + const char* inDefaultText) +//---------------------------------------------------------------------------------------- +{ + char* result = NULL; + CStr255 mesg(inMessage), ioString(inDefaultText); + mesg = NET_UnEscape(mesg); + + uint8 maxLength; + switch (GetCurrentCommand()) + { + case cmd_RenameFolder: + case cmd_NewFolder: + maxLength = 27; + break; + default: + maxLength = 255; + break; + } + + mWasPromptOkay = UStdDialogs::AskStandardTextPrompt( + inTitleBarText, mesg, ioString, NULL, NULL, maxLength ); + + if ( mWasPromptOkay ) + { + if (ioString.Length() > 0) + { + if ( GetCurrentCommand() == cmd_NewFolder || + GetCurrentCommand() == cmd_RenameFolder ) + { + ioString = NET_UnEscape(ioString); // make sure the path... + char * temp = NET_Escape(ioString, URL_PATH); // ...is fully escaped + if (temp) + { + ioString = temp; + XP_FREE(temp); + } + } + + result = (char*)XP_STRDUP((const char*)ioString); + } + } + + // If result is null then set as canceled + if ( !result ) + { + mWasPromptOkay = false; + SetCurrentCommand(cmd_Nothing); + } + return result; +} + +//---------------------------------------------------------------------------------------- +void CMailNewsContext::NoMoreUsers() +//---------------------------------------------------------------------------------------- +{ + Inherited::NoMoreUsers(); +#if 0 + // CNSContext::NoMoreUsers has so much unnecessary stuff in it now, that + // we have to AVOID calling it. We don't have mocha, images, layers, or any of that + // stuff. All we have is session history. + MWContext* cx = (MWContext*)*this; + LSharable::NoMoreUsers(); // which says "delete this". + cx->fe.newContext = nil; // prevent callbacks (and crashes). +//#if DEBUG + // Make sure we assert instead of crashing. + CNSContextCallbacks* theCallbacks = CNSContextCallbacks::GetContextCallbacks(); + Assert_(theCallbacks != NULL); + cx->funcs = &(theCallbacks->GetInternalCallbacks()); +//#endif +#endif // 0 +} + +//---------------------------------------------------------------------------------------- +void CMailNewsContext::SwitchLoadURL( + URL_Struct* inURL, + FO_Present_Types inOutputFormat) +//---------------------------------------------------------------------------------------- +{ + Inherited::SwitchLoadURL(inURL, inOutputFormat); +} + +//---------------------------------------------------------------------------------------- +void CMailNewsContext::AllConnectionsComplete() +//---------------------------------------------------------------------------------------- +{ + // Note: since this might lead to deletion of our host window (eg, a progress context), + // we might lose all our users after broadcasting here. On the other hand, we may be + // called from our destructor, in which case making a StSharer here led to + // double deletion on exit (when StSharer goes out of scope). + // The solution? Don't do anything if we know we are in a destruction sequence. + if (GetUseCount() <= 0) + return; + StSharer theShareLock(this); + Inherited::AllConnectionsComplete(); +} // CMailNewsContext::AllConnectionsComplete diff --git a/mozilla/cmd/macfe/MailNews/CMailNewsContext.h b/mozilla/cmd/macfe/MailNews/CMailNewsContext.h new file mode 100644 index 00000000000..625c23bedeb --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMailNewsContext.h @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMailNewsContext.h + +#pragma once + +// XP +#include "msgcom.h" + +// MacFE + +#include "CNSContext.h" + +//====================================== +class CMailNewsContext +//====================================== +: public CNSContext +{ +private: + typedef CNSContext Inherited; +public: + + CMailNewsContext(MWContextType inContextType = MWContextMail); + virtual ~CMailNewsContext(); + +// Overrides +public: +// virtual void DoProgress(const char* message, int level); // 1 called by netlib +// virtual void DoSetProgressBarPercent(int32 percent); + +// Safe and guaranteed access +protected: + static MSG_Prefs* GetMailPrefs(); +public: + static MSG_Master* GetMailMaster(); + + // This is for offline/online support. + // If mail/news isn't up and running, we shouldn't call GetMailMaster. + // This allows us to determine if we've already allocated the MSG_Master + static Boolean HaveMailMaster() + { return sMailMaster != NULL; } + static Boolean UserHasNoLocalInbox(); + + // Prompt callback support + virtual char* PromptWithCaption( + const char* inTitleBarText, + const char* inMessage, + const char* inDefaultText); + virtual void SwitchLoadURL( + URL_Struct* inURL, + FO_Present_Types inOutputFormat); + virtual void AllConnectionsComplete(); + Boolean GetWasPromptOkay() { return mWasPromptOkay; } + // LSharable + virtual void NoMoreUsers(); + static void ThrowUnlessPrefsSet(MWContextType inContextType); + static void AlertPrefAndThrow(PREF_Enum inPrefPaneSelector); + static void ThrowIfNoLocalInbox(); + +protected: + static Boolean IsPrefSet(const char* inPrefKey); + static Boolean ThrowUnlessPrefSet( + const char* inPrefKey, + PREF_Enum inPrefPaneSelector); + +// Data +private: + static MSG_Prefs* sMailPrefs; + static MSG_Master* sMailMaster; + static Int32 sMailMasterRefCount; + Boolean mWasPromptOkay; +}; // class CMailNewsContext diff --git a/mozilla/cmd/macfe/MailNews/CMailNewsWindow.cp b/mozilla/cmd/macfe/MailNews/CMailNewsWindow.cp new file mode 100644 index 00000000000..607dbcc7749 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMailNewsWindow.cp @@ -0,0 +1,449 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMailNewsWindow.cp + +#include "CMailNewsWindow.h" + +#include "MailNewsgroupWindow_Defines.h" +#include "CGrayBevelView.h" +#include "CProgressListener.h" +#include "CMailNewsContext.h" +#include "LTableViewHeader.h" +#include "CMailFlexTable.h" +#include "CMessageFolderView.h" +#include "CPrefsDialog.h" +#include "CPatternButton.h" +#include "URobustCreateWindow.h" +#include "CSpinningN.h" +#include "CProxyPane.h" +#include "CDragBarContainer.h" +#include "UDeferredTask.h" +#include "CWindowMenu.h" + +#include "resgui.h" +#include "macutil.h" +// helper function used by CThreadWindow and CMessageWindow to set the default action of +// the folder button. +#include "prefapi.h" + + + +#pragma mark - + +//====================================== +// class CMailNewsWindow +//====================================== + +const char* Pref_MailShowToolbar = "mailnews.chrome.show_toolbar"; +const char* Pref_MailShowLocationBar = "mailnews.chrome.show_url_bar"; + +//---------------------------------------------------------------------------------------- +CMailNewsWindow::CMailNewsWindow(LStream *inStream, DataIDT inWindowType) +//---------------------------------------------------------------------------------------- +: Inherited(inStream, inWindowType) +, CSaveWindowStatus(this) +, mProgressListener(NULL) +, mMailNewsContext(NULL) +{ + mToolbarShown[LOCATION_TOOLBAR] = false; // set to true in base class +} + +//---------------------------------------------------------------------------------------- +void CMailNewsWindow::DoDefaultPrefs() +//---------------------------------------------------------------------------------------- +{ + CPrefsDialog::EditPrefs(CPrefsDialog::eExpandMailNews); +} + + +//---------------------------------------------------------------------------------------- +CMailNewsWindow::~CMailNewsWindow() +//---------------------------------------------------------------------------------------- +{ + if (mMailNewsContext) + mMailNewsContext->RemoveUser(this); + delete mProgressListener; +} + +//---------------------------------------------------------------------------------------- +const char* CMailNewsWindow::GetLocationBarPrefName() const +//---------------------------------------------------------------------------------------- +{ + return Pref_MailShowLocationBar; +} + +//---------------------------------------------------------------------------------------- +void CMailNewsWindow::ReadGlobalDragbarStatus() + // Unfortunately, the visibility of the drag bars is determined in several ways, all + // of which must be kept in synch. These ways include, but are not limited to, + // the PPob resource (through the visible flag), the saved window status (through + // Read/WriteWindowStatus and Save/RestorePlace etc) and the preferences, which are + // used to save the values of mToolbarShown. +//---------------------------------------------------------------------------------------- +{ + XP_Bool value; + if (PREF_GetBoolPref(Pref_MailShowToolbar, &value) == PREF_NOERROR) + mToolbarShown[MESSAGE_TOOLBAR] = value; + if (PREF_GetBoolPref(GetLocationBarPrefName(), &value) == PREF_NOERROR) + mToolbarShown[LOCATION_TOOLBAR] = value; +} // CMailNewsWindow::ReadGlobalDragbarStatus + +//---------------------------------------------------------------------------------------- +void CMailNewsWindow::WriteGlobalDragbarStatus() +//---------------------------------------------------------------------------------------- +{ + PREF_SetBoolPref(Pref_MailShowToolbar, mToolbarShown[MESSAGE_TOOLBAR]); + PREF_SetBoolPref(GetLocationBarPrefName(), mToolbarShown[LOCATION_TOOLBAR]); +} // CMailNewsWindow::WriteGlobalDragbarStatus + +//---------------------------------------------------------------------------------------- +void CMailNewsWindow::FinishCreateSelf() +//---------------------------------------------------------------------------------------- +{ + Inherited::FinishCreateSelf(); + SetAttribute(windAttr_DelaySelect); + // should be in resource, but there's a resource freeze. + try + { + //================================================================================ + // WARNING! WARNING! WARNING! + // CThreadWindow::FinishCreateSelf() does not call CMailNewsWindow::FinishCreateSelf. + // So if you add any new stuff here, you have to add it there, too. + //================================================================================ + + ReadGlobalDragbarStatus(); + + // Let there be a mail-news context + mMailNewsContext = CreateContext(); + StSharer theLock(mMailNewsContext); + // Let there be a progress listener, placed in my firmament, + // which shall listen to the mail-news context + if (mProgressListener) + mMailNewsContext->AddListener(mProgressListener); + else + mProgressListener = new CProgressListener(this, mMailNewsContext); + ThrowIfNULL_(mProgressListener); + // The progress listener should be "just a bit" lazy during network activity + // and "not at all" at idle time to display the URLs pointed by the mouse cursor. + mProgressListener->SetLaziness(CProgressListener::lazy_NotAtAll); + mMailNewsContext->AddListener(mProgressListener); + mMailNewsContext->AddUser(this); + CMailFlexTable* table = GetActiveTable(); + if (table) + { + SetLatentSub(table); + mMailNewsContext->AddListener(table); // listen for all connections complete. + } + CSpinningN* theN = dynamic_cast(FindPaneByID(CSpinningN::class_ID)); + if (theN) + mMailNewsContext->AddListener(theN); + } + catch (...) { + throw; + } + // And behold, he saw that it was good. + CSaveWindowStatus::FinishCreateWindow(); +} // CMailNewsWindow::FinishCreateSelf + +//---------------------------------------------------------------------------------------- +CNSContext* CMailNewsWindow::CreateContext() const +//---------------------------------------------------------------------------------------- +{ + CMailNewsContext* result = new CMailNewsContext(); + FailNIL_(result); + return result; +} //CMailNewsWindow::CreateContext + +//---------------------------------------------------------------------------------------- +void CMailNewsWindow::AboutToClose() +//---------------------------------------------------------------------------------------- +{ + //================================================================================ + // WARNING! WARNING! WARNING! + // CThreadWindow::AboutToClose() does not call CMailNewsWindow::AboutToClose. + // So if you add any new stuff here, you have to add it there, too. + //================================================================================ + CSaveWindowStatus::AttemptCloseWindow(); // Do this first: uses table + WriteGlobalDragbarStatus(); + // Bug fix: must delete the pane before killing the context, because + // the destructor of the pane references the context when it cleans up. + CMailFlexTable* t = GetActiveTable(); + if (t) + { + if (mMailNewsContext) + mMailNewsContext->RemoveListener(t); // bad time to listen for all connections complete. + delete t; + } +} // CMailNewsWindow::AboutToClose + +//---------------------------------------------------------------------------------------- +void CMailNewsWindow::AttemptClose() +//---------------------------------------------------------------------------------------- +{ + CDeferredCloseTask::DeferredClose(this); +} + +//---------------------------------------------------------------------------------------- +void CMailNewsWindow::DoClose() +//---------------------------------------------------------------------------------------- +{ + AboutToClose(); + Inherited::DoClose(); +} + +//---------------------------------------------------------------------------------------- +Boolean CMailNewsWindow::AttemptQuitSelf(Int32 /* inSaveOption */) +// Derived classes should be careful to call DeferredClose if they override this fn. +//---------------------------------------------------------------------------------------- +{ + CDeferredCloseTask::DeferredClose(this); + return true; +} + +//---------------------------------------------------------------------------------------- +void CMailNewsWindow::ReadWindowStatus(LStream *inStatusData) +//---------------------------------------------------------------------------------------- +{ + CSaveWindowStatus::ReadWindowStatus(inStatusData); + CDragBarContainer* dragContainer = (CDragBarContainer*)FindPaneByID('DbCt'); + if (dragContainer && inStatusData) + dragContainer->RestorePlace(inStatusData); + + // CThreadWindow does this now + //CMailFlexTable* table = GetActiveTable(); + //if (table && inStatusData) + // table->GetTableHeader()->ReadColumnState(inStatusData); +} + +//---------------------------------------------------------------------------------------- +void CMailNewsWindow::WriteWindowStatus(LStream *outStatusData) +//---------------------------------------------------------------------------------------- +{ + CSaveWindowStatus::WriteWindowStatus(outStatusData); + CDragBarContainer* dragContainer = (CDragBarContainer*)FindPaneByID('DbCt'); + if (dragContainer && outStatusData) + dragContainer->SavePlace(outStatusData); + + // CThreadWindow does this now + //CMailFlexTable* table = GetActiveTable(); + //if (table && outStatusData) + // table->GetTableHeader()->WriteColumnState(outStatusData); +} + +//---------------------------------------------------------------------------------------- +void CMailNewsWindow::FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +//---------------------------------------------------------------------------------------- +{ + outUsesMark = false; + + switch (inCommand) { + + case cmd_ToggleToolbar: + outEnabled = true; + if (mToolbarShown[MESSAGE_TOOLBAR]) + ::GetIndString(outName, BROWSER_MENU_TOGGLE_STRINGS_ID, HIDE_MESSAGE_TOOLBAR_STRING); + else + ::GetIndString(outName, BROWSER_MENU_TOGGLE_STRINGS_ID, SHOW_MESSAGE_TOOLBAR_STRING); + break; + + case cmd_ToggleLocationBar: + outEnabled = true; + if (mToolbarShown[LOCATION_TOOLBAR]) + ::GetIndString(outName, BROWSER_MENU_TOGGLE_STRINGS_ID, HIDE_LOCATION_TOOLBAR_STRING); + else + ::GetIndString(outName, BROWSER_MENU_TOGGLE_STRINGS_ID, SHOW_LOCATION_TOOLBAR_STRING); + break; + case cmd_ShowLocationBar: + outEnabled = !mToolbarShown[LOCATION_TOOLBAR]; + break; + case cmd_HideLocationBar: + outEnabled = mToolbarShown[LOCATION_TOOLBAR]; + break; + default: + CNetscapeWindow::FindCommandStatus( + inCommand, outEnabled, outUsesMark, outMark, outName); + } +} // CMailNewsWindow::FindCommandStatus + +//---------------------------------------------------------------------------------------- +Boolean CMailNewsWindow::ObeyCommand( + CommandT inCommand, + void *ioParam) +//---------------------------------------------------------------------------------------- +{ + Boolean cmdHandled = false; + switch (inCommand) { + + case cmd_ToggleToolbar: + ToggleDragBar(cMessageToolbar, MESSAGE_TOOLBAR, Pref_MailShowToolbar); + cmdHandled = true; + break; + + case cmd_ShowLocationBar: + case cmd_HideLocationBar: + if (mToolbarShown[LOCATION_TOOLBAR] != (inCommand == cmd_ShowLocationBar)) + ToggleDragBar(cMailNewsLocationToolbar, LOCATION_TOOLBAR, GetLocationBarPrefName()); + cmdHandled = true; + break; + + case cmd_ToggleLocationBar: + ToggleDragBar(cMailNewsLocationToolbar, LOCATION_TOOLBAR, GetLocationBarPrefName()); + cmdHandled = true; + break; + + default: + cmdHandled = CNetscapeWindow::ObeyCommand(inCommand, ioParam); + + } + + return cmdHandled; +} // CMailNewsWindow::ObeyCommand + +#pragma mark - + +//====================================== +// class CMailNewsFolderWindow +//====================================== + +//---------------------------------------------------------------------------------------- +/* static */ CMailNewsFolderWindow* CMailNewsFolderWindow::FindAndShow( + Boolean inMakeNew, + CommandT inCommand) +// Handle the menu command that creates/shows/selects the MailNews window. +// Currently there can only be one of these. +//---------------------------------------------------------------------------------------- +{ + CMailNewsFolderWindow* result = NULL; + try + { + CMailNewsContext::ThrowUnlessPrefsSet(MWContextMail); + if (inCommand == cmd_NewsGroups) + CMailNewsContext::ThrowUnlessPrefsSet(MWContextNews); + CWindowIterator iter(WindowType_MailNews); + iter.Next(result); + if (!result && inMakeNew) + { + result = dynamic_cast( + URobustCreateWindow::CreateWindow(res_ID, LCommander::GetTopCommander()) + ); + ThrowIfNULL_(result); + } + if (result) + { + result->Show(); + result->Select(); + } + if (inCommand) + { + CMessageFolderView* folderView = (CMessageFolderView*)result->GetActiveTable(); + if (folderView) + { + switch (inCommand) + { + case cmd_NewsGroups: // Select first news host + case cmd_MailNewsFolderWindow: // Select first mail host + folderView->SelectFirstFolderWithFlags( + inCommand == cmd_NewsGroups + ? MSG_FOLDER_FLAG_NEWS_HOST + : MSG_FOLDER_FLAG_MAIL); + break; + default: + folderView->ObeyCommand(inCommand, nil); + } + } + } + } + catch( ... ) + { + } + return result; +} + +//---------------------------------------------------------------------------------------- +CMailNewsFolderWindow::CMailNewsFolderWindow(LStream *inStream) +//---------------------------------------------------------------------------------------- +: CMailNewsWindow(inStream, WindowType_MailNews) +{ +} + +//---------------------------------------------------------------------------------------- +CMailNewsFolderWindow::~CMailNewsFolderWindow() +//---------------------------------------------------------------------------------------- +{ +} + +//---------------------------------------------------------------------------------------- +void CMailNewsFolderWindow::FinishCreateSelf() +//---------------------------------------------------------------------------------------- +{ + Inherited::FinishCreateSelf(); + CMessageFolderView* list = (CMessageFolderView*)GetActiveTable(); + Assert_(list); + LCaption* locationCaption = (LCaption*)FindPaneByID('LCap'); + if (locationCaption && list) + { + CStr255 tempString; + list->GetLongWindowDescription(tempString); + locationCaption->SetDescriptor(tempString); + CProxyPane* proxy = dynamic_cast(FindPaneByID(CProxyPane::class_ID)); + if (proxy) + { + proxy->ListenToMessage(msg_NSCDocTitleChanged, (char*)tempString); + proxy->SetIconIDs(15393, 15393); + } + } + list->LoadFolderList(mMailNewsContext); + UReanimator::LinkListenerToControls((CMailFlexTable*)list, this, res_ID); +} // CMailNewsWindow::FinishCreateSelf + +//---------------------------------------------------------------------------------------- +void CMailNewsFolderWindow::CalcStandardBoundsForScreen( + const Rect &inScreenBounds, + Rect &outStdBounds) const +//---------------------------------------------------------------------------------------- +{ + LWindow::CalcStandardBoundsForScreen(inScreenBounds, outStdBounds); + Rect contRect = UWindows::GetWindowContentRect(mMacWindowP); + + outStdBounds.left = contRect.left; + outStdBounds.right = contRect.right; +} + +//---------------------------------------------------------------------------------------- +UInt16 CMailNewsFolderWindow::GetValidStatusVersion(void) const +//---------------------------------------------------------------------------------------- +{ + return 0x0113; +} + +//---------------------------------------------------------------------------------------- +CMailFlexTable* CMailNewsFolderWindow::GetActiveTable() +// Get the currently active table in the window. The active table is the table in +// the window that the user considers to be receiving input. +//---------------------------------------------------------------------------------------- +{ + return dynamic_cast(FindPaneByID('Flst')); +} diff --git a/mozilla/cmd/macfe/MailNews/CMailNewsWindow.h b/mozilla/cmd/macfe/MailNews/CMailNewsWindow.h new file mode 100644 index 00000000000..013a78f182f --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMailNewsWindow.h @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMailNewsWindow.h + +#pragma once + +// Mac UI Lib +#include "CNetscapeWindow.h" +#include "CSaveWindowStatus.h" + +class CProgressListener; +class CMailNewsContext; +class CMailNewsFolderContext; +class CMailFlexTable; + +const PaneIDT cMessageToolbar = 'BBar'; +const PaneIDT cMailNewsLocationToolbar = 'BnBr'; + + +//====================================== +class CMailNewsWindow : public CNetscapeWindow, public CSaveWindowStatus +// Base class for all content windows in mail. +// implements the progress bar and the status line etc. +//====================================== +{ +private: + typedef CNetscapeWindow Inherited; // trick suggested by the ANSI committee. + +protected: + // Indices into mToolbarShown for tracking visibility of toolbars + enum { MESSAGE_TOOLBAR, LOCATION_TOOLBAR }; + CMailNewsWindow(LStream *inStream, DataIDT inWindowType); + +public: + virtual ~CMailNewsWindow(); + + virtual CNSContext* CreateContext() const; // allow each window to create its own + virtual void FinishCreateSelf(); + virtual CNSContext* GetWindowContext() const { return (CNSContext*)mMailNewsContext; } + + // Return the currently active table in the window, nil if none. + // Can't be const, because derived classes need to call FindPaneByID(), which isn't. + virtual CMailFlexTable* GetActiveTable() { return nil; } + virtual CMailFlexTable* GetSearchTable() { return GetActiveTable(); } + virtual void FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName); + virtual Boolean ObeyCommand( + CommandT inCommand, + void *ioParam = nil); + + //----------------------------------- + // Window closing and saving - overrides for CSaveWindowStatus + //----------------------------------- +public: + virtual void AttemptClose(); + virtual Boolean AttemptQuitSelf(Int32 /* inSaveOption */); + virtual void DoClose(); + CProgressListener* GetProgressListener() {return mProgressListener;} +protected: + virtual void AboutToClose(); // place to put common code from [Attempt|Do]Close() + virtual void ReadWindowStatus(LStream *inStatusData); + virtual void WriteWindowStatus(LStream *outStatusData); + virtual void ReadGlobalDragbarStatus(); + virtual void WriteGlobalDragbarStatus(); + virtual const char* GetLocationBarPrefName() const; + + //----------------------------------- + // Preferences + //----------------------------------- + virtual void DoDefaultPrefs(); + + //----------------------------------- + //data + //----------------------------------- +protected: + CProgressListener* mProgressListener; + CNSContext* mMailNewsContext; + +}; + +//====================================== +class CMailNewsFolderWindow : public CMailNewsWindow +//====================================== +{ +private: + typedef CMailNewsWindow Inherited; +public: + enum { class_ID = 'mnWN', res_ID = 10507}; + + virtual ~CMailNewsFolderWindow(); + CMailNewsFolderWindow(LStream *inStream); + virtual ResIDT GetStatusResID(void) const { return res_ID; } + virtual UInt16 GetValidStatusVersion(void) const; + + virtual void FinishCreateSelf(); + virtual void CalcStandardBoundsForScreen(const Rect &inScreenBounds, + Rect &outStdBounds) const; + + // Return the currently active table in the window, nil if none + static CMailNewsFolderWindow* FindAndShow(Boolean inMakeNew, CommandT inCommand = 0); + virtual CMailFlexTable* GetActiveTable(); +}; + diff --git a/mozilla/cmd/macfe/MailNews/CMailProgressWindow.cp b/mozilla/cmd/macfe/MailNews/CMailProgressWindow.cp new file mode 100644 index 00000000000..ab51d74006b --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMailProgressWindow.cp @@ -0,0 +1,513 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMailProgressWindow.cp + +#include "CMailProgressWindow.h" + +#include "MailNewsCallbacks.h" +#include "URobustCreateWindow.h" +#include "CMailNewsContext.h" +#include "PascalString.h" +#include "CProgressBar.h" +#include "COffscreenCaption.h" +#include "uapp.h" // to check if we're quitting. +#include "CWindowMenu.h" +#include "prefapi.h" +#include "CNetscapeWindow.h" +#include "UOffline.h" +#include "StSetBroadcasting.h" + +const ResIDT cOfflineListID = 16010; +const Int16 cOfflineStrIndex = 3; + + +//====================================== +class CDownloadListener : public CMailCallbackListener +// All Connections Complete is not always sent by operations within msglib. Therefore, +// we must listen for the MSG_PaneProgressDone message via the pane-changed mechanism, too. +// So the progress window "has a" CDownloadListener, which has authority to force the +// progress window to close itself. +//====================================== +{ +public: + CDownloadListener( + CMailProgressWindow* inWindow, MSG_Pane* inPane); + virtual void PaneChanged( + MSG_Pane* inPane, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value); // must always be supported. + CMailProgressWindow* mProgressWindow; +}; // class CDownloadListener + +//----------------------------------- +CDownloadListener::CDownloadListener(CMailProgressWindow* inWindow, MSG_Pane* inPane) +//----------------------------------- +: mProgressWindow(inWindow) +{ + SetPane(inPane); +} + +//----------------------------------- +void CDownloadListener::PaneChanged( + MSG_Pane* inPane, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value) +//----------------------------------- +{ +#pragma unused (inPane) +#pragma unused (value) + if (inNotifyCode == MSG_PaneProgressDone + || inNotifyCode == MSG_PaneNotifySelectNewFolder // Bug #106165 + || inNotifyCode == MSG_PaneNotifyNewFolderFailed // '' + || inNotifyCode == MSG_PaneNotifyCopyFinished) // Bug #113142 and #113860 + { + mProgressWindow->ListenToMessage(msg_NSCAllConnectionsComplete, nil); + } +} + +//====================================== +// class CMailProgressWindow +//====================================== + +CMailProgressWindow* CMailProgressWindow::sModalMailProgressWindow = nil; +// if you initiate a get mail command, you can't start getting mail again before the +// first CMailProgressWindow closes +Boolean CMailProgressWindow::sGettingMail = false; + +//----------------------------------- +/*static*/ CMailProgressWindow* CMailProgressWindow::CreateWindow( + ResIDT inResID, + MSG_Pane* inPane, + const CStr255& inCommandName) +//----------------------------------- +{ + CMailProgressWindow* progressWindow = nil; + try + { + MSG_SetFEData(inPane, CMailCallbackManager::Get()); + progressWindow = dynamic_cast( + URobustCreateWindow::CreateWindow(inResID, LCommander::GetTopCommander())); + ThrowIfNULL_(progressWindow); + progressWindow->SetWindowContext(ExtractNSContext(MSG_GetContext(inPane))); + // the window will be shown after a decent interval. + if (inCommandName.Length() > 0) + progressWindow->SetDescriptor(inCommandName); + progressWindow->ListenToPane(inPane); + } + catch (...) + { + delete progressWindow; + throw; + } + return progressWindow; +} // CMailProgressWindow::CreateWindow + +//----------------------------------- +/*static*/ CMailProgressWindow* CMailProgressWindow::CreateIndependentWindow( + ResIDT inResID, + MSG_Pane* inPane, + const CStr255& inCommandName, + UInt16 inDelay) +// This is private to the class. The pane passed in must have been made by this class. +//----------------------------------- +{ + CMailProgressWindow* progressWindow = CreateWindow(inResID, inPane, inCommandName); + progressWindow->mPane = inPane; // This is the sign that we made it. + progressWindow->SetDelay(inDelay); + if (inDelay == 0) + progressWindow->Show(); + return progressWindow; +} // CMailProgressWindow::CreateModelessWindow + +//----------------------------------- +/*static*/ CMailProgressWindow* CMailProgressWindow::CreateModalParasite( + ResIDT inResID, + MSG_Pane* inPane, + const CStr255& inCommandName) +//----------------------------------- +{ + sModalMailProgressWindow = CreateWindow(inResID, inPane, inCommandName); + return sModalMailProgressWindow; +} // CMailProgressWindow::CreateModalParasite + +//----------------------------------- +/*static*/ CMailProgressWindow* CMailProgressWindow::ObeyMessageLibraryCommand( + ResIDT inResID, + MSG_Pane* inPane, + MSG_CommandType inCommand, + const CStr255& inCommandName, + MSG_ViewIndex* inSelectedIndices, + int32 inNumIndices) +//----------------------------------- +{ + CMailProgressWindow* progressWindow = nil; + try + { + CMailNewsContext* context = new CMailNewsContext(MWContextMailNewsProgress); + MSG_Pane* pane = MSG_CreateProgressPane(*context, CMailNewsContext::GetMailMaster(), inPane); + ThrowIfNULL_(pane); + progressWindow = CreateIndependentWindow(inResID, pane, inCommandName); + progressWindow->mParentPane = inPane; + + // remember if we're getting mail or news... + progressWindow->mCommandBeingServiced = inCommand; + // only allowed to get mail one dialog at a time... (in other words, you can't + // have two of these dialogs open at the same time both of them getting mail) + if (inCommand == MSG_GetNewMail) + { + sGettingMail = true; + // Note: for MSG_GetNewMail, we get the pane notification CopyFinished when + // messages are filtered, and later we get allconnectionscomplete from the + // context. So avoid closing the window too early by turning off pane + // notifications. + progressWindow->ListenToPane(nil); + + // This is getting messy. I think CDownloadListener should have a new built-in + // data member, which stores the notify code which it will use to destroy the window. + // eg, for a file copy, set this to MSG_PaneNotifyCopyFinished, etc etc. The problem + // now is that some operations, like GetNewMail, send one of these AS WELL AS all + // connections complete. Whereas some operations send only one or the other of these. + } + + ::MSG_Command(pane, inCommand, inSelectedIndices, inNumIndices); + } + catch (...) + { + delete progressWindow; + throw; + } + + return progressWindow; +} // CMailProgressWindow::ObeyMessageLibraryCommand + +//----------------------------------- +/*static*/ CMailProgressWindow* CMailProgressWindow::CleanUpFolders() +//----------------------------------- +{ + CMailProgressWindow* progressWindow = nil; + try + { + CMailNewsContext* context = new CMailNewsContext(MWContextMailNewsProgress); + MSG_Pane* pane = MSG_CreateFolderPane(*context, CMailNewsContext::GetMailMaster()); + ThrowIfNULL_(pane); + + progressWindow = CreateIndependentWindow(res_ID_modeless, pane, ""); + progressWindow->mCommandBeingServiced = (MSG_CommandType)'ClnF'; + MSG_CleanupFolders(pane); + // Of course, that should be "CleanUpFolders" + // Sigh. It's asynchronous. It works in the background. But we may be trying to quit. + Boolean quitting = (CFrontApp::GetApplication()->GetState() == programState_Quitting); + if (quitting) + { + do + { + // progress call will hide wind when done. + // Or user will cancel (and close and delete). This will set mPane to nil. + CFrontApp::GetApplication()->ProcessNextEvent(); + } while (progressWindow->IsVisible() && progressWindow->mPane == pane); + if (progressWindow->mPane == pane) + progressWindow->DoClose(); // close it if it's not closed already + } + + } + catch (...) + { + delete progressWindow; + throw; + } + return progressWindow; +} // CMailProgressWindow::CleanUpFolders + +//----------------------------------- +/*static*/ CMailProgressWindow* CMailProgressWindow::SynchronizeForOffline( + const CStr255& inCommandName, + ResIDT inResID) +//----------------------------------- +{ + CMailProgressWindow* progressWindow = nil; + try + { + // Read the prefs + XP_Bool getNews, getMail, sendMail, getDirectories, goOffline; + PREF_GetBoolPref("offline.download_discussions", &getNews); + PREF_GetBoolPref("offline.download_mail", &getMail); + PREF_GetBoolPref("offline.download_messages", &sendMail); + PREF_GetBoolPref("offline.download_directories", &getDirectories); + PREF_GetBoolPref("offline.offline_after_sync", &goOffline); + + // Synchronize + CMailNewsContext* context = new CMailNewsContext(MWContextMailNewsProgress); + MSG_Pane* pane = MSG_CreateFolderPane(*context, CMailNewsContext::GetMailMaster()); + ThrowIfNULL_(pane); + progressWindow = CreateIndependentWindow(inResID, pane, inCommandName); + ::MSG_SynchronizeOffline(CMailNewsContext::GetMailMaster(), pane, + getNews, getMail, sendMail, getDirectories, goOffline); + } + catch (...) + { + delete progressWindow; + throw; + } + return progressWindow; +} // CMailProgressWindow::SynchronizeForOffline + +//----------------------------------- +/*static*/ CMailProgressWindow* CMailProgressWindow::ToggleOffline( + ResIDT inResID) +//----------------------------------- +{ + CMailProgressWindow* progressWindow = nil; + try + { + CMailNewsContext* context = new CMailNewsContext(MWContextMailNewsProgress); + MSG_Pane* pane = MSG_CreateFolderPane(*context, CMailNewsContext::GetMailMaster()); + ThrowIfNULL_(pane); + progressWindow = CreateIndependentWindow(inResID, pane, "\p", 60); + ::MSG_GoOffline(CMailNewsContext::GetMailMaster(), pane, false, false, false, false); + + // force a menu update to toggle the menu command name + LCommander::SetUpdateCommandStatus(true); + + // display a status msg in all windows + LStr255 offlineString(""); + if (!UOffline::AreCurrentlyOnline()) + offlineString = LStr255(cOfflineListID, cOfflineStrIndex); + CNetscapeWindow::DisplayStatusMessageInAllWindows(offlineString); + } + catch (...) + { + delete progressWindow; + throw; + } + return progressWindow; +} // CMailProgressWindow::ToggleOffline + +//----------------------------------- +/*static*/ MSG_Pane* CMailProgressWindow::JustGiveMeAPane( + const CStr255& inCommandName, + MSG_Pane* inParentPane) +//----------------------------------- +{ + CMailProgressWindow* progressWindow = nil; + MSG_Pane* pane = nil; + try + { + CMailNewsContext* context = new CMailNewsContext(MWContextMailNewsProgress); + pane = ::MSG_CreateProgressPane( + *context, CMailNewsContext::GetMailMaster(), inParentPane); + ThrowIfNULL_(pane); + progressWindow = CreateIndependentWindow(res_ID_modeless, pane, inCommandName); + } + catch (...) + { + delete progressWindow; + throw; + } + return pane; +} // CMailProgressWindow::GoOffline + +//----------------------------------- +CMailProgressWindow::CMailProgressWindow(LStream* inStream) +//----------------------------------- +: CDownloadProgressWindow(inStream) +, mStartTime(::TickCount()) +, mCallbackListener(nil) +, mLastCall(0) +, mPane(nil) +, mParentPane(nil) +, mCancelCallback(nil) +{ + *inStream >> mDelay; + + // Don't list these windows in the window menu (unfortunately, the constructor + // of CMediatedWindow has already added us at this point). So tell Mr. WindowMenu + // that we're dead, dead, dead. Do this by calling Hide(), which has the side-effect + // of causing a broadcast that we've disappeared. The window is currently invisible + // anyway. + Hide(); + //CWindowMenu::sWindowMenu->ListenToMessage(msg_WindowDisposed, this); + + // Watch out for commands that never call us back: we won't have cleaned up + // the previous window until now... + delete sModalMailProgressWindow; + // this static is reassigned in the routine that makes modal ones. +} // CMailProgressWindow::CMailProgressWindow + +//----------------------------------- +CMailProgressWindow::~CMailProgressWindow() +//----------------------------------- +{ + if (sModalMailProgressWindow == this) + sModalMailProgressWindow = nil; + + // if we're the window servicing the get mail request then after we're destroyed, + // someone else can get issue a get mail command too + if ((mCommandBeingServiced == MSG_GetNewMail) && sGettingMail) + sGettingMail = false; + + delete mCallbackListener; + mCallbackListener = nil; + if (mPane) // ie, I made it... + { + // clear mPane so e won't come into this block again. + MSG_Pane* temp = mPane; + mPane = nil; + + // Now let the back end run its course + CNSContext* context = GetWindowContext(); + if (context) + XP_InterruptContext((MWContext*)*context); + ::MSG_DestroyPane(temp); + + // When we delete a progress pane it can affect CommandStatus for commands like GetMail + // since the window has already been activated, FindCommandStatus needs to be called + SetUpdateCommandStatus( true ); + } +} // CMailProgressWindow::~CMailProgressWindow + +//----------------------------------- +ResIDT CMailProgressWindow::GetStatusResID() const +//----------------------------------- +{ + return res_ID_modal; +} // client must provide! + +//----------------------------------- +UInt16 CMailProgressWindow::GetValidStatusVersion() const +//----------------------------------- +{ + return 0x0001; +} // CDownloadProgressWindow::GetValidStatusVersion + +//----------------------------------- +void CMailProgressWindow::FinishCreateSelf() +//----------------------------------- +{ + Inherited::FinishCreateSelf(); + if (HasAttribute(windAttr_Modal)) + { + Assert_(!sModalMailProgressWindow); + sModalMailProgressWindow = this; + } + StartIdling(); +} // CMailProgressWindow::FinishCreateSelf + +//----------------------------------- +void CMailProgressWindow::ListenToPane(MSG_Pane* inPane) +//----------------------------------- +{ + if (!mCallbackListener) + mCallbackListener = new CDownloadListener(this, inPane); + else + mCallbackListener->SetPane(inPane); +} + +//----------------------------------- +void CMailProgressWindow::SpendTime(const EventRecord&) +//----------------------------------- +{ + UInt32 time = ::TickCount(); + // First time, set the time called. + if (mLastCall == 0) + { + mLastCall = time; + return; + } + // If this is not the first call, but we're not getting tickled, close. + if (time > mStartTime + mDelay * 2 && !IsVisible()) + { + // prevent re-entrancy (DoClose can take time, because of callbacks + // from interrupt context.) + StopRepeating(); + DoClose(); + return; + } + // Normal case. Make sure the barber-pole spins. + if (mBar->GetValue() == CProgressBar::eIndefinite) + mBar->Refresh(); +} // CMailProgressWindow::SpendTime + +//----------------------------------- +void CMailProgressWindow::Show() +//----------------------------------- +{ + // Keep it out of the window menu! + StSetBroadcasting saver(CWindowMediator::GetWindowMediator(), false); + Inherited::Show(); +} + +//----------------------------------- +void CMailProgressWindow::ListenToMessage( + MessageT inMessage, + void* ioParam) +//----------------------------------- +{ + const UInt32 kMinTimeBetweenUpdates = 20; + UInt32 time = ::TickCount(); + if (!IsVisible() // ooh, it's been comfy here, invisible, but maybe there's work... + && inMessage != msg_NSCAllConnectionsComplete // heck, I'm not showing up for this... + && time > mStartTime + mDelay) // I only show up for LONG processes. + Show(); + mLastCall = time; + switch (inMessage) + { + case msg_NSCProgressUpdate: + // Ignore that message in mail/news windows (it's ignored on + // Windows too). Otherwise, the progress bar alternates between + // a percent value and a barber pole. + return; + + case msg_NSCAllConnectionsComplete: + if (mParentPane) + { + // send a fake progress-done call to parent pane. This helps with "get new mail" + // because the inbox has to scroll to display it. + FE_PaneChanged( + mParentPane, + PR_TRUE, // async + MSG_PaneProgressDone, + msg_NSCAllConnectionsComplete); + } + Hide(); // So that on idle we'll close. + // This avoids the base class behavior (calling DoClose()) immediately, + // and instead waits till idle time to close. + mDelay = 0 ; // Don't want to wait a long time to destroy the window + StopListening(); // Don't show ourselves on subsequent messages. + ListenToPane(nil); + return; + case msg_Cancel: + if (mCancelCallback && !mCancelCallback()) // result true means "execute default" + return; + break; + } + Inherited::ListenToMessage(inMessage, ioParam); +} // CMailProgressWindow::ListenToMessage + +//----------------------------------- +void CMailProgressWindow::NoteProgressBegin(const CContextProgress& inProgress) +//----------------------------------- +{ + // Base class will clobber the window title with a null string. Don't do that! + if (inProgress.mAction.length()) + SetDescriptor(CStr255(inProgress.mAction)); + mMessage->SetDescriptor(inProgress.mMessage); + mComment->SetDescriptor(inProgress.mComment); +} // CMailProgressWindow::NoteProgressBegin diff --git a/mozilla/cmd/macfe/MailNews/CMailProgressWindow.h b/mozilla/cmd/macfe/MailNews/CMailProgressWindow.h new file mode 100644 index 00000000000..32bed3ff1cd --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMailProgressWindow.h @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMailProgressWindow.h + +#include "CDownloadProgressWindow.h" + +#include "msgcom.h" + +class CMailCallbackListener; +class CMailNewsContext; +class CStr255; +class CContextProgress; + +//----------------------------------- +class CMailProgressWindow : public CDownloadProgressWindow, public LPeriodical +// This class exists because mail stuff does not get the "progress begin/end/update" calls. +// Also, it has a timer, and shows itself automatically after a short delay. It also +// takes itself down if nobody told it to come down after some time. +//----------------------------------- +{ + private: + typedef CDownloadProgressWindow Inherited; + public: + enum { class_ID = 'MPWd', res_ID_modal = 10526, res_ID_modeless = 10527 }; + //-------- + // static + //-------- + static CMailProgressWindow* CreateModalParasite( + ResIDT inResID, + MSG_Pane* inPane, + const CStr255& inCommandName); + static void RemoveModalParasite() { delete sModalMailProgressWindow; } + static CMailProgressWindow* GetModal() { return sModalMailProgressWindow; } + static CMailProgressWindow* ObeyMessageLibraryCommand( + ResIDT inResID, + MSG_Pane* inPane, + MSG_CommandType inCommand, + const CStr255& inCommandName, + MSG_ViewIndex* inSelectedIndices = nil, + int32 inNumIndices = 0); + static CMailProgressWindow* ToggleOffline( + ResIDT inResID = res_ID_modeless); + static CMailProgressWindow* SynchronizeForOffline( + const CStr255& inCommandName, + ResIDT inResID = res_ID_modeless); + static CMailProgressWindow* CleanUpFolders(); + static Boolean GettingMail () { return sGettingMail; } + + static MSG_Pane* JustGiveMeAPane( + const CStr255& inCommandName, + MSG_Pane* inParentPane = nil); + // creates a MSG_Pane attached to + // a modeless progress window. + //-------- + // non-static + //-------- + CMailProgressWindow(LStream* inStream); + virtual ~CMailProgressWindow(); + virtual ResIDT GetStatusResID() const; // client must provide! + virtual UInt16 GetValidStatusVersion() const; // client must provide! + + virtual void Show(); + virtual void ListenToMessage( + MessageT inMessage, + void* ioParam); + virtual void NoteProgressBegin(const CContextProgress& inProgress); + + void ListenToPane(MSG_Pane* inPane); + virtual void SpendTime(const EventRecord&); + void SetDelay(UInt16 delay) { mDelay = delay; } + void SetCancelCallback(Boolean (*f)()) { mCancelCallback = f; } + MSG_CommandType GetCommandBeingServiced() const { return mCommandBeingServiced; } + + protected: + + static CMailProgressWindow* CreateWindow( + ResIDT inResID, + MSG_Pane* inPane, + const CStr255& inCommandName); + static CMailProgressWindow* CreateIndependentWindow( + ResIDT inResID, + MSG_Pane* inPane, + const CStr255& inCommandName, + UInt16 inDelay = 0); + virtual void FinishCreateSelf(); + +//------- +// data +//------- + protected: + + UInt32 mStartTime; // in ticks; + UInt16 mDelay; // ditto + CMailCallbackListener* mCallbackListener; + UInt32 mLastCall; // ticks + MSG_Pane* mPane; // only used for the modeless window. + MSG_Pane* mParentPane; // ditto. + Boolean (*mCancelCallback)(); + MSG_CommandType mCommandBeingServiced; // the command we're handling +static Boolean sGettingMail; // true if an instance of us is getting mail +static CMailProgressWindow* sModalMailProgressWindow; +}; // CMailProgressWindow diff --git a/mozilla/cmd/macfe/MailNews/CMessageAttachmentView.cp b/mozilla/cmd/macfe/MailNews/CMessageAttachmentView.cp new file mode 100644 index 00000000000..1847a421a44 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMessageAttachmentView.cp @@ -0,0 +1,621 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMessageAttachmentView.cp + +#include "CMessageAttachmentView.h" + +#include "UDebugging.h" +#include "PascalString.h" +#include "UGraphicGizmos.h" +#include "xp_str.h" +#define WANT_ENUM_STRING_IDS // why don't we just have a header that does this? +#include "allxpstr.h" +#undef WANT_ENUM_STRING_IDS +#include "UDrawingUtils.h" +#include "CMessageView.h" +#include +#include +#include "CSwatchBrokerView.h" +#include "CURLDispatcher.h" +#include "cstring.h" +#include "CBrowserContext.h" +#include "uerrmgr.h" +#include "resgui.h" +#include "cstring.h" +#include +#include "macutil.h" +#include "ufilemgr.h" +#include "MoreDesktopMgr.h" +#include "CDeviceLoop.h" +#include "CContextMenuAttachment.h" +#include "CBevelView.h" +#include "macutil.h" +#include "miconutils.h" + +const Uint16 kColWidth = 100; +const Uint16 kRowHeight = 50; +const Uint16 kExpandedHeight = 56; + +CMessageAttachmentView::CMessageAttachmentView(LStream* inStream): + Inherited(inStream), LDragAndDrop(GetMacPort(), this), + mAttachmentList(NULL), + mMSGPane(NULL), + mExpandedHeight(kExpandedHeight), + mNumberAttachments(0), + mClickCountToOpen(2), + mAttachmentIconList(nil) +{ + SetUseDragSelect( false ); + mSendDataUPP = NewDragSendDataProc(LDropArea::HandleDragSendData); + Assert_(mSendDataUPP != NULL); + SetUpTableHelpers(); + LWindow* window = LWindow::FetchWindowObject( GetMacPort() ); + if( window ) + mBrokeredView = dynamic_cast(window->FindPaneByID( 'MAtV' ) ); + +} // CMessageAttachmentView::CMessageAttachmentView + +CMessageAttachmentView::~CMessageAttachmentView() +{ + if ( mSendDataUPP != NULL ) + DisposeRoutineDescriptor(mSendDataUPP); + + ClearMessageAttachmentView(); +} // CMessageAttachmentView::~CMessageAttachmentView() + +void CMessageAttachmentView::FinishCreateSelf() +{ + +} // CMessageAttachmentView::FinishCreateSelf + +void CMessageAttachmentView::SetUpTableHelpers() +{ + SetTableGeometry(new LTableMonoGeometry(this, kColWidth, kRowHeight) ); + SetTableSelector(new LTableMultiSelector(this)); +} // CMessageAttachmentView::SetUpTableHelpers + +void CMessageAttachmentView::SetMessageAttachmentList( MSG_Pane* pane, int32 numberAttachments ) +{ + ClearMessageAttachmentView(); + mMSGPane = pane; + if (numberAttachments <= 0) + { + Hide(); + return; + } + mNumberAttachments = numberAttachments; + Assert_( mMSGPane != NULL ); + MWContext* context = MSG_GetContext( mMSGPane ); + mMessageView = dynamic_cast( context->fe.newView ); + Assert_( mMessageView != NULL ); + + XP_Bool isAllAttachments; + MSG_GetViewedAttachments( mMSGPane, &mAttachmentList, &isAllAttachments); + if (mAttachmentList == nil) + { + ClearMessageAttachmentView(); + return; + } + CalculateRowsColumns(); + mAttachmentIconList = new CAttachmentIcon*[mNumberAttachments]; //careful! + + MSG_AttachmentData* attachment = &mAttachmentList[0]; + + for (int i = 0; i < mNumberAttachments; i++, attachment++) + { + CAttachmentIcon *attachIcon = NULL; + + if (attachment->x_mac_creator && attachment->x_mac_type) + { + OSType fileCreator; + OSType fileType; + + // creator and type are 8-byte hex representations... + sscanf(attachment->x_mac_creator, "%X", &fileCreator); + sscanf(attachment->x_mac_type, "%X", &fileType); + + attachIcon = new CAttachmentIcon(fileCreator, fileType); + } + else + { + attachIcon = new CAttachmentIcon(attachment->real_type); + } + + mAttachmentIconList[i] = attachIcon; + } +} // CMessageAttachmentView::SetMessageAttachmentList + + + +void CMessageAttachmentView::ClearMessageAttachmentView() +{ + if (mAttachmentIconList) + { + for (int i = 0; i < mNumberAttachments; i++) + { + CAttachmentIcon *thisAttachmentIcon = mAttachmentIconList[i]; + delete thisAttachmentIcon; + } + delete mAttachmentIconList; + mAttachmentIconList = NULL; + } + + if (mMSGPane && mAttachmentList) + { + MSG_FreeAttachmentList( mMSGPane, mAttachmentList); + } + mAttachmentList = NULL; + mMSGPane = NULL; + mNumberAttachments = 0; +} // CMessageAttachmentView::ClearMessageAttachmentView + +void CMessageAttachmentView::OpenSelection( Int32 action) +{ + STableCell selectedCell( 0,0); + while ( GetNextSelectedCell(selectedCell) ) + { + Int32 attachmentIndex = CalculateAttachmentIndex (selectedCell ); + const char* url = mAttachmentList[ attachmentIndex ].url; + URL_Struct* theURL = NET_CreateURLStruct( url, NET_DONT_RELOAD); + ThrowIfNULL_(theURL); + HandleURL(theURL, action ); + } +} // CMessageAttachmentView::OpenSelection() + +void CMessageAttachmentView::HandleURL( URL_Struct* inURL, int32 action ) +{ + CBrowserContext* browserContext = mMessageView->GetContext(); + if( browserContext ) + { + cstring theReferer = browserContext->GetCurrentURL(); + if (theReferer.length() > 0) + inURL->referer = XP_STRDUP(theReferer); + inURL->window_target = NULL; + if( action == FO_SAVE_AS ) + { + CStr31 fileName; + fe_FileNameFromContext(*browserContext, inURL->address, fileName); + StandardFileReply reply; + ::StandardPutFile(GetPString(SAVE_AS_RESID), fileName, &reply); + if (reply.sfGood) + { + CURLDispatcher::DispatchToStorage( inURL, reply.sfFile, FO_SAVE_AS, false); + } + } + else + mMessageView->DispatchURL( inURL, browserContext, false, false, action); + } +} // CMessageAttachmentView::HandleURL + +void CMessageAttachmentView::ResizeFrameBy( + Int16 inWidthDelta, + Int16 inHeightDelta, + Boolean inRefresh) +{ + LTableView::ResizeFrameBy( inWidthDelta, inHeightDelta, inRefresh); + CalculateRowsColumns(); +} // CMessageAttachmentView::ResizeFrameBy + +void CMessageAttachmentView::ToggleVisibility() +{ + if ( IsVisible() ) + Hide(); + else + Show(); +} // CMessageAttachmentView::ToggleVisibility + +void CMessageAttachmentView::Hide() +{ + if( mBrokeredView ) + { + if( mBrokeredView->IsVisible() ) + Remove(); + } +} // CMessageAttachmentView::Hide + +void CMessageAttachmentView::Show() +{ + + if ( mBrokeredView ) + { + if( !mBrokeredView->IsVisible() && mNumberAttachments ) + { + mBrokeredView->Show(); + mBrokeredView->MoveBy( 0, -mExpandedHeight, true ); + mBrokeredView->ResizeFrameBy(0, mExpandedHeight, true ); + CBevelView::SubPanesChanged(this, true); + Rect portRect; + if ( CalcPortFrameRect( portRect ) ) + ::EraseRect( &portRect ); + + } + } +} // CMessageAttachmentView::Show() + +void CMessageAttachmentView::Remove() +{ + if ( mBrokeredView ) + { + if ( mBrokeredView->IsVisible() ) + { + mBrokeredView->ResizeFrameBy(0, -mExpandedHeight, true); + mBrokeredView->MoveBy( 0, mExpandedHeight, false ); + mBrokeredView->Hide(); + } + } +} // CMessageAttachmentView::Remove + +void CMessageAttachmentView::FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +{ + switch (inCommand) + { + //case cmd_GetInfo: + // outEnabled = GetSelectedRowCount() == 1; + // break; + case cmd_SaveAs: + STableCell dummyCell( 0,0 ); + outEnabled = GetNextSelectedCell( dummyCell); + break; + case cmd_SelectAll: + outEnabled = true; + break; + default: + LCommander::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); + break; + } +} // CCMessageAttachmentView::FindCommandStatus + + +Boolean CMessageAttachmentView::ObeyCommand( + CommandT inCommand, + void *ioParam) +{ + Boolean result = false; + switch(inCommand) + { + case cmd_SaveAs: + OpenSelection( FO_SAVE_AS ); + return true; + + case cmd_Open: + OpenSelection( FO_CACHE_AND_PRESENT ); + return true; + + default: + return mMessageView->ObeyCommand( inCommand, ioParam ); + } +} // CMessageAttachmentView::ObeyCommand + +void CMessageAttachmentView::HiliteCell( + const STableCell &inCell, + Boolean /* inHilite */ ) + +{ + Rect cellRect; + if ( GetLocalCellRect( inCell, cellRect) ) + { + FocusDraw(); + DrawCell( inCell, cellRect ); + } +} // CMessageAttachmentView::HiliteCell + +void CMessageAttachmentView::HiliteSelection( + Boolean /* inActively */, + Boolean inHilite ) +{ + STableCell theCell; + + while (GetNextSelectedCell(theCell)) + { + if( !inHilite ) + UnselectCell( theCell ); + HiliteCell( theCell, inHilite ); + } +} + +Boolean CMessageAttachmentView::ClickSelect( + const STableCell &inCell, + const SMouseDownEvent &inMouseDown) +{ + + // select the cell + if (LTableView::ClickSelect(inCell, inMouseDown)) + { + // Handle it ourselves if the popup attachment doesn't want it. + CContextMenuAttachment::SExecuteParams params; + params.inMouseDown = &inMouseDown; + if (ExecuteAttachments(CContextMenuAttachment::msg_ContextMenu, (void*)¶ms)) + { + // drag ? - don't become target + if ( ::WaitMouseMoved(inMouseDown.macEvent.where)) + DragSelection(inCell, inMouseDown); + else + { + // become target + if (!IsTarget()) + SwitchTarget(this); + // open selection if we've got the right click count + if (GetClickCount() == mClickCountToOpen) + OpenSelection( FO_CACHE_AND_PRESENT ); + } + } + return true; + } + return false; +} // CMessageAttachmentView::ClickSelect + + + +void CMessageAttachmentView::DragSelection( + const STableCell& /*inCell*/, + const SMouseDownEvent& inMouseDown ) +{ + + DragReference dragRef = 0; + + if (!FocusDraw()) return; + + try + { + //StRegion dragRgn; + OSErr err; + + CBrowserDragTask attachmentDrag( inMouseDown.macEvent ); + + AddSelectionToDrag( attachmentDrag.GetDragReference() , attachmentDrag.GetDragRegion() ); + err = ::SetDragSendProc( attachmentDrag.GetDragReference() , mSendDataUPP, (LDragAndDrop*)this); + attachmentDrag.DoDrag(); + } + catch( ... ) {} +} //CMessageAttachmentView::DragSelection + +void CMessageAttachmentView::AddSelectionToDrag(DragReference /* inDragRef */, RgnHandle inDragRgn) +{ + StRegion tempRgn; + STableCell cell; + Rect cellRect; + + ::SetEmptyRgn(inDragRgn); + + cell.col = 1; + cell.row = 0; + while (GetNextSelectedCell(cell)) + { + if (GetLocalCellRect(cell, cellRect)) + { + Int32 attachmentIndex = CalculateAttachmentIndex( cell ); + + /* + ::LocalToGlobal(&(topLeft(cellRect))); + ::LocalToGlobal(&(botRight(cellRect))); + ::RectRgn(tempRgn, &cellRect); + ::UnionRgn(tempRgn, inDragRgn, inDragRgn); + */ + CAttachmentIcon *attachmentIcon = mAttachmentIconList[attachmentIndex]; + + if (attachmentIcon) + { + ::SetEmptyRgn(tempRgn); + + attachmentIcon->AddIconOutlineToRegion(tempRgn, CAttachmentIcon::kIconSizeLarge); + + int16 horizontalOffset = ( cellRect.right - cellRect.left - 32 ) / 2; + Rect localRect = cellRect; + ::LocalToGlobal(&(topLeft(cellRect))); + + OffsetRgn(tempRgn, horizontalOffset + localRect.left + cellRect.left - localRect.left, localRect.top + (cellRect.top - localRect.top)); + + ::UnionRgn(tempRgn, inDragRgn, inDragRgn); + } + } + } + + ::CopyRgn(inDragRgn, tempRgn); + ::InsetRgn(tempRgn, 1, 1); + ::DiffRgn(inDragRgn, tempRgn, inDragRgn); +} + +void CMessageAttachmentView::DoDragSendData(FlavorType inFlavor, + ItemReference inItemRef, + DragReference inDragRef) +{ + OSErr theErr; + cstring theUrl; + STableCell selectedCell( 0,0); + switch( inFlavor ) + { + case 'TEXT': + while ( GetNextSelectedCell(selectedCell) ) + { + Int32 attachmentIndex = CalculateAttachmentIndex (selectedCell ); + const char* url = mAttachmentList[ attachmentIndex ].url; + theErr = ::SetDragItemFlavorData(inDragRef, inItemRef, inFlavor, url, strlen(url), 0); + ThrowIfOSErr_ (theErr); + } + break; + + case emBookmarkFileDrag: + // Get the target drop location + AEDesc dropLocation; + + theErr = ::GetDropLocation(inDragRef, &dropLocation); + if (theErr != noErr) + return; + + // Get the directory ID and volume reference number from the drop location + SInt16 volume; + SInt32 directory; + + theErr = GetDropLocationDirectory(&dropLocation, &directory, &volume); + + // Ok, this is a hack, and here's why: This flavor type is sent with the FlavorFlag 'flavorSenderTranslated' which + // means that this send data routine will get called whenever someone accepts this flavor. The problem is that + // it is also called whenever someone calls GetFlavorDataSize(). This routine assumes that the drop location is + // something HFS related, but it's perfectly valid for something to query the data size, and not be a HFS + // derrivative (like the text widget for example). + // So, if the coercion to HFS thingy fails, then we just punt to the textual representation. + if (theErr == errAECoercionFail) + { + theErr = ::SetDragItemFlavorData(inDragRef, inItemRef, inFlavor, theUrl, strlen(theUrl), 0); + return; + } + + if (theErr != noErr) + return; + + // Combine with the unique name to make an FSSpec to the new file + FSSpec prototypeFilespec; + FSSpec locationSpec; + prototypeFilespec.vRefNum = volume; + prototypeFilespec.parID = directory; + + // Save the selection + while ( GetNextSelectedCell( selectedCell ) ) + { + MSG_AttachmentData* attachment = &mAttachmentList[ CalculateAttachmentIndex( selectedCell ) ]; + char* fileName = CFileMgr::MacPathFromUnixPath(attachment->real_name ); + if ( fileName ) + { + theErr = CFileMgr::UniqueFileSpec( prototypeFilespec, fileName , locationSpec ); + if (theErr && theErr != fnfErr) // need a unique name, so we want fnfErr! + ThrowIfOSErr_(theErr); + Int32 attachmentIndex = CalculateAttachmentIndex (selectedCell ); + const char* url = mAttachmentList[ attachmentIndex ].url; + URL_Struct* theURL = NET_CreateURLStruct( url, NET_DONT_RELOAD ); + theErr = ::SetDragItemFlavorData( inDragRef, inItemRef, inFlavor, &locationSpec, sizeof(FSSpec), 0 ); + ThrowIfOSErr_(theErr); + ThrowIfNULL_(theURL); + CURLDispatcher::DispatchToStorage( theURL, locationSpec, FO_SAVE_AS, true ); + XP_FREE( fileName ); + } + } + break; + } +} //CMessageAttachmentView::AddSelectionToDrag + + +void CMessageAttachmentView::DrawSelf() +{ + // This function is similar to what we had when the "Erase On Update" + // LWindow attribute was set in Constructor. This flag has been removed + // because it created a lot of flickers when browsing mails. + // The other objects in the Thread window continued to behave correctly + // but the CThreadView showed some update problems. Instead of fixing + // them as we are supposed to (ie. by invalidating and erasing only what + // needs to be redrawn), I prefered to emulate the way it used to work + // when "Erase On Update" was set. My apologies for this easy solution + // but we have something to ship next week. + + + // erase everything + ApplyForeAndBackColors(); + Rect frame; + CalcLocalFrameRect(frame); + ::EraseRect(&frame); + + // redraw everything + Inherited::DrawSelf(); +}// CMessageAttachmentView::DrawSelf() + + +void CMessageAttachmentView::DrawCell( const STableCell &inCell, const Rect &inLocalRect ) +{ + const Int32 kIconSize = 32; + Int32 attachmentIndex = CalculateAttachmentIndex( inCell ); + if( attachmentIndex < 0 || attachmentIndex >= mNumberAttachments ) + return ; + + MSG_AttachmentData& attachment = mAttachmentList[attachmentIndex]; + + char* attachmentName = NULL; + + if (attachment.real_name != NULL) + attachmentName = CFileMgr::MacPathFromUnixPath(attachment.real_name ); + else + attachmentName = XP_STRDUP( XP_GetString(XP_MSG_NONE)); + + Uint32 nameLength; + if( attachmentName ) + nameLength= XP_STRLEN(attachmentName); + + // file icon + Rect iconRect; + int16 horizontalOffset = ( inLocalRect.right - inLocalRect.left - kIconSize )/2; + iconRect.left = inLocalRect.left + horizontalOffset; + iconRect.right = iconRect.left + kIconSize; + iconRect.top = inLocalRect.top + 2; + iconRect.bottom = iconRect.top + kIconSize; + IconTransformType transformType = CellIsSelected(inCell) ? kTransformSelected : kTransformNone; + + CAttachmentIcon *attachmentIcon = mAttachmentIconList[attachmentIndex]; + + if (attachmentIcon != NULL) + attachmentIcon->PlotIcon(iconRect, kAlignNone, transformType); + + // file name and yes, you can have attachments without names + if( attachmentName != NULL ) + { + Rect textRect = inLocalRect; + textRect.left+=4; + textRect.top = iconRect.bottom; + FontInfo fontInfo; + UTextTraits::SetPortTextTraits( 130 ); + ::GetFontInfo(&fontInfo); + UGraphicGizmos::PlaceTextInRect( + attachmentName, nameLength, textRect, teCenter, teCenter, &fontInfo, true, truncMiddle); + XP_FREE( attachmentName ); + } +} // CMessageAttachmentView::DrawCell + +void CMessageAttachmentView::CalculateRowsColumns() +{ + SDimension16 frameSize; + GetFrameSize(frameSize); + if( mNumberAttachments <= 0 ) + return; + int16 numCols = frameSize.width / kColWidth; + if ( numCols == 0 ) + numCols = 1; // if the only cell is a fractionally visible cell, use it + if ( numCols > mNumberAttachments ) + numCols = mNumberAttachments; + + int16 numRows = mNumberAttachments / numCols; + if ( mNumberAttachments % numCols ) + numRows ++; + // Remove old table geometry + RemoveRows( mRows,0, false ); + RemoveCols (mCols, 0, false ); + // Insert the new geometry + InsertCols( numCols, 1, NULL, 0, false); + InsertRows( numRows, 1, NULL, 0, true ); +} // CMessageAttachmentView::CalculateRowsColumns() + +Int32 CMessageAttachmentView::CalculateAttachmentIndex( STableCell inCell) +{ + Int32 attachmentIndex = ( (inCell.row-1)*(mCols) + inCell.col -1 ); // Attachment Lists are zero based + Assert_( attachmentIndex >= 0 ); + return attachmentIndex; +} // CMessageAttachmentView::CalculateAttachmentIndex diff --git a/mozilla/cmd/macfe/MailNews/CMessageAttachmentView.h b/mozilla/cmd/macfe/MailNews/CMessageAttachmentView.h new file mode 100644 index 00000000000..6c4a74ec234 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMessageAttachmentView.h @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMessageAttachmentView.h + +#pragma once +#include "LTableView.h" +#include "msgcom.h" +#include + + +class CMessageView; +class CBrokeredView; +class CAttachmentIcon; + + +class CMessageAttachmentView: public LTableView, public LCommander, public LDragAndDrop +{ + +private: + typedef LTableView Inherited; +public: + enum { class_ID = 'MATv' }; + CMessageAttachmentView(LStream* inStream); + + virtual ~CMessageAttachmentView(); + virtual void FinishCreateSelf(); + virtual void SetUpTableHelpers(); + + void SetMessageAttachmentList( MSG_Pane* pane, int32 numberAttachments ); + void ClearMessageAttachmentView(); + virtual void OpenSelection( int32 action ); + virtual void HandleURL( URL_Struct* inURL, int32 action ); + +// LPane + virtual void ResizeFrameBy( + Int16 inWidthDelta, + Int16 inHeightDelta, + Boolean inRefresh); + void ToggleVisibility(); + virtual void Hide(); + virtual void Show(); + void Remove(); + + +// LCommander + virtual void FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName); + virtual Boolean ObeyCommand( CommandT inCommand, void *ioParam ); + +// LTableView + virtual void HiliteSelection( + Boolean inActively, + Boolean inHilite); + virtual void HiliteCell( + const STableCell &inCell, + Boolean inHilite); + virtual Boolean ClickSelect( + const STableCell &inCell, + const SMouseDownEvent &inMouseDown); + +// LDragandDrop +// ------------------------------------------------------------ + virtual void DragSelection(const STableCell& /*inCell*/, const SMouseDownEvent &inMouseDown); + virtual void AddSelectionToDrag(DragReference inDragRef, RgnHandle inDragRgn); + virtual void DoDragSendData(FlavorType inFlavor, + ItemReference inItemRef, + DragReference inDragRef); +protected: + virtual void DrawSelf(); + virtual void DrawCell( const STableCell &inCell, const Rect &inLocalRect ); + void CalculateRowsColumns(); + Int32 CalculateAttachmentIndex( STableCell inCell); + + CBrokeredView* mBrokeredView; + MSG_Pane *mMSGPane; + CMessageView *mMessageView; + Int16 mExpandedHeight; + Int16 mClickCountToOpen; + + MSG_AttachmentData *mAttachmentList; + Int32 mNumberAttachments; + CAttachmentIcon **mAttachmentIconList; + DragSendDataUPP mSendDataUPP; +}; diff --git a/mozilla/cmd/macfe/MailNews/CMessageFolder.cp b/mozilla/cmd/macfe/MailNews/CMessageFolder.cp new file mode 100644 index 00000000000..a9ba01d2e2a --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMessageFolder.cp @@ -0,0 +1,469 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMessageFolder.cp + +#include "CMessageFolder.h" + +#include "MailNewsCallbacks.h" +#include "CMailNewsContext.h" + +//====================================== +#pragma mark --- CCachedFolderLine +//====================================== + +#if 0 +//====================================== +class CMessageFolderListener : public CMailCallbackListener +//====================================== +{ +public: + CMessageFolderListener() { sMessageFolderListener = this; } + ~CMessageFolderListener() { sMessageFolderListener = nil; } + virtual void PaneChanged( + MSG_Pane* inPane, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value); // must always be supported. + static CMessageFolderListener* sMessageFolderListener; +}; +#endif // 0 + +//====================================== +class CFolderLineComparator : public LComparator +//====================================== +{ + virtual Int32 Compare( + const void* inItemOne, + const void* inItemTwo, + Uint32 inSizeOne, + Uint32 inSizeTwo) const; + virtual Int32 CompareToKey( + const void* inItem, + Uint32 /* inSize */, + const void* inKey) const; +}; // class CFolderLineComparator + +LArray* CCachedFolderLine::sLineList = nil; + +//---------------------------------------------------------------------------------------- +Int32 CFolderLineComparator::Compare( + const void* inItemOne, + const void* inItemTwo, + Uint32 /*inSizeOne*/, + Uint32 /*inSizeTwo*/) const +//---------------------------------------------------------------------------------------- +{ + return (Int32)(*(const CCachedFolderLine**)inItemOne)->mFolderLine.id + - (Int32)(*(const CCachedFolderLine**)inItemTwo)->mFolderLine.id; +} + +//---------------------------------------------------------------------------------------- +Int32 CFolderLineComparator::CompareToKey( + const void* inItem, + Uint32 /* inSize */, + const void* inKey) const +//---------------------------------------------------------------------------------------- +{ + return (Int32)(*(CCachedFolderLine**)inItem)->mFolderLine.id + - (Int32)(const MSG_FolderInfo*)inKey; +} + +//---------------------------------------------------------------------------------------- +CCachedFolderLine::CCachedFolderLine(MSG_Pane* inFolderPane, MSG_FolderInfo* inID) +//---------------------------------------------------------------------------------------- +: mRefCount(0) +{ + mFolderLine.id = inID; // set this up for the FolderInfoChanged() call. + FolderInfoChanged(inFolderPane); + // Listen for changes to the folder info. + // if (!CMessageFolderListener::sMessageFolderListener) + // new CMessageFolderListener; + if (!sLineList) + { + // Initialize the sorted array + sLineList = new LArray(sizeof(CCachedFolderLine*), new CFolderLineComparator, true); + sLineList->AdjustAllocation(50); // Allow for 50 items initially + } + sLineList->InsertItemsAt(1, 0/*ignored*/, &this); +} // CCachedFolderLine::CCachedFolderLine + +//---------------------------------------------------------------------------------------- +CCachedFolderLine::~CCachedFolderLine() +//---------------------------------------------------------------------------------------- +{ + if (sLineList) + { + sLineList->Remove(&this); + // When the last cached folderline goes, no need to have a listener. + if (sLineList->GetCount() == 0) + { + delete sLineList; + sLineList = nil; +// delete CMessageFolderListener::sMessageFolderListener; + } + } +} // CCachedFolderLine::~CCachedFolderLine + +//---------------------------------------------------------------------------------------- +void CCachedFolderLine::FolderInfoChanged(MSG_Pane* inFolderPane) +//---------------------------------------------------------------------------------------- +{ + MSG_FolderInfo* id = mFolderLine.id; // this is non-volatile + if (id) + { + MSG_ViewIndex index = MSG_VIEWINDEXNONE; + if (inFolderPane && ::MSG_GetPaneType(inFolderPane) == MSG_FOLDERPANE) + { + // Only relative to a pane can we get the "elided" bit right, to tell + // whether or not the twistie is open. + index = ::MSG_GetFolderIndex(inFolderPane, id); + } + if (index != MSG_VIEWINDEXNONE) + ::MSG_GetFolderLineByIndex(inFolderPane, index, 1, &mFolderLine); + else + ::MSG_GetFolderLineById(CMailNewsContext::GetMailMaster(), id, &mFolderLine); + } + else + { + // There is a hack in which we add a NULL MSG_FolderInfo* to the cache. + // Rather than call msglib to initialize the folder line (which will cause + // an assert) just zero out the folderline ourselves. + memset(&mFolderLine, 0, sizeof(MSG_FolderLine)); + } + Assert_(id==mFolderLine.id); +} + +//---------------------------------------------------------------------------------------- +void CMessageFolder::FolderLevelChanged(MSG_Pane* inFolderPane) +// Calls FolderInfoChanged on this and all its descendents. +//---------------------------------------------------------------------------------------- +{ + FolderInfoChanged(inFolderPane); + UInt32 childCount = CountSubFolders(); + if (childCount) + { + typedef MSG_FolderInfo* mfip; + try + { + MSG_FolderInfo** childList = new mfip[childCount]; // throws... + ::MSG_GetFolderChildren( + CMailNewsContext::GetMailMaster(), + GetFolderInfo(), + childList, + childCount); + MSG_FolderInfo** child = &childList[0]; + for (SInt32 j = 0; j < childCount; j++, child++) + { + CMessageFolder childFolder(*child, inFolderPane); + childFolder.FolderLevelChanged(inFolderPane); + } + delete [] childList; + } + catch(...) + { + } + } +} // CMessageFolder::FolderLevelChanged + +//---------------------------------------------------------------------------------------- +void CCachedFolderLine::FolderInfoChanged(MSG_Pane* inFolderPane, MSG_FolderInfo* inID) +//---------------------------------------------------------------------------------------- +{ + CCachedFolderLine* folder = FindFolderFor(inID); + if (folder) + folder->FolderInfoChanged(inFolderPane); +} + +//---------------------------------------------------------------------------------------- +CCachedFolderLine* CCachedFolderLine::FindFolderFor(MSG_FolderInfo* inID) +//---------------------------------------------------------------------------------------- +{ + if (!sLineList) + return nil; + CCachedFolderLine* result = nil; + ArrayIndexT arrayIndex = sLineList->FetchIndexOfKey(inID); + if (arrayIndex != LArray::index_Bad) + { + sLineList->FetchItemAt(arrayIndex, &result); + } + return result; +} + +//---------------------------------------------------------------------------------------- +CCachedFolderLine* CCachedFolderLine::GetFolderFor(MSG_Pane* inFolderPane, MSG_FolderInfo* inID) +//---------------------------------------------------------------------------------------- +{ + CCachedFolderLine* folderLine = FindFolderFor(inID); + if (!folderLine) + folderLine = new CCachedFolderLine(inFolderPane, inID); + return folderLine; +} + +//---------------------------------------------------------------------------------------- +CCachedFolderLine* CCachedFolderLine::GetFolderFor(MSG_Pane* inFolderPane, MSG_ViewIndex inIndex) +//---------------------------------------------------------------------------------------- +{ + MSG_FolderInfo* id = ::MSG_GetFolderInfo(inFolderPane, inIndex); + CCachedFolderLine* folderLine = FindFolderFor(id); + if (!folderLine) + folderLine = new CCachedFolderLine(inFolderPane, id); + return folderLine; +} + +#if 0 +//====================================== +#pragma mark --- CMessageFolderListener +//====================================== + +CMessageFolderListener* CMessageFolderListener::sMessageFolderListener = nil; + +//---------------------------------------------------------------------------------------- +void CMessageFolderListener::PaneChanged( + MSG_Pane* inPane, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value) +//---------------------------------------------------------------------------------------- +{ + if (inNotifyCode == MSG_PaneNotifyFolderInfoChanged) + CCachedFolderLine::FolderInfoChanged((MSG_FolderInfo*)value); +} +#endif // 0 + +//====================================== +#pragma mark --- CMessageFolder +//====================================== + +//---------------------------------------------------------------------------------------- +CMessageFolder::CMessageFolder(TableIndexT inRow, MSG_Pane* inFolderList) +//---------------------------------------------------------------------------------------- +{ + mCachedFolderLine = CCachedFolderLine::GetFolderFor(inFolderList, inRow - 1); + mCachedFolderLine->AddUser(this); +} + +//---------------------------------------------------------------------------------------- +CMessageFolder::CMessageFolder(const MSG_FolderInfo* id, MSG_Pane* inFolderPane) +//---------------------------------------------------------------------------------------- +{ + mCachedFolderLine = CCachedFolderLine::GetFolderFor(inFolderPane, (MSG_FolderInfo*)id); + mCachedFolderLine->AddUser(this); +} + +//---------------------------------------------------------------------------------------- +CMessageFolder::CMessageFolder(const CMessageFolder& inFolder) +//---------------------------------------------------------------------------------------- +{ + mCachedFolderLine = inFolder.mCachedFolderLine; + mCachedFolderLine->AddUser(this); +} + +//---------------------------------------------------------------------------------------- +CMessageFolder::~CMessageFolder() +//---------------------------------------------------------------------------------------- +{ + mCachedFolderLine->RemoveUser(this); +} + +//---------------------------------------------------------------------------------------- +void CMessageFolder::operator=(const CMessageFolder& other) +//---------------------------------------------------------------------------------------- +{ + if (!(*this == other)) // uses operator == + { + mCachedFolderLine->RemoveUser(this); + mCachedFolderLine = other.mCachedFolderLine; + mCachedFolderLine->AddUser(this); + } +} + +//---------------------------------------------------------------------------------------- +MSG_FolderInfo* CMessageFolder::GetFolderInfo() const +//---------------------------------------------------------------------------------------- +{ + return mCachedFolderLine->mFolderLine.id; +} + +//---------------------------------------------------------------------------------------- +void CMessageFolder::SetFolderInfo(const MSG_FolderInfo* id, MSG_Pane* inFolderPane) +//---------------------------------------------------------------------------------------- +{ + if (id != GetFolderInfo()) + { + mCachedFolderLine->RemoveUser(this); + mCachedFolderLine = CCachedFolderLine::GetFolderFor(inFolderPane, (MSG_FolderInfo*)id); + mCachedFolderLine->AddUser(this); + } +} + +//---------------------------------------------------------------------------------------- +MSG_Pane* CMessageFolder::GetThreadPane() const +//---------------------------------------------------------------------------------------- +{ + return ::MSG_FindPaneOfType( + CMailNewsContext::GetMailMaster(), + GetFolderInfo(), + MSG_THREADPANE); +} + +//---------------------------------------------------------------------------------------- +MSG_Pane* CMessageFolder::GetFolderPane() +//---------------------------------------------------------------------------------------- +{ + return ::MSG_FindPane( + nil, // Don't care about context matching + MSG_FOLDERPANE); +} + +//---------------------------------------------------------------------------------------- +void CMessageFolder::FolderInfoChanged(MSG_Pane* inFolderPane) +//---------------------------------------------------------------------------------------- +{ + mCachedFolderLine->FolderInfoChanged(inFolderPane); +} + +//---------------------------------------------------------------------------------------- +Int32 CMessageFolder::CountMessages() const +//---------------------------------------------------------------------------------------- +{ + if (mCachedFolderLine->mFolderLine.unseen < 0) + return -1; // hack: this indicates unknown. + return mCachedFolderLine->mFolderLine.total; +} + +//---------------------------------------------------------------------------------------- +UInt32 CMessageFolder::GetDeletedBytes() const +//---------------------------------------------------------------------------------------- +{ + if (mCachedFolderLine->mFolderLine.deletedBytes < 0) + return -1; // hack: this indicates unknown. + return mCachedFolderLine->mFolderLine.deletedBytes; +} + +// +// Icon suite IDs +// +enum +{ + + kNormalMessageFolderIconID = 15238 +, kInboxMessageFolderIconID = 15240 +, kInboxWithNewMailMessageFolderIconID = 15242 +, kOutboxMessageFolderIconID = 15244 +, kSentMailMessageFolderIconID = 15246 +, kDraftsMessageFolderIconID = 15248 +, kRemoteMessageFolderIconID = 15250 +, kEmptyTrashMessageFolderIcon = 270 +, kNonEmptyTrashMessageFolderIcon = 270 + + // Icons for message folders. These need to be in a menu, so their numbers are 256+ + // Add 1 for the "open" state (except for trash) + +, kMailServerFolderIconID = 15225 + // Icons for news groups/hosts +, kNewsHostFolderIconID = 15227 +, kNewsGroupFolderIconID = 15227 + +}; + + + +static ResIDT gIconIDTable[] = { +//---------------------------------------------------------------------------------------- +//NAME READ UNREAD +// LOCAL ONLINE LOCAL ONLINE +// DEFAULT SPECIAL DEFAULT SPECIAL DEFAULT SPECIAL DEFAULT SPECIAL +//---------------------------------------------------------------------------------------- +/* Folder*/ 15238 , 15238 , 15250 , 15250 , 15238 , 15394 , 15250 , 15394 , +/* Inbox */ 15240 , 15240 , 15240 , 15240 , 15242 , 15242 , 15242 , 15242 , +/* Outbox*/ 15244 , 15244 , 15244 , 15244 , 15244 , 15244 , 15244 , 15244 , +/* Sent */ 15246 , 15246 , 15246 , 15246 , 15246 , 15246 , 15246 , 15246 , +/* Drafts*/ 15248 , 15248 , 15248 , 15248 , 15248 , 15248 , 15248 , 15248 , +/* Trash */ 15252 , 15252 , 15252 , 15252 , 15252 , 15252 , 15252 , 15252 , +/* Newsgrp*/ 0 , 0 , 15231 , 15233 , 0 , 0 , 15231 , 15232 +}; + +enum { + kSpecial = 1 // offset +, kOnline = 2 // offset +, kNewMessages = 4 // offset +, kKindsPerRow = 8 +, kIconBaseIx = 0 // SPECIAL = Got new +, kInboxIx = 1 * kKindsPerRow // SPECIAL = Got new +, kOutboxIx = 2 * kKindsPerRow // SPECIAL = OCCUPIED +, kSentIx = 3 * kKindsPerRow +, kDraftsIx = 4 * kKindsPerRow +, kTrashIx = 5 * kKindsPerRow // SPECIAL = OCCUPIED +, kNewsGroupIx = 6 * kKindsPerRow // SPECIAL = SUBSCRIBED +}; + +//---------------------------------------------------------------------------------------- +ResIDT CMessageFolder::GetIconID() const +// To do: deal with the "Open" state, the "read" state, etc. +//---------------------------------------------------------------------------------------- +{ + UInt32 folderFlags = this->GetFolderType(); + if (GetLevel() == kRootLevel) + { + if ((folderFlags & MSG_FOLDER_FLAG_NEWS_HOST)) + return kNewsHostFolderIconID; // li'l computer + return kMailServerFolderIconID; // li'l computer + } + short iconIndex = kIconBaseIx; + switch (folderFlags & ~(MSG_FOLDER_FLAG_MAIL | MSG_FOLDER_FLAG_IMAPBOX)) + { + case MSG_FOLDER_FLAG_NEWSGROUP: + iconIndex = kNewsGroupIx + kOnline; // always online! + if (this->IsSubscribedNewsgroup()) + iconIndex += kSpecial; // special = subscribed + break; + case MSG_FOLDER_FLAG_TRASH: + iconIndex = kTrashIx; + if (this->CountMessages() != 0) + iconIndex += kSpecial; // special = nonempty + break; + case MSG_FOLDER_FLAG_SENTMAIL: + iconIndex = kSentIx; + break; + case MSG_FOLDER_FLAG_TEMPLATES: + case MSG_FOLDER_FLAG_DRAFTS: + iconIndex = kDraftsIx; + break; + case MSG_FOLDER_FLAG_QUEUE: + iconIndex = kOutboxIx; + if (this->CountMessages() != 0) + iconIndex += kSpecial; // special = nonempty + break; + case MSG_FOLDER_FLAG_INBOX: + iconIndex = kInboxIx; + if (this->HasNewMessages()) + iconIndex += kSpecial; // special = got new messages + break; + default: + iconIndex = kIconBaseIx; + if (this->HasNewMessages()) + iconIndex += kSpecial; // special = got new messages + } + if (this->HasNewMessages() ) + iconIndex += kNewMessages; + if ((folderFlags & MSG_FOLDER_FLAG_IMAPBOX) != 0) + iconIndex += kOnline; + return gIconIDTable[iconIndex]; +} // CMessageFolder::GetIconID + diff --git a/mozilla/cmd/macfe/MailNews/CMessageFolder.h b/mozilla/cmd/macfe/MailNews/CMessageFolder.h new file mode 100644 index 00000000000..c6bcbfa6cf3 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMessageFolder.h @@ -0,0 +1,322 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +#pragma once + +enum +{ kRootLevel = 1 // news host, local mail etc. +, kSpecialFolderLevel = 2 // inbox, sent, drafts... +}; + +#include // for TableIndexT +#include "msgcom.h" + +struct CCachedFolderLine; // forward + +//======================================================================================== +class CMessageFolder +//======================================================================================== +{ +public: + CMessageFolder(TableIndexT inRow, MSG_Pane* inFolderPane); + // In the next constructor (and similar functions that + // take a MSG_FolderInfo*, there is a MSG_Pane* param + // which is optional. Supplying the pane will allow + // the ELIDED flag to work properly, because it is + // a property of the "view" and not an intrinsic property + // of the folder itself. + CMessageFolder( + const MSG_FolderInfo* id = nil, + MSG_Pane* inFolderPane = nil); + // inFolderPane can be nil, as can id. + CMessageFolder(const CMessageFolder& inFolder); + ~CMessageFolder(); + + void operator=(const CMessageFolder& other); + Boolean operator==(const CMessageFolder& other) const; + operator MSG_FolderInfo*() const { return GetFolderInfo(); } + void SetFolderInfo(const MSG_FolderInfo* id, MSG_Pane* inFolderPane = nil); + // inFolderPane can be nil, as can id. + void FolderInfoChanged(MSG_Pane* inFolderPane = nil); + void FolderLevelChanged(MSG_Pane* inFolderPane = nil); + // calls FolderInfoChanged on children, too. + // inFolderPane can be nil. + UInt32 GetFolderType() const; + const char* GetName() const; + const char* GetPrettyName() const; + UInt32 GetLevel() const; + SInt32 CountMessages() const; // neg if unknown + SInt32 CountUnseen() const; // neg if unknown + SInt32 CountDeepUnseen() const; // neg if unknown + UInt32 CountSubFolders() const; + UInt32 GetDeletedBytes() const; + Boolean IsOpen() const; + MSG_ViewIndex GetIndex() const; + MSG_FolderInfo* GetFolderInfo() const; + const MSG_FolderLine* GetFolderLine() const; + MSG_Pane* GetThreadPane() const; // Returns a threadpane viewing this. + static MSG_Pane* GetFolderPane(); + // Flag property accessors: + UInt32 GetFolderFlags() const; + UInt32 GetFolderPrefFlags() const; + Boolean HasNewMessages() const; + Boolean ContainsCategories() const; + Boolean IsInbox() const; + Boolean IsTrash() const; + Boolean IsMailServer() const; + Boolean IsMailFolder() const; + Boolean IsLocalMailFolder() const; + Boolean IsIMAPMailFolder() const; + Boolean IsNewsgroup() const; + Boolean IsNewsHost() const; + Boolean CanContainThreads() const; + Boolean IsSubscribedNewsgroup() const; + ResIDT GetIconID() const; +// Data +// NOTE WELL: This class is currently 4 bytes. Many users rely on its being light weight, +// and it is passed around by value, not by reference. Note that the copy +// constructor does the right thing with reference counting. +// So BE VERY RELUCTANT TO ADD NEW DATA MEMBERS! +protected: + CCachedFolderLine* mCachedFolderLine; +}; // class CMessageFolder + +//======================================================================================== +class CCachedFolderLine +//======================================================================================== +{ +private: + CCachedFolderLine(MSG_Pane* inFolderPane, MSG_FolderInfo* id); + // inFolderPane can be nil. + ~CCachedFolderLine(); + static CCachedFolderLine* FindFolderFor(MSG_FolderInfo* id); + // doesn't create +public: + void AddUser(CMessageFolder*) { mRefCount++; } + void RemoveUser(CMessageFolder*) { if (--mRefCount <= 0) delete this; } + void FolderInfoChanged(MSG_Pane* inFolderPane); + // inFolderPane can be nil. + static void FolderInfoChanged(MSG_Pane* inFolderPane, MSG_FolderInfo* inID); + // inFolderPane can be nil. + static CCachedFolderLine* GetFolderFor(MSG_Pane* inFolderPane, MSG_FolderInfo* id); + // creates if nec. inFolderPane can be nil. + static CCachedFolderLine* GetFolderFor(MSG_Pane* inFolderPane, MSG_ViewIndex inIndex); + // creates if nec.inFolderPane IS REQUIRED. + + +//----- +// Data +//----- +public: + MSG_FolderLine mFolderLine; +private: + Int32 mRefCount; + static LArray* sLineList; +}; // class CCachedFolderLine + +#define kMessageFolderTypeMask ( MSG_FOLDER_FLAG_NEWSGROUP | \ + MSG_FOLDER_FLAG_NEWS_HOST | \ + MSG_FOLDER_FLAG_MAIL | \ + MSG_FOLDER_FLAG_TRASH | MSG_FOLDER_FLAG_SENTMAIL | \ + MSG_FOLDER_FLAG_DRAFTS | MSG_FOLDER_FLAG_QUEUE | \ + MSG_FOLDER_FLAG_TEMPLATES | MSG_FOLDER_FLAG_PERSONAL_SHARED | \ + MSG_FOLDER_FLAG_IMAP_OTHER_USER | MSG_FOLDER_FLAG_IMAP_PUBLIC | \ + MSG_FOLDER_FLAG_INBOX | MSG_FOLDER_FLAG_IMAPBOX) + +//---------------------------------------------------------------------------------------- +inline Boolean CMessageFolder::operator==(const CMessageFolder& other) const +//---------------------------------------------------------------------------------------- +{ + return GetFolderInfo() == other.GetFolderInfo(); +} + +//---------------------------------------------------------------------------------------- +inline UInt32 CMessageFolder::GetFolderPrefFlags() const +//---------------------------------------------------------------------------------------- +{ + return ::MSG_GetFolderPrefFlags(GetFolderInfo()); +} + +//---------------------------------------------------------------------------------------- +inline const MSG_FolderLine* CMessageFolder::GetFolderLine() const +//---------------------------------------------------------------------------------------- +{ + return &mCachedFolderLine->mFolderLine; +} + +//---------------------------------------------------------------------------------------- +inline UInt32 CMessageFolder::GetFolderType() const +//---------------------------------------------------------------------------------------- +{ + return (mCachedFolderLine->mFolderLine.flags & kMessageFolderTypeMask); +} + +//---------------------------------------------------------------------------------------- +inline const char* CMessageFolder::GetName() const +//---------------------------------------------------------------------------------------- +{ + return mCachedFolderLine->mFolderLine.name; +} + +//---------------------------------------------------------------------------------------- +inline const char* CMessageFolder::GetPrettyName() const +//---------------------------------------------------------------------------------------- +{ + return mCachedFolderLine->mFolderLine.prettyName; +} + +//---------------------------------------------------------------------------------------- +inline MSG_ViewIndex CMessageFolder::GetIndex() const +//---------------------------------------------------------------------------------------- +{ + Assert_(false); // this is probably a bad function, now that multiple f. panes exist. + return MSG_GetFolderIndex(GetFolderPane(), GetFolderInfo()); +} + +//---------------------------------------------------------------------------------------- +inline UInt32 CMessageFolder::GetFolderFlags() const +//---------------------------------------------------------------------------------------- +{ + return mCachedFolderLine->mFolderLine.flags; +} + +//---------------------------------------------------------------------------------------- +inline UInt32 CMessageFolder::GetLevel() const +//---------------------------------------------------------------------------------------- +{ + return mCachedFolderLine->mFolderLine.level; +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessageFolder::CanContainThreads() const +//---------------------------------------------------------------------------------------- +{ + return GetLevel() > kRootLevel; +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessageFolder::IsSubscribedNewsgroup() const +//---------------------------------------------------------------------------------------- +{ +#define SUBSCRIBED_NEWSGROUP (MSG_FOLDER_FLAG_SUBSCRIBED | MSG_FOLDER_FLAG_NEWSGROUP) + return ((GetFolderFlags() & SUBSCRIBED_NEWSGROUP) == SUBSCRIBED_NEWSGROUP ); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessageFolder::HasNewMessages() const +//---------------------------------------------------------------------------------------- +{ + return ((GetFolderFlags() & MSG_FOLDER_FLAG_GOT_NEW) != 0); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessageFolder::IsNewsgroup() const +//---------------------------------------------------------------------------------------- +{ + return ((GetFolderFlags() & MSG_FOLDER_FLAG_NEWSGROUP) != 0); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessageFolder::IsMailServer() const +//---------------------------------------------------------------------------------------- +{ + return ((GetFolderFlags() & MSG_FOLDER_FLAG_MAIL) == MSG_FOLDER_FLAG_MAIL + && GetLevel() == kRootLevel); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessageFolder::IsMailFolder() const +//---------------------------------------------------------------------------------------- +{ + return ((GetFolderFlags() & MSG_FOLDER_FLAG_MAIL) == MSG_FOLDER_FLAG_MAIL); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessageFolder::IsIMAPMailFolder() const +//---------------------------------------------------------------------------------------- +{ +#define IMAP_FOLDER (MSG_FOLDER_FLAG_IMAPBOX | MSG_FOLDER_FLAG_MAIL) + return ((GetFolderFlags() & IMAP_FOLDER) == IMAP_FOLDER); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessageFolder::IsLocalMailFolder() const +//---------------------------------------------------------------------------------------- +{ + // folder bit set and imap bit clear. + return ((GetFolderFlags() & IMAP_FOLDER) == MSG_FOLDER_FLAG_MAIL); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessageFolder::IsNewsHost() const +//---------------------------------------------------------------------------------------- +{ + return ((GetFolderFlags() & MSG_FOLDER_FLAG_NEWS_HOST) != 0); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessageFolder::IsInbox() const +//---------------------------------------------------------------------------------------- +{ + return ((GetFolderFlags() & MSG_FOLDER_FLAG_INBOX) != 0); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessageFolder::IsTrash() const +//---------------------------------------------------------------------------------------- +{ + return ((GetFolderFlags() & MSG_FOLDER_FLAG_TRASH) != 0); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessageFolder::ContainsCategories() const +//---------------------------------------------------------------------------------------- +{ +#define NEWSGROUP_WITH_CATEGORIES (MSG_FOLDER_FLAG_CAT_CONTAINER | MSG_FOLDER_FLAG_NEWSGROUP) + return ((GetFolderFlags() & NEWSGROUP_WITH_CATEGORIES) == NEWSGROUP_WITH_CATEGORIES ); +} + +//---------------------------------------------------------------------------------------- +inline Int32 CMessageFolder::CountUnseen() const +//---------------------------------------------------------------------------------------- +{ + return mCachedFolderLine->mFolderLine.unseen; +} + +//---------------------------------------------------------------------------------------- +inline Int32 CMessageFolder::CountDeepUnseen() const +//---------------------------------------------------------------------------------------- +{ + return mCachedFolderLine->mFolderLine.deepUnseen; +} + +//---------------------------------------------------------------------------------------- +inline UInt32 CMessageFolder::CountSubFolders() const +//---------------------------------------------------------------------------------------- +{ + return mCachedFolderLine->mFolderLine.numChildren; +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessageFolder::IsOpen() const +//---------------------------------------------------------------------------------------- +{ + return ((mCachedFolderLine->mFolderLine.flags & MSG_FOLDER_FLAG_ELIDED) == 0); +} // CMessageFolder::IsOpen diff --git a/mozilla/cmd/macfe/MailNews/CMessageFolderView.cp b/mozilla/cmd/macfe/MailNews/CMessageFolderView.cp new file mode 100644 index 00000000000..d8ca157b3b2 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMessageFolderView.cp @@ -0,0 +1,1184 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + + +#include "CMessageFolderView.h" + +// PowerPlant +#include +#include + +#include "prefapi.h" +#include "shist.h" +#include "uprefd.h" +#include "Secnav.h" + +// MacFE +#include "macutil.h" +#include "ufilemgr.h" + +// Netscape Mac Libs +#include "UGraphicGizmos.h" +#include "resgui.h" + +// Mail/News Specific +#include "UOffline.h" +#include "MailNewsgroupWindow_Defines.h" +#include "CThreadWindow.h" +#include "CMailProgressWindow.h" +#include "LTableViewHeader.h" +#include "CProgressListener.h" +#include "CMessageFolder.h" +#include "CMailNewsContext.h" +#include "CNewsSubscriber.h" +#include "UMailFolderMenus.h" +#include "StGetInfoHandler.h" +#include "UNewFolderDialog.h" +#include "CMessageWindow.h" +#include "UMailSelection.h" +#include "UMessageLibrary.h" + +#include "CPrefsDialog.h" +#include "CProxyDragTask.h" +#include "UDeferredTask.h" +#include "CInlineEditField.h" + +//======================================================================================== +class CFolderWatchingTask +//======================================================================================== +: public CDeferredTask +{ + public: + CFolderWatchingTask( + CMessageFolderView* inFolderView, + MSG_FolderInfo* inFolderInfo) + : mFolderView(inFolderView) + , mWatchedFolder(inFolderInfo, inFolderView->GetMessagePane()) + , mWatchedFolderChildCount( mWatchedFolder.CountSubFolders()) + , mAnotherExecuteAttemptCount(0) + { + } + virtual ExecuteResult ExecuteSelf(); + protected: + CMessageFolderView* mFolderView; + CMessageFolder mWatchedFolder; + UInt32 mWatchedFolderChildCount; + UInt32 mAnotherExecuteAttemptCount; +}; + +//---------------------------------------------------------------------------------------- +CDeferredTask::ExecuteResult CFolderWatchingTask::ExecuteSelf() +//---------------------------------------------------------------------------------------- +{ + // This is here for the case when folders were dragged into (or deleted into) + // a folder which was closed in the message center. We need to refresh the + // cache, because the newly added children have changed their level in the + // hierarchy. We don't get notified unless the new parent is expanded. + // Currently, this only goes one folder deep, which covers the most common case + // of folder deletion (move to trash). + if (++mAnotherExecuteAttemptCount >= 500) + return eDoneDelete; // give up and die + CNSContext* context = mFolderView->GetContext(); + if (context->GetCurrentCommand() == cmd_MoveMailMessages ) + context->SetCurrentCommand(cmd_Nothing); + mWatchedFolder.FolderInfoChanged(mFolderView->GetMessagePane()); // get new backend data + if (!mWatchedFolder.GetFolderInfo()) + return eDoneDelete; + UInt32 childCount = mWatchedFolder.CountSubFolders(); + if (childCount <= mWatchedFolderChildCount) + return eWaitStepBack; // no change yet, but ok to run another task. + // OK, it's stale. Fix it. + mWatchedFolder.FolderLevelChanged(mFolderView->GetMessagePane()); + MSG_ViewIndex index = ::MSG_GetFolderIndex(mFolderView->GetMessagePane(), mWatchedFolder); + if (index != MSG_VIEWINDEXNONE) + mFolderView->RefreshRowRange(index + 1, index + 1); + return eDoneDelete; +} // CFolderWatchingTask::ExecuteSelf + +#pragma mark -- CDeleteSelectionTask + +//======================================================================================== +class CDeleteSelectionTask +//======================================================================================== +: public CDeferredTask +{ + public: + CDeleteSelectionTask( + CMessageFolderView* inFolderView, + const CMailSelection& inSelection) + : mSelection(inSelection) + , mFolderView(inFolderView) + {} + virtual ExecuteResult ExecuteSelf(); + protected: + CPersistentFolderSelection mSelection; + CMessageFolderView* mFolderView; +}; // class CDeleteSelectionTask + +//---------------------------------------------------------------------------------------- +CDeferredTask::ExecuteResult CDeleteSelectionTask::ExecuteSelf() +//---------------------------------------------------------------------------------------- +{ + CNetscapeWindow* win = dynamic_cast + (LWindow::FetchWindowObject(mFolderView->GetMacPort())); + // Wait till any pending URLs are finished + if (win->IsAnyContextBusy()) + return eWaitStayFront; + const MSG_ViewIndex* indices = mSelection.GetSelectionList(); + MSG_ViewIndex numIndices = mSelection.selectionSize; + mFolderView->DoDeleteSelection(mSelection); + return eDoneDelete; +} // CDeleteSelectionTask::ExecuteSelf + +#pragma mark -- CDeferredDragTask + +//======================================================================================== +class CDeferredDragTask +//======================================================================================== +: public CDeferredTask +{ + public: + CDeferredDragTask( + CMessageFolderView* inFolderView, + const CMessageFolder& inDestFolder, + Boolean inDoCopy) + : mFolderView(inFolderView) + , mDestFolder(inDestFolder) + , mDoCopy(inDoCopy) + {} + protected: + CMessageFolderView* mFolderView; + CMessageFolder mDestFolder; + Boolean mDoCopy; // rather than move +}; + +//======================================================================================== +class CDeferredDragFolderTask +//======================================================================================== +: public CDeferredDragTask +{ + public: + CDeferredDragFolderTask( + CMessageFolderView* inFolderView, + const CMailSelection& inSelection, + const CMessageFolder& inDestFolder, + Boolean inDoCopy) + : CDeferredDragTask( + inFolderView + , inDestFolder + , inDoCopy) + , mSelection(inSelection) + {} + virtual ExecuteResult ExecuteSelf(); + protected: + CPersistentFolderSelection mSelection; +}; // class CDeferredDragFolderTask + +//---------------------------------------------------------------------------------------- +CDeferredTask::ExecuteResult CDeferredDragFolderTask::ExecuteSelf() +//---------------------------------------------------------------------------------------- +{ + // display modal dialog +// if (!NET_IsOffline()) // ? 97/08/13 someone removed this line. + // 98/01/26 it's because moving messages can take some time - even offline + { + short stringID = 15; // Copying / Moving Messages + CStr255 commandString; + ::GetIndString(commandString, 7099, stringID); + CMailProgressWindow* progressWindow = CMailProgressWindow::CreateModalParasite( + CMailProgressWindow::res_ID_modal, + mSelection.xpPane, + commandString); + if (progressWindow) + progressWindow->SetDelay(0); + } + + // copy / move messages + if (mDestFolder.IsTrash()) + { + // Special case. Back-end doesn't translate this into a delete, and + // presents an alert unless you're dragging mail folders. But it should + // work for news servers etc. So: + XP_Bool confirmPref; // Don't ever confirm a drag. + PREF_GetBoolPref("mailnews.confirm.moveFoldersToTrash", &confirmPref); + if (confirmPref) + PREF_SetBoolPref("mailnews.confirm.moveFoldersToTrash", FALSE); + mFolderView->DoDeleteSelection(mSelection); + if (confirmPref) + PREF_SetBoolPref("mailnews.confirm.moveFoldersToTrash", confirmPref); + } + else + { + mFolderView->GetContext()->SetCurrentCommand(cmd_MoveMailMessages); + mFolderView->WatchFolderForChildren(mDestFolder); + ::MSG_MoveFoldersInto( + mSelection.xpPane, + mSelection.GetSelectionList(), + mSelection.selectionSize, + mDestFolder + ); + } + return eDoneDelete; +} // CDeferredDragFolderTask::ExecuteSelf + +//======================================================================================== +class CDeferredDragMessageTask +//======================================================================================== +: public CDeferredDragTask +{ + public: + CDeferredDragMessageTask( + CMessageFolderView* inFolderView, + const CMailSelection& inSelection, + const CMessageFolder& inDestFolder, + Boolean inDoCopy) + : CDeferredDragTask( + inFolderView + , inDestFolder + , inDoCopy) + , mSelection(inSelection) + {} + virtual ExecuteResult ExecuteSelf(); + protected: + CPersistentMessageSelection mSelection; +}; // class CDeferredDragMessageTask + +//---------------------------------------------------------------------------------------- +CDeferredTask::ExecuteResult CDeferredDragMessageTask::ExecuteSelf() +//---------------------------------------------------------------------------------------- +{ + mFolderView->DropMessages(mSelection, mDestFolder, mDoCopy); + return eDoneDelete; +} // CDeferredDragMessageTask::ExecuteSelf + +#pragma mark -- CMessageFolderView + +//---------------------------------------------------------------------------------------- +CMessageFolderView::CMessageFolderView(LStream *inStream) +//---------------------------------------------------------------------------------------- +: Inherited(inStream) +, mUpdateMailFolderMenus(false) +{ + mAllowDropAfterLastRow = false; +} // CMessageFolderView::CMessageFolderView + + +//---------------------------------------------------------------------------------------- +CMessageFolderView::~CMessageFolderView() +//---------------------------------------------------------------------------------------- +{ +} // CMessageFolderView::~CMessageFolderView + +//---------------------------------------------------------------------------------------- +Boolean CMessageFolderView::ItemIsAcceptable( + DragReference inDragRef, + ItemReference inItemRef ) +//---------------------------------------------------------------------------------------- +{ + FlavorFlags flavorFlags; + if (::GetFlavorFlags(inDragRef, inItemRef, kMailNewsSelectionDragFlavor, &flavorFlags) == noErr) + { + CMailSelection selection; + if (!mIsInternalDrop && GetSelectionFromDrag(inDragRef, selection)) + mIsInternalDrop = (selection.xpPane == GetMessagePane()); + return true; + } + return false; +} // CMessageFolderView::ItemIsAcceptable + +//---------------------------------------------------------------------------------------- +Boolean CMessageFolderView::GetSelectionAndCopyStatusFromDrag( + DragReference inDragRef, + CMessageFolder& inDestFolder, + CMailSelection& outSelection, + Boolean& outCopy) +// Return true if this is an OK drop operation. +//---------------------------------------------------------------------------------------- +{ + if (!GetSelectionFromDrag(inDragRef, outSelection)) + return false; + Boolean isFolderDrop = (::MSG_GetPaneType(outSelection.xpPane) == MSG_FOLDERPANE); + // Don't do anything if the user aborted by dragging back into the same first row. + // There are other cases, but the backend will alert anyway. In this simple case, + // the back-end alert is annoying. + + SInt16 modifiers; + ::GetDragModifiers(inDragRef, NULL, &modifiers, NULL); + outCopy = ((modifiers & optionKey) != 0); + MSG_DragEffect effect = outCopy ? MSG_Require_Copy : MSG_Default_Drag; + + // Ask the back end for guidance on what is possible: + if (isFolderDrop) + { + effect = ::MSG_DragFoldersIntoStatus( + outSelection.xpPane, + outSelection.GetSelectionList(), + outSelection.selectionSize, + inDestFolder, + effect); + if (effect == MSG_Drag_Not_Allowed && outCopy) + { + // Well, maybe a move is ok + effect = ::MSG_DragFoldersIntoStatus( + outSelection.xpPane, + outSelection.GetSelectionList(), + outSelection.selectionSize, + inDestFolder, + MSG_Default_Drag); + } + } + else + { + effect = ::MSG_DragMessagesIntoFolderStatus( + outSelection.xpPane, + outSelection.GetSelectionList(), + outSelection.selectionSize, + inDestFolder, + effect); + if (effect == MSG_Drag_Not_Allowed && outCopy) + { + // Well, maybe a move is ok + effect = ::MSG_DragMessagesIntoFolderStatus( + outSelection.xpPane, + outSelection.GetSelectionList(), + outSelection.selectionSize, + inDestFolder, + MSG_Default_Drag); + } + } + outCopy = (effect & MSG_Require_Copy) != 0; + return (effect != MSG_Drag_Not_Allowed); // this looks ok as a drop operation. +} // CMessageFolderView::GetSelectionAndCopyStatusFromDrag + +//---------------------------------------------------------------------------------------- +void CMessageFolderView::DropMessages( + const CMailSelection& inSelection, + const CMessageFolder& inDestFolder, + Boolean inDoCopy) +//---------------------------------------------------------------------------------------- +{ + // display modal dialog +// if (!NET_IsOffline()) // ? 97/08/13 someone removed this line. + // 98/01/26 it's because moving messages can take some time - even offline + { + short stringID = (inDoCopy ? 19 : 15 ); // Copying / Moving Messages + CStr255 commandString; + ::GetIndString(commandString, 7099, stringID); + CMailProgressWindow* progressWindow = CMailProgressWindow::CreateModalParasite( + CMailProgressWindow::res_ID_modal, + inSelection.xpPane, + commandString); + if (progressWindow) + progressWindow->SetDelay(0); + } + + // Close any windows associated with these messages, if the preferences + // say we should. + CMessageWindow::NoteSelectionFiledOrDeleted(inSelection); + if (!inDoCopy) + ::MSG_MoveMessagesIntoFolder( + inSelection.xpPane, + inSelection.GetSelectionList(), + inSelection.selectionSize, + inDestFolder + ); + else + ::MSG_CopyMessagesIntoFolder( + inSelection.xpPane, + inSelection.GetSelectionList(), + inSelection.selectionSize, + inDestFolder + ); + +} // CMessageFolderView::DropMessages + +//---------------------------------------------------------------------------------------- +Boolean CMessageFolderView::CanDoInlineEditing() +// (override from CStandardFlexTable) +//---------------------------------------------------------------------------------------- +{ + CMessageFolder folder(mRowBeingEdited, GetMessagePane()); + Boolean outEnabled; + Boolean outUsesMark; + Char16 outMark; + Str255 outName; + FindCommandStatus(cmd_RenameFolder, outEnabled, outUsesMark, outMark, outName); + return outEnabled; +} + +//---------------------------------------------------------------------------------------- +void CMessageFolderView::InlineEditorTextChanged() +// (override from CStandardFlexTable) +//---------------------------------------------------------------------------------------- +{ +} +//---------------------------------------------------------------------------------------- +void CMessageFolderView::InlineEditorDone() +// (override from CStandardFlexTable) +//---------------------------------------------------------------------------------------- +{ + if (mRowBeingEdited == LArray::index_Bad || !mNameEditor) + return; + CMessageFolder folder(mRowBeingEdited, GetMessagePane()); + char oldName[256]; + CMailFolderMixin::GetFolderNameForDisplay(oldName, folder); + CStr255 newName; + mNameEditor->GetDescriptor(newName); + if (newName != oldName) + { + char* newNameC = XP_STRDUP(newName); + if (newNameC) + { + ::MSG_RenameMailFolder(GetMessagePane(), folder, newNameC); + folder.FolderInfoChanged(GetMessagePane()); + XP_FREE(newNameC); + } + } +} // CMessageFolderView::InlineEditorDone + +//---------------------------------------------------------------------------------------- +Boolean CMessageFolderView::RowCanAcceptDrop( + DragReference inDragRef, + TableIndexT inDropRow) +// (override from CStandardFlexTable) +//---------------------------------------------------------------------------------------- +{ + Boolean dropOK = false; + + SInt16 modifiers; + ::GetDragModifiers(inDragRef, NULL, &modifiers, NULL); + Boolean doCopy = ((modifiers & optionKey) != 0); + + if (inDropRow >= 1 && inDropRow <= mRows) + { + CMessageFolder destFolder(inDropRow, GetMessagePane()); + CMailSelection selection; + dropOK = (GetSelectionAndCopyStatusFromDrag( + inDragRef, destFolder, + selection, doCopy)); + } + if (dropOK && doCopy) + { + CursHandle copyDragCursor = ::GetCursor(curs_CopyDrag); // finder's copy-drag cursor + if (copyDragCursor != nil) + ::SetCursor(*copyDragCursor); + } + else + ::SetCursor(&qd.arrow); + return dropOK; +} // CMessageFolderView::RowCanAcceptDrop + +//---------------------------------------------------------------------------------------- +Boolean CMessageFolderView::RowCanAcceptDropBetweenAbove( + DragReference inDragRef, + TableIndexT inDropRow) +// (override from CStandardFlexTable) +//---------------------------------------------------------------------------------------- +{ + if (inDropRow >= 1 && inDropRow <= mRows) + { + CMailSelection selection; + if (GetSelectionFromDrag(inDragRef, selection)) + { + // Dropping between rows signifies reordering. This is only allowed when the + // source pane of the drag is our own pane. + if (selection.xpPane != GetMessagePane()) + return false; + CMessageFolder destFolder(inDropRow, GetMessagePane()); + CMailSelection selection; + Boolean doCopy; + return GetSelectionAndCopyStatusFromDrag( + inDragRef, destFolder, + selection, doCopy); + } + } + return false; +} // CMessageFolderView::RowCanAcceptDropBetweenAbove + +//---------------------------------------------------------------------------------------- +void CMessageFolderView::ReceiveDragItem( + DragReference inDragRef, + DragAttributes /*inDragAttrs*/, + ItemReference /*inItemRef*/, + Rect& /*inItemBounds*/) +//---------------------------------------------------------------------------------------- +{ + if (mDropRow == 0) return; // FIXME: when move to top level is implemented, do that! + + // What is the destination folder? + CMessageFolder destFolder(mDropRow, GetMessagePane()); + + // What are the items being dragged? + CMailSelection selection; + Boolean doCopy = false; + if (!GetSelectionAndCopyStatusFromDrag(inDragRef, destFolder, selection, doCopy)) + return; + + // We have to delay the drag in order to display any confirmation dialog + // from the BE (when moving a folder to the trash, for instance). + // Displaying a dialog in here crashes the Mac. + CDeferredDragTask* task = nil; + MSG_PaneType paneType = ::MSG_GetPaneType(selection.xpPane); + if (paneType == MSG_FOLDERPANE) + CDeferredTaskManager::Post( + new CDeferredDragFolderTask(this, selection, destFolder, doCopy), this); + else if (paneType == MSG_MESSAGEPANE) + CDeferredTaskManager::Post( + new CDeferredDragMessageTask(this, selection, destFolder, doCopy), this); + else // search pane? + DropMessages(selection, destFolder, doCopy); // devil take the hindmost. +} // CMessageFolderView::ReceiveDragItem + +//---------------------------------------------------------------------------------------- +void CMessageFolderView::WatchFolderForChildren(MSG_FolderInfo* inFolderInfo) +//---------------------------------------------------------------------------------------- +{ + CFolderWatchingTask* task = new CFolderWatchingTask(this, inFolderInfo); + CDeferredTaskManager::Post(task, this); +} // CMessageFolderView::WatchFolderForChildren + +//---------------------------------------------------------------------------------------- +void CMessageFolderView::DoAddNewsGroup() +//---------------------------------------------------------------------------------------- +{ + try + { + MSG_ViewIndex* indexList = NULL; + UInt32 numIndices = 0; + CMailSelection selection; + if (GetSelection(selection)) + { + //...unless there's a single item selected, and it's a newsgroup + if (selection.selectionSize == 1) + { + MSG_ViewIndex index = *(MSG_ViewIndex*)selection.GetSelectionList(); + MSG_FolderInfo* info = ::MSG_GetFolderInfo(GetMessagePane(), index); + CMessageFolder folder(info, GetMessagePane()); + if (folder.IsNewsHost()) + { + indexList = &index; + numIndices = 1; + } + } + } + MSG_Command( + GetMessagePane(), + MSG_AddNewsGroup, + indexList, + numIndices); + Refresh(); // works around a bug - we don't get notified. + } + catch(...) + { + } +} // CMessageFolderView::DoOpenNewsHost() + +//---------------------------------------------------------------------------------------- +void CMessageFolderView::DeleteSelection() +// Delete all selected folders +//---------------------------------------------------------------------------------------- +{ + CMailSelection selection; + if (GetSelection(selection)) + { + // Deletion cannot occur if we are viewing the contents in a thread pane. + // So make a task (which clones the selection) then unselect the selected + // item (triggering an unload of the folder pane). Then post a delete + // task. + CDeleteSelectionTask* task = new CDeleteSelectionTask(this, selection); + UnselectAllCells(); + CDeferredTaskManager::Post(task, this); + } +} // CMessageFolderView::DeleteSelection + +//---------------------------------------------------------------------------------------- +void CMessageFolderView::DoDeleteSelection(const CMailSelection& inSelection) +//---------------------------------------------------------------------------------------- +{ + try + { + mContext->SetCurrentCommand(cmd_Clear); // in case it was a keystroke + // If there's one mail folder in the selection being deleted, then + // watch the destination folder until the folder count changes. Otherwise + // (eg dragging newsgroups) we will wait forever, in vain... + MSG_ViewIndex* cur = (MSG_ViewIndex*)inSelection.GetSelectionList(); + MSG_FolderInfo* folderToWatch = nil; + for (int i = 0; i < inSelection.selectionSize; i++, cur++) + { + MSG_FolderInfo* info = ::MSG_GetFolderInfo(GetMessagePane(), *cur); + CMessageFolder folder(info, GetMessagePane()); + if (folder.IsMailFolder()) + { + ::MSG_GetFoldersWithFlag( + CMailNewsContext::GetMailMaster(), + MSG_FOLDER_FLAG_TRASH, + &folderToWatch, + 1); + break; + } + } + MSG_CommandType cmd = UMessageLibrary::GetMSGCommand(cmd_Clear); +#if 1 // (MSG_Delete != MSG_DeleteFolder) + if (cmd == MSG_Delete) + cmd = MSG_DeleteFolder; // This can go away when winfe becomes compliant. +#endif + ::MSG_Command( + GetMessagePane(), + cmd, + const_cast(inSelection.GetSelectionList()), + inSelection.selectionSize); + if (folderToWatch) + WatchFolderForChildren(folderToWatch); + } + catch(...) + { + } +} // CMessageFolderView::DoDeleteSelection() + +//---------------------------------------------------------------------------------------- +void CMessageFolderView::AddRowDataToDrag(TableIndexT inRow, DragReference inDragRef) +//---------------------------------------------------------------------------------------- +{ + if (inRow == LArray::index_Bad || inRow > mRows) + return; + + Inherited::AddRowDataToDrag(inRow, inDragRef); // does nothing right now + + CMessageFolder folder(inRow, GetMessagePane()); + + // allow newsgroup text drags into compose window + if (folder.IsNewsgroup()) + { + MSG_FolderInfo *folderInfo = folder.GetFolderInfo(); + + URL_Struct *theURLStruct = ::MSG_ConstructUrlForFolder(GetMessagePane(), folderInfo); + + if (theURLStruct != nil) + { + + OSErr err = ::AddDragItemFlavor( inDragRef, inRow, 'TEXT', + theURLStruct->address, XP_STRLEN(theURLStruct->address), flavorSenderOnly); + + NET_FreeURLStruct(theURLStruct); + } + } + +} // CMessageFolderView::AddRowToDrag + +//---------------------------------------------------------------------------------------- +Boolean CMessageFolderView::CellInitiatesDrag(const STableCell& inCell) const +//---------------------------------------------------------------------------------------- +{ + return (GetCellDataType(inCell) == kFolderNameColumn); +} // CMessageFolderView::CellInitiatesDrag + +//---------------------------------------------------------------------------------------- +Boolean CMessageFolderView::GetRowDragRgn(TableIndexT inRow, RgnHandle ioHiliteRgn) const +// The drag region is the hilite region plus the icon +// Note that the row we are dragging may or may not be selected, so don't rely +// on the selection for anything. +//---------------------------------------------------------------------------------------- +{ + ::SetEmptyRgn(ioHiliteRgn); + Rect cellRect; + + TableIndexT col = GetHiliteColumn(); + if ( !col ) + col = 1; + STableCell cell(inRow, col); + if (!GetLocalCellRect(cell, cellRect)) + return false; + ResIDT iconID = GetIconID(inRow); + if (iconID) + { + GetIconRect(cell, cellRect, cellRect); + ::IconIDToRgn(ioHiliteRgn, &cellRect, kAlignNone, iconID); + } + StRegion cellRgn; + GetRowHiliteRgn(inRow, cellRgn); + ::UnionRgn(ioHiliteRgn, cellRgn, ioHiliteRgn); + + CMessageFolder folder(inRow, GetMessagePane()); + + // If this row is an expanded folder with children, then we need to + // add each child row to the drag also. + // If one of these child rows is selected, and we are dragging the selection, + // then it will get added twice, but this should not be a problem. + if (folder.CountSubFolders() > 0 && folder.IsOpen()) + { + UInt32 folderLevel = folder.GetLevel(); + STableCell nextCell(cell.row + 1, 1); + StRegion tempRgn; + + while (GetNextCell(nextCell)) + { + CMessageFolder subFolder(nextCell.row, GetMessagePane()); + UInt32 subFolderLevel = subFolder.GetLevel(); + + if (subFolderLevel <= folderLevel) + break; + + Inherited::GetRowDragRgn(nextCell.row, tempRgn); + ::UnionRgn(tempRgn, ioHiliteRgn, ioHiliteRgn); + } + } + + return true; +} // CStandardFlexTable::GetRowHiliteRgn + +//---------------------------------------------------------------------------------------- +void CMessageFolderView::GetInfo() +//---------------------------------------------------------------------------------------- +{ + CMessageFolder folder(GetSelectedFolder()); + if (folder.GetFolderInfo()) + UGetInfo::ConductFolderInfoDialog(folder, this); +} + +//---------------------------------------------------------------------------------------- +void CMessageFolderView::OpenRow(TableIndexT inRow) +// See also CFolderThreadController::ListenToMessage +//---------------------------------------------------------------------------------------- +{ + CMessageFolder folder(inRow, GetMessagePane()); + if (folder.IsMailServer()) + { + UGetInfo::ConductFolderInfoDialog(folder, this); + return; + } + if (folder.IsNewsHost()) + { + MSG_Host* selectedHost = MSG_GetHostForFolder(folder); + CNewsSubscriber::DoSubscribeNewsGroup(selectedHost); + return; + } + if (!folder.CanContainThreads()) + return; + + Boolean forceNewWindow = false; + if (GetSelectedRowCount() > 1 && inRow != GetTableSelector()->GetFirstSelectedRow()) + { + STableCell cell(inRow, 1); + if (CellIsSelected(cell)) + forceNewWindow = true; + } + + CThreadWindow* threadWindow + = CThreadWindow::FindAndShow( + folder.GetFolderInfo(), + CThreadWindow::kMakeNew, + cmd_Nothing, + forceNewWindow); // Force new window for multiple selection. +} // CMessageFolderView::OpenRow + +//---------------------------------------------------------------------------------------- +void CMessageFolderView::GetLongWindowDescription(CStr255& outDescription) +//---------------------------------------------------------------------------------------- +{ + ::GetIndString(outDescription, 7099, 11); // "Message Center For ^0" + char buffer[256]; int bufferLength = sizeof(buffer); + ::PREF_GetCharPref("mail.identity.username", buffer, &bufferLength); + ::StringParamText(outDescription, buffer); +} + +//---------------------------------------------------------------------------------------- +void CMessageFolderView::SelectionChanged() +//---------------------------------------------------------------------------------------- +{ + Inherited::SelectionChanged(); + TableIndexT rowCount = GetSelectedRowCount(); + URL_Struct* url = nil; + char entryName[128]; + entryName[0] = '\0'; + if (rowCount == 1) + { + CMailSelection selection; + GetSelection( selection ); + MSG_ViewIndex index = *(MSG_ViewIndex*)selection.GetSelectionList(); + MSG_FolderInfo* info = MSG_GetFolderInfo(GetMessagePane(), index); + MSG_FolderLine folderLine; + XP_Bool gotIt = ::MSG_GetFolderLineById( + CMailNewsContext::GetMailMaster(), + info, + &folderLine); + if (gotIt) + { + url = ::MSG_ConstructUrlForFolder(GetMessagePane(), info); + strcpy(entryName, folderLine.name); + } + } + else + { + // Use the name in the location bar for any potential bookmarks... + url = ::MSG_ConstructUrlForPane(GetMessagePane()); + CStr255 tempString; + GetLongWindowDescription(tempString); + strcpy(entryName, tempString); + } + if (url && *entryName) + { + History_entry* theNewEntry = SHIST_CreateHistoryEntry( + url, + entryName); + SHIST_AddDocument(*mContext, theNewEntry); + } + XP_FREEIF(url); +} + +//---------------------------------------------------------------------------------------- +void CMessageFolderView::ListenToMessage(MessageT inMessage, void* ioParam) +//---------------------------------------------------------------------------------------- +{ + // button messages. + if (!IsOnDuty() || !ObeyCommand(inMessage, ioParam)) + { + Inherited::ListenToMessage(inMessage, ioParam); + } +} // CMessageFolderView::ListenToMessage + +//---------------------------------------------------------------------------------------- +Boolean CMessageFolderView::FindMessageLibraryCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +// returns false if not a msglib command. +//---------------------------------------------------------------------------------------- +{ + // Interpret MarkSelectedRead as "mark all read" (ie, in this folder) + if (inCommand == cmd_MarkSelectedRead) + inCommand = cmd_MarkAllRead; + return Inherited::FindMessageLibraryCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); +} + +//---------------------------------------------------------------------------------------- +void CMessageFolderView::FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +//---------------------------------------------------------------------------------------- +{ + // Preprocessing before calling UMessageLibrary + switch (inCommand) + { + case cmd_NewFolder: + outEnabled = true; + return; + case cmd_NewNewsgroup: + MSG_FolderInfo* folderInfo = GetSelectedFolder(); + inCommand = cmd_NewFolder; + if (folderInfo != nil) + { + CMessageFolder folder(folderInfo, GetMessagePane()); + if (folder.IsNewsgroup() || folder.IsNewsHost()) + inCommand = cmd_NewNewsgroup; + } + break; + + case cmd_OpenSelection: // enabled in base class + { + MSG_FolderInfo* folderInfo = GetSelectedFolder(); + if (folderInfo != nil) + { + CMessageFolder folder(folderInfo, GetMessagePane()); + short strID = 0; + if (folder.IsNewsgroup()) + strID = kOpenDiscussionStrID; + else if (folder.IsMailServer()) + strID = kOpenMailServerStrID; + else if (folder.IsNewsHost()) + strID = kOpenNewsServerStrID; + else if (folder.IsMailFolder()) + strID = kOpenFolderStrID; + if (strID != 0) + ::GetIndString(outName, kMailNewsMenuStrings, strID); + } + break; + } + case cmd_OpenSelectionNewWindow: + { + MSG_FolderInfo* folderInfo = GetSelectedFolder(); + if (folderInfo != nil) + { + CMessageFolder folder(folderInfo, GetMessagePane()); + if (folder.IsMailServer() || folder.IsNewsHost()) + { + outEnabled = false; + return; + } + } + break; + } + case cmd_GetMoreMessages: + CStr255 cmdStr; + CStr255 numStr; + ::GetIndString(cmdStr, kMailNewsMenuStrings, kNextChunkMessagesStrID); + ::NumToString(CPrefs::GetLong(CPrefs::NewsArticlesMax), numStr); + cmdStr += numStr; + memcpy(outName, (StringPtr)cmdStr, cmdStr.Length() + 1); + break; + + case cmd_Stop: + if (mStillLoading) + { + outEnabled = true; // stop button on, nothing else. + return; + } + break; + #if 0 + // 97/10/20. Fall through to the inherited version. + // We don't handle it as a menu command - only internally. + case cmd_NewsGroups: // Expand news host + case cmd_MailNewsFolderWindow: // Expand mail host + outEnabled = true; + return; +#endif // 0 + case cmd_GetInfo: + if (GetSelectedRowCount() != 1) + { + outEnabled = false; + return; + } + outEnabled = true; + return; + + case cmd_Clear: + folderInfo = GetSelectedFolder(); + if (folderInfo != nil) { + CMessageFolder folder(folderInfo, GetMessagePane()); + if (!folder.IsMailServer()) + outEnabled = true; + } + return; + + case cmd_AddToBookmarks: + { + MSG_FolderInfo* folderInfo = GetSelectedFolder(); + if (folderInfo != nil) + { + CMessageFolder folder(folderInfo, GetMessagePane()); + if (folder.IsIMAPMailFolder() || folder.IsNewsHost()) + { + outEnabled = false; + return; + } + } + break; // call inherited:: + } + } // switch + + if (!mStillLoading && FindMessageLibraryCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName)) + return; + + switch (inCommand) + { + case cmd_OpenNewsHost: + case cmd_AddNewsgroup: + case cmd_SubscribeNewsgroups: + outEnabled = !mStillLoading; + break; + default: + Inherited::FindCommandStatus( + inCommand, outEnabled, outUsesMark, outMark, outName); + break; + } +} // CMessageFolderView::FindCommandStatus + +//---------------------------------------------------------------------------------------- +Boolean CMessageFolderView::ObeyCommand( + CommandT inCommand, + void *ioParam) +//---------------------------------------------------------------------------------------- +{ + if (!mContext) + return false; + + Boolean cmdHandled = false; + CNSContext* originalContext = mContext; // in case we close the window & delete it! + CommandT saveCommand = mContext->GetCurrentCommand(); + mContext->SetCurrentCommand(inCommand); + switch (inCommand) + { + case msg_TabSelect: + return (IsVisible()); // Allow selection only if pane is visible +#if 0 + // This now HAS to be handled by the top commander (application), because the behavior + // has to be different depending on whether we are in a message center window or a 3-pane + // thread window. + case cmd_NewsGroups: // Select first news host + case cmd_MailNewsFolderWindow: // Select first mail host + SelectFirstFolderWithFlags( + inCommand == cmd_NewsGroups + ? MSG_FOLDER_FLAG_NEWS_HOST + : MSG_FOLDER_FLAG_MAIL); + cmdHandled = true; + break; +#endif // 0 + case cmd_SecurityInfo: + MWContext * context = *mContext; + SECNAV_SecurityAdvisor( context, NULL ); + cmdHandled =true; + break; + //----------------------------------- + // News + //---------------------------------------------------------------------------------------- + case cmd_OpenNewsHost: + CNewsSubscriber::DoAddNewsHost(); + cmdHandled = true; + break; + case cmd_SubscribeNewsgroups: + MSG_Host* selectedHost = MSG_GetHostForFolder(GetSelectedFolder()); + CNewsSubscriber::DoSubscribeNewsGroup(selectedHost); + cmdHandled = true; + break; + case cmd_AddNewsgroup: + DoAddNewsGroup(); + cmdHandled = true; + break; + //----------------------------------- + // Special cases + //---------------------------------------------------------------------------------------- +#if 0 + // Not needed - have inline editing now (and the menu item is gone). + case cmd_RenameFolder: + UpdateMailFolderMenusOnNextUpdate(); + break; +#endif + case cmd_NewFolder: + case cmd_NewNewsgroup: + { + MSG_FolderInfo* folderInfo = GetSelectedFolder(); + CMessageFolder folder(folderInfo, GetMessagePane()); + if (folderInfo && (folder.IsNewsgroup() || folder.IsNewsHost())) + inCommand = cmd_NewNewsgroup; // no 'cmdHandled = true' here + else + { + UFolderDialogs::ConductNewFolderDialog(folder); + cmdHandled = true; + } + break; + } + } + //----------------------------------- + // MSGLIB commands + //----------------------------------- + if (!cmdHandled) + cmdHandled = ObeyMessageLibraryCommand(inCommand, ioParam) + || Inherited::ObeyCommand(inCommand, ioParam); + //----------------------------------- + // Cleanup + //----------------------------------- + // The following test against originalContext protects against the cases (quit, close) + // when the object has been deleted. The test against cmdHandled protects against + // re-entrant calls to ListenToMessage. + + if (cmdHandled && mContext == originalContext) + mContext->SetCurrentCommand(cmd_Nothing); + + if (! cmdHandled) + mContext->SetCurrentCommand(saveCommand); + return cmdHandled; +} // CMessageFolderView::ObeyCommand + +#if defined(QAP_BUILD) +//---------------------------------------------------------------------------------------- +void CMessageFolderView::GetQapRowText( + TableIndexT inRow, + char* outText, + UInt16 inMaxBufferLength) const +// Calculate the text and (if ioRect is not passed in as null) a rectangle that fits it. +// Return result indicates if any of the text is visible in the cell +//---------------------------------------------------------------------------------------- +{ + if (!outText || inMaxBufferLength == 0) + return; + + cstring rowText(""); + char tempStr[cMaxMailFolderNameLength]; + CMessageFolder folder(inRow, GetMessagePane()); + short colCount = mTableHeader->CountVisibleColumns(); + + CMailNewsWindow * myWindow = dynamic_cast(LWindow::FetchWindowObject(GetMacPort())); + if (!myWindow) return; + + for (short col = 1; col <= colCount; col ++) + { + STableCell aCell(inRow, col); + LTableHeader::SColumnData * colData = mTableHeader->GetColumnData(col); + if (!colData) break; + LPane * colPane = myWindow->FindPaneByID(colData->paneID); + if (!colPane) break; + + // get column name + CStr255 descriptor; + colPane->GetDescriptor(descriptor); + rowText += descriptor; + rowText += "=\042"; + + // add cell text + switch (GetCellDataType(aCell)) + { + case kFolderNameColumn: + Boolean expanded; + if (CellHasDropFlag(aCell, expanded)) + { + if (expanded) + rowText += "-"; + else + rowText += "+"; + } + else + rowText += " "; + + CMailFolderMixin::GetFolderNameForDisplay(tempStr, folder); + NET_UnEscape(tempStr); + rowText += tempStr; + break; + + case kFolderNumUnreadColumn: + if (folder.CanContainThreads()) + if (folder.CountMessages() != 0) + { + sprintf(tempStr, "%d", folder.CountUnseen()); + rowText += tempStr; + } + break; + + case kFolderNumTotalColumn: + if (folder.CanContainThreads()) + if (folder.CountMessages() != 0) + { + sprintf(tempStr, "%d", folder.CountMessages()); + rowText += tempStr; + } + break; + } + if (col < colCount) + rowText += "\042 | "; + else + rowText += "\042\r"; + } + strncpy(outText, (char*)rowText, inMaxBufferLength); + outText[inMaxBufferLength - 1] = '\0'; +} // CMessageFolderView::GetQapRowText +#endif //QAP_BUILD diff --git a/mozilla/cmd/macfe/MailNews/CMessageFolderView.h b/mozilla/cmd/macfe/MailNews/CMessageFolderView.h new file mode 100644 index 00000000000..abd337f110a --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMessageFolderView.h @@ -0,0 +1,149 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMessageFolderView.h + +#pragma once + +#include +#include + +#include "CSimpleFolderView.h" +#include "msgcom.h" + +class CThreadView; +class CThreadWindow; +class CStr255; +class CMessageFolder; +class CMailSelection; + +//====================================== +class CMessageFolderView : public CSimpleFolderView +//====================================== +{ +private: + typedef CSimpleFolderView Inherited; +public: + enum + { class_ID = 'msFv' + }; + + CMessageFolderView(LStream *inStream); + virtual ~CMessageFolderView(); + + // ------------------------------------------------------------ + // Command implementation + // ------------------------------------------------------------ + void DoAddNewsGroup(); + + virtual void DeleteSelection(); + void DoDeleteSelection(const CMailSelection& inSelection); + + void GetLongWindowDescription(CStr255& outDescription); + virtual void OpenRow(TableIndexT inRow); + virtual void SelectionChanged(); // maintain history + virtual void GetInfo(); // called from the base class. + void DropMessages( + const CMailSelection& inSelection, + const CMessageFolder& inDestFolder, + Boolean doCopy); + +protected: + + inline void OpenFolder(UInt32 inFolderIndex) { OpenRow(inFolderIndex); } + + // ------------------------------------------------------------ + // Hierarchy + // ------------------------------------------------------------ + virtual Boolean CellInitiatesDrag(const STableCell& inCell) const; + virtual Boolean GetRowDragRgn(TableIndexT inRow, RgnHandle ioHiliteRgn) const; + + // ------------------------------------------------------------ + // LDragAndDrop overrides + // ------------------------------------------------------------ + virtual void AddRowDataToDrag(TableIndexT inRow, DragReference inDragRef); + virtual Boolean ItemIsAcceptable(DragReference inDragRef, ItemReference inItemRef); + virtual void ReceiveDragItem( + DragReference inDragRef, + DragAttributes inDragAttrs, + ItemReference inItemRef, + Rect& inItemBounds); + + // Specials from CStandardFlexTable + virtual Boolean CanDoInlineEditing(); + virtual void InlineEditorTextChanged(); + virtual void InlineEditorDone(); + virtual Boolean RowCanAcceptDrop( + DragReference inDragRef, + TableIndexT inDropRow); + virtual Boolean RowCanAcceptDropBetweenAbove( + DragReference inDragRef, + TableIndexT inDropRow); + Boolean GetSelectionAndCopyStatusFromDrag( + DragReference inDragRef, + CMessageFolder& inDestFolder, + CMailSelection& outSelection, + Boolean& outCopy); + + //----------------------------------- + // Commands + //----------------------------------- + virtual void FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName); +public: + virtual Boolean ObeyCommand( + CommandT inCommand, + void *ioParam); +protected: + virtual Boolean FindMessageLibraryCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName); + + //----------------------------------- + // Messaging + //----------------------------------- + virtual void ListenToMessage(MessageT inMessage, void* ioParam); + // msg + +public: + void WatchFolderForChildren(MSG_FolderInfo* inFolderInfo); + + // ------------------------------------------------------------ + // QA Partner support + // ------------------------------------------------------------ +#if defined(QAP_BUILD) +public: + virtual void GetQapRowText(TableIndexT inRow, char* outText, UInt16 inMaxBufferLength) const; +#endif + + // ------------------------------------------------------------ + // Data + // ------------------------------------------------------------ + + Boolean mUpdateMailFolderMenus; +}; // class CMessageFolderView + diff --git a/mozilla/cmd/macfe/MailNews/CMessageSearchWindow.cp b/mozilla/cmd/macfe/MailNews/CMessageSearchWindow.cp new file mode 100644 index 00000000000..568eb5d2865 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMessageSearchWindow.cp @@ -0,0 +1,328 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMessageSearchWindow.cp + +#define DEBUGGER_ASSERTIONS + +#include "CMessageSearchWindow.h" +#include "LGAPushButton.h" +#include "SearchHelpers.h" +#include "CMessageWindow.h" +#include "CMessageView.h" +#include "msgcom.h" +#include "CMailNewsContext.h" +#include "CSearchTableView.h" +#include "CMailFolderButtonPopup.h" +#include "libi18n.h" +#include "nc_exception.h" +#include "UIHelper.h" +#include "UMailSelection.h" + +//----------------------------------- +CMessageSearchWindow::CMessageSearchWindow(LStream *inStream) : + CSearchWindowBase(inStream, WindowType_SearchMailNews), + mGoToFolderBtn( nil ), mMsgScopePopup( nil ), + mFileMessagePopup( nil ), scopeInfo( nil ) +//----------------------------------- +{ + SetPaneID( pane_ID ); + mNumBasicScopeMenuItems = 3; +} + +//----------------------------------- +CMessageSearchWindow::~CMessageSearchWindow() +//----------------------------------- +{ +} + +//----------------------------------- +void CMessageSearchWindow::FinishCreateSelf() +//----------------------------------- +{ + // get controls + FindUIItemPtr( this, paneID_MsgScopePopup, mMsgScopePopup ); + FindUIItemPtr( this, paneID_GoToFolder, mGoToFolderBtn ); + FindUIItemPtr( this, paneID_FilePopup, mFileMessagePopup ); + + // initialize the move popup control + CMailFolderMixin::FolderChoices filePopupChoices + = static_cast(CMailFolderMixin::eWantPOP + CMailFolderMixin::eWantIMAP); + mFileMessagePopup->MSetFolderChoices( filePopupChoices ); + CMailFolderMixin::UpdateMailFolderMixinsNow( mFileMessagePopup ); + + // add listeners + mGoToFolderBtn->AddListener( this ); + mFileMessagePopup->AddListener( this ); + mMsgScopePopup->AddListener( this ); + + // disable the selection controls until we get a result + DisableSelectionControls(); + + Inherited::FinishCreateSelf(); +} + +//----------------------------------- +void CMessageSearchWindow::SetUpBeforeSelecting() +// Set up the window just before it is selected and brought to the front of other +// windows in the app. +//----------------------------------- +{ + // Clear the selected search list + mSearchFolders.RemoveItemsAt(LArray::index_Last, LArray::index_First); + + // Determine front window status + CMailFlexTable* frontSearchTable = nil; + CMailNewsWindow* frontMailWindow = nil; + GetDefaultSearchTable(frontMailWindow, frontSearchTable); + + //Figure out which folderinfo to use as the scope. + MSG_FolderInfo* msgFolderInfo = nil; + + if (frontSearchTable) + { + // Get the xp message pane + MSG_Pane* msgPane = frontSearchTable->GetMessagePane(); + // Set up search flags and selected folder list + AssertFail_(msgPane != nil); + MSG_PaneType paneType = MSG_GetPaneType(msgPane); + switch ( paneType ) + { + case MSG_FOLDERPANE: + if (!frontSearchTable->IsValidRow(1)) + return; + // If there's a single selection, make it the default. Otherwise, + // use the first row. + CMailSelection theSelection; + frontSearchTable->GetSelection(theSelection); + AssertFail_(MSG_GetPaneType(theSelection.xpPane) == MSG_FOLDERPANE); + AssertFail_(theSelection.xpPane == msgPane); + MSG_ViewIndex defaultIndex = 0; + if (theSelection.selectionSize == 1) + defaultIndex = *theSelection.GetSelectionList(); + msgFolderInfo = ::MSG_GetFolderInfo( + theSelection.xpPane, + defaultIndex); + break; + + case MSG_THREADPANE: + msgFolderInfo = ::MSG_GetCurFolder(msgPane); + AssertFail_(msgFolderInfo); + break; + + default: + AssertFail_(false); // No other types accepted now! + break; + } // switch + } + else // message window case + { + CMessageWindow* messageWindow = dynamic_cast(frontMailWindow); + if (messageWindow) + { + CMessageView* messageView = messageWindow->GetMessageView(); + if (messageView) + msgFolderInfo = messageView->GetFolderInfo(); + } + } + // As a last resort, use the inbox + if (!msgFolderInfo) + { + ::MSG_GetFoldersWithFlag( + CMailNewsContext::GetMailMaster(), + MSG_FOLDER_FLAG_INBOX, + &msgFolderInfo, + 1); + } + AssertFail_(msgFolderInfo); + mMsgScopePopup->MSetSelectedFolder(msgFolderInfo, false); + + // Get the folder csid and set it + SetWinCSID(INTL_DocToWinCharSetID( MSG_GetFolderCSID( msgFolderInfo ) )); + + mSearchFolders.InsertItemsAt(1, LArray::index_Last, &msgFolderInfo); + mSearchManager.SetSearchScope( + (MSG_ScopeAttribute)CSearchManager::cScopeMailSelectedItems, + &mSearchFolders); +} // CMessageSearchWindow::SetUpBeforeSelecting + +//----------------------------------- +void CMessageSearchWindow::MessageWindStop(Boolean inUserAborted) +//----------------------------------- +{ + if ( mSearchManager.IsSearching() ) + { + Inherited::MessageWindStop(inUserAborted); + + // enable controls + EnableSelectionControls(); + UpdatePort(); + } +} + +//----------------------------------- +void CMessageSearchWindow::MessageWindSearch() +//----------------------------------- +{ + // Disable controls + DisableSelectionControls(); + UpdatePort(); + + Inherited::MessageWindSearch(); +} // CMessageSearchWindow::MessageWindSearch() + + +//----------------------------------- +void CMessageSearchWindow::UpdateTableStatusDisplay() +//----------------------------------- +{ + AssertFail_(mResultsTable != nil); + Inherited::UpdateTableStatusDisplay(); + + XP_Bool enabled = false; + TableIndexT numSelectedItems = mResultsTable->GetSelectedRowCount(); + if ( numSelectedItems > 0 ) + { + CMailSelection selection; + mResultsTable->GetSelection(selection); + enabled = MSG_GoToFolderStatus(mSearchManager.GetMsgPane(), + (MSG_ViewIndex*)selection.GetSelectionList(), + selection.selectionSize); + } + + if (enabled) + EnableSelectionControls(); + else + DisableSelectionControls(); +} // CMessageSearchWindow::UpdateTableStatusDisplay() + +//----------------------------------- +void CMessageSearchWindow::EnableSelectionControls() +// +// Enable selection controls +//----------------------------------- +{ + mGoToFolderBtn->Enable(); + mFileMessagePopup->Enable(); +} + +//----------------------------------- +void CMessageSearchWindow::DisableSelectionControls() +// +// Disable selection controls +//----------------------------------- +{ + mGoToFolderBtn->Disable(); + mFileMessagePopup->Disable(); +} + +//----------------------------------- +void CMessageSearchWindow::ListenToMessage(MessageT inMessage, void *ioParam) +//----------------------------------- +{ + switch (inMessage) + { + case msg_FilePopup: + // make sure we have a result table + AssertFail_(mResultsTable != nil); + + // get the selection from the table + CMailSelection moveSelection; + mResultsTable->GetSelection( moveSelection ); + + // get the destination + MSG_FolderInfo* selectedDestination = ( (MSG_FolderInfo*) ioParam ); + + // get the search pane + MSG_Pane *searchPane = mSearchManager.GetMsgPane(); + + // sanity check + #ifdef DEBUG + if( selectedDestination == nil || searchPane == nil ) + throw ; + #endif + + try + { + // move the selected messages + int status = MSG_MoveMessagesIntoFolder( searchPane, moveSelection.GetSelectionList(), + moveSelection.selectionSize, selectedDestination ); + + // Currently the enum eSuccess is doubly defined in two different BE headers. So in order + // to prevent possible conflicts I just check for the succes value ( 0 ) + if ( status != 0 ) + throw mail_exception( eMoveMessageError ); + } + catch( mail_exception& error ) + { + error.DisplaySimpleAlert(); + } + break; + + case msg_GoToFolder: + AssertFail_(mResultsTable != nil); + CMailSelection selection; + mResultsTable->GetSelection(selection); + for (MSG_ViewIndex index = 0; index < selection.selectionSize; index ++) + { + mResultsTable->FindOrCreateThreadWindowForMessage( + selection.GetSelectionList()[index]); + } + + { + // HACK to support opening of more than one folder + EventRecord ignoredEvent = {0}; + LPeriodical::DevoteTimeToIdlers(ignoredEvent); + } + + for (MSG_ViewIndex index = 0; index < selection.selectionSize; index ++) + { + mResultsTable->ShowMessage( + selection.GetSelectionList()[index], + CSearchTableView::kAddToThreadWindowSelection); + } + break; + + case msg_MsgScope: + // get folder info + scopeInfo = ( ( MSG_FolderInfo*) ioParam ); + if (mSearchFolders.GetCount() == 0) + mSearchFolders.InsertItemsAt(1, 1, &scopeInfo); + else + mSearchFolders.AssignItemsAt(1, 1, &scopeInfo); + + // set the scope + mSearchManager.SetSearchScope( static_cast( CSearchManager::cScopeMailSelectedItems ), + &mSearchFolders ); + + SetWinCSID(INTL_DocToWinCharSetID(MSG_GetFolderCSID((MSG_FolderInfo *) ioParam))); + break; + + default: + Inherited::ListenToMessage(inMessage, ioParam); + break; + } +} // CMessageSearchWindow::ListenToMessage + +MSG_ScopeAttribute +CMessageSearchWindow::GetWindowScope() const +{ + return static_cast( CSearchManager::cScopeMailSelectedItems ); +} diff --git a/mozilla/cmd/macfe/MailNews/CMessageSearchWindow.h b/mozilla/cmd/macfe/MailNews/CMessageSearchWindow.h new file mode 100644 index 00000000000..083db8c6a90 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMessageSearchWindow.h @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMessageSearchWindow.h + +#pragma once + +#include "CSearchWindowBase.h" + +class LGAPushButton; +class CFolderScopeGAPopup; +class CFolderMoveGAPopup; + +//====================================== +class CMessageSearchWindow : public CSearchWindowBase +//====================================== +{ +private: + typedef CSearchWindowBase Inherited; +public: + + enum { class_ID = 'SchW', pane_ID = class_ID, res_ID = 8600 }; + + enum { + paneID_GoToFolder = 'GOTO' + ,paneID_FilePopup = 'FILm' + ,paneID_MsgScopePopup = 'SCOP' + }; + + enum { + msg_FilePopup = 'FILm' + ,msg_GoToFolder = 'GOTO' + ,msg_MsgScope = 'ScMs' + }; + + CMessageSearchWindow(LStream *inStream); + virtual ~CMessageSearchWindow(); + virtual void SetUpBeforeSelecting(); + +protected: + virtual MSG_ScopeAttribute GetWindowScope() const; + + virtual void FinishCreateSelf(); + virtual void ListenToMessage(MessageT inMessage, void *ioParam = nil); + virtual ResIDT GetStatusResID() const { return res_ID; } + virtual void MessageWindStop(Boolean inUserAborted); + virtual void MessageWindSearch(); + virtual void UpdateTableStatusDisplay(); + +private: + void EnableSelectionControls(); + void DisableSelectionControls(); + + CFolderScopeGAPopup *mMsgScopePopup; + LGAPushButton *mGoToFolderBtn; + CFolderMoveGAPopup *mFileMessagePopup; + MSG_FolderInfo *scopeInfo; + +}; // class CMessageSearchWindow diff --git a/mozilla/cmd/macfe/MailNews/CMessageView.cp b/mozilla/cmd/macfe/MailNews/CMessageView.cp new file mode 100644 index 00000000000..12fc524a472 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMessageView.cp @@ -0,0 +1,1367 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMessageView.cp + +#include "CMessageView.h" + +#include "CMailNewsContext.h" +#include "CBrowserContext.h" + +#include "resgui.h" +#include "Netscape_Constants.h" +#include "MailNewsgroupWindow_Defines.h" + +#include "CNewsSubscriber.h" +#include "UMessageLibrary.h" +#include "UMailFolderMenus.h" +#include "CMailFolderButtonPopup.h" +#include "CMailProgressWindow.h" +#include "CHTMLClickRecord.h" +#include "CHyperScroller.h" +#include "UOffline.h" + +#include "msg_srch.h" + +#include "uprefd.h" +#include "libi18n.h" // for INTL_CanAutoSelect proto +#include "secnav.h" +#include "CMessageAttachmentView.h" +#include "CApplicationEventAttachment.h" +#include "macutil.h" // TrySetCursor +#include "uerrmgr.h" // GetPString prototype +#include "ufilemgr.h" +#include "URobustCreateWindow.h" +#include "CUrlDispatcher.h" +#include "UDeferredTask.h" + +#include "shist.h" +#include "prefapi.h" + +#include "CURLDispatcher.h" +// The msglib callbacks +// The call backs +extern "C" +{ + + void MacFe_AttachmentCount(MSG_Pane *messagepane, void* closure, + int32 numattachments, XP_Bool finishedloading); + void MacFe_UserWantsToSeeAttachments(MSG_Pane* messagepane, void* closure); +} + +//---------------------------------------------------------------------------------------- +void MacFe_AttachmentCount(MSG_Pane *messagepane, void* /*closure*/, + int32 numattachments, XP_Bool finishedloading) +//---------------------------------------------------------------------------------------- +{ + MWContext * context = MSG_GetContext( messagepane); + CMessageView* messageView =dynamic_cast( context->fe.newView); + + if( messageView ) + { + LWindow* window = LWindow::FetchWindowObject(messageView->GetMacPort()); + + CMessageAttachmentView* attachmentView = + dynamic_cast(window->FindPaneByID('MATv')); + + if( attachmentView ) + { + #if 0 // code automatically shows attachment pane + // should be disabled when the attachment button works + if( numattachments >0 ) + attachmentView->Show(); + #endif + if( finishedloading || attachmentView->IsVisible() ) + { + attachmentView->SetMessageAttachmentList(messagepane, numattachments); + } + } + } +} + +//---------------------------------------------------------------------------------------- +void MacFe_UserWantsToSeeAttachments(MSG_Pane* messagepane, void* /*closure*/) +//---------------------------------------------------------------------------------------- +{ + MWContext * context = MSG_GetContext( messagepane); + CMessageView* messageView =dynamic_cast( context->fe.newView); + + if( messageView ) + { + LWindow* window = LWindow::FetchWindowObject(messageView->GetMacPort()); + + CMessageAttachmentView* attachmentView = + dynamic_cast(window->FindPaneByID('MATv')); + if( attachmentView ) + attachmentView->ToggleVisibility(); + } +} + +MSG_MessagePaneCallbacks MsgPaneCallBacks = { + MacFe_AttachmentCount, + MacFe_UserWantsToSeeAttachments +}; + +#pragma mark - + +//======================================================================================== +class CDeferredMessageViewTask +//======================================================================================== +: public CDeferredTask +{ + public: + CDeferredMessageViewTask(CMessageView* inView); + protected: + Boolean AvailableForDeferredTask() const; +// data + protected: + CMessageView* mMessageView; + UInt32 mEarliestExecuteTime; +}; // class CDeferredLoadKeyTask + + // It's expensive to load a message, and if the user is nervously clicking + // on multiple messages we only want to load the last one. +#define LOAD_MESSAGE_DELAY 15 // was GetDblTime() + +//---------------------------------------------------------------------------------------- +CDeferredMessageViewTask::CDeferredMessageViewTask(CMessageView* inView) +//---------------------------------------------------------------------------------------- +: mMessageView(inView) +, mEarliestExecuteTime(::TickCount() + LOAD_MESSAGE_DELAY) +{ +} + +//---------------------------------------------------------------------------------------- +Boolean CDeferredMessageViewTask::AvailableForDeferredTask() const +//---------------------------------------------------------------------------------------- +{ + // Wait till any pending URLs are finished + CNSContext* context = mMessageView->GetContext(); + if (!context) + return false; + if (XP_IsContextBusy((MWContext*)(*context))) + return false; + if (CMailProgressWindow::GetModal()) + return false; // wait until other modal tasks are done. + // Wait for the earliest execute time. + if (::TickCount() < mEarliestExecuteTime) + return false; + return true; +} // CDeferredMessageViewTask::AvailableForDeferredTask + +#pragma mark - + +//======================================================================================== +class CDeferredLoadKeyTask +//======================================================================================== +: public CDeferredMessageViewTask +{ + public: + CDeferredLoadKeyTask( + CMessageView* inView, + MSG_FolderInfo* inFolder, + MessageKey inKey); + protected: + virtual ExecuteResult ExecuteSelf(); +// data + protected: + MSG_FolderInfo* mFolderToLoadAtIdle; + MessageKey mMessageToLoadAtIdle; +}; // class CDeferredLoadKeyTask + +//---------------------------------------------------------------------------------------- +CDeferredLoadKeyTask::CDeferredLoadKeyTask( + CMessageView* inView, + MSG_FolderInfo* inFolder, + MessageKey inKey) +//---------------------------------------------------------------------------------------- +: CDeferredMessageViewTask(inView) +, mFolderToLoadAtIdle(inFolder) +, mMessageToLoadAtIdle(inKey) +{ +} + +//---------------------------------------------------------------------------------------- +CDeferredTask::ExecuteResult CDeferredLoadKeyTask::ExecuteSelf() +//---------------------------------------------------------------------------------------- +{ + if (!AvailableForDeferredTask()) + return eWaitStayFront; + mMessageView->ShowMessage( + CMailNewsContext::GetMailMaster(), + mFolderToLoadAtIdle, + mMessageToLoadAtIdle, + true); + return eDoneDelete; +} // CDeferredLoadKeyTask::ExecuteSelf + +#pragma mark - + +//======================================================================================== +class CDeferredLoadURLTask +//======================================================================================== +: public CDeferredMessageViewTask +{ + public: + CDeferredLoadURLTask( + CMessageView* inView, + const char* inURLToLoadAtIdle); + virtual ~CDeferredLoadURLTask(); + protected: + virtual ExecuteResult ExecuteSelf(); +// data + protected: + const char* mURLToLoadAtIdle; +}; // class CDeferredLoadURLTask + +//---------------------------------------------------------------------------------------- +CDeferredLoadURLTask::CDeferredLoadURLTask( + CMessageView* inView, + const char* inURLToLoadAtIdle) +//---------------------------------------------------------------------------------------- +: CDeferredMessageViewTask(inView) +, mURLToLoadAtIdle(XP_STRDUP(inURLToLoadAtIdle)) +{ +} + +//---------------------------------------------------------------------------------------- +CDeferredLoadURLTask::~CDeferredLoadURLTask() +//---------------------------------------------------------------------------------------- +{ + XP_FREEIF((char*)mURLToLoadAtIdle); +} + +//---------------------------------------------------------------------------------------- +CDeferredTask::ExecuteResult CDeferredLoadURLTask::ExecuteSelf() +//---------------------------------------------------------------------------------------- +{ + if (!AvailableForDeferredTask()) + return eWaitStayFront; + mMessageView->ShowURLMessage(mURLToLoadAtIdle, true); + return eDoneDelete; +} // CDeferredLoadURLTask::ExecuteSelf + +#pragma mark - + +//---------------------------------------------------------------------------- +CMessageView::CMessageView(LStream* inStream) +// --------------------------------------------------------------------------- +#ifdef INHERIT_FROM_BROWSERVIEW +: CBrowserView(inStream) +#else +: CHTMLView(inStream) +#endif +, mMessagePane(nil) +, mMasterCommander(nil) +, mClosing(false) +, mAttachmentView(nil) +, mLoadingNakedURL(false) +, mMotionPendingCommand((MSG_MotionType)-1) +, mDeferredCloseTask(nil) +{ +} // CMessageView::CMessageView + +// --------------------------------------------------------------------------- +CMessageView::~CMessageView() +// --------------------------------------------------------------------------- +{ + mClosing = true; + + if (mMessagePane) + { + MSG_SetMessagePaneCallbacks( mMessagePane, NULL, NULL); + MSG_DestroyPane(mMessagePane); + } +} // CMessageView::~CMessageView + +// --------------------------------------------------------------------------- +void CMessageView::FinishCreateSelf() +// --------------------------------------------------------------------------- +{ + Inherited::FinishCreateSelf(); + SetDefaultScrollMode(LO_SCROLL_YES); // always display scrollbars + SetEraseBackground(FALSE); // don't erase background when browsing mails +} + +// --------------------------------------------------------------------------- +Boolean CMessageView::IsDueToCloseLater() const +// --------------------------------------------------------------------------- +{ + return mDeferredCloseTask != nil; +} + +// --------------------------------------------------------------------------- +void CMessageView::SetDueToCloseLater() +// --------------------------------------------------------------------------- +{ + mDeferredCloseTask = CDeferredCloseTask::DeferredClose(this); +} + + +// --------------------------------------------------------------------------- +void CMessageView::ShowMessage( + MSG_Master* inMsgMaster, + MSG_FolderInfo* inMsgFolderInfo, + MessageKey inMessageKey, + Boolean inLoadNow) +// --------------------------------------------------------------------------- +{ + // There has to be a folder, unless we're clearing the message. + Assert_(inMsgMaster && (inMsgFolderInfo || inMessageKey == MSG_MESSAGEKEYNONE)); + if (!inLoadNow) + { + CDeferredLoadKeyTask* task = new CDeferredLoadKeyTask(this, inMsgFolderInfo, inMessageKey); + CDeferredTaskManager::Post1(task, this); + return; + } + if (!mMessagePane) + { + mMessagePane = ::MSG_CreateMessagePane(*mContext, inMsgMaster); + ThrowIfNULL_(mMessagePane); + ::MSG_SetFEData(mMessagePane, CMailCallbackManager::Get()); + CMailCallbackListener::SetPane( mMessagePane ); + ::MSG_SetMessagePaneCallbacks( mMessagePane, &MsgPaneCallBacks, NULL); + + } + if (inMessageKey != GetCurMessageKey()) + { + if( mAttachmentView ) + { + mAttachmentView->ClearMessageAttachmentView(); + mAttachmentView->Hide(); + } + // ::SetCursor(*::GetCursor(watchCursor)); + mLoadingNakedURL = false; + + if (::MSG_LoadMessage(mMessagePane, inMsgFolderInfo, inMessageKey) != 0) + { + CloseLater(); + throw (OSErr)memFullErr; + } + if (inMessageKey == MSG_MESSAGEKEYNONE) + ClearMessageArea(); // please read comments in that routine. + } +} // CMessageView::ShowMessage + +// --------------------------------------------------------------------------- +void CMessageView::ClearMessageArea() +// MSG_LoadMessage, with MSG_MESSAGEKEYNONE calls msg_ClearMessageArea, which +// has a bug (drawing the background in grey). So I just changed MSG_LoadMessage() to +// do nothing for XP_MAC except set its m_Key to MSG_MESSAGEKEYNONE and exit. This transfers +// the responsibility for clearing the message area to the front end. So far, so good. +// +// Now, it's no good just painting the area, because the next refresh will redraw using the +// existing history entry. So somehow we have to remove the history entry. +// There's no API for doing this, except calling SHIST_AddDocument with an entry whose +// address string is null or empty. +// +// If this state of affairs changes, this code will break, but I put in asserts to +// notify us about it. 98/01/21 +// --------------------------------------------------------------------------- +{ + if ( XP_IsContextBusy((MWContext*)(*mContext)) ) // #107826 + return; + LO_DiscardDocument(*mContext); // Necessary cleanup. See also CThreadView::UpdateHistoryEntry(). + // To create a URL_Struct, you have to pass a non-null string for the address, or it returns nil. + // Next-best thing is an empty string, which works. + URL_Struct* url = NET_CreateURLStruct("", NET_NORMAL_RELOAD); + Assert_(url); + History_entry* newEntry = ::SHIST_CreateHistoryEntry(url, "Nobody Home"); + XP_FREEIF(url); + Assert_(newEntry); + // Using an empty address string will cause "AddDocument" to do a removal of the old entry, + // then delete the new entry, and exit. + ::SHIST_AddDocument(*mContext, newEntry); + Assert_(!mContext->GetCurrentHistoryEntry()); // Yay! That was the point of all this. + Refresh(); + // HTMLView will now clear the background on redraw. +} // CMessageView::ClearMessageArea + +// --------------------------------------------------------------------------- +void CMessageView::AdjustCursorSelf(Point inPortPt, const EventRecord& inMacEvent) +// --------------------------------------------------------------------------- +{ + if (GetCurMessageKey() == MSG_MESSAGEKEYNONE) + ::SetCursor(&qd.arrow); + else if (mContext && XP_IsContextBusy((MWContext*)(*mContext))) + ::SetCursor(*::GetCursor(watchCursor)); + else + Inherited::AdjustCursorSelf(inPortPt, inMacEvent); +} // CMessageView::AdjustCursorSelf + +// --------------------------------------------------------------------------- +void CMessageView::ShowURLMessage( + const char* inURL, + Boolean inLoadNow) +// --------------------------------------------------------------------------- +{ + Assert_(inURL); + if (!inLoadNow) + { + CDeferredLoadURLTask* task = new CDeferredLoadURLTask(this, inURL); + CDeferredTaskManager::Post1(task, this); + return; + } + if (!mMessagePane) + { + mMessagePane = MSG_CreateMessagePane(*mContext, CMailNewsContext::GetMailMaster()); + ThrowIfNULL_(mMessagePane); + MSG_SetFEData(mMessagePane, CMailCallbackManager::Get()); + CMailCallbackListener::SetPane( mMessagePane ); + MSG_SetMessagePaneCallbacks(mMessagePane, &MsgPaneCallBacks, NULL); + } + ::SetCursor(*::GetCursor(watchCursor)); + SetDefaultCSID(mContext->GetDefaultCSID()); + URL_Struct* urls = NET_CreateURLStruct(inURL, NET_DONT_RELOAD); + ThrowIfNULL_(urls); + urls->msg_pane = mMessagePane; + mContext->SwitchLoadURL(urls, FO_CACHE_AND_PRESENT); + mLoadingNakedURL = true; // so we can synthesise FE_PaneChanged on AllConnectionsComplete. + //FE_PaneChanged(GetMessagePane(), false, MSG_PaneNotifyMessageLoaded, 0); + +} // CMessageView::ShowURLMessage + +// --------------------------------------------------------------------------- +void CMessageView::ShowSearchMessage( + MSG_Master *inMsgMaster, + MSG_ResultElement *inResult, + Boolean inNoFolder) +// --------------------------------------------------------------------------- +{ + Assert_(inMsgMaster); + if (!mMessagePane) + { + mMessagePane = MSG_CreateMessagePane(*mContext, inMsgMaster); + ThrowIfNULL_(mMessagePane); + MSG_SetFEData(mMessagePane, CMailCallbackManager::Get()); + } + mLoadingNakedURL = inNoFolder; + MSG_SearchError error = MSG_OpenResultElement(inResult, mMessagePane); + if ( error != SearchError_Success ) { + if ( (error == SearchError_OutOfMemory) || (error == SearchError_NullPointer) ) { + FailOSErr_(memFullErr); + } else { + FailOSErr_(paramErr); + } + } +} // CMessageView::ShowSearchMessage + +// --------------------------------------------------------------------------- +void CMessageView::FileMessageToSelectedPopupFolder(const char *ioFolderName, + Boolean inMoveMessages) +// File selected messages to the specified BE folder. If inMoveMessages is true, move +// the messages, otherwise copy them. +// --------------------------------------------------------------------------- +{ +#ifdef Debug_Signal + const char *curName = ::MSG_GetFolderNameFromID(GetFolderInfo()); + Assert_(strcasecomp(curName, ioFolderName) != 0); + // Specified folder should not be the same as this folder +#endif // Debug_Signal + + if ( GetFolderFlags() & MSG_FOLDER_FLAG_NEWSGROUP ) + inMoveMessages = false; + + MSG_ViewIndex index = GetCurMessageViewIndex(); + if ( inMoveMessages ) { + ::MSG_MoveMessagesInto(GetMessagePane(), &index, 1, ioFolderName); + } else { + ::MSG_CopyMessagesInto(GetMessagePane(), &index, 1, ioFolderName); + } +} // CThreadWindow::FileMessagesToSelectedPopupFolder + +// --------------------------------------------------------------------------- +void CMessageView::YieldToMaster() +// An ugly solution, but after trying many, it's the only one that worked. +// make sure this view isn't target if there's a thread view in the same window. +// --------------------------------------------------------------------------- +{ + if (mMasterCommander) + SwitchTarget(mMasterCommander); +} + +// --------------------------------------------------------------------------- +void CMessageView::ClickSelf(const SMouseDownEvent& where) +// make sure a message view isn't target if there's a thread view. +// 97/10/21. In Gromit (v5.0), we don't do this any more. +// --------------------------------------------------------------------------- +{ + Inherited::ClickSelf(where); +// YieldToMaster(); +} // CMessageView::ClickSelf + +// --------------------------------------------------------------------------- +void CMessageView::FindCommandStatus( + CommandT inCommand, + Boolean& outEnabled, + Boolean& outUsesMark, + Char16& outMark, + Str255 outName) +// --------------------------------------------------------------------------- +{ + outUsesMark = false; + outEnabled = false; + if (mClosing) // don't respond to BE callbacks when being destroyed + return; + // Need to this up here other wise the HTMLView gets a crack at this command + if( inCommand == cmd_SecurityInfo ) + { + if( mMessagePane ) + outEnabled = ( GetCurMessageKey() != MSG_MESSAGEKEYNONE ); + return; + } + + if (mMessagePane && mContext) + { + if (XP_IsContextBusy((MWContext*)(*mContext))) + { + // Disable everything except the "stop" button + // (and cmd_About: kludge for BUG#68886) + // + // It is of course really bad that the recalculation + // of command status is being short-circuited like this. + // TODO: Someone should fix this the right way. + if (inCommand == cmd_Stop) + { + outEnabled = true; + ::GetIndString(outName, STOP_STRING_LIST, STOP_LOADING_INDEX ); + } + else if (inCommand == cmd_About) + { + outEnabled = true; + } + return; + } + + + switch (inCommand) + { + // Should always be enabled + case cmd_SubscribeNewsgroups: + outEnabled = true; + return; + + case cmd_ViewSource: + case cmd_DocumentInfo: + if (GetCurMessageKey() == MSG_MESSAGEKEYNONE) + return; + break; + + case cmd_Stop: + { + // Note that the context is not busy, but "stop" might mean "stop animations" + if (mContext->IsContextLooping()) + { + outEnabled = true; + ::GetIndString(outName, STOP_STRING_LIST, STOP_ANIMATIONS_INDEX ); + } + return; + } + + } // switch + if (inCommand >= ENCODING_BASE && inCommand < ENCODING_CEILING) + { + // yeah, I know, this code isn't pretty, but I copied and + // pasted this from Akbar (v3.0) + outEnabled = true; + outUsesMark = true; + + Int16 csid = CPrefs::CmdNumToDocCsid( inCommand ); + outMark = (csid == mContext->GetDefaultCSID()) ? checkMark : ' '; + return; + } + if (mLoadingNakedURL) + { + // Do NOT allow any message library commands + Inherited::FindCommandStatus(inCommand, outEnabled, outUsesMark, + outMark, outName); + return; + } + if (UMessageLibrary::FindMessageLibraryCommandStatus( + GetMessagePane(), + NULL, + 0, + inCommand, + outEnabled, + outUsesMark, + outMark, + outName)) + return; + MSG_MotionType cmd = UMessageLibrary::GetMotionType(inCommand); + if (UMessageLibrary::IsValidMotion(cmd)) + { + XP_Bool selectable; + outEnabled = ( + mMessagePane + && MSG_NavigateStatus(mMessagePane, cmd, GetCurMessageViewIndex(), &selectable, NULL) == 0 + && selectable + ); + outUsesMark = false; + return; + } + else if ( (inCommand == cmd_MoveMailMessages) || (inCommand == cmd_CopyMailMessages) || + CMailFolderSubmenu::IsMailFolderCommand(&inCommand) ) + { + // Mail folder commands + outEnabled = true; + return; + } + } + Inherited::FindCommandStatus(inCommand, outEnabled, outUsesMark, + outMark, outName); +} // CMessageView::FindCommandStatus + +// --------------------------------------------------------------------------- +Boolean CMessageView::ObeyMotionCommand(MSG_MotionType cmd) +// --------------------------------------------------------------------------- +{ + if (!mMessagePane) return false; + Assert_(UMessageLibrary::IsValidMotion(cmd)); + try + { + MSG_ViewIndex resultIndex; + MessageKey resultKey; + MSG_FolderInfo* finfo; + if (MSG_ViewNavigate( + mMessagePane, + cmd, + GetCurMessageViewIndex(), + &resultKey, + &resultIndex, + NULL, + &finfo) == 0) + { + /*ThrowIfError_(MSG_LoadMessage( + mMessagePane, + finfo ? finfo : GetFolderInfo(), + resultKey));*/ + if (resultKey != MSG_MESSAGEKEYNONE) + { + ShowMessage( + CMailNewsContext::GetMailMaster(), + finfo ? finfo : GetFolderInfo(), + resultKey); + } + else if ( finfo != NULL ) + { + switch( cmd ) + { + case MSG_NextFolder: + case MSG_NextMessage: + mMotionPendingCommand = MSG_FirstMessage; + break; + case MSG_NextUnreadMessage: + case MSG_NextUnreadThread: + case MSG_NextUnreadGroup: + case MSG_LaterMessage: + case (MSG_MotionType)MSG_ToggleThreadKilled: + mMotionPendingCommand = MSG_NextUnreadMessage; + break; + default: + break; + } + MSG_LoadFolder( mMessagePane, finfo ); + } + return true; + } + } + catch (...) + { + SysBeep(1); + } + return false; + +} // CMessageView::ObeyMotionCommand + +//---------------------------------------------------------------------------------------- +void CMessageView::CloseLater() +//---------------------------------------------------------------------------------------- +{ + // Oh, I wish: + //((MWContext*)*mContext)->msgCopyInfo->moveState.nextKeyToLoad = MSG_MESSAGEKEYNONE; + SetDueToCloseLater(); // delete me next time + StartIdling(); +} // CMessageView::CloseLater + +//---------------------------------------------------------------------------------------- +Boolean CMessageView::MaybeCloseLater(CommandT inCommand) +// This checks that we are a stand-alone message window, then, according to the preference, +// marks the window for death on the next idle. +//---------------------------------------------------------------------------------------- +{ + // If we are not a stand-alone pane, don't do this. To detect if we're stand-alone, use + // mMasterCommander, which will be set to the thread view if we're in a multipane view. + if (mMasterCommander) + return false; + const char* prefName = nil; + if (inCommand == cmd_Clear) + prefName = "mail.close_message_window.on_delete"; + else if (inCommand == cmd_MoveMailMessages || inCommand == cmd_CopyMailMessages) + prefName = "mail.close_message_window.on_file"; + else + return false; + XP_Bool closeOption; + if (PREF_GetBoolPref("mail.close_message_window.on_delete", &closeOption) == PREF_NOERROR + && closeOption) + { + CloseLater(); + return true; + } + return false; +} // CMessageView::MaybeCloseLater + +// --------------------------------------------------------------------------- +Boolean CMessageView::ObeyCommand(CommandT inCommand, void *ioParam) +// --------------------------------------------------------------------------- +{ + if (!mContext) + return false; + if (mClosing) // don't respond to BE callbacks when being destroyed + return false; + if (inCommand != cmd_Stop && XP_IsContextBusy((MWContext*)(*mContext))) + return false; + + // If you're reading a discussion message, and you want to compose a new message, the new + // message is, by default, addressed to that discussion group... this test and reassignment + // are necessary to make this happen + if ( (inCommand == cmd_NewMailMessage) && (GetFolderFlags() & MSG_FOLDER_FLAG_NEWSGROUP) ) { + inCommand = cmd_PostNew; + } + + switch (inCommand) + { + case msg_TabSelect: + return (GetCurMessageKey()); // Allow selection only if a message is loaded. + case cmd_Stop: + { + TrySetCursor(watchCursor); + XP_InterruptContext(*mContext); + SetCursor( &qd.arrow ); + return true; + } + + // Implement this here instead of browser view since for browser windows we want + // the benefit of AE recording. The AE sending/receiving code is currenty in + // CBrowserWindow and we don't want the view to know about the window. + case cmd_Reload: + { + if (GetContext()) + { + CBrowserContext* theTopContext = GetContext()->GetTopContext(); + + if (CApplicationEventAttachment::CurrentEventHasModifiers(optionKey) || + CApplicationEventAttachment::CurrentEventHasModifiers(shiftKey)) + { + theTopContext->LoadHistoryEntry(CBrowserContext::index_Reload, true); + } + else + { + theTopContext->LoadHistoryEntry(CBrowserContext::index_Reload); + } + } + + return true; + } + break; + + case cmd_SecurityInfo: + { + URL_Struct* url = MSG_ConstructUrlForMessage(GetMessagePane(), GetCurMessageKey()); + SECNAV_SecurityAdvisor((MWContext*)*(GetContext()), url ); + NET_FreeURLStruct(url); + return true; + } + + case cmd_SubscribeNewsgroups: + { + MSG_Host* selectedHost = MSG_GetHostForFolder(GetFolderInfo()); + CNewsSubscriber::DoSubscribeNewsGroup(selectedHost); + return true; + } + + + #if 0 // CHTMLView handles this + case cmd_SAVE_LINK_AS: + { + + // Convert cmd_SAVE_LINK_AS to cmd_OPEN_LINK for an attachment link. + // This is a temporary fix for the issue that "Save link as" will save + // mime otherwise. A better solution would be to have a different command + // in the context menu (cmd_SAVE_ATTACHMENT_AS). + CHTMLClickRecord* cr = mCurrentClickRecord; // for brevity. + const char* url = cr->GetClickURL(); + if (!strncasecomp (url, "mailbox:", 8)) + { + if (XP_STRSTR(url, "?part=") || XP_STRSTR(url, "&part=")) + { + // Yep, mail attachment. + URL_Struct* theURL = NET_CreateURLStruct( url, NET_DONT_RELOAD); + ThrowIfNULL_(theURL); + FSSpec locationSpec; + memset(&locationSpec, 0, sizeof(locationSpec)); + CURLDispatcher::GetURLDispatcher()->DispatchToStorage( + theURL, locationSpec, FO_SAVE_AS, true); + return true; + } + } + break; + + } + #endif // 0 + } // switch + Boolean cmdHandled = false; + MSG_CommandType cmd = UMessageLibrary::GetMSGCommand(inCommand); + if (UMessageLibrary::IsValidCommand(cmd)) + { + // If there is a the thread view (we're not really supposed to know about it, :-) ) + // then give it first crack. + if (mMasterCommander && mMasterCommander->ObeyCommand(inCommand, ioParam)) + return true; + Boolean enabled; Boolean usesMark; Char16 mark; CStr255 commandName; + + if (! UMessageLibrary::FindMessageLibraryCommandStatus( + GetMessagePane(), + (MSG_ViewIndex*)nil, + 0, + inCommand, enabled, usesMark, mark, commandName) + || !enabled) + return false; + try + { + if (cmd == MSG_GetNewMail || cmd == MSG_GetNextChunkMessages) + { + if (NET_IsOffline()) + { + // Bug #105393. This fails unhelpfully if the user is offline. There + // used to be a test for this here, but for some reason it was + // removed. This being so, the newly agreed-upon fix is that, if + // the user requests new messages while offline, we should instead + // present the "Go Online" dialog. See also CMailFlexTable.cp. + // - 98/02/10 jrm. + PREF_SetBoolPref("offline.download_discussions", cmd==MSG_GetNextChunkMessages); + PREF_SetBoolPref("offline.download_mail", cmd==MSG_GetNewMail); + UOffline::ObeySynchronizeCommand(); + } + else + { + CMailProgressWindow::ObeyMessageLibraryCommand( + CMailProgressWindow::res_ID_modeless, + GetMessagePane(), + cmd, + commandName); + } + } + else + { + MSG_Command(GetMessagePane(), cmd, nil, 0); + MaybeCloseLater(inCommand); + } + } + catch(...) + { + } + cmdHandled = true; + } + if (!cmdHandled) + { + MSG_MotionType mcmd = UMessageLibrary::GetMotionType(inCommand); + if (UMessageLibrary::IsValidMotion(mcmd)) + { + if (mMasterCommander) // give these to the thread view (for select after delete) + return mMasterCommander->ObeyCommand(inCommand, ioParam); + cmdHandled = ObeyMotionCommand(mcmd); + } + + } + if (!cmdHandled) + { + // Mail folder commands. We come here either from a button (broadcast) or + // from a menu command (synthetic). + const char* folderPath = nil; + if (inCommand == cmd_MoveMailMessages || inCommand == cmd_CopyMailMessages) + { + // Button case + if ( ioParam ) + folderPath = ::MSG_GetFolderNameFromID((MSG_FolderInfo*)ioParam); + } + else if (!CMailFolderSubmenu::IsMailFolderCommand(&inCommand, &folderPath)) // menu case + { + folderPath = nil; // any other case. + } + if (folderPath && *folderPath) + { + if (mMasterCommander) // give these to the thread view (for select after delete) + return mMasterCommander->ObeyCommand(inCommand, ioParam); + FileMessageToSelectedPopupFolder( + folderPath, + inCommand == cmd_MoveMailMessages); + MaybeCloseLater(inCommand); + return true; + } + } + + if (!cmdHandled) + { + switch ( inCommand ) + { + case cmd_SaveDefaultCharset: + { + Int32 default_csid = mContext->GetDefaultCSID(); + CPrefs::SetLong(default_csid, CPrefs::DefaultCharSetID); + cmdHandled = true; + } + break; + + default: + { + if ( inCommand > ENCODING_BASE && inCommand < ENCODING_CEILING ) + { + SetDefaultCSID(CPrefs::CmdNumToDocCsid(inCommand)); + cmdHandled = true; + } + } + } + } + if (!cmdHandled) + cmdHandled = Inherited::ObeyCommand(inCommand, ioParam); + return cmdHandled; +} // CMessageView::ObeyCommand + +// --------------------------------------------------------------------------- +void CMessageView::ListenToMessage(MessageT inMessage, void* ioParam) +// --------------------------------------------------------------------------- +{ + switch(inMessage) + { + case msg_NSCAllConnectionsComplete: + SetCursor(&UQDGlobals::GetQDGlobals()->arrow); + if (mLoadingNakedURL) + FE_PaneChanged(GetMessagePane(), false, MSG_PaneNotifyMessageLoaded, 0); + if( mMotionPendingCommand != (MSG_MotionType)-1 ) + { + ObeyMotionCommand( mMotionPendingCommand ); + mMotionPendingCommand = (MSG_MotionType)-1; + } + // Need to enable the menus since we may just have loaded a new message using imap + SetUpdateCommandStatus( true ); + break; + + /* + case cmd_SecurityInfo: + case cmd_Reload: + case cmd_Stop: + ObeyCommand(inMessage, ioParam); + return; + */ + + default: + if (!IsOnDuty() || !ObeyCommand(inMessage, ioParam)) + Inherited::ListenToMessage(inMessage, ioParam); + } // switch + + CMailCallbackListener::ListenToMessage(inMessage, ioParam); +} // CMessageView::ListenToMessage + +// --------------------------------------------------------------------------- +Boolean CMessageView::HandleKeyPress(const EventRecord& inKeyEvent) +// --------------------------------------------------------------------------- +{ + if (inKeyEvent.what == keyUp) + return false; + Char16 c = inKeyEvent.message & charCodeMask; + switch (c) + { + case char_Backspace: + case char_FwdDelete: + ObeyCommand(cmd_Clear, NULL); + return true; + + case char_Space: + CHyperScroller* messageViewScroller = GetScroller(); + Boolean shiftKeyDown = (inKeyEvent.modifiers & shiftKey) != 0; + + if ( messageViewScroller ) + { + if ( !shiftKeyDown && messageViewScroller->ScrolledToMaxVerticalExtent() ) + { + CDeferredCommand* deferredGoCommand = new CDeferredCommand(mMasterCommander, cmd_NextMessage, NULL); + CDeferredTaskManager::Post1(deferredGoCommand, this); + return true; + } + + if ( shiftKeyDown && messageViewScroller->ScrolledToMinVerticalExtent() ) + { + CDeferredCommand* deferredGoCommand = new CDeferredCommand(mMasterCommander, cmd_PreviousMessage, NULL); + CDeferredTaskManager::Post1(deferredGoCommand, this); + return true; + } + } + // FALL THROUGH + + default: + Inherited::HandleKeyPress(inKeyEvent); + return true; + } // switch + return false; +} // CMessageView::HandleKeyPress + +// --------------------------------------------------------------------------- +MessageKey CMessageView::GetCurMessageKey() const +// --------------------------------------------------------------------------- +{ + MessageKey result = MSG_MESSAGEKEYNONE; + if (mMessagePane && !mLoadingNakedURL) + ::MSG_GetCurMessage(mMessagePane, NULL, &result, NULL); + return result; +} // CMessageView::GetCurMessageKey + +// --------------------------------------------------------------------------- +MSG_FolderInfo* CMessageView::GetFolderInfo() const +// --------------------------------------------------------------------------- +{ + MSG_FolderInfo* result = NULL; + if (mMessagePane) + ::MSG_GetCurMessage(mMessagePane, &result, NULL, NULL); + return result; +} // CMessageView::GetFolderInfo + +// --------------------------------------------------------------------------- +MSG_ViewIndex CMessageView::GetCurMessageViewIndex() const +// --------------------------------------------------------------------------- +{ + MSG_ViewIndex result; + ::MSG_GetCurMessage(mMessagePane, NULL, NULL, &result); + return result; +} // CMessageView::GetCurMessageViewIndex + +// --------------------------------------------------------------------------- +uint32 CMessageView::GetFolderFlags() const +// --------------------------------------------------------------------------- +{ + MSG_FolderInfo* folderInfo = GetFolderInfo(); + if (folderInfo) + { + MSG_FolderLine folderLine; + MSG_GetFolderLineById(CMailNewsContext::GetMailMaster(), folderInfo, &folderLine); + return folderLine.flags; + } + return 0; +} // CMessageView::GetFolderFlags + +// --------------------------------------------------------------------------- +uint32 CMessageView::GetCurMessageFlags() const +// --------------------------------------------------------------------------- +{ + MessageKey key = GetCurMessageKey(); + if (key != MSG_MESSAGEKEYNONE) + { + MSG_MessageLine info; + ::MSG_GetThreadLineById(mMessagePane, key, &info); + return info.flags; + } + return 0; +} // CMessageView::GetCurMessageFlags + +enum { MSG_PaneNotifyOKToLoadNewMessage = 999 }; // also in msgmpane.cpp + +// --------------------------------------------------------------------------- +void CMessageView::PaneChanged( + MSG_Pane*, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value) +// --------------------------------------------------------------------------- +{ + switch (inNotifyCode) + { + case MSG_PaneNotifyOKToLoadNewMessage: + *(XP_Bool*)value = !IsDueToCloseLater(); + return; + case MSG_PaneNotifyFolderLoaded: + /*if (mMotionPendingCommand != (MSG_MotionType)-1) + { + ObeyMotionCommand( mMotionPendingCommand ); + mMotionPendingCommand = (MSG_MotionType)-1; + }*/ + break; + + case MSG_PaneNotifyMessageLoaded: + if (!mMasterCommander) + { + // If we're in a a stand-alone message window, we're responsible for + // this. Otherwise, the thread view is. + if (MSG_GetBacktrackState(GetMessagePane()) == MSG_BacktrackIdle) + { + MSG_AddBacktrackMessage( + GetMessagePane(), + GetFolderInfo(), + GetCurMessageKey()); + } + else + MSG_SetBacktrackState(GetMessagePane(), MSG_BacktrackIdle); + // Need to enable the menus since we may just have loaded a new message using imap + SetUpdateCommandStatus(true); + } + break; + case MSG_PaneNotifyFolderDeleted: + if ((MSG_FolderInfo*)value != GetFolderInfo()) + break; + // else fall through + case MSG_PaneNotifyMessageDeleted: + // As of 98/01/23, this notification is never received. + LWindow* win = LWindow::FetchWindowObject(GetMacPort()); + if (win) + win->AttemptClose(); + break; + case MSG_PaneNotifyCopyFinished: + // This means that a copy that was run in this messagepane has completed. + MaybeCloseLater(cmd_MoveMailMessages); + break; + default: + break; + } +} // CMessageView::PaneChanged + +// --------------------------------------------------------------------------- +void CMessageView::SetContext(CBrowserContext* inNewContext) +// --------------------------------------------------------------------------- +{ + if (mAttachmentView) + { + mAttachmentView->ClearMessageAttachmentView(); // before we destroy the pane! + mAttachmentView->Hide(); + } + // If this is a "set to nil" call, then we must call MSG_DestroyPane + // before calling the inherited method, because MSG_Lib will attempt + // to call back into the context during destruction.... + if (!inNewContext && mMessagePane) + { + // Interrupting the context later after the message pane is destroyed can + // cause problems. + // Bug #106218 (crash closing before load finished). + // Before calling interrupt context, make sure it doesn't call us back. Previously, + // this next call was after the call to XP_InterruptContext() -- jrm 98/02/13 + ::MSG_SetMessagePaneCallbacks(mMessagePane, NULL, NULL); + if( mContext ) + XP_InterruptContext(*mContext); + ::MSG_DestroyPane(mMessagePane); + mMessagePane = nil; + } + Inherited::SetContext(inNewContext); +} // CMessageView::SetContext + +// --------------------------------------------------------------------------- +void CMessageView::InstallBackgroundColor() +// Overridden to use a white background +// --------------------------------------------------------------------------- +{ + // ¥ install the user's default solid background color & pattern + memset(&mBackgroundColor, 0xFF, sizeof(mBackgroundColor)); // white is default +} + +// --------------------------------------------------------------------------- +void CMessageView::SetBackgroundColor( + Uint8 /*inRed*/, + Uint8 /*inGreen*/, + Uint8 /*inBlue*/) +// Overridden to use a white background +// --------------------------------------------------------------------------- +{ + // Do not allow the layout weenies to clobber mail message backgrounds. + Inherited::SetBackgroundColor(0xFF, 0xFF, 0xFF); // white is default +} + +// --------------------------------------------------------------------------- +Boolean CMessageView::SetDefaultCSID(Int16 default_csid, Boolean forceRepaginate /* = false */) +// --------------------------------------------------------------------------- +{ + if (mContext && default_csid != mContext->GetDefaultCSID()) + mContext->SetDocCSID(default_csid); + + Inherited::SetDefaultCSID(default_csid); // ¥¥¥ do we want the repaginate fix?? + return true; +} // CMessageView::SetDefaultCSID + +// --------------------------------------------------------------------------- +void CMessageView::GetDefaultFileNameForSaveAs(URL_Struct* url, CStr31& defaultName) +// --------------------------------------------------------------------------- +{ + // Overridden by CMessageView to use the message subject. + MSG_MessageLine info; + MSG_GetThreadLineById(GetMessagePane(), GetCurMessageKey(), &info); + // remove useless and bad characters. + char buf[32]; + char* dst = buf; + char* src = info.subject; + size_t len = strlen(src); + char* srcEnd = src + len - 1; + while (*srcEnd == ']') + { + *srcEnd-- = '\0'; + len--; + } + while (true) + { + char* bracket = strchr(src, '['); + if (bracket) + { + len -= (bracket - src + 1); + src = bracket + 1; + continue; + } + break; + } + + char* lastPlaceInBuffer = buf+31; + char c; + do + { + c = *src++; + // rough stab at removing junk from the subject. + if (c == ':' || c == '[' || c == ']') + continue; + *dst++ = c; + } while (c && dst < lastPlaceInBuffer ); + defaultName = buf; // CStr31 does the truncation. + if (defaultName.Length() == 0) + Inherited::GetDefaultFileNameForSaveAs(url, defaultName); +} // CMessageView::GetDefaultFileNameForSaveAs + + +// 97-06-18 +// This method gets called when window changes size. +// Make sure we call mContext->Repaginate() +// In mail/news call reload method to NET_NORMAL_RELOAD +// ** NOTE: ** +// This method goes around CHTMLView::AdaptToSuperFrameSize, so if that code +// changes, we should make the appropriate changes here also. +// --------------------------------------------------------------------------- +void CMessageView::AdaptToSuperFrameSize( +// --------------------------------------------------------------------------- + Int32 inSurrWidthDelta, + Int32 inSurrHeightDelta, + Boolean inRefresh) +{ + LView::AdaptToSuperFrameSize(inSurrWidthDelta, inSurrHeightDelta, inRefresh); + + if (IsRootHTMLView()) + { + if ((mContext != NULL) && ((inSurrWidthDelta != 0) || (inSurrHeightDelta != 0))) + mContext->Repaginate(NET_NORMAL_RELOAD); + } +} + +// --------------------------------------------------------------------------- +void CMessageView::DispatchURL( + URL_Struct* inURLStruct, + CNSContext* inTargetContext, + Boolean inDelay, + Boolean inForceCreate, + FO_Present_Types inOutputFormat) +// --------------------------------------------------------------------------- +{ + Inherited::DispatchURL(inURLStruct, inTargetContext, inDelay, inForceCreate, inOutputFormat); +} + +// --------------------------------------------------------------------------- +void CMessageView::DispatchURL(CURLDispatchInfo* inDispatchInfo) +// --------------------------------------------------------------------------- +{ + Boolean openBrowserWindow = true; + const char* urlOfLink = inDispatchInfo->GetURL(); + + char* mimeType = MimeGetURLContentType((MWContext *)(*mContext), urlOfLink); + + if (mimeType) + { + CMimeMapper* mapper = CPrefs::sMimeTypes.FindMimeType( mimeType ); + if (mapper) + switch (mapper->GetLoadAction()) { + case CMimeMapper::Save: + case CMimeMapper::Launch: + case CMimeMapper::Unknown: + openBrowserWindow = false; + break; + } + if (!XP_STRCMP( mimeType, MULTIPART_APPLEDOUBLE )) + openBrowserWindow = false; + + XP_FREE(mimeType); + } + + if (!openBrowserWindow) { + // Decide where to put the file... + CStr31 fileName; + OSErr err; + FSSpec parentFolder; + FSSpec destSpec; + + fe_FileNameFromContext(*mContext, urlOfLink, fileName); + parentFolder = CPrefs::GetFilePrototype( CPrefs::DownloadFolder ); + err = CFileMgr::UniqueFileSpec( parentFolder, fileName, destSpec ); + ThrowIfError_(err); + + + + inDispatchInfo->SetFileSpec(destSpec); + CBrowserContext* theContext = nil; + CDownloadProgressWindow* theProgressWindow = nil; + try + { + theContext = new CBrowserContext(MWContextSaveToDisk); + ThrowIfNULL_(theContext); + theProgressWindow = dynamic_cast( + URobustCreateWindow::CreateWindow(WIND_DownloadProgress, LCommander::GetTopCommander())); + ThrowIfNULL_(theProgressWindow); + + theProgressWindow->SetWindowContext(theContext); + + theProgressWindow->Show(); + theProgressWindow->Select(); + + theContext->ImmediateLoadURL(inDispatchInfo->ReleaseURLStruct(), FO_CACHE_AND_PRESENT); + } + catch(...) + { + delete theContext; + delete theProgressWindow; + } + } else { + + // Set target context to nil if we're not dispatching to an internal anchor + // so that we don't lay out into message view. An internal anchor will START + // with a '#'. Note that dispatching to a new window from a message view + // is never wrong. But loading weird stuff into the same message view IS. + if (!XP_STRCHR(urlOfLink, '#')) + inDispatchInfo->SetTargetContext(nil); // not an anchor of any kind + else if (urlOfLink[0] != '#') // If starts with '#', definitely internal - doesn't occur in practice. + { + // It's an anchor. But is it internal? + URL_Struct* urlStructForMessage = MSG_ConstructUrlForMessage(GetMessagePane(), GetCurMessageKey()); + const char* urlForMessage = urlStructForMessage->address; + if (strstr(urlOfLink, urlForMessage) != urlOfLink) + { + // it's not a link to THIS message + inDispatchInfo->SetTargetContext(nil); + } + NET_FreeURLStruct(urlStructForMessage); + } + Inherited::DispatchURL(inDispatchInfo); + } +} // CMessageView::DispatchURL diff --git a/mozilla/cmd/macfe/MailNews/CMessageView.h b/mozilla/cmd/macfe/MailNews/CMessageView.h new file mode 100644 index 00000000000..fcb11a963e5 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMessageView.h @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMessageView.h + +#pragma once + +#define INHERIT_FROM_BROWSERVIEW +#ifdef INHERIT_FROM_BROWSERVIEW +#include "CBrowserView.h" +#else +#include "CHTMLView.h" +#endif + +#include "MailNewsCallbacks.h" + +typedef struct MSG_ResultElement MSG_ResultElement; +class CMessageAttachmentView; +class CURLDispatchInfo; +class CDeferredCloseTask; + +//====================================== +class CMessageView +#ifdef INHERIT_FROM_BROWSERVIEW + : public CBrowserView +#else + : public CHTMLView +#endif + , public CMailCallbackListener +//====================================== +{ + private: +#ifdef INHERIT_FROM_BROWSERVIEW + typedef CBrowserView Inherited; +#else + typedef CHTMLView Inherited; +#endif + public: + enum { class_ID = 'MsVw' }; + public: + CMessageView(LStream* inStream); + virtual ~CMessageView(); + virtual void FinishCreateSelf(void); + + virtual void ClickSelf(const SMouseDownEvent& where); + virtual void ListenToMessage(MessageT inMessage, void* ioParam); + + void SetAttachmentView( CMessageAttachmentView* attachmentView) + { mAttachmentView = attachmentView; } + void SetMasterCommander(LCommander* inCommander) + { mMasterCommander = inCommander; } + LCommander* GetMasterCommander() const + { return mMasterCommander; } + virtual void SetContext( + CBrowserContext* inNewContext); + MSG_Pane* GetMessagePane() const { return mMessagePane; } + + // Info about the parent folder: must be queried, not cached! + MSG_FolderInfo* GetFolderInfo() const; + uint32 GetFolderFlags() const; + + // Info about the message currently displayed. + MSG_ViewIndex GetCurMessageViewIndex() const; + MessageKey GetCurMessageKey() const; + uint32 GetCurMessageFlags() const; + Boolean IsDueToCloseLater() const; + void SetDueToCloseLater(); + + void ShowMessage(MSG_Master* inMsgMaster, + MSG_FolderInfo* inMsgFolderInfo, + MessageKey inMessageKey, + Boolean inLoadNow = false); + void ClearMessageArea(); + + void ShowURLMessage( + const char* url, + Boolean inLoadNow = false); + void ShowSearchMessage( + MSG_Master *inMsgMaster, + MSG_ResultElement *inResult, + Boolean inNoFolder = false); + void FileMessageToSelectedPopupFolder(const char *ioFolderName, + Boolean inMoveMessages);//¥¥TSM + + + virtual void PaneChanged( + MSG_Pane* inPane, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value); + + virtual void FindCommandStatus( + CommandT inCommand, + Boolean& outEnabled, Boolean& outUsesMark, + Char16& outMark,Str255 outName); + virtual Boolean ObeyCommand(CommandT inCommand, void *ioParam); + Boolean ObeyMotionCommand(MSG_MotionType inCommand); + virtual Boolean HandleKeyPress(const EventRecord& inKeyEvent); + virtual Boolean SetDefaultCSID(Int16 default_csid, Boolean forceRepaginate = false); + + virtual void AdaptToSuperFrameSize( + Int32 inSurrWidthDelta, + Int32 inSurrHeightDelta, + Boolean inRefresh); + virtual void AdjustCursorSelf(Point inPortPt, const EventRecord& inMacEvent); + Boolean MaybeCloseLater(CommandT inCommand); // check prefs + + protected: + + void YieldToMaster(); + // An ugly solution, but after trying many, it's the only one that worked. + // This makes sure this view isn't target if there's a thread view in the same + // window. + + virtual void InstallBackgroundColor(); + // Sets mBackgroundColor. Called from ClearBackground(). + // The base class implementation uses the text background + // preference, but derived classes can override this. + virtual void SetBackgroundColor( + Uint8 inRed, + Uint8 inGreen, + Uint8 inBlue); + // Avoids changing the color, cheats and sets it to white, which + // is what we want for mail messages. 98/01/13. + virtual void GetDefaultFileNameForSaveAs(URL_Struct* url, CStr31& defaultName); + // overridden by CMessageView to use subject. + + virtual void DispatchURL( + URL_Struct* inURLStruct, + CNSContext* inTargetContext, + Boolean inDelay = false, + Boolean inForceCreate = false, + FO_Present_Types inOutputFormat = FO_CACHE_AND_PRESENT + ); + + virtual void DispatchURL(CURLDispatchInfo* inDispatchInfo); + + void CloseLater(); // Close on next idle + + + protected: + MSG_Pane* mMessagePane; + LCommander* mMasterCommander; // Not the super commander. See YieldToMaster. + Boolean mLoadingNakedURL; + CMessageAttachmentView* mAttachmentView; // the attachment pane + Boolean mClosing; + MSG_MotionType mMotionPendingCommand; + CDeferredCloseTask* mDeferredCloseTask; + + friend class CMessageAttachmentView; +}; diff --git a/mozilla/cmd/macfe/MailNews/CMessageWindow.cp b/mozilla/cmd/macfe/MailNews/CMessageWindow.cp new file mode 100644 index 00000000000..2e37f5e128f --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMessageWindow.cp @@ -0,0 +1,571 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMessageWindow.cp + +#include "CMessageWindow.h" + +#include "CMessageAttachmentView.h" +#include "CThreadView.h" // for CMessage + +#include "uapp.h" +#include "ntypes.h" +#include "macutil.h" +#include "resgui.h" +#include "MailNewsgroupWindow_Defines.h" + +#include "CBrowserContext.h" +#include "CMessageView.h" +#include "CProgressListener.h" +#include "CMailFolderButtonPopup.h" +#include "CThreadWindow.h" +#include "CMailNewsContext.h" +#include "CSpinningN.h" +#include "prefapi.h" +#include "URobustCreateWindow.h" +#include "CApplicationEventAttachment.h" +#include "CProxyPane.h" +#include "CProxyDragTask.h" +#include "UMailSelection.h" +#include "CSearchTableView.h" + +#include "libi18n.h" + +#include + +//---------------------------------------------------------------------------------------- +CMessageWindow::CMessageWindow(LStream* inStream) +//---------------------------------------------------------------------------------------- +: CMailNewsWindow(inStream, WindowType_Message) +, mContext(NULL) +{ +} + +//---------------------------------------------------------------------------------------- +CMessageWindow::~CMessageWindow() +//---------------------------------------------------------------------------------------- +{ + SetWindowContext(nil); +} + +//---------------------------------------------------------------------------------------- +void CMessageWindow::FinishCreateSelf() +//---------------------------------------------------------------------------------------- +{ + UReanimator::LinkListenerToControls(this, this, res_ID); + Inherited::FinishCreateSelf(); + CMessageView* messageView = GetMessageView(); + Assert_(messageView); + SetLatentSub(messageView); + CMessageAttachmentView* attachmentView = + dynamic_cast( FindPaneByID('MATv') ); + if( attachmentView ) + { + messageView->SetAttachmentView( attachmentView ); + attachmentView->ClearMessageAttachmentView(); + attachmentView->Hide(); + } + + // the location toolbar is a waste of space and should be hidden by default + LPane *locationBar = FindPaneByID(cMailNewsLocationToolbar); + if (mToolbarShown[CMailNewsWindow::LOCATION_TOOLBAR]) + ToggleDragBar(cMailNewsLocationToolbar, CMailNewsWindow::LOCATION_TOOLBAR); + + USecurityIconHelpers::AddListenerToSmallButton( + this /*LWindow**/, + (CHTMLView*)messageView /*LListener**/); +} + +//---------------------------------------------------------------------------------------- +CMessageView* CMessageWindow::GetMessageView() +//---------------------------------------------------------------------------------------- +{ + return dynamic_cast(FindPaneByID(CMessageView::class_ID)); +} + +//---------------------------------------------------------------------------------------- +void CMessageWindow::SetWindowContext(CBrowserContext* inContext) +//---------------------------------------------------------------------------------------- +{ + CBrowserContext* oldContext = mContext; // save for below. + CSpinningN* theN = dynamic_cast(FindPaneByID(CSpinningN::class_ID)); + mContext = inContext; + if (mContext != NULL) + { + mContext->AddListener(this); + mContext->AddUser(this); + Assert_(mProgressListener != NULL); + mContext->AddListener(mProgressListener); + mContext->AddListener( &mSecurityListener); + if (theN) + mContext->AddListener(theN); + } + CMessageView* theMessageView = GetMessageView(); + Assert_(theMessageView); // Can happen in lowmem, if creation fails + if (theMessageView) + { + theMessageView->SetContext(mContext); + mSecurityListener.SetMessageView( theMessageView ); + // This call links up the model object hierarchy for any potential + // sub model that gets created within the scope of the html view. + theMessageView->SetFormElemBaseModel(this); + } + // Now it's safe to delete the old context, because the view's done with it. + if (oldContext) + { + oldContext->RemoveListener( &mSecurityListener ); + mSecurityListener.SetMessageView( NULL ); + oldContext->RemoveListener(this); + if (theN) + oldContext->RemoveListener(theN); + oldContext->RemoveUser(this); // and delete, probably. + } +} // CMessageWindow::SetWindowContext + +//---------------------------------------------------------------------------------------- +cstring CMessageWindow::GetCurrentURL() +//---------------------------------------------------------------------------------------- +{ + if (mContext) + return mContext->GetCurrentURL(); + else + return cstring(""); +} + +//---------------------------------------------------------------------------------------- +Int16 CMessageWindow::DefaultCSIDForNewWindow() +//---------------------------------------------------------------------------------------- +{ + Int16 csid = 0; + if (mContext != NULL) + csid = mContext->GetDefaultCSID(); + if(0 == csid) + { + CMessageView* theMessageView = GetMessageView(); + Assert_(theMessageView != NULL); + csid = theMessageView->DefaultCSIDForNewWindow(); + } + return csid; +} // CMessageWindow::DefaultCSIDForNewWindow + +//---------------------------------------------------------------------------------------- +void CMessageWindow::ListenToMessage(MessageT inMessage, void* ioParam) +//---------------------------------------------------------------------------------------- +{ + switch (inMessage) + { + case msg_NSCDocTitleChanged: + { + CStr255 theTitle((const char*)ioParam); + // if we have a message view use the subject as the title + CMessageView* messageView = GetMessageView(); + if (messageView) + { + MessageKey id = messageView->GetCurMessageKey(); + if (id != MSG_MESSAGEKEYNONE ) + { + MSG_MessageLine messageLine; + ::MSG_GetThreadLineById(messageView->GetMessagePane(), id, &messageLine); + char buffer[256]; + const char* raw = CMessage::GetSubject(&messageLine, buffer, sizeof(buffer)-1); + char* conv = IntlDecodeMimePartIIStr(raw, GetWindowContext()->GetWinCSID(), FALSE); + theTitle = CStr255((conv != NULL) ? conv : raw); + if (conv) + XP_FREE(conv); + } + } + SetDescriptor(theTitle); + CProxyPane* proxy = dynamic_cast(FindPaneByID(CProxyPane::class_ID)); + if (proxy) + proxy->ListenToMessage(inMessage, (char*)theTitle); + break; + } + case msg_NSCLayoutNewDocument: + //NoteBeginLayout(); + break; + + case msg_NSCFinishedLayout: + //NoteFinishedLayout(); + break; + + case msg_NSCAllConnectionsComplete: + //NoteAllConnectionsComplete(); + CSpinningN* theN = dynamic_cast(FindPaneByID(CSpinningN::class_ID)); + if (theN) + theN->StopSpinningNow(); + break; + case CMailCallbackManager::msg_ChangeStarting: + case CMailCallbackManager::msg_ChangeFinished: + case CMailCallbackManager::msg_PaneChanged: + CMailCallbackListener::SetPane(GetMessageView()->GetMessagePane()); + if (IsMyPane(ioParam)) + CMailCallbackListener::ListenToMessage(inMessage, ioParam); + break; + default: + // assume it's a button message. + GetMessageView()->ObeyCommand(inMessage, ioParam); + } +} // CMessageWindow::ListenToMessage + +//---------------------------------------------------------------------------------------- +void CMessageWindow::ChangeStarting( + MSG_Pane* /*inPane*/, + MSG_NOTIFY_CODE /*inChangeCode*/, + TableIndexT /*inStartRow*/, + SInt32 /*inRowCount*/) +//---------------------------------------------------------------------------------------- +{ +} + +//---------------------------------------------------------------------------------------- +void CMessageWindow::ChangeFinished( + MSG_Pane* /*inPane*/, + MSG_NOTIFY_CODE /*inChangeCode*/, + TableIndexT /*inStartRow*/, + SInt32 /*inRowCount*/) +//---------------------------------------------------------------------------------------- +{ +} + +//---------------------------------------------------------------------------------------- +void CMessageWindow::PaneChanged( + MSG_Pane* /*inPane*/, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value) +//---------------------------------------------------------------------------------------- +{ + switch (inNotifyCode) + { + case MSG_PaneNotifyMessageLoaded: + AdaptToolbarToMessage(); + if (!GetMessageView()->IsDueToCloseLater()) + Show(); + break; + case MSG_PaneNotifyLastMessageDeleted: + // AttemptClose() here leads to nasty re-entrant interrrupt-context. + // What to do? Should close the window, but how? + break; + case MSG_PaneNotifyFolderDeleted: + if ((MSG_FolderInfo*)value != GetMessageView()->GetFolderInfo()) + break; + // ELSE FALL THROUGH... + case MSG_PaneNotifyMessageDeleted: + AttemptClose(); // Causes reentrant InterruptContext call. + break; + } +} // CMessageWindow::PaneChanged + +//---------------------------------------------------------------------------------------- +CMessageWindow* CMessageWindow::FindAndShow(MessageKey inMessageKey) +//---------------------------------------------------------------------------------------- +{ + CWindowIterator iter(WindowType_Message, false); + CMessageWindow* window; + for (iter.Next(window); window; iter.Next(window)) + { + window = dynamic_cast(window); + ThrowIfNULL_(window); + CMessageView* messageView = window->GetMessageView(); + // inMessageKey zero means return the first message window of any kind + if (!inMessageKey || (messageView && messageView->GetCurMessageKey() == inMessageKey)) + return window; + } + return nil; +} // CMessageWindow::FindAndShow + +//---------------------------------------------------------------------------------------- +void CMessageWindow::OpenFromURL(const char* url) +//---------------------------------------------------------------------------------------- +{ + MSG_MessageLine msgLine; + MSG_GetMessageLineForURL(CMailNewsContext::GetMailMaster(), url, &msgLine); + CMessageWindow* messageWindow = CMessageWindow::FindAndShow(msgLine.messageKey); + if (messageWindow) + { + // Found it. Bring it to the front. + messageWindow->Select(); + return; + } + XP_Bool prefReuseWindow = 0; // recycle any message window + PREF_GetBoolPref("mailnews.reuse_message_window", &prefReuseWindow); + if (CApplicationEventAttachment::CurrentEventHasModifiers(optionKey) ^ prefReuseWindow) + { + messageWindow = CMessageWindow::FindAndShow(0); + } + // If we couldn't (or shouldn't) recycle one, make a new one. + if (!messageWindow) + { + try + { + messageWindow = + (CMessageWindow*)URobustCreateWindow::CreateWindow( + CMessageWindow::res_ID, + LCommander::GetTopCommander()); + CBrowserContext* theContext = new CBrowserContext(MWContextMailMsg); + StSharer theShareLock(theContext); // if we throw, theContext will have no users & die + messageWindow->SetWindowContext(theContext); + } + catch(...) + { + delete messageWindow; + messageWindow = NULL; + throw; + } + } + // Whether it's a new one or an old one, load the message now. + if (messageWindow) + { + try + { + CMessageView* messageView = messageWindow->GetMessageView(); + ThrowIfNULL_(messageView); + messageView->ShowURLMessage(url, false); + messageWindow->Select(); + messageWindow->Show(); + } + catch(...) + { + delete messageWindow; + messageWindow = NULL; + throw; + } + } +} // CMessageWindow::OpenFromURL + +//---------------------------------------------------------------------------------------- +/* static */ void CMessageWindow::CloseAll(MessageKey inMessageKey) +//---------------------------------------------------------------------------------------- +{ + CMessageWindow* win; + do { + win = CMessageWindow::FindAndShow(inMessageKey); + if (win ) + win->AttemptClose(); + } while (win); +} // CMessageWindow::CloseAll + +//---------------------------------------------------------------------------------------- +/* static */ void CMessageWindow::NoteSelectionFiledOrDeleted(const CMailSelection& inSelection) +// Tell every relevant message window that its message has been moved, so that it +// can, according to the preferences, close itself. +//---------------------------------------------------------------------------------------- +{ + const MSG_ViewIndex* index = inSelection.GetSelectionList(); + MSG_PaneType paneType = ::MSG_GetPaneType(inSelection.xpPane); + for (int count = 0; count < inSelection.selectionSize; count++, index++) + { + MessageKey key; + switch (paneType) + { + case MSG_THREADPANE: + // These are the list panes: + key = ::MSG_GetMessageKey(inSelection.xpPane, *index); + break; + case MSG_SEARCHPANE: + // These are the list panes: + MSG_ResultElement* ignoredElement; + MSG_FolderInfo* ignoredFolder; + if (!CSearchTableView::GetMessageResultInfo( + inSelection.xpPane, + *index, + ignoredElement, + ignoredFolder, + key)) + continue; + break; + case MSG_MESSAGEPANE: + // The message pane itself is running the copy. It will be notified + // on completion and close itself. Fall through and return. + default: + return; // no messages involved! Zero loop iterations. + } + CWindowIterator iter(WindowType_Message, false); + CMessageWindow* window; + for (iter.Next(window); window; iter.Next(window)) + { + window = dynamic_cast(window); + Assert_(window); + CMessageView* messageView = window->GetMessageView(); + if (messageView->GetCurMessageKey() == key) + messageView->MaybeCloseLater(cmd_MoveMailMessages); + } // for iter + } // for count +} // CMessageWindow::CloseAll + +//---------------------------------------------------------------------------------------- +void CMessageWindow::ActivateSelf() +//---------------------------------------------------------------------------------------- +{ + CMailNewsWindow::ActivateSelf(); +} + +//---------------------------------------------------------------------------------------- +void CMessageWindow::AdaptToolbarToMessage() +//---------------------------------------------------------------------------------------- +{ + + const PaneIDT paneID_MessageFileButton = 'Bfil'; + const PaneIDT paneID_MessageReplyButton = 'Brep'; + + const PaneIDT paneID_MessageGetMailButton = 'Bget'; // mail + const PaneIDT paneID_MessageGetNewsButton = 'Bmor'; // news + + const PaneIDT paneID_MessageComposeButton = 'Bcmp'; // mail + const PaneIDT paneID_MessagePostNewButton = 'Bpst'; // news + + const PaneIDT paneID_MessagePrintButton = 'Bprn'; // mail + const PaneIDT paneID_MessageBackButton = 'Bbck'; // news + + const PaneIDT paneID_MessageDeleteButton = 'Bdel'; // mail + const PaneIDT paneID_MessageMarkButton = 'Bmrk'; // news + + CMessageView* messageView = GetMessageView(); + Assert_(messageView != NULL); + + uint32 folderFlags = messageView->GetFolderFlags(); + + // Set the window title to the subject of the message. + MessageKey id = messageView->GetCurMessageKey(); + if (id != MSG_MESSAGEKEYNONE) + { + MSG_MessageLine messageLine; + ::MSG_GetThreadLineById(messageView->GetMessagePane(), id, &messageLine); + char buffer[256]; + const char* raw = CMessage::GetSubject(&messageLine, buffer, sizeof(buffer)-1); + char* conv = IntlDecodeMimePartIIStr(raw, GetWindowContext()->GetWinCSID(), FALSE); + SetDescriptor(CStr255((conv != NULL) ? conv : raw)); + if (conv) + XP_FREE(conv); + } + + // show/hide buttons depending on the window type (News vs Mail) + LControl * aControl; + Boolean isNewsWindow = ((folderFlags & MSG_FOLDER_FLAG_NEWSGROUP) != 0); + const short kBtnCount = 3; + + PaneIDT mailBtn[kBtnCount] = + { + paneID_MessageGetMailButton, + paneID_MessageComposeButton, + paneID_MessageDeleteButton // update kBtnCount if you add a btn + }; + + PaneIDT newsBtn[kBtnCount] = + { + paneID_MessageGetNewsButton, + paneID_MessagePostNewButton, + paneID_MessageMarkButton // update kBtnCount if you add a btn + }; + + for (short btnIndex = 0; btnIndex < kBtnCount; btnIndex ++) + { + if (isNewsWindow) + { + aControl = dynamic_cast(FindPaneByID(mailBtn[btnIndex])); + if (aControl != nil) aControl->Hide(); + aControl = dynamic_cast(FindPaneByID(newsBtn[btnIndex])); + if (aControl != nil) aControl->Show(); + } + else + { + aControl = dynamic_cast(FindPaneByID(newsBtn[btnIndex])); + if (aControl != nil) aControl->Hide(); + aControl = dynamic_cast(FindPaneByID(mailBtn[btnIndex])); + if (aControl != nil) aControl->Show(); + } + } + + // other changes depending on the window type + + if (isNewsWindow) + { + aControl = dynamic_cast(FindPaneByID(paneID_MessageReplyButton)); + if (aControl != nil) aControl->SetValueMessage(cmd_PostReply); // quick-click default + } + else + { + aControl = dynamic_cast(FindPaneByID(paneID_MessageReplyButton)); + if (aControl != nil) aControl->SetValueMessage(cmd_ReplyToSender); // quick-click default + } + + UInt32 messageFlags = messageView->GetCurMessageFlags(); + ResIDT iconID = (id == MSG_MESSAGEKEYNONE) + ? CProxyPane::kProxyIconNormalID + : CMessage::GetIconID(folderFlags, messageFlags); + LIconPane* proxyIcon = dynamic_cast(FindPaneByID('poxy')); + if (proxyIcon) + { + proxyIcon->SetIconID(iconID); + } + CProxyPane* newProxy = dynamic_cast(FindPaneByID(CProxyPane::class_ID)); + if (newProxy) + { + newProxy->SetIconIDs(iconID, iconID); + } +} // CMessageWindow::AdaptToolbarToMessage + +//---------------------------------------------------------------------------------------- +CExtraFlavorAdder* CMessageWindow::CreateExtraFlavorAdder() const +//---------------------------------------------------------------------------------------- +{ + class MessageWindowFlavorAdder : public CExtraFlavorAdder + { + public: + MessageWindowFlavorAdder(CMessageView* inMessageView) + : mMessageView(inMessageView) + { + } + virtual void AddExtraFlavorData(DragReference inDragRef, ItemReference inItemRef) + { + #if 1 + // Pass a selection using the message view. Not sure if this will work with the BE + mSelection.xpPane = mMessageView->GetMessagePane(); + if (!mSelection.xpPane) + return; + MSG_ViewIndex viewIndex = mMessageView->GetCurMessageViewIndex(); + #else + // Pass a selection, as if this is done from a thread view. + mSelection.xpPane = ::MSG_FindPaneOfType( + CMailNewsContext::GetMailMaster(), + mMessageView->GetFolderInfo(), + MSG_THREADPANE); + if (!mSelection.xpPane) + return; // drat. This is a real drag. + MSG_ViewIndex viewIndex = mMessageView->GetCurMessageKey(); + MSG_ViewIndex viewIndex = MSG_GetIndexForKey(mSelection.xpPane, key, true); + #endif + if (viewIndex == MSG_VIEWINDEXNONE) + return; + mSelection.SetSingleSelection(viewIndex); + ::AddDragItemFlavor( + inDragRef, + inItemRef, + kMailNewsSelectionDragFlavor, + &mSelection, + sizeof(mSelection), + 0); + } + private: + CMessageView* mMessageView; + CMailSelection mSelection; + }; + return new MessageWindowFlavorAdder(const_cast(this)->GetMessageView()); + +} // CMessageWindow::CreateExtraFlavorAdder diff --git a/mozilla/cmd/macfe/MailNews/CMessageWindow.h b/mozilla/cmd/macfe/MailNews/CMessageWindow.h new file mode 100644 index 00000000000..496383d9cb3 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CMessageWindow.h @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CMessageWindow.h + +#pragma once + +#include "CMailNewsWindow.h" + +#include + +#include "CSaveWindowStatus.h" +#include "MailNewsCallbacks.h" +#include "CSecurityButton.h" +#include "cstring.h" + +const ResIDT cMessageWindowPPobID = 10667; + +class CBrowserContext; +class CNSContext; +class CMessageView; +class CMailSelection; +struct SPaneChangeInfo; + +//====================================== +class CMessageWindow + : public CMailNewsWindow + , public CMailCallbackListener +//====================================== +{ +private: + typedef CMailNewsWindow Inherited; // trick suggested by the ANSI committee. +public: + enum { class_ID = 'MsgW', res_ID = cMessageWindowPPobID }; + + CMessageWindow(LStream* inStream); + virtual ~CMessageWindow(); + virtual void FinishCreateSelf(); + +// MY VERSION + virtual void SetWindowContext(CBrowserContext* inContext); + virtual CNSContext* GetWindowContext() const { return (CNSContext*)mContext; } +public: + CMessageView* GetMessageView(); + static CMessageWindow* FindAndShow(MessageKey inMessageKey); + static void OpenFromURL(const char* url); + static void CloseAll(MessageKey inMessageKey); + static void NoteSelectionFiledOrDeleted(const CMailSelection& inSelection); + virtual void ActivateSelf(void); + cstring GetCurrentURL(); + +// CMailCallbackListener overrides: + virtual void ChangeStarting( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount); + virtual void ChangeFinished( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount); + virtual void PaneChanged( + MSG_Pane* inPane, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value); + virtual void ListenToMessage(MessageT inMessage,void *ioParam); +/* + virtual void SetWindowContext(CBrowserContext* inContext); + + // Info about the parent folder: must be queried, not cached! + MessageKey GetMessageKey() const; + MSG_FolderInfo* GetFolderInfo() const; + MSG_ViewIndex GetViewIndex() const; + uint16 GetFolderFlags() const; + + void ShowMessage(MSG_Master* inMsgMaster, + MSG_FolderInfo* inMsgFolderInfo, + MessageKey inMessageKey); + void ShowSearchMessage(MSG_Master *inMsgMaster, + MSG_ResultElement *inResult); +*/ + + // I18N stuff + virtual Int16 DefaultCSIDForNewWindow(void); + +// CNetscapeWindow overrides + virtual CExtraFlavorAdder* CreateExtraFlavorAdder() const; + + //----------------------------------- + // Window Saving - overrides for CSaveWindowStatus. + //----------------------------------- +protected: + virtual ResIDT GetStatusResID(void) const { return res_ID; } + virtual UInt16 GetValidStatusVersion(void) const { return 0x0113; } + virtual void AdaptToolbarToMessage(void); + +protected: + CBrowserContext* mContext; + CMailSecurityListener mSecurityListener; +}; + diff --git a/mozilla/cmd/macfe/MailNews/CNewsSubscriber.cp b/mozilla/cmd/macfe/MailNews/CNewsSubscriber.cp new file mode 100644 index 00000000000..69d96ea4892 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CNewsSubscriber.cp @@ -0,0 +1,305 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CNewsSubscriber.cp + +#include "CNewsSubscriber.h" + +#include "CMailNewsContext.h" +#include "CSubscribeWindow.h" + +#include "UModalDialogs.h" +#include "LGAEditField.h" +#include "LGACheckbox.h" +#include "LGARadioButton.h" + +#include "prefapi.h" +#include "msgcom.h" +#include "uprefd.h" +#include "uerrmgr.h" + +extern "C" +{ + extern int MK_NNTP_SERVER_NOT_CONFIGURED; +} + +MSG_Host* CNewsSubscriber::mHost = nil; + + +//----------------------------------- +Boolean CNewsSubscriber::DoSubscribeNewsGroup(MSG_Host* host, Boolean inModal) +//----------------------------------- +{ + + Boolean result = false; + + // When no host is specified, we try to use the previously + // accessed host or the default host. If there isn't any, + // we display an alert and ask the user to configure one. + if (host == nil) + host = GetHost(); + + if (host == nil) + host = (MSG_Host*)MSG_GetDefaultNewsHost(CMailNewsContext::GetMailMaster()); + + if (host == nil) + { + FE_Alert(nil, XP_GetString(MK_NNTP_SERVER_NOT_CONFIGURED)); + FE_EditPreference(PREF_NewsHost); + } + else + { + SetHost(host); + if (inModal) + { + result = CSubscribeWindow::DisplayDialog(); + } + else + { + CSubscribeWindow::FindAndShow(true); + result = true; + } + } + return result; + +} // CMessageFolderView::DoSubscribeNewsGroup + + +//----------------------------------- +Boolean CNewsSubscriber::DoAddNewsHost() +//----------------------------------- +{ + // Put up dialog + StDialogHandler handler(14000, NULL); + + // Select the "Host" edit field + LWindow* dialog = handler.GetDialog(); + LGAEditField *hostfield = (LGAEditField*)dialog->FindPaneByID('Host'); + SignalIf_(!hostfield); + LGAEditField* portfield = (LGAEditField*)dialog->FindPaneByID('Port'); + SignalIf_(!portfield); + LGACheckbox* securebox = (LGACheckbox*)dialog->FindPaneByID('Secu'); + SignalIf_(!securebox); + if (!hostfield || ! portfield || !securebox) + return false; + dialog->SetLatentSub(hostfield); + + // Run the dialog + MessageT message = 'Secu'; // so that the port is initialized correctly. + CStr255 porttext; + Int32 port = 0; + Boolean userChangedPort = false; + LControl* okButton = dynamic_cast(dialog->FindPaneByID ('Add_') ); + XP_ASSERT( okButton ); + do { + if (message == 'Secu' && !userChangedPort) + { + port = securebox->GetValue() ? 563 : 119; + NumToString(port, porttext); + portfield->SetDescriptor(porttext); + } + message = handler.DoDialog(); + portfield->GetDescriptor(porttext); + Int32 newport; + StringToNum(porttext, &newport); + userChangedPort = (newport != port); + port = newport; + + CStr255 hosttext; + hostfield->GetDescriptor(hosttext); + if( hosttext.Length() > 0 ) + okButton->Enable(); + else + okButton->Disable(); + } while (message != msg_OK && message != msg_Cancel); + + // Use the result. + if (message == msg_OK) + { + CStr255 hosttext; + hostfield->GetDescriptor(hosttext); + MSG_NewsHost* newHost = MSG_CreateNewsHost(CMailNewsContext::GetMailMaster(), + hosttext, securebox->GetValue(), port); + + CNewsSubscriber::SetHost(MSG_GetMSGHostFromNewsHost(newHost)); // make it the default host + + // Be kind to the user: if there was no News server configured yet, + // then use this one (otherwise, we'll throw the Prefs dialog + // on the next common MSG_ call, such as MSG_SubscribeSetNewsHost()) + if (MSG_GetDefaultNewsHost(CMailNewsContext::GetMailMaster()) == NULL) + (void)CPrefs::SetString(hosttext, CPrefs::NewsHost); + } + return (message == msg_OK); +} // CNewsSubscriber::DoAddNewsHost() + +//====================================== +// FE_NewsDownloadPrompt +//====================================== + +// From listngst.cpp +extern "C" XP_Bool FE_NewsDownloadPrompt( + MWContext *context, + int32 numMessagesToDownload, + XP_Bool *downloadAll); + +//----------------------------------- +class StDownloadDialogHandler: public StDialogHandler +//----------------------------------- +{ + public: + StDownloadDialogHandler( + MWContext* context, + int32 numMessagesToDownload, + XP_Bool& downloadAll); + Boolean InitFields(); + void ReadFields(); + + UInt32 mNumberOfMessages; + XP_Bool mDownloadSome; + Int32 mDownloadMax; + XP_Bool mMarkRead; + + LGAEditField* mMaxHeadersField; + LGACheckbox* mMarkReadCheckbox; + LGARadioButton* mDownloadSomeRadio; +}; // class StDownloadDialogHandler + +//----------------------------------- +StDownloadDialogHandler::StDownloadDialogHandler( + MWContext* context, + int32 numMessages, + XP_Bool& downloadAll) +//----------------------------------- +: StDialogHandler(14001, NULL) +, mNumberOfMessages(numMessages) +, mDownloadMax(0) +, mDownloadSome(false) +, mMarkRead(false) + +, mMaxHeadersField(NULL) +, mMarkReadCheckbox(NULL) +, mDownloadSomeRadio(NULL) +{ +#pragma unused (context) +#pragma unused (downloadAll) +} + +//----------------------------------- +void StDownloadDialogHandler::ReadFields() +//----------------------------------- +{ + mDownloadSome = mDownloadSomeRadio->GetValue(); + mMarkRead = mMarkReadCheckbox->GetValue(); + CStr255 headersText; + mMaxHeadersField->GetDescriptor(headersText); + StringToNum(headersText, &mDownloadMax); + if (mDownloadSome) + { + PREF_SetBoolPref("news.mark_old_read", mMarkRead); + PREF_SetIntPref("news.max_articles", mDownloadMax); + } +} + +//----------------------------------- +Boolean StDownloadDialogHandler::InitFields() +//----------------------------------- +{ + // Select the "Host" edit field + LWindow* dialog = GetDialog(); + + mMaxHeadersField = (LGAEditField*)dialog->FindPaneByID('maxH'); + SignalIf_(!mMaxHeadersField); + + mMarkReadCheckbox = (LGACheckbox*)dialog->FindPaneByID('MkUn'); + SignalIf_(!mMarkReadCheckbox); + + mDownloadSomeRadio = (LGARadioButton*)dialog->FindPaneByID('DnSm'); + SignalIf_(!mDownloadSomeRadio); + + LCaption* messagefield = (LCaption*)dialog->FindPaneByID('Mesg'); + SignalIf_(!messagefield); + + if (!messagefield || !mMaxHeadersField || !mMarkReadCheckbox || !mDownloadSomeRadio) + return false; + + // The caption has the message format string with %d in it... + CStr255 messageText; + messagefield->GetDescriptor(messageText); + char messageString[255]; + sprintf(messageString, messageText, mNumberOfMessages); + messageText = messageString; + messagefield->SetDescriptor(messageText); + + PREF_GetBoolPref( "news.mark_old_read", &mMarkRead ); + mMarkReadCheckbox->SetValue(mMarkRead); + + PREF_GetIntPref("news.max_articles", &mDownloadMax); + CStr255 downloadString; + NumToString(mDownloadMax, downloadString); + mMaxHeadersField->SetDescriptor(downloadString); + + return true; +} + +//----------------------------------- +XP_Bool FE_NewsDownloadPrompt( + MWContext* context, + int32 numMessagesToDownload, + XP_Bool* downloadAll) +//----------------------------------- +{ + // Put up dialog + StDownloadDialogHandler handler(context, numMessagesToDownload, *downloadAll); + + // Set up the dialog + if (!handler.InitFields()) + return false; + + // Run the dialog + MessageT message = msg_Nothing; + do { + message = handler.DoDialog(); + } while (message != msg_Cancel && message != msg_OK); + + // Use the result. + if (message == msg_OK) + { + handler.ReadFields(); + *downloadAll = !handler.mDownloadSome; + return true; + } + return false; +} // FE_NewsDownloadPrompt + + +//----------------------------------- +XP_Bool FE_CreateSubscribePaneOnHost( + MSG_Master* master, + MWContext* parentContext, + MSG_Host* host) +//----------------------------------- +{ +#pragma unused (master) +#pragma unused (parentContext) + XP_Bool result; + result = CNewsSubscriber::DoSubscribeNewsGroup(host, false); + // modeless, as required by API spec for this call. + return result; +} diff --git a/mozilla/cmd/macfe/MailNews/CNewsSubscriber.h b/mozilla/cmd/macfe/MailNews/CNewsSubscriber.h new file mode 100644 index 00000000000..6a7a817f196 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CNewsSubscriber.h @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CNewsSubscriber.h + +#pragma once +#include "msgcom.h" + +//====================================== +class CNewsSubscriber +//====================================== +{ + public: + + static Boolean DoAddNewsHost(); + static Boolean DoSubscribeNewsGroup(MSG_Host* host = nil, Boolean inModal = false); + + static void SetHost(MSG_Host* host) { mHost = host; }; + static MSG_Host* GetHost() { return mHost; }; + + private: + + static MSG_Host* mHost; + +}; // class CNewsSubscriber diff --git a/mozilla/cmd/macfe/MailNews/COfflinePicker.cp b/mozilla/cmd/macfe/MailNews/COfflinePicker.cp new file mode 100644 index 00000000000..2c2b08dbeb5 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/COfflinePicker.cp @@ -0,0 +1,925 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// COfflinePicker.cp + +#include "COfflinePicker.h" + +#include "CMessageFolder.h" +#include "MailNewsAddressBook.h" +#include "URobustCreateWindow.h" +#include "UModalDialogs.h" + +#include "cstring.h" +#include "macutil.h" +#include "MailNewsgroupWindow_Defines.h" +#include "LGAPushButton.h" +#include "dirprefs.h" + +enum +{ + kCheckedIconID = 15237 +, kUncheckedIconID = 15235 +, kLDAPHdrIconID = 15258 //15226 +, kLDAPIconID = 15258 +}; + + +//------------------------------------------------------------------------------ +// ¥ COfflineItem +//------------------------------------------------------------------------------ +// +class COfflineItem +{ +public: + COfflineItem(const TableIndexT inRow, const COfflinePickerView* inPickerView); + ~COfflineItem(); + + UInt32 GetLevel() const; + ResIDT GetIconID() const; + const char* GetName() const; + + UInt32 GetFolderPrefFlags() const; + void SetFolderPrefFlags(const UInt32) const; + + Boolean IsOpen() const; + Boolean IsNewsgroup() const; + Boolean IsLocalMailFolder() const; + Boolean IsIMAPMailFolder() const; + Boolean IsLDAPDirectory() const; + + Boolean HasNewMessages() const; + UInt32 CountSubFolders() const; + + + TableIndexT GetRow() const {return mRow;}; + COfflinePickerView::RowType GetRowType() const {return mRowType;}; + void * GetInfo() const {return (mMessageFolder ? (void*)mMessageFolder->GetFolderInfo() : (void*)mDirServer);}; + +protected: + const COfflinePickerView * mPickerView; + const TableIndexT mRow; + COfflinePickerView::RowType mRowType; + CMessageFolder * mMessageFolder; + DIR_Server * mDirServer; +}; + +// ¥ COfflineItem +COfflineItem::COfflineItem(const TableIndexT inRow, const COfflinePickerView* inPickerView) + : mPickerView(inPickerView) + , mRow(inRow) + , mMessageFolder(nil) + , mDirServer(nil) +{ + mRowType = mPickerView->GetRowType(inRow); + if (mRowType == COfflinePickerView::kRowMailNews) + mMessageFolder = new CMessageFolder(inRow, inPickerView->GetMessagePane()); + else + if (mRowType == COfflinePickerView::kRowLDAP) + { + TableIndexT outRows, outCols; + mPickerView->GetTableSize(outRows, outCols); + mDirServer = (DIR_Server *)XP_ListGetObjectNum(mPickerView->mLDAPList, outRows - mRow + 1); + } +} + +// ¥ ~COfflineItem +COfflineItem::~COfflineItem() +{ + delete mMessageFolder; +} + +// ¥ GetLevel +UInt32 COfflineItem::GetLevel() const +{ + switch (mRowType) + { + case COfflinePickerView::kRowMailNews: return mMessageFolder->GetLevel(); + case COfflinePickerView::kRowLDAPHdr: return kRootLevel; + case COfflinePickerView::kRowLDAP: return kRootLevel + 1; + } + return kRootLevel; +} + +// ¥ GetIconID +ResIDT COfflineItem::GetIconID() const +{ + switch (mRowType) + { + case COfflinePickerView::kRowMailNews: return mMessageFolder->GetIconID(); + case COfflinePickerView::kRowLDAPHdr: return kLDAPHdrIconID; + case COfflinePickerView::kRowLDAP: return kLDAPIconID; + } + return 0; +} + +// ¥ GetName +const char* COfflineItem::GetName() const +{ + switch (mRowType) + { + case COfflinePickerView::kRowMailNews: return mMessageFolder->GetName(); + case COfflinePickerView::kRowLDAPHdr: return (char*)&mPickerView->mLDAPHdrStr[1]; + case COfflinePickerView::kRowLDAP: return mDirServer->description; + } + return nil; +} + +// ¥ GetFolderPrefFlags +UInt32 COfflineItem::GetFolderPrefFlags() const +{ + switch (mRowType) + { + case COfflinePickerView::kRowMailNews: return mMessageFolder->GetFolderPrefFlags(); + case COfflinePickerView::kRowLDAPHdr: return 0; + case COfflinePickerView::kRowLDAP: return (DIR_TestFlag(mDirServer, DIR_REPLICATION_ENABLED) ? MSG_FOLDER_PREF_OFFLINE : 0); + } + return 0; +} + +// ¥ SetFolderPrefFlags +void COfflineItem::SetFolderPrefFlags(const UInt32 inPrefFlags) const +{ + switch (mRowType) + { + case COfflinePickerView::kRowMailNews: + ::MSG_SetFolderPrefFlags(mMessageFolder->GetFolderInfo(), inPrefFlags); + break; + case COfflinePickerView::kRowLDAPHdr: + // nothing + break; + case COfflinePickerView::kRowLDAP: + DIR_ForceFlag(mDirServer, DIR_REPLICATION_ENABLED, ((inPrefFlags & MSG_FOLDER_PREF_OFFLINE) != 0)); + break; + } +} + +// ¥ IsOpen +Boolean COfflineItem::IsOpen() const +{ + switch (mRowType) + { + case COfflinePickerView::kRowMailNews: return mMessageFolder->IsOpen(); + case COfflinePickerView::kRowLDAPHdr: return mPickerView->IsLDAPExpanded(); + case COfflinePickerView::kRowLDAP: return false; + } + return false; +} + +// ¥ IsNewsgroup +Boolean COfflineItem::IsNewsgroup() const +{ + return (mMessageFolder ? mMessageFolder->IsNewsgroup() : false); +} + +// ¥ IsLocalMailFolder +Boolean COfflineItem::IsLocalMailFolder() const +{ + if (mRowType == COfflinePickerView::kRowMailNews) + return mMessageFolder->IsLocalMailFolder(); + return false; +} + +// ¥ IsIMAPMailFolder +Boolean COfflineItem::IsIMAPMailFolder() const +{ + return (mMessageFolder ? mMessageFolder->IsIMAPMailFolder() : false); +} + +// ¥ IsLDAPDirectory +Boolean COfflineItem::IsLDAPDirectory() const +{ + return (mRowType == COfflinePickerView::kRowLDAP); +} + +// ¥ HasNewMessages +Boolean COfflineItem::HasNewMessages() const +{ + return (mMessageFolder ? mMessageFolder->HasNewMessages() : false); +} + +// ¥ CountSubFolders +UInt32 COfflineItem::CountSubFolders() const +{ + switch (mRowType) + { + case COfflinePickerView::kRowMailNews: return mMessageFolder->CountSubFolders(); + case COfflinePickerView::kRowLDAPHdr: return mPickerView->CountLDAPItems(); + case COfflinePickerView::kRowLDAP: return 0; + } + return 0; + +} + + +#pragma mark - +//------------------------------------------------------------------------------ +// ¥ COfflinePickerView +//------------------------------------------------------------------------------ +// +COfflinePickerView::COfflinePickerView(LStream *inStream) + : Inherited(inStream) + , mWantLDAP(true) + , mLDAPList(nil) + , mLDAPCount(0) + , mLDAPExpanded(false) + , mSaveItemArray(sizeof(SSaveItemRec)) +{ +} + + +//------------------------------------------------------------------------------ +// ¥ ~COfflinePickerView +//------------------------------------------------------------------------------ +// +COfflinePickerView::~COfflinePickerView() +{ + if (mLDAPList) + XP_ListDestroy(mLDAPList); +} + + +//------------------------------------------------------------------------------ +// ¥ View / GetRowType +//------------------------------------------------------------------------------ +// +COfflinePickerView::RowType + COfflinePickerView::GetRowType(TableIndexT inRow) const +{ + if (! mWantLDAP) + return kRowMailNews; + + TableIndexT outRows, outCols; + GetTableSize(outRows, outCols); + + TableIndexT ldapHdrRow; + if (mLDAPExpanded) + ldapHdrRow = outRows - mLDAPCount; + else + ldapHdrRow = outRows; + + if (inRow < ldapHdrRow) + return kRowMailNews; + else if (inRow == ldapHdrRow) + return kRowLDAPHdr; + else + return kRowLDAP; +} + + +//------------------------------------------------------------------------------ +// ¥ View / AppendLDAPList +//------------------------------------------------------------------------------ +// +void COfflinePickerView::AppendLDAPList() +{ + if (!mWantLDAP) + return; + + // get list (which also contains Personal ABook + HTML directories) + XP_List *serverList = CAddressBookManager::GetDirServerList(); + if (!serverList) + { + mWantLDAP = false; + return; + } + mLDAPList = XP_ListNew(); + ThrowIfNULL_(mLDAPList); + + // extract LDAP items + int totalCount = XP_ListCount(serverList); + for (Int32 i = 1; i <= totalCount ; i++) + { + DIR_Server *server = (DIR_Server *) XP_ListGetObjectNum(serverList, i); + if (server->dirType == LDAPDirectory) + XP_ListAddObject(mLDAPList, server); + } + mLDAPCount = XP_ListCount(mLDAPList); + if (mLDAPCount == 0) + { + XP_ListDestroy(mLDAPList); + mLDAPList = nil; + mWantLDAP = false; + return; + } + + // get the LDAP header string and insert a row at the end of the list + ::GetIndString(mLDAPHdrStr, 7099, 26); + mLDAPHdrStr[mLDAPHdrStr[0]+1] = '\0'; + + TableIndexT outRows, outCols; + GetTableSize(outRows, outCols); + InsertRows(1, outRows, nil, 0, true); +} + + +//---------------------------------------------------------------------------- +// ¥ View / SaveItemPrefFlags +//---------------------------------------------------------------------------- +// +void COfflinePickerView::SaveItemPrefFlags(const COfflineItem * inOfflineItem, UInt32 inPrefsFlags) +{ + // prepare new item to save + SSaveItemRec newItemRec; + newItemRec.itemType = inOfflineItem->GetRowType(); + newItemRec.itemInfo = inOfflineItem->GetInfo(); + newItemRec.originalPrefsFlags = inPrefsFlags; + + // check if item has already been saved + LArrayIterator iterator(mSaveItemArray, LArrayIterator::from_Start); + SSaveItemRec itemRec; + while (iterator.Next(&itemRec)) + { + if ((itemRec.itemType == newItemRec.itemType) + && (itemRec.itemInfo == newItemRec.itemInfo)) + return; // item already saved + } + + // save new item + mSaveItemArray.InsertItemsAt(1, LArray::index_Last, (void*)&newItemRec, sizeof(newItemRec)); +} + + +//---------------------------------------------------------------------------- +// ¥ View / CancelSelection +//---------------------------------------------------------------------------- +// +void COfflinePickerView::CancelSelection() +{ + LArrayIterator iterator(mSaveItemArray, LArrayIterator::from_Start); + SSaveItemRec itemRec; + while (iterator.Next(&itemRec)) + { + switch (itemRec.itemType) + { + case COfflinePickerView::kRowMailNews: + ::MSG_SetFolderPrefFlags((MSG_FolderInfo *)itemRec.itemInfo, itemRec.originalPrefsFlags); + break; + + case COfflinePickerView::kRowLDAP: + DIR_ForceFlag((DIR_Server *)itemRec.itemInfo, DIR_REPLICATION_ENABLED, ((itemRec.originalPrefsFlags & MSG_FOLDER_PREF_OFFLINE) != 0)); + DIR_SaveServerPreferences(CAddressBookManager::GetDirServerList()); + break; + } + } + mSaveItemArray.RemoveItemsAt(1, mSaveItemArray.GetCount()); +} + + +//---------------------------------------------------------------------------- +// ¥ View / CommitSelection +//---------------------------------------------------------------------------- +// +void COfflinePickerView::CommitSelection() +{ + if (mLDAPList) + DIR_SaveServerPreferences(CAddressBookManager::GetDirServerList()); +} + + +//---------------------------------------------------------------------------- +// ¥ View / DrawCell +//---------------------------------------------------------------------------- +// +void COfflinePickerView::DrawCell(const STableCell& inCell, const Rect& inLocalRect) +{ + PaneIDT cellType = GetCellDataType(inCell); + switch (cellType) + { + case kFolderNameColumn: + Inherited::DrawCell(inCell, inLocalRect); + break; + + case kSelectFolderColumn: + COfflineItem item(inCell.row, this); + if (item.GetLevel() > kRootLevel) + { + short iconID, transformType; + if (item.IsLocalMailFolder()) + { + iconID = kCheckedIconID; + transformType = ttDisabled; + } + else + { + UInt32 folderPrefFlags = item.GetFolderPrefFlags(); + if (folderPrefFlags & MSG_FOLDER_PREF_OFFLINE) + iconID = kCheckedIconID; + else + iconID = kUncheckedIconID; + transformType = ttNone; + } + DrawIconFamily(iconID, 16, 16, transformType, inLocalRect); + } + break; + } +} + + +//---------------------------------------------------------------------------- +// ¥ View / ClickSelect +//---------------------------------------------------------------------------- +// We don't want any fancy behavior on mouse clicks: no cell selection, +// no Finder selection, nothing. Just toggle the check-box. +// + Boolean COfflinePickerView::ClickSelect( + const STableCell &inCell, + const SMouseDownEvent &inMouseDown) +{ +#pragma unused (inCell) +#pragma unused (inMouseDown) + return true; +} + + +//---------------------------------------------------------------------------- +// ¥ View / ClickCell +//---------------------------------------------------------------------------- +// We don't want any fancy behavior on mouse clicks: no cell selection, +// no Finder selection, nothing. Just toggle the check-box. +// +void COfflinePickerView::ClickCell( + const STableCell &inCell, + const SMouseDownEvent &inMouseDown) +{ + SPoint32 currentPoint; + STableCell hitCell = inCell; + + currentPoint.h = inMouseDown.whereLocal.h; + currentPoint.v = inMouseDown.whereLocal.v; + + COfflineItem item(hitCell.row, this); + if (item.GetLevel() > kRootLevel) + { + if (item.IsNewsgroup() || item.IsIMAPMailFolder() || item.IsLDAPDirectory()) + { + UInt32 folderPrefFlags = item.GetFolderPrefFlags(); + SaveItemPrefFlags(&item, folderPrefFlags); + + folderPrefFlags ^= MSG_FOLDER_PREF_OFFLINE; + item.SetFolderPrefFlags(folderPrefFlags); + + for (int i = 1; i <= mTableHeader->CountVisibleColumns(); i++) + { + hitCell.col = i; + RefreshCell(hitCell); + } + } + } +} + + +// --------------------------------------------------------------------------- +// ¥ View / HandleKeyPress +// --------------------------------------------------------------------------- +// Overide CStandardFlexTable: Return and Enter don't open the selection. +// + +Boolean +COfflinePickerView::HandleKeyPress( + const EventRecord &inKeyEvent) +{ + Boolean keyHandled = false; + LControl *keyButton = nil; + + switch (inKeyEvent.message & charCodeMask) + { + case char_Enter: + case char_Return: + LCommander::HandleKeyPress(inKeyEvent); + return true; + } + + return Inherited::HandleKeyPress(inKeyEvent); +} + + +//---------------------------------------------------------------------------- +// ¥ View / GetQapRowText +//---------------------------------------------------------------------------- +// Return info for QA Partner +// +#if defined(QAP_BUILD) +void COfflinePickerView::GetQapRowText( + TableIndexT inRow, + char* outText, + UInt16 inMaxBufferLength) const +{ + if (!outText || inMaxBufferLength == 0) + return; + + cstring rowText(""); + short colCount = mTableHeader->CountVisibleColumns(); + COfflineItem item(inRow, this); + + CMailNewsWindow * myWindow = dynamic_cast(LWindow::FetchWindowObject(GetMacPort())); + if (!myWindow) return; + + for (short col = 1; col <= colCount; col ++) + { + STableCell aCell(inRow, col); + LTableHeader::SColumnData * colData = mTableHeader->GetColumnData(col); + if (!colData) break; + LPane * colPane = myWindow->FindPaneByID(colData->paneID); + if (!colPane) break; + + // get column name + CStr255 descriptor; + colPane->GetDescriptor(descriptor); + rowText += descriptor; + rowText += "=\042"; + + // add cell text + switch (PaneIDT dataType = GetCellDataType(aCell)) + { + case kFolderNameColumn: + Boolean isExpanded; + if (CellHasDropFlag(aCell, isExpanded)) + { + if (isExpanded) + rowText += "-"; + else + rowText += "+"; + } + else + rowText += " "; + rowText += item.GetName(); + break; + + case kSelectFolderColumn: + if (item.GetLevel() > kRootLevel) + { + UInt32 folderPrefFlags = item.GetFolderPrefFlags(); + if (folderPrefFlags & MSG_FOLDER_PREF_OFFLINE) + rowText += "+"; + } + break; + } + + if (col < colCount) + rowText += "\042 | "; + else + rowText += "\042\r"; + } + strncpy(outText, (char*)rowText, inMaxBufferLength); + outText[inMaxBufferLength - 1] = '\0'; +} +#endif //QAP_BUILD + + +#pragma mark -- +//------------------------------------------------------------------------------ +// ¥ View / GetIconID +//------------------------------------------------------------------------------ +// +ResIDT COfflinePickerView::GetIconID(TableIndexT inRow) const +{ + COfflineItem item(inRow, this); + return item.GetIconID(); +} + + +//------------------------------------------------------------------------------ +// ¥ View / GetNestedLevel +//------------------------------------------------------------------------------ +// +UInt16 COfflinePickerView::GetNestedLevel(TableIndexT inRow) const +{ + COfflineItem item(inRow, this); + return item.GetLevel() - 1; +} + + +//------------------------------------------------------------------------------ +// ¥ View / ApplyTextStyle +//------------------------------------------------------------------------------ +// +void COfflinePickerView::ApplyTextStyle(TableIndexT inRow) const +{ + COfflineItem item(inRow, this); + ::TextFace(item.HasNewMessages() ? bold : normal); +} + + +//------------------------------------------------------------------------------ +// ¥ View / CellHasDropFlag +//------------------------------------------------------------------------------ +// Check if a cell has a twistee icon and if the twistee is open. +// +Boolean COfflinePickerView::CellHasDropFlag( + const STableCell& inCell, + Boolean& outIsExpanded) const +{ + COfflineItem item(inCell.row, this); + if (GetCellDataType(inCell) == kFolderNameColumn && item.CountSubFolders() != 0) + { + outIsExpanded = item.IsOpen(); + return true; + } + return false; +} + + +//------------------------------------------------------------------------------ +// ¥ View / SetCellExpansion +//------------------------------------------------------------------------------ +// +void COfflinePickerView::SetCellExpansion( + const STableCell& inCell, + Boolean inExpand) +{ + switch (GetRowType(inCell.row)) + { + case kRowMailNews: + Inherited::SetCellExpansion(inCell, inExpand); + break; + + case kRowLDAPHdr: + mLDAPExpanded = inExpand; + TableIndexT outRows, outCols; + GetTableSize(outRows, outCols); + if (inExpand) + InsertRows(mLDAPCount, outRows, nil, 0, true); + else + RemoveRows(mLDAPCount, outRows - mLDAPCount, true); + break; + + case kRowLDAP: + break; + } +} + + +//------------------------------------------------------------------------------ +// ¥ View / GetMainRowText +//------------------------------------------------------------------------------ +// +void COfflinePickerView::GetMainRowText( + TableIndexT inRow, + char* outText, + UInt16 inMaxBufferLength) const +{ + switch (GetRowType(inRow)) + { + case kRowMailNews: + Inherited::GetMainRowText(inRow, outText, inMaxBufferLength); + break; + + case kRowLDAPHdr: + case kRowLDAP: + if (outText) + { + COfflineItem item(inRow, this); + ::strncpy(outText, item.GetName(), inMaxBufferLength); + } + break; + } +} + + +//------------------------------------------------------------------------------ +// ¥ View / ChangeFinished +//------------------------------------------------------------------------------ +// + +void COfflinePickerView::ChangeFinished( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount) +{ + // call the inherited method, restoring the original BE row count + // to work around a check in CMailFlexTable::ChangeFinished() + // on MSG_NotifyInsertOrDelete + if ((mLDAPCount > 0) && (inChangeCode == MSG_NotifyInsertOrDelete)) + { + mRows -= (mLDAPExpanded ? mLDAPCount + 1 : 1); + Inherited::ChangeFinished(inPane, inChangeCode, inStartRow, inRowCount); + mRows += (mLDAPExpanded ? mLDAPCount + 1 : 1); + } + else + Inherited::ChangeFinished(inPane, inChangeCode, inStartRow, inRowCount); + + switch (inChangeCode) + { + case MSG_NotifyScramble: + case MSG_NotifyAll: + if (mWantLDAP) + { + COfflineItem item(1, this); + if (item.IsLocalMailFolder() && item.IsOpen()) + { + STableCell aCell(1, 1); + SetCellExpansion(aCell, false); + } + AppendLDAPList(); + } + break; + } +} + + +#pragma mark - + +//------------------------------------------------------------------------------ +// ¥ COfflinePickerWindow +//------------------------------------------------------------------------------ +// +COfflinePickerWindow::COfflinePickerWindow(LStream *inStream) + : CMailNewsWindow(inStream, WindowType_OfflinePicker) + , mList(nil) +{ +} + + +//------------------------------------------------------------------------------ +// ¥ ~COfflinePickerWindow +//------------------------------------------------------------------------------ +// +COfflinePickerWindow::~COfflinePickerWindow() +{ +} + + +//------------------------------------------------------------------------------ +// ¥ Window / FinishCreateSelf +//------------------------------------------------------------------------------ +// +void COfflinePickerWindow::FinishCreateSelf() +{ + Inherited::FinishCreateSelf(); + mList = (COfflinePickerView*)GetActiveTable(); + Assert_(mList); + mList->LoadFolderList(mMailNewsContext); + + UReanimator::LinkListenerToControls(this, this, GetPaneID()); + + LGAPushButton * okBtn = dynamic_cast(FindPaneByID(paneID_OkButton)); + if (okBtn) okBtn->SetDefaultButton(true, true); + + Show(); + Select(); +} + + +//------------------------------------------------------------------------------ +// ¥ Window / CalcStandardBoundsForScreen +//------------------------------------------------------------------------------ +// Zoom in the vertical direction only. +// +void COfflinePickerWindow::CalcStandardBoundsForScreen( + const Rect &inScreenBounds, + Rect &outStdBounds) const +{ + LWindow::CalcStandardBoundsForScreen(inScreenBounds, outStdBounds); + Rect contRect = UWindows::GetWindowContentRect(mMacWindowP); + + outStdBounds.left = contRect.left; + outStdBounds.right = contRect.right; +} + + +//---------------------------------------------------------------------------- +// ¥ Window / ListenToMessage +//---------------------------------------------------------------------------- + +void COfflinePickerWindow::ListenToMessage(MessageT inMessage, void* ioParam) +{ +#pragma unused (ioParam) + switch (inMessage) + { + case msg_Cancel: + if (mList) + mList->CancelSelection(); + Inherited::DoClose(); + break; + + case msg_OK: + if (mList) + mList->CommitSelection(); + Inherited::DoClose(); + break; + } +} + + +// --------------------------------------------------------------------------- +// ¥ Window / HandleKeyPress +// --------------------------------------------------------------------------- +// As usual, copied and adapted from LDialogBox. +// Can't we do an attachment with that? +// + +Boolean +COfflinePickerWindow::HandleKeyPress( + const EventRecord &inKeyEvent) +{ + Boolean keyHandled = false; + LControl *keyButton = nil; + + switch (inKeyEvent.message & charCodeMask) { + + case char_Enter: + case char_Return: + keyButton = (LControl*) FindPaneByID(paneID_OkButton); + break; + + case char_Escape: + if ((inKeyEvent.message & keyCodeMask) == vkey_Escape) { + keyButton = (LControl*) FindPaneByID(paneID_CancelButton); + } + break; + + default: + if (UKeyFilters::IsCmdPeriod(inKeyEvent)) { + keyButton = (LControl*) FindPaneByID(paneID_CancelButton); + } else { + keyHandled = LWindow::HandleKeyPress(inKeyEvent); + } + break; + } + + if (keyButton != nil) { + keyButton->SimulateHotSpotClick(kControlButtonPart); + keyHandled = true; + } + + return keyHandled; +} + + +//------------------------------------------------------------------------------ +// ¥ Window / GetActiveTable +//------------------------------------------------------------------------------ +// From CMailNewsWindow. Get the currently active table in the window. +// The active table is the one that the user considers to be receiving input. +// +CMailFlexTable* COfflinePickerWindow::GetActiveTable() +{ + return dynamic_cast(FindPaneByID('Flst')); +} + + +//------------------------------------------------------------------------------ +// ¥ Window / FindAndShow [static] +//------------------------------------------------------------------------------ +// Creates/shows/selects the Offline Picker window. There can only be one of these. +// Use COfflinePickerWindow::DisplayDialog() for a modal dialog. +// +COfflinePickerWindow* COfflinePickerWindow::FindAndShow(Boolean inMakeNew) +{ + COfflinePickerWindow* result = NULL; + try + { + CWindowIterator iter(WindowType_OfflinePicker); + iter.Next(result); + if (!result && inMakeNew) + { + result = dynamic_cast( + URobustCreateWindow::CreateWindow( + res_ID, LCommander::GetTopCommander())); + ThrowIfNULL_(result); + } + } + catch (...) + { + } + return result; +} + + +//------------------------------------------------------------------------------ +// ¥ Window / DisplayDialog [static] +//------------------------------------------------------------------------------ +// Creates/shows/selects the Offline Picker window. There can only be one of these. +// Use COfflinePickerWindow::FindAndShow() for a non-modal window. +// +Boolean COfflinePickerWindow::DisplayDialog() +{ + StDialogHandler handler(res_ID, NULL); + + MessageT message; + do + { + message = handler.DoDialog(); + } while (message != msg_OK && message != msg_Cancel); + + return (message == msg_OK); +} diff --git a/mozilla/cmd/macfe/MailNews/COfflinePicker.h b/mozilla/cmd/macfe/MailNews/COfflinePicker.h new file mode 100644 index 00000000000..2acb33631ff --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/COfflinePicker.h @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// COfflinePicker.h + +#pragma once + +#include "CMailNewsWindow.h" +#include "CSimpleFolderView.h" + + +//------------------------------------------------------------------------------ +// ¥ COfflinePickerView +//------------------------------------------------------------------------------ +// +class COfflinePickerView : public CSimpleFolderView +{ + friend class COfflineItem; + +private: + typedef CSimpleFolderView Inherited; + +public: + enum { class_ID = 'ofVW' }; + + COfflinePickerView(LStream *inStream); + virtual ~COfflinePickerView(); + + //----------------------------------- + // LDAP folders + //----------------------------------- +protected: + typedef enum { kRowMailNews = 1, kRowLDAPHdr, kRowLDAP} RowType; + + typedef struct SSaveItemRec + { + RowType itemType; + void * itemInfo; + UInt32 originalPrefsFlags; + } SSaveItemRec; + + virtual RowType GetRowType(TableIndexT inRow) const; + virtual UInt16 CountLDAPItems() const {return mLDAPCount;}; + virtual Boolean IsLDAPExpanded() const {return mLDAPExpanded;}; + virtual void AppendLDAPList(); + virtual void SaveItemPrefFlags(const COfflineItem * inOfflineItem, UInt32 inPrefsFlags); + +public: + virtual void CancelSelection(); + virtual void CommitSelection(); + + //----------------------------------- + // Command implementation + //----------------------------------- +public: + virtual void DrawCell( + const STableCell &inCell, + const Rect &inLocalRect); + virtual Boolean ClickSelect( + const STableCell &inCell, + const SMouseDownEvent &inMouseDown); + virtual void ClickCell( + const STableCell &inCell, + const SMouseDownEvent &inMouseDown); + + //----------------------------------- + // Commands + //----------------------------------- + + virtual Boolean HandleKeyPress(const EventRecord &inKeyEvent); + + //----------------------------------- + // Drawing (Overrides of CSimpleFolderView) + //----------------------------------- + virtual Boolean TableDesiresSelectionTracking ( ) { return false; } + virtual ResIDT GetIconID(TableIndexT inRow) const; + virtual UInt16 GetNestedLevel(TableIndexT inRow) const; + + virtual void ApplyTextStyle(TableIndexT inRow) const; + + virtual Boolean CellHasDropFlag(const STableCell& inCell, Boolean& outIsExpanded) const; + virtual void SetCellExpansion(const STableCell& inCell, Boolean inExpand); + virtual void GetMainRowText( + TableIndexT inRow, + char* outText, + UInt16 inMaxBufferLength) const; + + virtual void ChangeFinished( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount); + + // ------------------------------------------------------------ + // QA Partner support + // ------------------------------------------------------------ +#if defined(QAP_BUILD) +public: + virtual void GetQapRowText(TableIndexT inRow, char* outText, UInt16 inMaxBufferLength) const; +#endif + + //----------------------------------- + // Data + //----------------------------------- +protected: + Boolean mWantLDAP; + XP_List * mLDAPList; + UInt16 mLDAPCount; + Boolean mLDAPExpanded; + Str63 mLDAPHdrStr; + + LArray mSaveItemArray; +}; + + +//------------------------------------------------------------------------------ +// ¥ COfflinePickerWindow +//------------------------------------------------------------------------------ +// +class COfflinePickerWindow : public CMailNewsWindow, + public LListener +{ +private: + typedef CMailNewsWindow Inherited; + +public: + enum { class_ID = 'ofWN', res_ID = 20003}; + + enum { + paneID_OkButton = 'BtOk', + paneID_CancelButton = 'Canc' + }; + +protected: + virtual ResIDT GetStatusResID(void) const { return res_ID; } + virtual UInt16 GetValidStatusVersion(void) const { return 0x0112; } + +public: + COfflinePickerWindow(LStream *inStream); + virtual ~COfflinePickerWindow(); + + virtual void FinishCreateSelf(); + virtual void CalcStandardBoundsForScreen( + const Rect &inScreenBounds, + Rect &outStdBounds) const; + virtual void ListenToMessage(MessageT inMessage, void* ioParam); + virtual Boolean HandleKeyPress(const EventRecord &inKeyEvent); + + virtual CMailFlexTable * GetActiveTable(); + static COfflinePickerWindow * FindAndShow(Boolean inMakeNew); + static Boolean DisplayDialog(); + +protected: + COfflinePickerView * mList; +}; diff --git a/mozilla/cmd/macfe/MailNews/CProgressBroadcaster.h b/mozilla/cmd/macfe/MailNews/CProgressBroadcaster.h new file mode 100644 index 00000000000..3057b9c5d17 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CProgressBroadcaster.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CProgressBroadcaster.h + +#pragma once + +#include + +//====================================== +class CProgressBroadcaster : public LBroadcaster +//====================================== +{ +public: + enum { msg_StatusText = 'StRp', + msg_StatusPercent = 'StPc', + msg_StatusComplete = 'StCt' + }; + + + struct StatusInfo + { + const char* message; + Int32 percent; + int level; + + StatusInfo () : message(NULL), percent(0), level(0) {} + }; + + CProgressBroadcaster() {} + virtual ~CProgressBroadcaster() {} +}; diff --git a/mozilla/cmd/macfe/MailNews/CProgressListener.cp b/mozilla/cmd/macfe/MailNews/CProgressListener.cp new file mode 100644 index 00000000000..19cd5acd95e --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CProgressListener.cp @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CProgressListener.cp + +#include "CProgressListener.h" + +#include "CPatternProgressBar.h" +#include "CNSContext.h" // for progress messages +#include "xp.h" +#include "MailNewsgroupWindow_Defines.h" + +#include + +#define MIN_TICKS (60/4) // Don't refresh the progress bar more than 4x /sec. + +//----------------------------------- +CProgressListener::CProgressListener(LView* super, LBroadcaster* context) +//----------------------------------- +{ + Assert_(super); + Assert_(context); + mProgressCaption = dynamic_cast(super->FindPaneByID(kMailNewsStatusPaneID)); + mProgressLastTicks = 0; + mMessageLastTicks = 0; + mPercentLastTicks = 0; + mLaziness = mPreviousLaziness = lazy_NotAtAll; + Assert_(mProgressCaption); + context->AddListener(this); +} // CProgressListener::CProgressListener + +//----------------------------------- +CProgressListener::~CProgressListener() +//----------------------------------- +{ +} // CProgressListener::~CProgressListener() + +//----------------------------------- +void CProgressListener::SetLaziness(ProgressBarLaziness inLaziness) +//----------------------------------- +{ + mPreviousLaziness = mLaziness; + mLaziness = inLaziness; +} + +//----------------------------------- +void CProgressListener::ListenToMessage(MessageT inMessage, void *ioParam) +//----------------------------------- +{ + if (!mProgressCaption) + return; + + switch (inMessage) + { +// case msg_NSCStartLoadURL: +// case msg_NSCProgressBegin: +// // context is busy +// break; + + case msg_NSCProgressEnd: + case msg_NSCAllConnectionsComplete: + if (mLaziness == lazy_VeryButForThisCommandOnly) + mLaziness = mPreviousLaziness; + mProgressCaption->SetValue(CPatternProgressCaption::eSeamless); + mProgressCaption->SetDescriptor("\p"); + break; + + case msg_NSCProgressUpdate: + if (mLaziness != lazy_VeryButForThisCommandOnly) + { + if (::TickCount() - mProgressLastTicks >= MIN_TICKS) + { + mProgressLastTicks = ::TickCount(); + CContextProgress* progress = (CContextProgress*)ioParam; + mProgressCaption->SetDescriptor(progress->mMessage); +// mProgressCaption->SetValue(progress->mPercent); + } + } + break; + + case msg_NSCProgressMessageChanged: + Boolean displayIt = true; + if (mLaziness == lazy_JustABit || mLaziness == lazy_VeryButForThisCommandOnly) + { + if (::TickCount() - mMessageLastTicks < MIN_TICKS) + displayIt = false; + } + if (displayIt) + { + mMessageLastTicks = ::TickCount(); + if (ioParam) + mProgressCaption->SetDescriptor(TString((const char*)ioParam)); + else + mProgressCaption->SetDescriptor("\p"); + } + break; + case msg_NSCProgressPercentChanged: + if (::TickCount() - mPercentLastTicks >= MIN_TICKS) + { + mPercentLastTicks = ::TickCount(); + mProgressCaption->SetValue(*(Int32*)ioParam); + } + break; + } +} // CProgressListener::ListenToMessage diff --git a/mozilla/cmd/macfe/MailNews/CProgressListener.h b/mozilla/cmd/macfe/MailNews/CProgressListener.h new file mode 100644 index 00000000000..cc11ecea250 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CProgressListener.h @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CProgressListener.h + +#pragma once + +#include + +class LCaption; +class CPatternProgressCaption; +class LBroadcaster; + +//====================================== +class CProgressListener : public LListener +//====================================== +{ +public: + // We have three kinds of information displayed by the Progress Bar: + // - graphical status level showing a percent value (msg_NSCProgressPercentChanged) + // - text message showing a percent value (msg_NSCProgressUpdate) + // - general purpose text message (msg_NSCProgressMessageChanged) + // + // The first two messages (those about the percent value) are always filtered + // in order to avoid flickers generated by frequent updates. + // + // The last one is not filtered by default: it allows for instance + // to keep track of the mouse cursor over an HTML page and display as fast + // as possible the URLs corresponding to the links pointed by the cursor. + // + // The first level of laziness (lazy_JustABit) filters frequent updates of + // the text messages and allows to go much faster when loading a mail. + // + // The second level of laziness (lazy_VeryButForThisCommandOnly) filters + // the normal text messages and completely ignores the text messages showing + // a percent value. It allows to go much faster when downloading news articles. + // This mode is automatically reset to the previous one when the command has completed. + // + enum ProgressBarLaziness + { + lazy_NotAtAll = 1, + // display all messages, still filter frequent refresh of the progress bar + lazy_JustABit, + // filter + lazy_VeryButForThisCommandOnly + }; + + CProgressListener(CPatternProgressCaption* progressCaption) + : mProgressCaption(progressCaption) {} + CProgressListener(LView* superview, LBroadcaster* broadcaster); + // The broadcaster is the one to listen to. + ~CProgressListener(); + void ListenToMessage(MessageT inMessage, void *ioParam); + void SetLaziness(ProgressBarLaziness inLaziness); + +protected: +// LCaption* mCaption; +// CProgressBar* mProgressBar; + CPatternProgressCaption* mProgressCaption; + unsigned long mProgressLastTicks; + unsigned long mMessageLastTicks; + unsigned long mPercentLastTicks; + ProgressBarLaziness mLaziness; + ProgressBarLaziness mPreviousLaziness; +}; // class ProgressListener diff --git a/mozilla/cmd/macfe/MailNews/CSearchManager.cp b/mozilla/cmd/macfe/MailNews/CSearchManager.cp new file mode 100644 index 00000000000..e63ec923236 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CSearchManager.cp @@ -0,0 +1,1760 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CSearchManager.cp + +/*====================================================================================*/ + #pragma mark INCLUDE FILES +/*====================================================================================*/ + +#define DEBUGGER_ASSERTIONS + +#include "CSearchManager.h" +#include "SearchHelpers.h" + +#include "uerrmgr.h" +#include "CMailNewsContext.h" +#include "libi18n.h" +#include "uprefd.h" +#include +#include +#include "LGARadioButton.h" +#include "CCaption.h" +#include "prefapi.h" +#include +#include "CSingleTextColumn.h" +#include "UMailFilters.h" +#include "CTSMEditField.h" +#include +#include "UIHelper.h" +#include "nc_exception.h" + +#pragma mark - +/*====================================================================================*/ + #pragma mark CONSTANTS +/*====================================================================================*/ + +const UInt16 deltaAttributes = 8; // extra array elements to allocate for the attributes menu +const UInt16 resIDT_CustomHeadersDialog = 8652; // resource id for the custom header edit dialog box +const UInt16 resIDT_CustomHeadersInputDialog = 8654; // resource id for the input dialog box for custom header +const UInt16 kMaxStringLength = 256; + +const char kCustomHeaderPref[] = "mailnews.customHeaders"; // preference name for custom headers + +// custom headers messages +const MessageT msg_NewHeader = 'chNW'; // new custom header message +const MessageT msg_EditHeader = 'chED'; // edit custom header message +const MessageT msg_DeleteHeader = 'chDE'; // delete custom header message + +/*====================================================================================*/ + #pragma mark INTERNAL CLASS DECLARATIONS +/*====================================================================================*/ +#pragma mark - + +//----------------------------------- +CSearchManager::CSearchManager() : + LListener(), + LBroadcaster(), + mNumLevels(0), + mNumVisLevels(0), + mLastMenuItemsScope((MSG_ScopeAttribute) -1), + mLastMenuItemsAttribute((MSG_SearchAttribute) -1), + mNumLastMenuItems(0), + mCurrentScope((MSG_ScopeAttribute) -1), + mMoreButton(nil), + mFewerButton(nil), + mMsgPane(nil), + mCanRotateTarget(true), + mBroadcastUserChanges(true), + mFolderScopeList(nil), + mIsSearching(false), + mMatchAllRadio(nil), + mMatchAnyRadio(nil), + mBoolOperator( true ), + mAttributeItems( nil ), + mLastNumOfAttributes( 0 ) +//----------------------------------- +{ + memset(mLevelFields, 0, sizeof(SearchLevelInfoT) * cMaxNumSearchLevels); +} + +//----------------------------------- +CSearchManager::~CSearchManager(void) +//----------------------------------- +{ + AboutToClose(); + delete mAttributeItems; +} + +//----------------------------------- +void CSearchManager::AboutToClose() +//----------------------------------- +{ + // Designed to be safe to call multiple times. + Assert_(!IsSearching()); +} + +//----------------------------------- +OSErr CSearchManager::InitSearchManager( + LView *inContainer, CSearchTabGroup *inTabGroup, + MSG_ScopeAttribute inScope, LArray *inFolderScopeList ) +// Initialize the window. This class assumes that all level parameters are defined +// within the paneID_ParamEncl view in the correct order. +//----------------------------------- +{ + OSErr error = noErr; + + StValueChanger change(mBroadcastUserChanges, false); + + Try_ { + if ( inTabGroup ) inTabGroup->SetRotateWatchValue(&mCanRotateTarget); + mParamEnclosure = USearchHelper::FindViewSubview(inContainer, paneID_ParamEncl); + + + FindUIItemPtr( mParamEnclosure, paneID_More, mMoreButton ); + FindUIItemPtr( mParamEnclosure, paneID_Fewer, mFewerButton ); + FindUIItemPtr( mParamEnclosure, paneID_MatchAllRadio, mMatchAllRadio ); + FindUIItemPtr( mParamEnclosure, paneID_MatchAnyRadio, mMatchAnyRadio ); + + // Link up this object to its sub broadcasters + USearchHelper::LinkListenerToBroadcasters(mParamEnclosure, this); + + // Locate and cache references to the relevant search fields + LArray *subPanes; + LView *theView = dynamic_cast(mParamEnclosure->FindPaneByID(paneID_ParamSubEncl)); + + if ( theView != nil ) { + subPanes = &theView->GetSubPanes(); + theView->SetRefreshAllWhenResized(false); + } else { + subPanes = &mParamEnclosure->GetSubPanes(); + } + + LArrayIterator iterator(*subPanes); + + AssertFail_(mNumLevels == 0); + AssertFail_(mNumVisLevels == 0); + + LPane *pane; + while ( iterator.Next(&pane) ) + { + Int32 userCon = pane->GetUserCon() - 1; + Boolean updateNumLevels = true; + PaneIDT thePane = pane->GetPaneID(); + switch ( thePane ) + { + case paneID_Attributes: + AssertFail_(IsBetween(userCon, 0, cMaxNumSearchLevels - 1)); + mLevelFields[userCon].attributesPopup = dynamic_cast(pane); + break; + case paneID_Operators: + AssertFail_(IsBetween(userCon, 0, cMaxNumSearchLevels - 1)); + mLevelFields[userCon].operatorsPopup = dynamic_cast(pane); + break; + case paneID_TextValue: + AssertFail_(IsBetween(userCon, 0, cMaxNumSearchLevels - 1)); + mLevelFields[userCon].valueText = dynamic_cast(pane); + break; + case paneID_IntValue: + AssertFail_(IsBetween(userCon, 0, cMaxNumSearchLevels - 1)); + mLevelFields[userCon].valueInt = dynamic_cast(pane); + break; + case paneID_PopupValue: + AssertFail_(IsBetween(userCon, 0, cMaxNumSearchLevels - 1)); + mLevelFields[userCon].valuePopup = dynamic_cast(pane); + break; + case paneID_DateValue: + AssertFail_(IsBetween(userCon, 0, cMaxNumSearchLevels - 1)); + mLevelFields[userCon].valueDate = dynamic_cast(pane); + break; + default: + updateNumLevels = false; + // Pass to next pane + break; + } // switch + if ( updateNumLevels ) + { + if ( userCon >= mNumLevels ) + { + mNumLevels = userCon + 1; + mNumVisLevels = mNumLevels; + } + } + } // while +#ifdef Debug_Signal + // Validate all the fields + AssertFail_(mNumLevels > 0); + for (Int32 i = 0; i < mNumLevels; ++i) + { + AssertFail_((mLevelFields[i].attributesPopup != nil) && (mLevelFields[i].operatorsPopup != nil) && + (mLevelFields[i].valueText != nil) && (mLevelFields[i].valuePopup != nil) && + (mLevelFields[i].valueDate != nil) && (mLevelFields[i].valueInt != nil) ); + } +#endif // gDebugSignal + + mParamEnclosure->SetRefreshAllWhenResized(false); + + // Find the margin between search levels + if ( mNumLevels > 1 ) + { + Rect portFrame; + mLevelFields[1].attributesPopup->CalcPortFrameRect(portFrame); + mLevelResizeV = portFrame.bottom; + mLevelFields[0].attributesPopup->CalcPortFrameRect(portFrame); + mLevelResizeV -= portFrame.bottom; + } + SetSearchScope(inScope, inFolderScopeList); // 97/03/03 second param was NULL. + } + Catch_(inErr) + { + error = inErr; + } + EndCatch_ + + return error; +} // CSearchManager::InitSearchManager + +//----------------------------------- +Boolean CSearchManager::CanSearch(void) const +//----------------------------------- +{ + if ( IsSearching() ) return false; + + for (Int32 i = 0; i < mNumVisLevels; ++i) + { + if ( (USearchHelper::GetEditFieldLength(mLevelFields[i].valueText) < 1) && mLevelFields[i].valueText->IsVisible() ) + return false; + } + return true; +} + + +/*====================================================================================== + React to scope message. +======================================================================================*/ + +void CSearchManager::SetSearchScope(MSG_ScopeAttribute inScope, LArray *inFolderScopeList) +{ + // Set the scope and the list of folders selected. Currently only one + // selection at the time is available - 11/19/97 + + StValueChanger change(mBroadcastUserChanges, false); + + mCurrentScope = inScope; + mFolderScopeList = inFolderScopeList; + + PopulateAttributesMenus(mCurrentScope); + + for (Int32 i = 0; i < mNumLevels; ++i) + { +// SearchLevelInfoT *levelInfo = &mLevelFields[inAttributesMenu->GetUserCon() - 1]; + MessageAttributes(mLevelFields[i]); + } +} + + +/*====================================================================================== + Set the number of visible levels. +======================================================================================*/ + +void CSearchManager::SetNumVisibleLevels(Int32 inNumLevels) { + + Int16 resizeAmount; + + { // Begin scope for stack-based classes. + + StValueChanger change(mBroadcastUserChanges, false); + StValueChanger change2(mIsBroadcasting, false); + + if ( inNumLevels < 1 ) { + inNumLevels = 1; + } else if ( inNumLevels > mNumLevels ) { + inNumLevels = mNumLevels; + } + Int32 delta = inNumLevels - mNumVisLevels; + + Boolean more = (delta < 0) ? false : true; + + SDimension16 startFrameSize; + mParamEnclosure->GetFrameSize(startFrameSize); + + // Hide or show the levels + delta = abs( delta ); + while ( delta != 0 ) + { + MessageMoreFewer( more ); + --delta; + } + + SDimension16 endFrameSize; + mParamEnclosure->GetFrameSize(endFrameSize); + resizeAmount = endFrameSize.height - startFrameSize.height; + + } // End scope for stack-based classes. + + + BroadcastMessage(msg_SearchParametersResized, &resizeAmount); +} + + +/*====================================================================================== + React to stop message. Pass in a valid context if the user cancelled. +======================================================================================*/ + +void CSearchManager::StopSearch(MWContext *inContext) { + + if ( !IsSearching() ) return; + + mIsSearching = false; + + if ( inContext ) { + Int32 rtnVal = MSG_InterruptSearch(inContext); + } + + mParamEnclosure->Enable(); +} + + +/*====================================================================================== + React to save message. +======================================================================================*/ + +/* Not implemented +void CSearchManager::SaveSearchResults(void) +{ +} +*/ + +/*====================================================================================== + Adjust the search parameters to saved status data. +======================================================================================*/ + +void CSearchManager::ReadSavedSearchStatus(LStream *inStatusData) +{ + + StValueChanger change(mBroadcastUserChanges, false); + + if ( inStatusData == nil ) + SetNumVisibleLevels(1); + else + { + Int32 numVisibleLevels; + *inStatusData >> numVisibleLevels; + + SetNumVisibleLevels(numVisibleLevels); + + Int32 numLevels; + *inStatusData >> numLevels; + *inStatusData >> mBoolOperator; + + (mBoolOperator ? mMatchAllRadio : mMatchAnyRadio)->SetValue(Button_On); + + SearchLevelInfoT *curLevel = mLevelFields; + for (int i = 0; i < numLevels; i++) + { + CommandT command1, command2; + SearchTextValueT text; + long age; + Str255 ageStr; + + *inStatusData >> command1; + *inStatusData >> command2; + *inStatusData >> ((StringPtr) text.text); + *inStatusData >> age; + // be cautious: if some future version supports a larger number of levels, + // just skip the data, don't do anything with it. This caused a crash + // when the search window was used in 4.01 after running it in 4.02 (7/30 build). + if (i < mNumLevels) + { + curLevel->attributesPopup->SetCurrentItemByCommand(command1); + curLevel->operatorsPopup->SetCurrentItemByCommand(command2); + curLevel->valueText->SetDescriptor((StringPtr) text.text); + ::NumToString( age, ageStr ); + curLevel->valueInt->SetDescriptor( ageStr ); + curLevel++; + } + + } + } +} + + +Boolean +CSearchManager::GetBooleanOperator() const +{ + return mBoolOperator; +} + +/*====================================================================================== + Get the search status to save. +======================================================================================*/ + +void CSearchManager::WriteSavedSearchStatus(LStream *outStatusData) +{ + + *outStatusData << mNumVisLevels; + *outStatusData << mNumLevels; + *outStatusData << mBoolOperator; + + SearchLevelInfoT *curLevel = mLevelFields; + SearchLevelInfoT *endLevel = curLevel + mNumLevels; + + SearchTextValueT text; + Str255 ageStr; + long age; + + do { + *outStatusData << curLevel->attributesPopup->GetCurrentItemCommandNum(); + *outStatusData << curLevel->operatorsPopup->GetCurrentItemCommandNum(); + *outStatusData << curLevel->valueText->GetDescriptor((StringPtr) text.text); + curLevel->valueInt->GetDescriptor( ageStr ); + ::StringToNum(ageStr, &age); + *outStatusData << age; + } while ( ++curLevel < endLevel ); +} + + +/*====================================================================================== + Get the current search parameters. outSearchParams must be able to hold at least + GetNumVisibleLevels() elements. Strings are output as c-strings. +======================================================================================*/ + +void CSearchManager::GetSearchParameters(SearchLevelParamT *outSearchParams) { + + SearchLevelInfoT *curLevel = mLevelFields; + SearchLevelInfoT *endLevel = curLevel + mNumVisLevels; + + do { + CommandT attributeCommand = curLevel->attributesPopup->GetCurrentItemCommandNum(); + outSearchParams->val.attribute = ((MSG_SearchAttribute) attributeCommand ); + + // *** if custom header get the the menu location and save it + if( attributeCommand == attribOtherHeader ) + outSearchParams->customHeaderPos = curLevel->attributesPopup->GetValue(); + else + outSearchParams->customHeaderPos = 0; + + outSearchParams->op = (MSG_SearchOperator) + curLevel->operatorsPopup->GetCurrentItemCommandNum(); + + outSearchParams->boolOp = mBoolOperator; + + MSG_SearchValueWidget widget; + FailSearchError(MSG_GetSearchWidgetForAttribute(outSearchParams->val.attribute, &widget)); + + switch ( widget ) { + case widgetText: { + if ( curLevel->valueText->IsVisible() ) { + curLevel->valueText->GetDescriptor((StringPtr) outSearchParams->val.u.string); + if ( outSearchParams->val.u.string[0] == 0 ) { + outSearchParams->op = opIsEmpty; + } else { + P2CStr((StringPtr) outSearchParams->val.u.string); + } + } else { + outSearchParams->op = opIsEmpty; + *outSearchParams->val.u.string = '\0'; + } + } + break; + + case widgetInt: + if ( curLevel->valueInt->IsVisible() ) + { + Str255 age; + curLevel->valueInt->GetDescriptor( age ); + if ( age[0] == 0 ) + outSearchParams->op = opIsEmpty; + else + { + Int32 ageNum; + ::StringToNum( age, &ageNum); + outSearchParams->val.u.age = ageNum; + } + } + else + { + outSearchParams->op = opIsEmpty; + outSearchParams->val.u.age = 0; + } + break; + + case widgetDate: { + AssertFail_(curLevel->valueDate->IsVisible()); + Int16 year; UInt8 month, day; + curLevel->valueDate->GetDate(&year, &month, &day); + tm time; + + time.tm_sec = 1; + time.tm_min = 1; + time.tm_hour = 1; + time.tm_mday = day; + time.tm_mon = month - 1; + time.tm_year = year - 1900; + time.tm_wday = -1; + time.tm_yday = -1; + time.tm_isdst = -1; + + outSearchParams->val.u.date = ::mktime(&time); +#ifdef Debug_Signal + tm *checkTime = ::localtime(&outSearchParams->val.u.date); + Assert_((time.tm_sec == checkTime->tm_sec) && (time.tm_min == checkTime->tm_min) && + (time.tm_hour == checkTime->tm_hour) && (time.tm_mday == checkTime->tm_mday) && + (time.tm_mon == checkTime->tm_mon) && (time.tm_year == checkTime->tm_year)); +#endif // Debug_Signal + } + break; + + case widgetMenu: { + AssertFail_(curLevel->valuePopup->IsVisible()); + if ( outSearchParams->val.attribute == attribPriority ) { + outSearchParams->val.u.priority = (MSG_PRIORITY) + curLevel->valuePopup->GetCurrentItemCommandNum(); + } else { + AssertFail_(outSearchParams->val.attribute == attribMsgStatus); + outSearchParams->val.u.msgStatus = (uint32) + curLevel->valuePopup->GetCurrentItemCommandNum(); + } + } + break; + + default: + FailOSErr_(paramErr); // Should never get here! + break; + } + ++outSearchParams; + } while ( ++curLevel < endLevel ); +} + + +/*====================================================================================== + Set the current search parameters. inSearchParams must hold at least inNumVisLevels + elements. If inNumVisLevels < GetNumLevels(), the remaining levels are set + to their cleared values. The InitSearchManager() method must be called before calling + this method. Set inSearchParams to nil to clear all the values. Strings are input + as c-strings. +======================================================================================*/ + +void CSearchManager::SetSearchParameters(Int16 inNumVisLevels, const SearchLevelParamT *inSearchParams) { + + AssertFail_((mNumVisLevels > 0) && (mNumLevels > 0) && IsBetween(inNumVisLevels, 1, mNumLevels)); + StValueChanger change(mBroadcastUserChanges, false); + + SetNumVisibleLevels(inNumVisLevels); + + AssertFail_(inNumVisLevels == mNumVisLevels); + + if ( inSearchParams != nil ) { + SearchLevelInfoT *curLevel = mLevelFields, *endLevel = curLevel + mNumVisLevels; + const SearchLevelParamT *curParam = inSearchParams; + do { + curLevel->attributesPopup->SetCurrentItemByCommand(curParam->val.attribute); + curLevel->operatorsPopup->SetCurrentItemByCommand(curParam->op); + + MSG_SearchValueWidget widget; + FailSearchError(MSG_GetSearchWidgetForAttribute(curParam->val.attribute, &widget)); + switch ( widget ) { + case widgetText: + { +// if (curParam->op == opIsEmpty) // +// *curParam->val.u.string = '\0'; // + CStr255 string(curParam->val.u.string); + if ( string.Length() == 0 ) + curLevel->operatorsPopup->SetCurrentItemByCommand(opIsEmpty); + curLevel->valueText->SetDescriptor(string); + } + curLevel->valueDate->SetToToday(); + curLevel->valuePopup->SetToDefault(); + break; + + case widgetInt: + if( curParam->val.u.age == 0 ) + { + curLevel->operatorsPopup->SetCurrentItemByCommand(opIsEmpty); + curLevel->valueInt->SetDescriptor( CStr255("") ); + } + else + { + Str255 ageString; + ::NumToString( curParam->val.u.age, ageString); + curLevel->valueInt->SetDescriptor( ageString ); + } + curLevel->valueDate->SetToToday(); + curLevel->valuePopup->SetToDefault(); + break; + + case widgetDate: { + tm *time = ::localtime(&curParam->val.u.date); + AssertFail_(time != nil); +#ifdef Debug_Signal + time_t checkTime = ::mktime(time); + Assert_(checkTime == curParam->val.u.date); +#endif // Debug_Signal + curLevel->valueDate->SetDate(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday); + } + curLevel->valueText->SetDescriptor("\p"); + curLevel->valuePopup->SetToDefault(); + break; + + case widgetMenu: + if ( curParam->val.attribute == attribPriority ) { + curLevel->valuePopup->SetCurrentItemByCommand(curParam->val.u.priority); + } else { + AssertFail_(curParam->val.attribute == attribMsgStatus); // Only other option! + curLevel->valuePopup->SetCurrentItemByCommand(curParam->val.u.msgStatus); + } + curLevel->valueText->SetDescriptor("\p"); + curLevel->valueDate->SetToToday(); + break; + + default: + AssertFail_(false); // Should never get here! + break; + } + ++curParam; + } while ( ++curLevel < endLevel ); + } else { + inNumVisLevels = 0; + } + + if ( inNumVisLevels < mNumLevels ) { + ClearSearchParams(inNumVisLevels + 1, mNumLevels); + } +} + + + + +#if 0 +/*====================================================================================== + If the returned MWContext is non-nil, the specified row is currently open and the + returned context belongs to the opened pane. This method always return the context + type of the specified result element. +======================================================================================*/ + +MWContext *CSearchManager::IsResultElementOpen(MSG_ViewIndex inIndex, MWContextType *outType) { + + UpdateMsgResult(inIndex); + + MWContext *rtnContext = MSG_IsResultElementOpen(mResultElement); + *outType = MSG_GetResultElementType(mResultElement); + + return rtnContext; +} + +#endif + + + +/*====================================================================================== + Populate the priority menu. +======================================================================================*/ + +void CSearchManager::PopulatePriorityMenu(CSearchPopupMenu *inMenu) { + + // Call BE to get menu items + + if ( (mLastMenuItemsScope != (MSG_ScopeAttribute) cPriorityScope) || (mLastMenuItemsAttribute != kNumAttributes) ) { + MSG_SearchMenuItem *curMenuItem = mSearchMenuItems; + for (Int32 i = MSG_LowestPriority; i <= (Int32) MSG_HighestPriority; ++i, ++curMenuItem) { + MSG_GetPriorityName((MSG_PRIORITY) i, curMenuItem->name, sizeof(curMenuItem->name)); + C2PStr(curMenuItem->name); + curMenuItem->attrib = i; + curMenuItem->isEnabled = true; + } + mNumLastMenuItems = curMenuItem - mSearchMenuItems; + mLastMenuItemsScope = (MSG_ScopeAttribute) cPriorityScope; + mLastMenuItemsAttribute = kNumAttributes; + } + + UInt32 populateID = 0x7000000; + inMenu->PopulateMenu(mSearchMenuItems, mNumLastMenuItems, MSG_NormalPriority, populateID); +} + + +/*====================================================================================== + Populate the status menu. +======================================================================================*/ + +void CSearchManager::PopulateStatusMenu(CSearchPopupMenu *inMenu) { + + const int32 cStatusValues[] = { MSG_FLAG_READ, + MSG_FLAG_REPLIED, + MSG_FLAG_FORWARDED }; + + uint16 nStatus = sizeof(cStatusValues) / sizeof(int32); + + // Call BE to get menu items + + if ( (mLastMenuItemsScope != (MSG_ScopeAttribute) cStatusScope) || (mLastMenuItemsAttribute != kNumAttributes) ) { + MSG_SearchMenuItem *curMenuItem = mSearchMenuItems; + for (Int32 i = 0; i < nStatus && i < cMaxNumSearchMenuItems; ++i) { + Int32 status = cStatusValues[i]; + MSG_GetStatusName(status, curMenuItem->name, sizeof(curMenuItem->name)); + C2PStr(curMenuItem->name); + if ( curMenuItem->name[0] > 0 ) { + curMenuItem->attrib = status; + curMenuItem->isEnabled = true; + ++curMenuItem; + } + } + mNumLastMenuItems = curMenuItem - mSearchMenuItems; + mLastMenuItemsScope = (MSG_ScopeAttribute) cStatusScope; + mLastMenuItemsAttribute = kNumAttributes; + } + + UInt32 populateID = 0x6000000; + inMenu->PopulateMenu(mSearchMenuItems, mNumLastMenuItems, MSG_FLAG_READ, populateID); +} + + +/*====================================================================================== + Respond to broadcaster messages. +======================================================================================*/ + +void CSearchManager::ListenToMessage(MessageT inMessage, void *ioParam) { + + Boolean userChanged = true; + + switch ( inMessage ) { + + case paneID_Attributes: + CSearchPopupMenu* currentSearchPopup = static_cast( ioParam ); + + // Check if the attribute is the customize command. + // If it is execute it. + CommandT command = currentSearchPopup->GetCurrentItemCommandNum( currentSearchPopup->GetValue() ); + if( command == eCustomizeSearchItem ) + { + StValueChanger change(mBroadcastUserChanges, false); // stop broadcasting inside this scope + EditCustomHeaders(); + userChanged = false; + } + else + { + MessageAttributes( mLevelFields[currentSearchPopup->GetUserCon() - 1] ); + if ( mBroadcastUserChanges ) + SelectSearchLevel(((CSearchPopupMenu *) ioParam)->GetUserCon(), eCheckNothing); + } + break; + + case paneID_Operators: + MessageOperators((CSearchPopupMenu *) ioParam); + if ( mBroadcastUserChanges ) SelectSearchLevel(((CSearchPopupMenu *) ioParam)->GetUserCon(), eCheckNothing); + break; + + case msg_AND_OR_Toggle: + mBoolOperator = (mMatchAllRadio->GetValue() == Button_On ? true : false); + break; + + case paneID_More: + case paneID_Fewer: { + StValueChanger change(mCanRotateTarget, false); + Int32 curSelectedLevel = GetSelectedSearchLevel(); + MessageMoreFewer(inMessage == paneID_More); + if ( mBroadcastUserChanges ) { + if ( (inMessage == paneID_More) || !curSelectedLevel || + (curSelectedLevel == (mNumVisLevels + 1)) ) { + SelectSearchLevel(mNumVisLevels, eCheckBackward); + } + } + } + break; + + case paneID_Clear: + MessageClear(); + if ( mBroadcastUserChanges ) SelectSearchLevel(1, eCheckForward); + break; + + case CSearchEditField::msg_UserChangedText: + break; + + default: + userChanged = false; + // Nothing to do, no inherited method + break; + } + + if ( userChanged ) UserChangedParameters(); +} + + +//----------------------------------- +MSG_ScopeAttribute CSearchManager::GetBEScope(MSG_ScopeAttribute inScope) const +// Convert the current search scope as we store it in this manager to a scope that is +// understood by the BE. +//----------------------------------- +{ + MSG_ScopeAttribute beScope = scopeMailFolder; + + if ( (inScope == scopeNewsgroup) || (inScope == cScopeNewsSelectedItems) ) + beScope = scopeNewsgroup; + else if ( inScope == scopeLdapDirectory ) + beScope = scopeLdapDirectory; + return beScope; +} // CSearchManager::GetBEScope + +//----------------------------------- +void CSearchManager::PopulateAttributesMenus(MSG_ScopeAttribute inScope) +// Populate the attributes menus according to the specified scope. +//----------------------------------- +{ + MSG_ScopeAttribute beScope = GetBEScope(inScope); + + // Call BE to get menu items + + + if ( (mLastMenuItemsScope != beScope) || (mLastMenuItemsAttribute != kNumAttributes) ) + { + // mNumLastMenuItems = cMaxNumSearchMenuItems; + void* scopeItem = nil; + + if (!mFolderScopeList) + { + // This happens with filters. Get a folder info for the inbox. + MSG_FolderInfo* inbox; + ::MSG_GetFoldersWithFlag( + CMailNewsContext::GetMailMaster(), + MSG_FOLDER_FLAG_INBOX, + &inbox, + 1); + scopeItem = inbox; + } + else if (!mFolderScopeList->FetchItemAt(LArray::index_First, &scopeItem)) + return; + + if (scopeItem) + { + // *** get number of items + mLastNumOfAttributes = GetNumberOfAttributes( beScope, scopeItem ); + + if (mFolderScopeList) + FailSearchError(::MSG_GetAttributesForSearchScopes( + CMailNewsContext::GetMailMaster(), + beScope, + &scopeItem, + 1, + mAttributeItems, + &mLastNumOfAttributes)); + else + FailSearchError(::MSG_GetAttributesForFilterScopes( + CMailNewsContext::GetMailMaster(), + beScope, + &scopeItem, + 1, + mAttributeItems, + &mLastNumOfAttributes)); + + // Finish creating the attributes menu for the filter and message search + if( beScope != scopeLdapDirectory ) + FinishCreateAttributeMenu( mAttributeItems, mLastNumOfAttributes ); + + mLastMenuItemsScope = beScope; + mLastMenuItemsAttribute = kNumAttributes; + + // Convert to pascal string + for (Int32 j = 0; j < mLastNumOfAttributes; ++j) + C2PStr(mAttributeItems[j].name); + + //UInt32 populateID = ((((UInt32) beScope)<<16) | ((UInt32) 0) | 0x4000000); + UInt32 populateID = (UInt32)scopeItem; + for (Int32 i = 0; i < mNumLevels; ++i) + { + mLevelFields[i].attributesPopup->PopulateMenu( + mAttributeItems, mLastNumOfAttributes, attribSender, populateID); + } + } + } +} // CSearchManager::PopulateAttributesMenus + +//----------------------------------- +void CSearchManager::PopulateOperatorsMenus(MSG_ScopeAttribute inScope) +// Populate the operators menus according to the specified scope. This method assumes +// that the attributes menus have been setup to the scope already by calling the +// PopulateAttributesMenus() method. +//----------------------------------- +{ + MSG_ScopeAttribute beScope = GetBEScope(inScope); + + // Call BE to get menu items + + MSG_SearchAttribute lastAttrib = kNumAttributes; + CommandT newCommandIfInvalid; + UInt32 populateID; + + void* scopeItem = nil; + if (!mFolderScopeList) + { + // This happens with filters. Get a folder info for the inbox. + MSG_FolderInfo* inbox; + ::MSG_GetFoldersWithFlag( + CMailNewsContext::GetMailMaster(), + MSG_FOLDER_FLAG_INBOX, + &inbox, + 1); + scopeItem = inbox; + } + else if (!mFolderScopeList->FetchItemAt(LArray::index_First, &scopeItem)) + return; + if (!scopeItem) + return; + for (Int32 i = 0; i < mNumLevels; ++i) + { + MSG_SearchAttribute attrib + = (MSG_SearchAttribute)mLevelFields[i].attributesPopup->GetCurrentItemCommandNum(); + if ( lastAttrib != attrib ) + { + if ( (mLastMenuItemsScope != beScope) || (mLastMenuItemsAttribute != attrib) ) + { + mNumLastMenuItems = cMaxNumSearchMenuItems; + if (mFolderScopeList) + FailSearchError(MSG_GetOperatorsForSearchScopes( + CMailNewsContext::GetMailMaster(), + beScope, + &scopeItem, + 1, + attrib, + mSearchMenuItems, + &mNumLastMenuItems)); + else + FailSearchError(MSG_GetOperatorsForFilterScopes( + CMailNewsContext::GetMailMaster(), + beScope, + &scopeItem, + 1, + attrib, + mSearchMenuItems, + &mNumLastMenuItems)); + mLastMenuItemsScope = beScope; + mLastMenuItemsAttribute = attrib; + // Convert to pascal string + for (Int32 j = 0; j < mNumLastMenuItems; ++j) + C2PStr(mSearchMenuItems[j].name); + } + lastAttrib = attrib; + + MSG_SearchValueWidget widget; + FailSearchError(MSG_GetSearchWidgetForAttribute(lastAttrib, &widget)); + + switch ( widget ) { + case widgetText: newCommandIfInvalid = opContains; break; + case widgetDate: newCommandIfInvalid = opIsBefore; break; + case widgetMenu: newCommandIfInvalid = opIs; break; + case widgetInt: newCommandIfInvalid = opIsGreaterThan; break; + default: + AssertFail_(false); // Should never get here! + newCommandIfInvalid = 0; + break; + } + //populateID = ((((UInt32) beScope)<<16) | ((UInt32) lastAttrib) | 0x5000000); + populateID = ((((UInt32) scopeItem)<<16) | ((UInt32) lastAttrib) | 0x5000000); + } + mLevelFields[i].operatorsPopup->PopulateMenu( + mSearchMenuItems, mNumLastMenuItems, + newCommandIfInvalid, populateID); + } // for +} // CSearchManager::PopulateOperatorsMenus + +/*====================================================================================== + Select the active input search field in the specified search parameter level. +======================================================================================*/ + +void CSearchManager::SelectSearchLevel(Int16 inBeginLevel, ECheckDirection inCheckDirection) { + + if ( !IsBetween(inBeginLevel, 1, mNumVisLevels) ) return; + Int32 inc, end; + + switch ( inCheckDirection ) { + case eCheckForward: inc = 1; end = mNumVisLevels + 1; break; + case eCheckBackward: inc = -1; end = 0; break; + default: inc = 1; end = inBeginLevel + 1; break; + } + + for (Int32 i = inBeginLevel; i != end; i += inc) { + SearchLevelInfoT *levelInfo = &mLevelFields[i - 1]; + MSG_SearchAttribute attrib = (MSG_SearchAttribute) levelInfo->attributesPopup->GetCurrentItemCommandNum(); + MSG_SearchValueWidget widget; + FailSearchError(MSG_GetSearchWidgetForAttribute(attrib, &widget)); + + if ( widget == widgetText ) + { + Assert_(levelInfo->valueText->IsLatentVisible()); + USearchHelper::SelectEditField(levelInfo->valueText); + return; + } + else if ( widget == widgetDate ) + { + Assert_(levelInfo->valueDate->IsLatentVisible()); + USearchHelper::SelectDateView(levelInfo->valueDate); + return; + } + else if( widget == widgetInt ) + { + Assert_(levelInfo->valueInt->IsLatentVisible()); + USearchHelper::SelectEditField(levelInfo->valueInt); + return; + } + } +} +/*====================================================================================== + Set the wincsid for the editfield +======================================================================================*/ + +void CSearchManager::SetWinCSID(int16 wincsid) { + ResIDT textTraitsID = 8603; + if( wincsid != INTL_CharSetNameToID(INTL_ResourceCharSet()) ) + textTraitsID = CPrefs::GetTextFieldTextResIDs(wincsid); + + for (Int32 i = 0; i < mNumVisLevels ; i++) { + SearchLevelInfoT *levelInfo = &mLevelFields[i]; + MSG_SearchAttribute attrib = (MSG_SearchAttribute) levelInfo->attributesPopup->GetCurrentItemCommandNum(); + if( attrib == eCustomizeSearchItem ) + attrib = (MSG_SearchAttribute) levelInfo->attributesPopup->GetOldItemCommand(); + + // ******************************************************************************* + // + // !!! HACK ALERT !!! + // + // The current search architecture assumes that the location of the menu item correspond to the + // command number. Bad assumption. The "Age in Days" attribute does not follow this system. The + // following code is a hack to fix this problem. It checks for the item number and current scope + // and substitutes the correct command for the bad assumption. + // + // 12/3/97 + // + // ******************************************************************************* + + if( mCurrentScope != scopeLdapDirectory && attrib == attribCommonName ) + attrib = attribAgeInDays; + + // ******************************************************************************* + + MSG_SearchValueWidget widget; + FailSearchError(MSG_GetSearchWidgetForAttribute(attrib, &widget)); + + if ( widget == widgetText ) + { + Assert_(levelInfo->valueText->IsLatentVisible()); + levelInfo->valueText->SetTextTraitsID(textTraitsID); + levelInfo->valueText->Refresh(); + } + else if ( widget == widgetInt ) + { + Assert_(levelInfo->valueInt->IsLatentVisible()); + levelInfo->valueInt->SetTextTraitsID(textTraitsID); + levelInfo->valueInt->Refresh(); + } + } +} + + +/*====================================================================================== + Find the currently selected (i.e. active user input) search level, 0 if none. +======================================================================================*/ + +Int32 CSearchManager::GetSelectedSearchLevel(void) { + + SearchLevelInfoT *curlevelInfo = &mLevelFields[0]; + SearchLevelInfoT *endlevelInfo = curlevelInfo + mNumVisLevels; + + while ( curlevelInfo < endlevelInfo ) { + if ( curlevelInfo->valueText->IsTarget() || curlevelInfo->valueDate->ContainsTarget() + || curlevelInfo->valueInt->IsTarget() ) + { + return (curlevelInfo - &mLevelFields[0] + 1); + } + ++curlevelInfo; + } + return 0; +} + + +/*====================================================================================== + React to attributes message. +======================================================================================*/ + +//void CSearchManager::MessageAttributes(CSearchPopupMenu *inAttributesMenu) { +void CSearchManager::MessageAttributes( SearchLevelInfoT& searchLevel ) { + +// SearchLevelInfoT *levelInfo = &mLevelFields[inAttributesMenu->GetUserCon() - 1]; + CSearchPopupMenu* inAttributesMenu = searchLevel.attributesPopup; + + PopulateOperatorsMenus(mCurrentScope); + + // Find out which value type to display + + MSG_SearchAttribute attrib = (MSG_SearchAttribute) inAttributesMenu->GetCurrentItemCommandNum(); + MSG_SearchValueWidget widget; + FailSearchError(MSG_GetSearchWidgetForAttribute(attrib, &widget)); + + if ( widget == widgetMenu ) { + if ( attrib == attribPriority ) { + PopulatePriorityMenu(searchLevel.valuePopup); + } else { + PopulateStatusMenu(searchLevel.valuePopup); + } + } + + if ( inAttributesMenu->IsLatentVisible() ) { + // !!! + USearchHelper::ShowHidePane(searchLevel.valueText, widget == widgetText); + USearchHelper::ShowHidePane(searchLevel.valueInt, widget == widgetInt); + USearchHelper::ShowHidePane(searchLevel.valueDate, widget == widgetDate); + USearchHelper::ShowHidePane(searchLevel.valuePopup, widget == widgetMenu); + } else { + switch ( widget ) + // !!! need to add support for days + { + case widgetText: searchLevel.lastVisibleValue = searchLevel.valueText; break; + case widgetInt: searchLevel.lastVisibleValue = searchLevel.valueInt; break; + case widgetDate: searchLevel.lastVisibleValue = searchLevel.valueDate; break; + case widgetMenu: searchLevel.lastVisibleValue = searchLevel.valuePopup; break; + default: + AssertFail_(false); // Should never get here! + break; + } + } + + MessageOperators(searchLevel.operatorsPopup); +} + + +/*====================================================================================== + React to operators message. +======================================================================================*/ + +void CSearchManager::MessageOperators(CSearchPopupMenu *inOperatorsMenu) { + + SearchLevelInfoT *levelInfo = &mLevelFields[inOperatorsMenu->GetUserCon() - 1]; + + MSG_SearchOperator op = (MSG_SearchOperator) inOperatorsMenu->GetCurrentItemCommandNum(); + MSG_SearchAttribute attrib = (MSG_SearchAttribute) levelInfo->attributesPopup->GetCurrentItemCommandNum(); + MSG_SearchValueWidget widget; + FailSearchError(MSG_GetSearchWidgetForAttribute(attrib, &widget)); + + if ( widget == widgetText ) + { + if ( op == opIsEmpty ) levelInfo->lastVisibleValue = nil; + if ( inOperatorsMenu->IsLatentVisible() ) + USearchHelper::ShowHidePane(levelInfo->valueText, op != opIsEmpty); + } + else if ( widget == widgetInt ) + { + if ( op == opIsEmpty ) levelInfo->lastVisibleValue = nil; + if ( inOperatorsMenu->IsLatentVisible() ) + USearchHelper::ShowHidePane(levelInfo->valueInt, op != opIsEmpty); + } +} + + +/*====================================================================================== + React to more message. +======================================================================================*/ + +void CSearchManager::MessageMoreFewer(Boolean inMore) { + + AssertFail_((mNumLevels > 0) && (mNumLevels <= cMaxNumSearchLevels)); + AssertFail_((mNumVisLevels > 0) && (mNumVisLevels <= mNumLevels)); + AssertFail_(!(inMore && (mNumVisLevels == mNumLevels)) || (!inMore && (mNumVisLevels == 1))); + + // Show/hide subviews + + SearchLevelInfoT *showHideLevel = &mLevelFields[inMore ? mNumVisLevels : (mNumVisLevels - 1)]; + + if ( !inMore ) { + if ( showHideLevel->valuePopup->IsLatentVisible() ) { + showHideLevel->lastVisibleValue = showHideLevel->valuePopup; + } else if ( showHideLevel->valueText->IsLatentVisible() ) { + showHideLevel->lastVisibleValue = showHideLevel->valueText; + } else if ( showHideLevel->valueInt->IsLatentVisible() ) { + showHideLevel->lastVisibleValue = showHideLevel->valueInt; + } else if ( showHideLevel->valueDate->IsLatentVisible() ) { + showHideLevel->lastVisibleValue = showHideLevel->valueDate; + } else { + showHideLevel->lastVisibleValue = nil; + } + } + USearchHelper::ShowHidePane(showHideLevel->attributesPopup, inMore); + USearchHelper::ShowHidePane(showHideLevel->operatorsPopup, inMore); + + if ( showHideLevel->lastVisibleValue != nil ) { + USearchHelper::ShowHidePane(showHideLevel->lastVisibleValue, inMore); + } + + // Resize the enclosing pane + + Int16 resizeAmount = inMore ? mLevelResizeV : -mLevelResizeV; + mParamEnclosure->ResizeFrameBy(0, resizeAmount, true); + + // Enable/disable more/fewer + + mNumVisLevels += (inMore ? 1 : -1); + USearchHelper::EnableDisablePane(mMatchAllRadio, (mNumVisLevels > 1)); + USearchHelper::EnableDisablePane(mMatchAnyRadio, (mNumVisLevels > 1)); + USearchHelper::EnableDisablePane(mMoreButton, (mNumVisLevels < mNumLevels)); + USearchHelper::EnableDisablePane(mFewerButton, (mNumVisLevels > 1)); + + BroadcastMessage(msg_SearchParametersResized, &resizeAmount); +} + + +/*====================================================================================== + React to clear message. inStartLevel and inEndLevel are 1 based. +======================================================================================*/ + +void CSearchManager::MessageClear(void) { + + ClearSearchParams(1, mNumLevels); + + BroadcastMessage(msg_SearchParametersCleared); +} + + +/*====================================================================================== + Clear the search params for the specified levels. +======================================================================================*/ + +void CSearchManager::ClearSearchParams(Int16 inStartLevel, Int16 inEndLevel) { + + AssertFail_(IsBetween(inStartLevel, 1, mNumLevels)); + AssertFail_(IsBetween(inEndLevel, 1, mNumLevels)); + AssertFail_(inStartLevel <= inEndLevel); + + SearchLevelInfoT *curLevel = mLevelFields + inStartLevel - 1; + SearchLevelInfoT *endLevel = mLevelFields + inEndLevel; + + do { + curLevel->attributesPopup->SetToDefault(); + curLevel->operatorsPopup->SetToDefault(); + curLevel->valueText->SetDescriptor("\p"); + curLevel->valueInt->SetDescriptor( CStr255("") ); + curLevel->valueDate->SetToToday(); + curLevel->valuePopup->SetToDefault(); + } while ( ++curLevel < endLevel ); +} + + + + +//----------------------------------- +void CSearchManager::AddScopesToSearch(MSG_Master *inMaster) +//----------------------------------- +{ + AssertFail_(mMsgPane != nil); + Boolean addedScope = false; + switch (mCurrentScope) + { + case cScopeMailFolderNewsgroup: + FailSearchError(MSG_AddAllScopes(mMsgPane, inMaster, scopeMailFolder)); + FailSearchError(MSG_AddAllScopes(mMsgPane, inMaster, scopeNewsgroup)); + addedScope = true; + break; + case scopeMailFolder: + case scopeNewsgroup: + FailSearchError(MSG_AddAllScopes(mMsgPane, inMaster, mCurrentScope)); + addedScope = true; + break; + case scopeLdapDirectory: + { + // In this case, mFolderScopeList contains only the single LDAP directory. + LArrayIterator iterator(*mFolderScopeList); + DIR_Server *dirServer; + while ( iterator.Next(&dirServer) ) + { + FailSearchError(MSG_AddLdapScope(mMsgPane, dirServer)); + addedScope = true; // in the loop, in case there are no servers! + } + break; + } + } + if ( !addedScope ) + { + // SOME scope must be specified! + if ( (mFolderScopeList == nil) || (mFolderScopeList->GetCount() < 1) || + ((mCurrentScope != cScopeMailSelectedItems) && (mCurrentScope != cScopeNewsSelectedItems)) ) + { + FailOSErr_(paramErr); + } + LArrayIterator iterator(*mFolderScopeList); + MSG_FolderInfo *msgFolderInfo; + while ( iterator.Next(&msgFolderInfo) ) + { + MSG_FolderLine folderLine; + if( MSG_GetFolderLineById(inMaster, + msgFolderInfo, + &folderLine)) + { + FailSearchError( + MSG_AddScopeTerm(mMsgPane, + (folderLine.flags & MSG_FOLDER_FLAG_MAIL) ? scopeMailFolder : scopeNewsgroup, + msgFolderInfo) ); + } + } + } +} // CSearchManager::AddScopesToSearch + +//----------------------------------- +void CSearchManager::AddParametersToSearch(void) +//----------------------------------- +{ + AssertFail_(mMsgPane != nil); + + // Get the search parameters + StSearchDataBlock searchParams(mNumVisLevels, StSearchDataBlock::eAllocateStrings); + GetSearchParameters(searchParams.GetData()); + + // Add parameters to the search + SearchLevelParamT *curLevel = searchParams.GetData(); + SearchLevelParamT *endLevel = curLevel + mNumVisLevels; + do { + char *customHeader = nil; + + // *** If the attribute is a custom header pass it in using the variable + // customHeader, otherwise pass a nil value. + if( curLevel->val.attribute == attribOtherHeader ) + customHeader = GetSelectedCustomHeader( curLevel ); + + FailSearchError(MSG_AddSearchTerm(mMsgPane, curLevel->val.attribute, curLevel->op, + &curLevel->val, mBoolOperator, customHeader )); + + } while ( ++curLevel < endLevel ); +} // CSearchManager::AddParametersToSearch + +//----------------------------------- +void CSearchManager::FailSearchError(MSG_SearchError inError) +// Raise a relevant exception if MSG_SearchError is not SearchError_Success. +//----------------------------------- +{ + if ( inError == SearchError_Success ) return; + + switch ( inError ) + { + case SearchError_OutOfMemory: + Throw_(memFullErr); + break; + + case SearchError_NotImplemented: + { +#ifdef Debug_Signal + CStr255 errorString; + USearchHelper::AssignUString( + USearchHelper::uStr_SearchFunctionNotImplemented, errorString); + ErrorManager::PlainAlert((StringPtr) errorString); +#endif // Debug_Signal + Throw_(unimpErr); + } + break; + + case SearchError_InvalidPane: // probably searching a server with no subscribed groups + CStr255 errorString; + USearchHelper::AssignUString(USearchHelper::uStr_SearchFunctionNothingToSearch, errorString); + ErrorManager::PlainAlert((StringPtr) errorString); + Throw_(userCanceledErr); + break; + + case SearchError_NullPointer: + case SearchError_ScopeAgreement: + case SearchError_ListTooSmall: + case SearchError_InvalidAttribute: + case SearchError_InvalidScope: + case SearchError_InvalidOperator: + case SearchError_InvalidSearchTerm: + case SearchError_InvalidScopeTerm: + case SearchError_InvalidResultElement: + case SearchError_InvalidStream: + case SearchError_InvalidFolder: { +#ifdef Debug_Signal + CStr255 errorString; + USearchHelper::AssignUString(USearchHelper::uStr_SearchFunctionParamError, errorString); + ErrorManager::PlainAlert((StringPtr) errorString); +#endif // Debug_Signal + Throw_(paramErr); + } + break; + + case SearchError_ResultSetEmpty: + Throw_(paramErr); + break; + + case SearchError_ResultSetTooBig: + Throw_(memFullErr); + break; + + default: + AssertFail_(false); + Throw_(32000); // Who knows? + break; + + } +} + + + + +#pragma mark - +#pragma mark CUSTOM HEADERS +#pragma mark ÊÊÊ +//////////////////////////////////////////////////////////////////////////////////////// +// +// Custom headers +// +// Code to support the "Custom Header" feature. +// +// 12/1/97 +// +//////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////// +// FinishCreateAttributeMenu( MSG_SearchMenuItem* inMenuItems, UInt16& inNumItems ) +// +// Finish creating the attributes menu. Add the item separator and the "EditÉ" item. + +void +CSearchManager::FinishCreateAttributeMenu( MSG_SearchMenuItem* inMenuItems, UInt16& inNumItems ) +{ + if( PREF_PrefIsLocked(kCustomHeaderPref) ) // check if the preference is locked + return; // if it is bail out. + + inMenuItems[inNumItems].attrib = 0; // add the seaparation item + inMenuItems[inNumItems].name[0] = '-'; + inMenuItems[inNumItems].name[1] = '\0'; + inMenuItems[inNumItems].isEnabled = false; // disabled + + CStr31 string; + ::GetIndString( string, eSTRINGS_RES_ID, eEditMenuItem ); // get Edit item from resource ( macfe.r) + + strcpy( inMenuItems[inNumItems + 1].name, string); // add the edit item + inMenuItems[inNumItems + 1].isEnabled = true; // enabled + inMenuItems[inNumItems + 1].attrib = eCustomizeSearchItem; + + inNumItems = inNumItems + 2; // increase the menu item total +} + +//////////////////////////////////////////////////////////////////////////////////////// +// GetNumberOfAttributes( MSG_ScopeAttribute& scope, void* scopeItem ) +// +// Get the current number of attributes from the BE + +UInt16 +CSearchManager::GetNumberOfAttributes( MSG_ScopeAttribute& scope, void* scopeItem ) +{ + UInt16 numberOfAttributes; + + FailSearchError(::MSG_GetNumAttributesForSearchScopes( + CMailNewsContext::GetMailMaster(), + scope, + &scopeItem, + 1, + &numberOfAttributes)); + + // check if the array is allocated + if( mAttributeItems == nil ) + mAttributeItems = new MSG_SearchMenuItem[numberOfAttributes + deltaAttributes]; + else + // check that is allocated and big enough + if( numberOfAttributes > mLastNumOfAttributes ) + { + delete mAttributeItems; + mAttributeItems = new MSG_SearchMenuItem[numberOfAttributes + deltaAttributes]; + } + + // return the number of attributes + return numberOfAttributes; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// EditCustomHeaders() +// +// Bring up the dialog for editing the custom headers + +void +CSearchManager::EditCustomHeaders() +{ + StDialogHandler customHeadersDlgHandler( resIDT_CustomHeadersDialog, nil ); + + LWindow* customHeadersDlg = customHeadersDlgHandler.GetDialog(); + AssertFail_( customHeadersDlg != nil ); + + // get the new, edit, delete buttons and table + LGAPushButton* newHeaderBtn = dynamic_cast( customHeadersDlg->FindPaneByID('chNW') ); + AssertFail_( newHeaderBtn != nil ); + + LGAPushButton* editHeaderBtn = dynamic_cast( customHeadersDlg->FindPaneByID('chED') ); + AssertFail_( editHeaderBtn != nil ); + + LGAPushButton* deleteHeaderBtn = dynamic_cast( customHeadersDlg->FindPaneByID('chDE') ); + AssertFail_( deleteHeaderBtn != nil ); + + CSingleTextColumn* customHeaderTbl = dynamic_cast( customHeadersDlg->FindPaneByID('chTL') ); + AssertFail_( customHeaderTbl != nil ); + + // get the custom headers + char *customHeaderBuffer = nil; + try + { + int error = ::PREF_CopyCharPref( kCustomHeaderPref, &customHeaderBuffer ); + } + catch( mail_exception& error ) + { + error.DisplaySimpleAlert(); + return; + } + + // fill up the table + STableCell cell(1, 1); + if ((customHeaderBuffer != nil) && (*customHeaderBuffer != '\0')) // make sure you got something to display + { + FillUpCustomHeaderTable( customHeaderBuffer, customHeaderTbl ); + customHeaderTbl->SelectCell( cell ); + } + + // free the custom header buffer + if (customHeaderBuffer) + XP_FREE(customHeaderBuffer); + + customHeaderTbl->AddListener( &customHeadersDlgHandler ); + customHeadersDlg->FocusDraw(); + + // loop while the dialog box is up + bool notDone = true; + MessageT theMessage; + + while( notDone ) + { + // pool the message + theMessage = customHeadersDlgHandler.DoDialog(); + switch( theMessage ) + { + case msg_OK: + const char *customHeadersBuffer = GetCustomHeadersFromTable( customHeaderTbl ); + ::PREF_SetCharPref( kCustomHeaderPref, customHeadersBuffer ); + ::PREF_SavePrefFile(); + delete customHeadersBuffer; // delete the buffer + notDone = false; + break; + + case msg_Cancel: + notDone = false; + break; + + case msg_NewHeader: + Str255 newHeader; + if( InputCustomHeader( newHeader, true) ) + { + customHeaderTbl->InsertRows(1, 0, newHeader, (newHeader[0] + 1), true ); + customHeaderTbl->SelectCell( STableCell(1, 1) ); + } + break; + + case msg_EditHeader: + case msg_SingleTextColumnDoubleClick: + cell.SetCell(0, 0); + Str255 chosenHeader; + Uint32 size = kMaxStringLength; + if( customHeaderTbl->GetNextSelectedCell( cell )) // get selected cell + { + customHeaderTbl->GetCellData( cell, chosenHeader, size ); + if( InputCustomHeader( chosenHeader, false ) ) + { + customHeaderTbl->RemoveRows(1L, cell.row, true ); + customHeaderTbl->InsertRows(1, 0, chosenHeader, (chosenHeader[0] + 1), true ); + customHeaderTbl->SelectCell( STableCell(1, 1) ); + if( !deleteHeaderBtn->IsEnabled() ) + { + deleteHeaderBtn->EnableSelf(); + editHeaderBtn->EnableSelf(); + } + } + } + break; + + case msg_DeleteHeader: + cell.SetCell(0, 0); + if( customHeaderTbl->GetNextSelectedCell( cell ) ) // get selected cell + { + customHeaderTbl->RemoveRows(1L, cell.row, true ); // remove it + STableCell firstCell(1, 1); + customHeaderTbl->SelectCell( firstCell ); + } + break; + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// InputCustomHeader( const StringPtr ioHeader, bool new ) +// +// Edit or input new custom header + +bool +CSearchManager::InputCustomHeader( const StringPtr ioHeader, bool newHeader ) +{ + StDialogHandler inputHeadersDlgHandler( resIDT_CustomHeadersInputDialog, nil ); + + LWindow* inputHeadersDlg = inputHeadersDlgHandler.GetDialog(); + AssertFail_( inputHeadersDlg != nil ); + + CSearchEditField* inHeaderField = dynamic_cast( inputHeadersDlg->FindPaneByID('chIO') ); + AssertFail_( inHeaderField != nil ); + + // initialize edit field + + if( !newHeader ) + { + inHeaderField->SetDescriptor( ioHeader ); + inHeaderField->SelectAll(); + } + else + inHeaderField->SetDescriptor( CStr255("") ); + + LCommander::SwitchTarget(inHeaderField); + inHeaderField->Refresh(); + + // attach the custom header validation field + inHeaderField->SetKeyFilter( UMailFilters::CustomHeaderFilter ); + + MessageT theMessage; + bool changed = false; + bool notDone = true; + + while( notDone ) + { + // pool the message + theMessage = inputHeadersDlgHandler.DoDialog(); + switch( theMessage ) + { + case msg_OK: + inHeaderField->GetDescriptor( ioHeader ); + changed = true; + notDone = false; + break; + + case msg_Cancel: + notDone = false; + break; + } + } + return changed; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// FillUpCustomHeaderTable( const char* buffer, const CSingleTextColumn* table ) +// +// Fill up the table in the dialog + +void +CSearchManager::FillUpCustomHeaderTable( const char* buffer, CSingleTextColumn* table ) +{ + int i; + int start = 0; + int stringLength; + char customHeaderStr[256]; + Uint32 bufferLength = strlen(buffer); + Uint32 customHeaderLength; + TableIndexT row = 0; + + // get the individual custom headers + for (i = 0; i <= bufferLength; i++) + { + if (buffer[i] == eCustomHeaderSeparator || buffer[i] == '\0') + { + // fill up a string + stringLength = MIN(i - start, sizeof(customHeaderStr) - 1); + strncpy(&customHeaderStr[0], &buffer[start], stringLength); + customHeaderStr[stringLength] = '\0'; + start = i + 1; + customHeaderLength = strlen(customHeaderStr) + 1; + // proper insertion + table->InsertRows(1, row++, c2pstr(customHeaderStr), customHeaderLength, false); + } + } + table->Refresh(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// GetCustomHeadersFromTable( CSingleTextColumn* table, char *buffer ) +// +// Return the data from the custom header table, properly formated for the preferences +// +// *** Be aware that you have to delete the returned pointer *** +// +const char* +CSearchManager::GetCustomHeadersFromTable( CSingleTextColumn* table ) +{ + STableCell cell; + Uint32 bufferSize; + Uint32 customHeaderLength; + + // get size of custom headers + bufferSize = 0; + cell.SetCell(0, 0); + while( table->GetNextCell( cell ) ) + { + customHeaderLength = 0; + table->GetCellData( cell, nil, customHeaderLength ); + bufferSize += customHeaderLength; // the header is a pascal string, the space taken by the extra + // byte at offset 0 will be used to store the string separators + } + + // allocate buffer for custom headers + char* buffer = nil; + char* marker = nil; + + if (bufferSize == 0) + bufferSize = 1; // need space for '\0' + buffer = new char[ bufferSize ]; + if( buffer == nil ) // if can't allocate throw exception + throw bufferSize; + marker = buffer; + + // separators + char separator = eCustomHeaderSeparator; + Str255 temp; + + cell.SetCell(0, 0); // reset the cell to beginning of the table + while( table->GetNextCell( cell ) ) + { + customHeaderLength = kMaxStringLength; + table->GetCellData( cell, temp, customHeaderLength ); // get the cell data + customHeaderLength = temp[0]; // find its length + memcpy( marker, &temp[1], customHeaderLength ); // copy it to the buffer + marker += customHeaderLength; // increase the data marker + *(marker ++) = eCustomHeaderSeparator; // add the headers separator + } + + // add the end of line character + if (marker > buffer) + marker --; // overwrite the last separator + *marker = '\0'; + + return buffer; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// GetSelectedCustomHeader( const SearchLevelInfoT* curLevel ) const +// +// Returns the string corresponding to the particular custom header menu item + +char* +CSearchManager::GetSelectedCustomHeader( const SearchLevelParamT* curLevel ) const +{ + return mAttributeItems[curLevel->customHeaderPos - 1].name; +} diff --git a/mozilla/cmd/macfe/MailNews/CSearchManager.h b/mozilla/cmd/macfe/MailNews/CSearchManager.h new file mode 100644 index 00000000000..ef3a91042c7 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CSearchManager.h @@ -0,0 +1,342 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CSearchManager.h + +#pragma once + +/*====================================================================================== + DESCRIPTION: Handles interaction with BE and management of search window. + All calls to the BE search engine bottleneak through this class. +======================================================================================*/ + + +/*====================================================================================*/ + #pragma mark INCLUDE FILES +/*====================================================================================*/ + +// BE stuff + +#include "msg_srch.h" + +// Other files + +//"search.cpp" + +class CSearchPopupMenu; +class CSearchEditField; +class CSearchDateField; +class CSearchTabGroup; +class CStr255; + +class LGAPushButton; +class LGAPopup; +class LGARadioButton; +class CSingleTextColumn; + +#pragma mark - +/*====================================================================================*/ + #pragma mark TYPEDEFS +/*====================================================================================*/ + +// Information about a given search level +typedef struct { +// CListenerCaption *booleanOperator; no longer used: we have a global AND/OR now (the code was removed, it was not very clean anyway) + CSearchPopupMenu *attributesPopup; + CSearchPopupMenu *operatorsPopup; + CSearchPopupMenu *valuePopup; + CSearchEditField *valueText; + CSearchEditField *valueInt; + CSearchDateField *valueDate; + LPane *lastVisibleValue; +} SearchLevelInfoT; + +typedef struct { + char text[128]; +} SearchTextValueT; + +typedef struct { + MSG_SearchValue val; + MSG_SearchOperator op; + XP_Bool boolOp; // we have a global AND/OR now but we continue to use + // that flag for compatibility with existing rules + long customHeaderPos; +} SearchLevelParamT; + +#pragma mark - +/*====================================================================================*/ + #pragma mark CLASS DECLARATIONS +/*====================================================================================*/ + +class CSearchManager : public LListener, + public LBroadcaster { + +public: + CSearchManager(); + virtual ~CSearchManager(); + void AboutToClose(); + Boolean GetBooleanOperator() const; + + // IDs for panes in associated view, also messages that are broadcast to this object + + enum { + paneID_Attributes = 'ATTR' // CSearchPopupMenu *, attributes popup + , paneID_Operators = 'OPER' // CSearchPopupMenu *, operators popup + , paneID_TextValue = 'TXVL' // nil, text value + , paneID_IntValue = 'ioNT' // nil, integer value + , paneID_PopupValue = 'POVL' // nil, popup value + , paneID_DateValue = 'DTVL' // nil, date value + , paneID_More = 'MORE' // nil, more button + , paneID_Fewer = 'FEWR' // nil, fewer button + , paneID_Clear = 'CLER' // nil, clear button + , paneID_ParamEncl = 'PENC' // nil, parameter enclosure + , paneID_ParamSubEncl = 'SENC' // nil, subview parameter enclosure + , paneID_MatchAllRadio = 'MAND' // AND radio + , paneID_MatchAnyRadio = 'MOR ' // OR radio + }; + + // Messages that this object broadcasts + + enum { + msg_SearchParametersResized = paneID_ParamEncl // Int16 *, search parameters enclosure was resized + , msg_SearchParametersCleared = paneID_Clear // nil, search parameters were cleared + , msg_UserChangedSearchParameters = 'UCHG' // nil, search parameters were changed, broadcast + // on ANY change of the parameters by the user + , msg_AND_OR_Toggle = 'ANOR' + }; + + // Other constants + + enum { + cMaxNumSearchLevels = 5 + , cMaxNumSearchMenuItems = 20 + , cScopeMailFolderNewsgroup = 20 + , cScopeMailSelectedItems = 21 + , cScopeNewsSelectedItems = 22 + , cPriorityScope = 30 + , cStatusScope = 31 + }; + + // this constants are used for the search dialogs + enum { + eAND_STR = 1, // location of the AND string in macfe.r + eOR_STR = 2, // location of the OR string in macfe.r + eEditMenuItem = 3, // location of the Customize string in macfe.r + eSTRINGS_RES_ID = 901, // string resource number + eCustomizeSearchItem = -3 // command number for the customize menu item + }; + + OSErr InitSearchManager(LView *inContainer, CSearchTabGroup *inTabGroup, + MSG_ScopeAttribute inScope, LArray *inFolderScopeList = nil ); + + Boolean CanSearch() const; + void SetIsSearching( Boolean inValue ) { mIsSearching = inValue; } + Boolean IsSearching() const { + return mIsSearching; + } + MSG_Pane *GetMsgPane() const { + return mMsgPane; + } + Int16 GetNumVisibleLevels() const { + return mNumVisLevels; + } + Int16 GetNumDefaultLevels() const { + return 1; + } + Int16 GetNumLevels() const { + return mNumLevels; + } + Int32 GetNumResultRows() const { + return (mMsgPane == nil) ? 0 : MSG_GetNumLines(mMsgPane); + } + + void SetSearchScope(MSG_ScopeAttribute inScope, LArray *inFolderScopeList = nil); + void SetNumVisibleLevels(Int32 inNumLevels); + + + void StopSearch(MWContext *inContext = nil); + void StartSearch() + { + // Don't switch target when disabling + StValueChanger change(mCanRotateTarget, false); + mParamEnclosure->Disable(); + } + /* void SaveSearchResults(); Not implemented -- 10/30/97 */ + + void ReadSavedSearchStatus(LStream *inStatusData); + void WriteSavedSearchStatus(LStream *outStatusData); + + void GetSearchParameters(SearchLevelParamT *outSearchParams); + void SetSearchParameters(Int16 inNumVisLevels, const SearchLevelParamT *inSearchParams); + + // For accessing result data + + + +// MWContext *IsResultElementOpen(MSG_ViewIndex inIndex, MWContextType *outType); + + void PopulateAttributesMenus(MSG_ScopeAttribute inScope); + void PopulatePriorityMenu(CSearchPopupMenu *inMenu); + void PopulateStatusMenu(CSearchPopupMenu *inMenu); + static void FailSearchError(MSG_SearchError inError); + void SetWinCSID(int16 wincsid); + + //////////////////////////////////////////////////////////////////////////////////////// + // Custom headers + virtual char* GetSelectedCustomHeader( const SearchLevelParamT* curLevel ) const; + virtual UInt16 GetNumberOfAttributes( MSG_ScopeAttribute& scope, void* scopeItem ); + virtual const char* GetCustomHeadersFromTable( CSingleTextColumn* table ); + virtual const MSG_SearchMenuItem* GetSearchMenuItems() const { return mAttributeItems; } + virtual const UInt16& GetLastNumOfAttributes() const { return mLastNumOfAttributes; }; + + void AddScopesToSearch(MSG_Master *inMaster); + void AddParametersToSearch(); + void SetMSGPane( MSG_Pane* inPane) { mMsgPane = inPane ;} +protected: + + void MessageAttributes( SearchLevelInfoT& searchLevel ); + + virtual void ListenToMessage(MessageT inMessage, void *ioParam = nil); + + // Utility methods + + MSG_ScopeAttribute GetBEScope(MSG_ScopeAttribute inScope) const; + + void PopulateOperatorsMenus(MSG_ScopeAttribute inScope); + + enum ECheckDirection { eCheckForward = 1, eCheckBackward = 2, eCheckNothing = 3 }; + void SelectSearchLevel(Int16 inBeginLevel, ECheckDirection inCheckDirection); + Int32 GetSelectedSearchLevel(); + +// void MessageAttributes(CSearchPopupMenu *inAttributesMenu); + + void MessageOperators(CSearchPopupMenu *inOperatorsMenu); + void MessageSave(); + void MessageMoreFewer(Boolean inMore); + void MessageClear(); + + void ClearSearchParams(Int16 inStartLevel, Int16 inEndLevel); + + + + + //void UpdateMsgResult(MSG_ViewIndex inIndex); + + void UserChangedParameters() { + if ( mBroadcastUserChanges ) BroadcastMessage(msg_UserChangedSearchParameters); + } + + //////////////////////////////////////////////////////////////////////////////////////// + // Custom headers + virtual void EditCustomHeaders(); + virtual void FillUpCustomHeaderTable( const char buffer[], CSingleTextColumn* table ); + virtual void FinishCreateAttributeMenu( MSG_SearchMenuItem* inMenuItems, UInt16& inNumItems ); + virtual bool InputCustomHeader( const StringPtr ioHeader, bool newHeader ); + + + enum { + eCustomHeaderSeparator = ' ' + }; + + // Instance variables + + Int32 mNumLevels; // Number of possible search levels + Int32 mNumVisLevels; // Number of currently visible search levels + Int32 mLevelResizeV; // Visible margin between levels + + SearchLevelInfoT mLevelFields[cMaxNumSearchLevels]; + + LGAPushButton *mMoreButton; + LGAPushButton *mFewerButton; + LGARadioButton *mMatchAllRadio; + LGARadioButton *mMatchAnyRadio; + LView *mParamEnclosure; + + Boolean mCanRotateTarget; + Boolean mIsSearching; + + // BE related + + MSG_SearchMenuItem mSearchMenuItems[cMaxNumSearchMenuItems]; + + // *** Attributes menu support + MSG_SearchMenuItem *mAttributeItems; // menu items array for attributes + UInt16 mLastNumOfAttributes; // last number of attributes + + MSG_ScopeAttribute mLastMenuItemsScope; + MSG_SearchAttribute mLastMenuItemsAttribute; + UInt16 mNumLastMenuItems; + + MSG_ScopeAttribute mCurrentScope; + LArray *mFolderScopeList; // List of MSG_FolderInfo * for current scope + + MSG_Pane *mMsgPane; // Search message pane during an actual search + + + + Boolean mBroadcastUserChanges; + + Boolean mBoolOperator; +}; + + +// StSearchDataBlock + +class StSearchDataBlock { + +public: + + enum { eAllocateStrings = true, eDontAllocateStrings = false }; + + StSearchDataBlock() : + mData(nil) { + } + StSearchDataBlock(Int16 inNumSearchLevels, Boolean inAllocateStrings = true) : + mData(nil) { + Allocate(inNumSearchLevels, inAllocateStrings); + } + ~StSearchDataBlock() { + Dispose(); + } + + void Allocate(Int16 inNumSearchLevels, Boolean inAllocateStrings = true) { + Int32 size = inAllocateStrings ? ((sizeof(SearchLevelParamT) + sizeof(SearchTextValueT)) * inNumSearchLevels) : + (sizeof(SearchLevelParamT) * inNumSearchLevels); + + delete mData; + + mData = (SearchLevelParamT *) new char[size]; + FailNIL_(mData); + SearchLevelParamT *data = mData; + SearchTextValueT *text = (SearchTextValueT *) &data[inNumSearchLevels]; + for (Int16 i = 0; i < inNumSearchLevels; ++i) data[i].val.u.string = inAllocateStrings ? text[i].text : nil; + } + void Dispose() { + delete mData; + mData = nil; + + } + SearchLevelParamT *GetData() { + return mData; + } +private: + + SearchLevelParamT *mData; +}; diff --git a/mozilla/cmd/macfe/MailNews/CSearchTableView.cp b/mozilla/cmd/macfe/MailNews/CSearchTableView.cp new file mode 100644 index 00000000000..dfddfdbb4cc --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CSearchTableView.cp @@ -0,0 +1,809 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +#define DEBUGGER_ASSERTIONS +#include "CSearchTableView.h" + +#include "CApplicationEventAttachment.h" +#include "CSearchWindowBase.h" +#include "CMessageSearchWindow.h" +#include "LFlexTableGeometry.h" +#include "CTableKeyAttachment.h" +#include "CThreadWindow.h" +#include "CThreadView.h" +#include "CMessageWindow.h" +#include "CMessageView.h" +#include "UMessageLibrary.h" +#include "UMailSelection.h" +#include "UMailFolderMenus.h" +#include "UGraphicGizmos.h" + +#include "UException.h" +#include "UIHelper.h" +#include "CBrowserContext.h" +#include "LSharable.h" +#include "CComposeAddressTableView.h" +#include "libi18n.h" +#include "LCommander.h" + +#include "resgui.h" +#include "MailNewsgroupWindow_Defines.h" + +#include "ntypes.h" + +//----------------------------------- +CSearchTableView::~CSearchTableView() +//----------------------------------- +{ + SetMessagePane( NULL ); +} + +void CSearchTableView::SetSearchManager(CSearchManager *inSearchManager) +{ + mSearchManager = inSearchManager; + SetMessagePane( inSearchManager->GetMsgPane() ); +} + +//----------------------------------- +void CSearchTableView::DrawCellContents( const STableCell& inCell, const Rect& inLocalRect) + +//----------------------------------- +{ + // Get the text to display + CStr255 displayText; + MSG_SearchAttribute inAttribute = CSearchWindowBase::AttributeFromDataType(GetCellDataType(inCell)); + + if ( !GetDisplayText(inCell.row - 1, inAttribute, displayText) ) + return; // Error occurred getting the text + + // Display the text + + Boolean cellIsSelected = CellIsSelected(inCell); + + Rect cellDrawFrame = inLocalRect; + ::InsetRect(&cellDrawFrame, 2, 0); // For a nicer look between cells + + StSectClipRgnState saveSetClip(&cellDrawFrame); + + displayText = NET_UnEscape(displayText); + if ( cellIsSelected ) + { + + UGraphicGizmos::PlaceTextInRect((char *) &displayText[1], displayText[0], cellDrawFrame, teFlushLeft, teCenter, + &mTextFontInfo, true, truncMiddle); + // PlaceHilitedTextInRect((char *) &displayText[1], displayText[0], cellDrawFrame, teFlushLeft, teCenter, + // &mTextFontInfo, true, truncMiddle); + } + else + UGraphicGizmos::PlaceTextInRect((char *) &displayText[1], displayText[0], cellDrawFrame, teFlushLeft, teCenter, + &mTextFontInfo, true, truncMiddle); +} + + + +/*====================================================================================== + Set up helpers for the table. +======================================================================================*/ + +//----------------------------------- +void CSearchTableView::SetUpTableHelpers() +//----------------------------------- +{ + SetTableGeometry( new LFlexTableGeometry(this, mTableHeader) ); + SetTableSelector( new LTableRowSelector(this) ); + + // standard keyboard navigation. + AddAttachment( new CTableKeyAttachment(this) ); + +} + +//----------------------------------- +void CSearchTableView::ListenToMessage( + MessageT inCommand, + void *ioParam) +//----------------------------------- +{ + if (!ObeyCommand((CommandT)inCommand, ioParam)) + CMailFlexTable::ListenToMessage(inCommand, ioParam); +} + +//----------------------------------- +Boolean +CSearchTableView::ObeyCommand( CommandT inCommand, void* ioParam ) +//----------------------------------- +{ + Boolean commandObeyed = false; + + switch( inCommand ) + { + case msg_TabSelect: + break; + + default: + commandObeyed = Inherited::ObeyCommand( inCommand, ioParam ); + break; + } + + return commandObeyed; +} + +//----------------------------------- +// Delete selected messages either via menu command or +// key. +// +//----------------------------------- +void CSearchTableView::DeleteSelection() +//----------------------------------- +{ + // see if you got a selection + CMailSelection selection; + if ( GetSelection( selection ) ) + { + const MSG_ViewIndex *indices = selection.GetSelectionList(); + MSG_ViewIndex numIndices = selection.selectionSize; + MSG_Pane* searchPane = GetMessagePane(); + + // try closing all the open message windows + MSG_ResultElement* outElement; + MSG_FolderInfo* outFolder; + MessageKey outKey; + + const MSG_ViewIndex *indexCounter = indices; + for ( int i = 0; i < numIndices; i++, indexCounter++ ) + { + if( GetMessageResultInfo( *indexCounter, outElement, outFolder, outKey ) ) + CMessageWindow::CloseAll( outKey ); + } + + // Get correct BE message. It can either be for mail or news messages, but not for both + CMessageFolder messageFolder( outFolder ); + UInt32 folderFlags = messageFolder.GetFolderFlags(); + MSG_CommandType cmd = MSG_CancelMessage; // news posting - cancel command + + if ( (folderFlags & MSG_FOLDER_FLAG_NEWSGROUP) == 0 ) + cmd = UMessageLibrary::GetMSGCommand( cmd_Clear ); // mail message - delete command + + // send command to the BE. Copy the index list, because it gets clobbered by + // RemoveRows(). + MSG_ViewIndex* copyOfIndices = new MSG_ViewIndex[numIndices]; + const MSG_ViewIndex* src = &indices[0]; + MSG_ViewIndex* dst = ©OfIndices[0]; + for (int i = 0; i < numIndices; i++, src++, dst++) + *dst = *src; + + // BE delete command + MSG_Command( searchPane, cmd, copyOfIndices, numIndices ); + + // delete the list + delete [] copyOfIndices; + + SelectionChanged(); + } +} + +//----------------------------------- +void CSearchTableView::OpenRow(TableIndexT inRow) +//----------------------------------- +{ + MSG_ViewIndex index = inRow - 1; + ShowMessage(index, kInMessageWindow); +} + +//----------------------------------- +void CSearchTableView::FindOrCreateThreadWindowForMessage(MSG_ViewIndex inMsgIndex) +//----------------------------------- +{ + AssertFail_(mSearchManager != nil); + Try_ + { + MSG_ResultElement *elem = NULL; + MSG_GetResultElement(GetMessagePane(), inMsgIndex, &elem); + if (!elem) + return; + + MWContextType cxType = MSG_GetResultElementType(elem); + if (! (cxType == MWContextMail || cxType == MWContextMailMsg + || cxType == MWContextNews || cxType == MWContextNewsMsg)) + return; + + // Get info about the message + MSG_SearchValue *value; + MSG_GetResultAttribute(elem, attribFolderInfo, &value); + MSG_FolderInfo *folderInfo = value->u.folder; + if (!folderInfo) + return; + + // Create the thread window if it doesn't exist + CThreadWindow *threadWindow = CThreadWindow::FindOrCreate( folderInfo, CThreadView::cmd_UnselectAllCells); + } + Catch_(inErr) + { + Throw_(inErr); + } + EndCatch_ +} // CSearchTableView::FindOrCreateThreadWindowForMessage + + +//----------------------------------- +void CSearchTableView::FindCommandStatus(CommandT inCommand, Boolean &outEnabled, + Boolean &outUsesMark, Char16 &outMark, Str255 outName) +//----------------------------------- +{ + ResIDT menuID; + Int16 menuItem; + + // Check for synthetic commands + if( IsSyntheticCommand( inCommand, menuID, menuItem ) ) + { + switch( menuID ) + { + case menu_Navigator: // enable menus + outEnabled = true; + break; + + case menu_Go: // disable menus + case menu_View: + outEnabled = false; + break; + } + } + else // check for regular commands + { + switch (inCommand) + { + case cmd_AboutPlugins: // always enabled + case cmd_About: + case cmd_Quit: + case cmd_Close: + case cmd_Preferences: + outEnabled = true; + break; + + case cmd_SelectAll: // need at least one result + if( IsValidRow( 1 ) ) + outEnabled = true; + else + outEnabled = false; + break; + + + case cmd_Clear: // need at least one result and one selection + // Only support deleting of Mail Messages not news. + CMailSelection selection; + if ( GetSelection( selection ) ) + { + const MSG_ViewIndex *indices = selection.GetSelectionList(); + + + MSG_ResultElement* outElement; + MSG_FolderInfo* outFolder; + MessageKey outKey; + + GetMessageResultInfo( *indices, outElement, outFolder, outKey ); + + + CMessageFolder messageFolder( outFolder ); + UInt32 folderFlags = messageFolder.GetFolderFlags(); + + if ( (folderFlags & MSG_FOLDER_FLAG_NEWSGROUP) == 0 ) + outEnabled = true; // mail message - delete command + } + break; + + default: + Inherited::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); + break; + } + } +} + +//----------------------------------- +Boolean CSearchTableView::GetMessageResultInfo( + MSG_ViewIndex inMsgIndex, + MSG_ResultElement*& outElement, + MSG_FolderInfo*& outFolder, + MessageKey& outKey) +//----------------------------------- +{ + return GetMessageResultInfo( + GetMessagePane(), inMsgIndex, outElement, outFolder, outKey); +} // SearchTableView::GetMessageResultInfo + +//----------------------------------- +Boolean CSearchTableView::GetMessageResultInfo( + MSG_Pane* inPane, + MSG_ViewIndex inMsgIndex, + MSG_ResultElement*& outElement, + MSG_FolderInfo*& outFolder, + MessageKey& outKey) +//----------------------------------- +{ + MSG_SearchError err; + err = ::MSG_GetResultElement(inPane, inMsgIndex, &outElement); + if (err != SearchError_Success || outElement == nil) + return false; + + MWContextType cxType = ::MSG_GetResultElementType(outElement); + if (! (cxType == MWContextMail || cxType == MWContextMailMsg + || cxType == MWContextNews || cxType == MWContextNewsMsg)) + return false; + + // Get info about the message + MSG_SearchValue *value; + err = ::MSG_GetResultAttribute(outElement, attribMessageKey, &value); + if (err != SearchError_Success || value == nil) + return false; + + outKey = value->u.key; + if (outKey == MSG_MESSAGEKEYNONE) + return false; + + // Get info about the folder the message is in + err = ::MSG_GetResultAttribute(outElement, attribFolderInfo, &value); + if (err == SearchError_Success && value != nil) + outFolder = value->u.folder; + return true; +} // SearchTableView::GetMessageResultInfo + +//----------------------------------- +void CSearchTableView::AddRowDataToDrag(TableIndexT inRow, DragReference inDragRef) +// Base class implementation is for a message search. Return the URL. +//----------------------------------- +{ + + MSG_ResultElement *elem = NULL; + MSG_FolderInfo *folderInfo = nil; + MessageKey key; + + // get key and folder info + if (!GetMessageResultInfo(inRow - 1, elem, folderInfo, key)) + return; + + if (folderInfo == nil) + return; + +// MSG_Pane* pane = ::MSG_FindPaneOfType( +// CMailNewsContext::GetMailMaster(), folderInfo, MSG_THREADPANE); + + MSG_Pane* pane = GetMessagePane(); // get search pane from the search manager + + if (!pane) // fails unless there is a threadpane open to this folder. Too bad. + return; // That's why I turned off dragging. + + + // create URL + URL_Struct* url = ::MSG_ConstructUrlForMessage( pane, key ); + if (!url) + return; // we always return here, as far as I can see + + // add the drag item + OSErr err = ::AddDragItemFlavor( inDragRef, inRow, 'TEXT', url->address, XP_STRLEN(url->address), flavorSenderOnly); + + NET_FreeURLStruct(url); +} + +//----------------------------------- +void CSearchTableView::ShowMessage(MSG_ViewIndex inMsgIndex, SearchWindowOpener inWindow) +//----------------------------------- +{ + AssertFail_(mSearchManager != nil); + Try_ + { + MSG_ResultElement *elem = NULL; + MSG_FolderInfo *folderInfo = nil; + MessageKey key; + if (!GetMessageResultInfo(inMsgIndex, elem, folderInfo, key)) + return; + if (folderInfo == nil && inWindow != kInMessageWindow) + return; + + // Got the info we wanted: we can display now... + if (inWindow == kInMessageWindow) + { + // Check if there's an open window with this message. + CMessageWindow *messageWindow = CMessageWindow::FindAndShow(key); + if (messageWindow) + { + messageWindow->Select(); + return; + } + + // If option key down, try to reuse a window + if ( CApplicationEventAttachment::CurrentEventHasModifiers(optionKey) ) + { + messageWindow = CMessageWindow::FindAndShow(0); + messageWindow->Select(); + } + + // Create the window + if (messageWindow == nil) + { + // We should probably create a generic utility function to generate + // windows that contain a CHTMLView + CBrowserContext *theContext = new CBrowserContext(MWContextMailMsg); + FailNIL_(theContext); + StSharer theShareLock(theContext); + + // Create a message window with this view as its super commander. + LWindow *theWindow = LWindow::CreateWindow(cMessageWindowPPobID, LCommander::GetTopCommander()); + FailNIL_(theWindow); + messageWindow = dynamic_cast(theWindow); + FailNIL_(messageWindow); + + messageWindow->SetWindowContext(theContext); + } + + // Show the message + messageWindow->GetMessageView()->ShowSearchMessage( + CMailNewsContext::GetMailMaster(), + elem, + folderInfo == nil); + } + else + { + CommandT command; + switch (inWindow) + { + case kInThreadWindow: command = CThreadView::cmd_UnselectAllCells; break; + case kAddToThreadWindowSelection: command = cmd_Nothing; break; + default: return; + } + + // Check if there's an open window with this folder, and open one if needed + CThreadWindow *threadWindow = CThreadWindow::FindAndShow(folderInfo, true, command); + if (threadWindow) + { + // Show the message + threadWindow->ShowMessageKey(key); + } + } + } + Catch_(inErr) + { + Throw_(inErr); + } + EndCatch_ +} // CSearchTableView::ShowMessage + + +//----------------------------------- +Boolean CSearchTableView::IsValidRow(TableIndexT inRow) const +// *** WARNING *** +// USING mRows is WRONG WHEN DELETING ROWS, it's possibly changed already! +// Use the Table Selector's value instead. That's why this override is here! +//----------------------------------- +{ + if (inRow < 1) + return false; + if (inRow > mRows ) /* ((LTableRowSelector*)mTableSelector)->GetRowCount()) */ + return false; + return true; +} + + +//----------------------------------- +MessageKey CSearchTableView::GetRowMessageKey(TableIndexT inRow) +// Get the message key for the specified row. Return MSG_MESSAGEKEYNONE if the +// specified inRow is invalid. +//----------------------------------- +{ + AssertFail_(mSearchManager != nil); + if ( !IsValidRow(inRow) ) return MSG_MESSAGEKEYNONE; + MSG_ViewIndex inIndex = inRow - 1; + + UpdateMsgResult(inIndex); + + MSG_SearchValue *value = nil; + + MSG_SearchAttribute attribute = attribMessageKey; + + CSearchManager::FailSearchError(MSG_GetResultAttribute(mResultElement, attribute, &value)); + MessageKey key = value->u.key; + + MSG_SearchError error = MSG_DestroySearchValue(value); + AssertFail_(error == SearchError_Success); // What to do with an error? + + return key; +} + +//----------------------------------- +MSG_ResultElement* CSearchTableView::GetCurrentResultElement(TableIndexT inRow) +//----------------------------------- +{ + AssertFail_(mSearchManager != nil); + if ( !IsValidRow(inRow) ) return nil; + MSG_ResultElement *result; + CSearchManager::FailSearchError(MSG_GetResultElement(GetMessagePane(), inRow-1, &result)); + return result; +} + + +/*====================================================================================== + Get the current sort params for the result data. +======================================================================================*/ + +void CSearchTableView::GetSortParams(MSG_SearchAttribute *outSortKey, Boolean *outSortBackwards) +{ + AssertFail_(mTableHeader != nil); + AssertFail_(mSearchManager != nil); + PaneIDT sortColumnID; + mTableHeader->GetSortedColumn(sortColumnID); + *outSortKey = CSearchWindowBase::AttributeFromDataType(sortColumnID); + AssertFail_(*outSortKey != kNumAttributes); + *outSortBackwards = mTableHeader->IsSortedBackwards(); +} + + +/*====================================================================================== + Force a sort to occur based upon the current sort column. +======================================================================================*/ + +void CSearchTableView::ForceCurrentSort() +{ + MSG_SearchAttribute sortKey; + Boolean sortBackwards; + + GetSortParams(&sortKey, &sortBackwards); + + CSearchManager::FailSearchError(MSG_SortResultList(GetMessagePane(), sortKey, sortBackwards)); +} + +//----------------------------------- +void CSearchTableView::ChangeSort(const LTableHeader::SortChange *inSortChange) +// Notification to sort the table. +//----------------------------------- +{ + AssertFail_(mSearchManager != nil); + + Inherited::ChangeSort(inSortChange); + + MSG_SearchAttribute sortKey = CSearchWindowBase::AttributeFromDataType(inSortChange->sortColumnID); + + if ( sortKey != kNumAttributes ) { + + ::SetCursor(*::GetCursor(watchCursor)); + // Call BE to sort the table + AssertFail_(GetMessagePane() != nil); + CSearchManager::FailSearchError(MSG_SortResultList(GetMessagePane(), sortKey, inSortChange->reverseSort)); + } +} + +//----------------------------------- +void CSearchTableView::SetWinCSID(Int16 wincsid) +// Set the font for the view +//----------------------------------- +{ + if (wincsid == INTL_CharSetNameToID(INTL_ResourceCharSet())) + this->SetTextTraits( 8603 ); + else + this->SetTextTraits(CPrefs::GetTextFieldTextResIDs(wincsid)); + Refresh(); +} + +//----------------------------------- +//void CSearchTableView::AddSelectionToDrag( +// DragReference inDragRef, +// RgnHandle inDragRgn ) +//----------------------------------- +//{ + // Since we inherit from CMailFlexTable, we want to avoid it and just call the + // CStandardFlexTable method, thereby avoiding the CMailFlexTable behavior + +// CStandardFlexTable::AddSelectionToDrag(inDragRef, inDragRgn); + +//} // CAddressSearchTableView::AddSelectionToDrag + + +//----------------------------------- +void CSearchTableView::SelectionChanged() +// adjust the 'Move Message To' popup accordingly to the current selection +//----------------------------------- +{ + Inherited::SelectionChanged(); + + // Set the 'Move Message' popup to the folder of the first selected item + CMailSelection selection; + if (GetSelection(selection)) + { + CMessageSearchWindow* myWindow = dynamic_cast + (LWindow::FetchWindowObject(GetMacPort())); + if (myWindow) + { + CMailFolderPopupMixin* fileMessagePopup; + FindUIItemPtr(myWindow, CMessageSearchWindow::paneID_FilePopup, fileMessagePopup); + + const MSG_ViewIndex *indices = selection.GetSelectionList(); + MSG_ViewIndex numIndices = selection.selectionSize; + MSG_ResultElement* outElement; + MSG_FolderInfo* outFolder; + MessageKey outKey; + + if (GetMessageResultInfo(*indices, outElement, outFolder, outKey)) + fileMessagePopup->MSetSelectedFolder(outFolder, false); + } + } +} +// + +/*====================================================================================== + Get the display text for the specified attribute and table row index. Return + true if the text could be gotten. +======================================================================================*/ + +Boolean CSearchTableView::GetDisplayText(MSG_ViewIndex inIndex, MSG_SearchAttribute inAttribute, + CStr255& outString) +{ + AssertFail_(GetMessagePane() != nil); + UpdateMsgResult(inIndex); + + MSG_SearchValue *value = nil; + + MSG_SearchError error = MSG_GetResultAttribute(mResultElement, inAttribute, &value); + //Assert_(error == SearchError_Success); + if ( error != SearchError_Success ) return false; + + Int16 wincsid = 0; + MSG_SearchValue *folderinfoValue = nil; + error = MSG_GetResultAttribute(mResultElement, attribFolderInfo, &folderinfoValue); + if( ( error == SearchError_Success ) && folderinfoValue && folderinfoValue->u.folder) + { + folderinfoValue->attribute = attribFolderInfo; + wincsid = INTL_DocToWinCharSetID(MSG_GetFolderCSID(folderinfoValue->u.folder)); + error = MSG_DestroySearchValue(folderinfoValue); + Assert_(error == SearchError_Success); + } + + switch ( inAttribute ) { + + case attribSender: + case attribSubject: + outString = ""; + char *buf = IntlDecodeMimePartIIStr(value->u.string, + wincsid, + FALSE); + if (buf) { + outString = buf; + XP_FREE(buf); + break; + } + outString = value->u.string; + break; + + case attribLocation: + case attribCommonName: + case attrib822Address: + case attribOrganization: + case attribLocality: + case attribPhoneNumber: + outString = value->u.string; + break; + + case attribDate: + outString = MSG_FormatDate(GetMessagePane(), value->u.date); + break; + + case attribMsgStatus: + case attribPriority: + { + char name[32]; + if ( inAttribute == attribMsgStatus ) { + MSG_GetStatusName(value->u.msgStatus, name, sizeof(name)); + } else { + MSG_GetPriorityName(value->u.priority, name, sizeof(name)); + } + outString = name; + } + break; + + default: + AssertFail_(false); + break; + } + + error = MSG_DestroySearchValue(value); + Assert_(error == SearchError_Success); + + return true; +} + +/*====================================================================================== + Call this method whenever the indexes in the message pane change. +======================================================================================*/ + +void CSearchTableView::UpdateMsgResult(MSG_ViewIndex inIndex) { + + AssertFail_(GetMessagePane() != nil); + + if ( (mResultElement == nil) || (mResultIndex != inIndex) ) { + + mResultElement = nil; + + CSearchManager::FailSearchError(MSG_GetResultElement(GetMessagePane(), inIndex, &mResultElement)); + + mResultIndex = inIndex; + } +} + +/*====================================================================================== + React to search message. +======================================================================================*/ + +void CSearchTableView::StartSearch(MWContext *inContext, MSG_Master *inMaster, MSG_SearchAttribute inSortKey, + Boolean inSortBackwards) { + + if ( !mSearchManager->CanSearch() || mSearchManager->IsSearching() ) return; + + MsgPaneChanged(); // For results + + // Prepare for a new search session + NewSearchSession(inContext, inMaster, inSortKey, inSortBackwards); + mSearchManager->SetMSGPane( GetMessagePane() ); + // Add scopes to search + mSearchManager->AddScopesToSearch(inMaster); + + // Add search parameters + mSearchManager->AddParametersToSearch(); + + CSearchManager::FailSearchError(MSG_Search(GetMessagePane())); + + mSearchManager->StartSearch(); + + mSearchManager->SetIsSearching( true ); +} + +/*====================================================================================== + Prepare for a new search session. +======================================================================================*/ + +void CSearchTableView::NewSearchSession(MWContext *inContext, MSG_Master *inMaster, MSG_SearchAttribute inSortKey, + Boolean inSortBackwards) +{ + AssertFail_(inContext != nil); + AssertFail_(inMaster != nil); + + if ( GetMessagePane() == nil ) + { + // Create the search pane and store related date + MSG_Pane* msgPane = MSG_CreateSearchPane(inContext, inMaster); + FailNIL_(msgPane); + SetMessagePane( msgPane ); + MSG_SetFEData( GetMessagePane(), CMailCallbackManager::Get() ); + + } else + { + // Free any previously allocated search memory + CSearchManager::FailSearchError(MSG_SearchFree(GetMessagePane())); + } + + // Alloc mem for new search parameters + CSearchManager::FailSearchError(MSG_SearchAlloc(GetMessagePane())); + + // Setup the sort order + CSearchManager::FailSearchError( + MSG_SortResultList(GetMessagePane(), inSortKey, inSortBackwards) ); + +} + +void CSearchTableView::ChangeFinished(MSG_Pane * inPane, MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, SInt32 inRowCount) +{ + MsgPaneChanged(); + CMailFlexTable::ChangeFinished( inPane, inChangeCode, inStartRow, inRowCount ); +} diff --git a/mozilla/cmd/macfe/MailNews/CSearchTableView.h b/mozilla/cmd/macfe/MailNews/CSearchTableView.h new file mode 100644 index 00000000000..8f5f1ff5b43 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CSearchTableView.h @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CSearchTableView.h + +#pragma once + +#include "SearchHelpers.h" +#include "CSearchManager.h" +#include "CDateView.h" +#include "NetscapeDragFlavors.h" + +/////////////////////////////////////////////////////////////////////////////////// +// CSearchTableView +class CSearchTableView : public CMailFlexTable +{ +private: + typedef CMailFlexTable Inherited; + +public: + enum { class_ID = 'SrTb' }; + enum { + menu_Navigator = 305, + menu_View = 502, + menu_Go = 503 + }; + typedef enum { + kInMessageWindow, + kInThreadWindow, + kAddToThreadWindowSelection + } SearchWindowOpener; + + CSearchTableView(LStream *inStream); + virtual ~CSearchTableView(); + + void SetSearchManager(CSearchManager *inSearchManager); + + + void ForceCurrentSort(); + void GetSortParams(MSG_SearchAttribute *outSortKey, Boolean *outSortBackwards); + + virtual void ChangeSort(const LTableHeader::SortChange *inSortChange); + virtual void OpenRow(TableIndexT inRow); + + + virtual void ShowMessage(MSG_ViewIndex inMsgIndex, SearchWindowOpener inWindow); + virtual void FindOrCreateThreadWindowForMessage(MSG_ViewIndex inMsgIndex); + virtual void FindCommandStatus( CommandT inCommand, Boolean &outEnabled, + Boolean &outUsesMark, Char16 &outMark, Str255 outName); + + MessageKey GetRowMessageKey(TableIndexT inRow); + MSG_ResultElement* GetCurrentResultElement(TableIndexT inRow); + virtual void SetWinCSID(Int16 wincsid); + + virtual void ListenToMessage( MessageT inCommand, void *ioParam); + virtual Boolean ObeyCommand( CommandT inCommand, void* ioParam ); + void StartSearch(MWContext *inContext, MSG_Master *inMaster, + MSG_SearchAttribute inSortKey, Boolean inSortBackwards); + static Boolean GetMessageResultInfo( + MSG_Pane* inPane, + MSG_ViewIndex inMsgIndex, MSG_ResultElement*& outElement, + MSG_FolderInfo*& outFolder, MessageKey& outKey); +protected: + Boolean GetDisplayText(MSG_ViewIndex inIndex, MSG_SearchAttribute inAttribute, + CStr255& outString); + void UpdateMsgResult(MSG_ViewIndex inIndex); + void NewSearchSession(MWContext *inContext, MSG_Master *inMaster, MSG_SearchAttribute inSortKey, + Boolean inSortBackwards); + + + + virtual Boolean IsValidRow(TableIndexT inRow) const; + virtual void SetUpTableHelpers(); + virtual void DeleteSelection(); + virtual void DrawCellContents(const STableCell &inCell, const Rect &inLocalRect); + + virtual void MsgPaneChanged(); + virtual Int32 GetBENumRows(); + Boolean GetMessageResultInfo( MSG_ViewIndex inMsgIndex, MSG_ResultElement*& outElement, + MSG_FolderInfo*& outFolder, MessageKey& outKey); + +// drag support + virtual Boolean CellInitiatesDrag(const STableCell&) const { return true; } + // uncomment the line above to turn on drag from the message search + // results window. Problem is, URL not obtainable for message search + // unless the enclosing folder is open in a thread window. SO it's + // turned off for now. +// virtual void AddSelectionToDrag( DragReference inDragRef, RgnHandle inDragRgn ); + virtual void AddRowDataToDrag(TableIndexT inRow, DragReference inDragRef); + virtual void SelectionChanged(); + virtual void ChangeFinished(MSG_Pane */*inPane*/, MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, SInt32 inRowCount); + // Instance variables ========================================================== + enum { eInvalidResultIndex = -1 }; + + CSearchManager *mSearchManager; // Belongs to the window, not us. + MSG_ResultElement *mResultElement; + MSG_ViewIndex mResultIndex; +}; + +/////////////////////////////////////////////////////////////////////////////////// +// inlines + +inline +CSearchTableView::CSearchTableView(LStream *inStream) +: CMailFlexTable(inStream), mSearchManager(nil), mResultElement( nil ), mResultIndex (eInvalidResultIndex ) +{ + mDragFlavor = kMailNewsSelectionDragFlavor; + SetRefreshAllWhenResized(false); +} + + +inline void +CSearchTableView::MsgPaneChanged() +{ + mResultElement = nil; +} + +inline Int32 +CSearchTableView::GetBENumRows() +{ + return (GetMessagePane() == nil) ? 0 : MSG_GetNumLines(GetMessagePane()); +} + diff --git a/mozilla/cmd/macfe/MailNews/CSearchWindowBase.cp b/mozilla/cmd/macfe/MailNews/CSearchWindowBase.cp new file mode 100644 index 00000000000..259ba1956f6 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CSearchWindowBase.cp @@ -0,0 +1,931 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CSearchWindowBase.cp + +#define DEBUGGER_ASSERTIONS + +#include "CSearchWindowBase.h" +#include "SearchHelpers.h" +#include "CMailNewsContext.h" +#include "CSearchTableView.h" +#include "libi18n.h" +#include "LGAPushButton.h" +#include "CPatternProgressBar.h" +#include "MailNewsgroupWindow_Defines.h" +#include "resgui.h" +#include +#include "prefapi.h" +#include "LGARadioButton.h" +#include "UStClasses.h" +#include "CThreadWindow.h" +#include "CMessageWindow.h" +#include "CProgressListener.h" + +const UInt16 resIDT_SearchOptionsDialog = 8851; +const char* const searchSubfolderPrefName = "mailnews.searchSubFolders"; +const char* const searchServerPrefName = "mailnews.searchServer"; + +//----------------------------------- +CSearchWindowBase::CSearchWindowBase(LStream *inStream, DataIDT inWindowType) : + CMailNewsWindow(inStream, inWindowType), + LListener(), + LPeriodical(), + mSearchManager(), + mResultsEnclosure(nil), + mResultsTable(nil), + mResultsVertWindExtension(0), + mResultsTableVisible(true), + mSearchFolders(), + mProgressBar(nil), + mSearchButton(nil), + mSearchOptionsButton(nil) +//----------------------------------- +{ + SetRefreshAllWhenResized(false); +} + +CSearchWindowBase::~CSearchWindowBase() +// Stop any current search. +//----------------------------------- +{ + Boolean canRotate = false; + if ( IsVisible() ) + USearchHelper::FindWindowTabGroup(&mSubCommanders)->SetRotateWatchValue(&canRotate); +} // CSearchWindowBase::~CSearchWindowBase + +// -------------------------------------------------------------------------- +UInt16 +CSearchWindowBase::GetValidStatusVersion() const +// +// -- 10/30/97 --------------------------------------------------------- +// +// This is the proper place for this method, because +// every time the two subclasses of this class stream out they stream out a member of +// this class ( CSearchManager). Therefore when the search manager parameters for stream out +// are changed both subclasses are affected. +// +// Copied the following comments from the CAddressSearchWindow implementation, now defunct. +// +// --------------------------------------------------------------------------- +// So here's the skinny on the directory (aka address) search window. +// You see, when we shipped version 4.01, there were three "levels" of criteria, +// and the version we shipped with was: +// +// static const UInt16 cAddressSearchSaveWindowStatusVersion = 0x0203; +// +// Then on 97/06/30, the number of levels has changed from 3 to 5 in MailNewsSearch.cnst, +// in the enclosures include view. That meant that builds from 7/1 - 7/30 were writing +// out saved window status records containing five levels of criteria. +// Unfortunately, the data is written out as: +// +// bounds/vertextension/levelsdata/tableheaderdata +// +// so that the extra data was written out in the middle. In version 4.01, the routine +// CSearchManager::ReadSavedSearchStatus was not skipping the extra fields, so a crash +// occurred when reading in the table header stuff. This happened when the search window +// was used in 4.01 after running it in 4.02 (7/30 build). +// +// This is bug #78713. There are two parts of the fix. (1) start with a version number +// of zero. Then, version 4.01 will ignore the resource as being "too old", and write +// out a resource with version 0x203. This will be handled OK by version 4.02. +// +// The other part of the fix is that CSearchManager::ReadSavedSearchStatus will now skip +// extra fields, so that this crash won't happen if somebody further increases the number +// of levels. +// +// FINAL NOTE (97/10/13) Because of this same problem, I changed CSaveWindowStatus so that +// it always writes out zero in the first field, which will be interpreted as a zero version +// by Communicator 4.0x, and ignored. So now it's OK to increment this as originally +// intended. +{ + return eValidStreamVersion; +} + +//----------------------------------- +void CSearchWindowBase::AboutToClose() +//----------------------------------- +{ + // Not re-entrant! Don't call this twice. + CSearchWindowBase::MessageWindStop(true); + + Inherited::AttemptCloseWindow(); // Do this first: uses table + + // Bug fix: Do these things in the right order here + if (mMailNewsContext) + mMailNewsContext->RemoveUser(this); + mMailNewsContext = nil; + mSearchManager.AboutToClose(); + + if (mResultsTable) + { + if (mMailNewsContext) + mMailNewsContext->RemoveListener(mResultsTable); // bad time to listen for all connections complete. + delete mResultsTable; + mResultsTable = nil; + } +} + +//----------------------------------- +void CSearchWindowBase::FinishCreateSelf() +//----------------------------------- +{ + // Create context and and progress listener + + mMailNewsContext = new CMailNewsContext(MWContextSearch); + FailNIL_(mMailNewsContext); + + mMailNewsContext->AddUser(this); + mMailNewsContext->AddListener(this); + + CMediatedWindow::FinishCreateSelf(); // Call CMediatedWindow for now since we need to + // create a different context than CMailNewsWindow + if (mProgressListener) + mMailNewsContext->AddListener(mProgressListener); + else + mProgressListener = new CProgressListener(this, mMailNewsContext); + + // SetAttribute(windAttr_DelaySelect); put in resource + // should be in resource, but there's a resource freeze. + + mMailNewsContext->SetWinCSID(INTL_DocToWinCharSetID(mMailNewsContext->GetDefaultCSID())); + + USearchHelper::FindWindowTabGroup(&mSubCommanders)->SetRotateWatchValue(&mCanRotateTarget); + + // Populate scope window + + mResultsEnclosure = USearchHelper::FindViewSubview(this, paneID_ResultsEnclosure); + FailNILRes_(mResultsEnclosure); + mResultsTable = dynamic_cast(USearchHelper::FindViewSubview(this, paneID_ResultsTable)); + FailNILRes_(mResultsTable); + mSearchButton = dynamic_cast(USearchHelper::FindViewControl(this, paneID_Search)); + FailNILRes_(mSearchButton); + mProgressBar = dynamic_cast(USearchHelper::FindViewSubpane(this, paneID_ProgressBar)); + FailNILRes_(mProgressBar); + + mResultsTable->AddListener(this); + mSearchButton->SetDefaultButton(true, false); + mResultsEnclosure->SetRefreshAllWhenResized(false); + + Rect windowBounds; + this->CalcPortFrameRect(windowBounds); + mMinVResultsSize = windowBounds.bottom - windowBounds.top; + + UReanimator::LinkListenerToControls(this, this, GetStatusResID()); + + ShowHideSearchResults(false); + + // Initialize the search manager + mSearchManager.AddListener(this); + mSearchManager.InitSearchManager( + this, USearchHelper::FindWindowTabGroup(&mSubCommanders), + GetWindowScope(), + &mSearchFolders ); + mResultsTable->SetSearchManager(&mSearchManager); + + // Call inherited method + FinishCreateWindow(); +} + +//----------------------------------- +Boolean CSearchWindowBase::ObeyCommand(CommandT inCommand, void *ioParam) +//----------------------------------- +{ + if ( IsResultsTableVisible() ) + { + switch ( inCommand ) + { + case cmd_PreviousMessage: + case cmd_NextMessage: + case cmd_NextUnreadMessage: + return true; + + case cmd_SortBySubject: + case cmd_SortBySender: + case cmd_SortByDate: + case cmd_SortByLocation: + case cmd_SortByPriority: + case cmd_SortByStatus: + { + LTableHeader *theHeader = mResultsTable->GetTableHeader(); + PaneIDT dataType = DataTypeFromSortCommand(inCommand); + if ( theHeader != nil ) + { + theHeader->SetSortedColumn(theHeader->ColumnFromID(dataType), + mResultsTable->IsSortedBackwards(), true); + } + } + return true; + + case cmd_SortAscending: + case cmd_SortDescending: + LTableHeader *theHeader = mResultsTable->GetTableHeader(); + if ( theHeader != nil ) + { + theHeader->SetSortedColumn(theHeader->ColumnFromID(mResultsTable->GetSortedColumn()), + inCommand == cmd_SortDescending, true); + } + return true; + + case msg_TabSelect: + break; + + } + } + + switch ( inCommand ) { + + default: + return Inherited::ObeyCommand(inCommand, ioParam); + } +} + +//----------------------------------- +void CSearchWindowBase::FindCommandStatus(CommandT inCommand, Boolean &outEnabled, + Boolean &outUsesMark, Char16 &outMark, + Str255 outName) +//----------------------------------- +{ + // Boolean isLineSelected = (GetSelectedRowCount() > 0); + + if ( IsResultsTableVisible() ) + { + switch ( inCommand ) + { + case cmd_PreviousMessage: + case cmd_NextMessage: + case cmd_NextUnreadMessage: + outEnabled = true; + outUsesMark = false; + return; + + case cmd_SortBySubject: + case cmd_SortBySender: + case cmd_SortByDate: + case cmd_SortByLocation: + case cmd_SortByPriority: + case cmd_SortByStatus: + { + PaneIDT dataType = DataTypeFromSortCommand(inCommand); + PaneIDT paneID = mResultsTable->GetSortedColumn(); + outUsesMark = true; + outEnabled = true; + outMark = ((dataType == paneID) ? checkMark : noMark); + } + return; + + case cmd_SortAscending: + case cmd_SortDescending: + outMark = + ((inCommand == cmd_SortDescending) == mResultsTable->IsSortedBackwards()) ? + checkMark : 0; + outUsesMark = true; + outEnabled = true; + return; + } + } + + switch ( inCommand ) { + + default: + if (inCommand == cmd_OpenSelection) // enabled in base class + ::GetIndString(outName, kMailNewsMenuStrings, kOpenMessageStrID); + + Inherited::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, + outName); + break; + } +} + +//----------------------------------- +void CSearchWindowBase::ListenToMessage(MessageT inMessage, void *ioParam) +//----------------------------------- +{ + switch ( inMessage ) + { + case paneID_Search: + if (mProgressBar) mProgressBar->SetDescriptor("\p"); + MessageWindSearch(); + break; + + case paneID_SearchOptions: + SearchOptions(); + break; + + case paneID_Stop: + MessageWindStop(true); + UpdateTableStatusDisplay(); + break; + + case CStandardFlexTable::msg_SelectionChanged: + UpdateTableStatusDisplay(); + break; + + case CSearchManager::msg_SearchParametersCleared: + ShowHideSearchResults(false); + break; + + case CSearchManager::msg_SearchParametersResized: + MessageSearchParamsResized(*((Int16 *) ioParam)); + break; + + // Status messages + + case msg_NSCAllConnectionsComplete: + MessageWindStop(false); + UpdateTableStatusDisplay(); + // no break here + + default: + // No superclass method + break; + } +} // CSearchWindowBase::ListenToMessage + +//----------------------------------- +void CSearchWindowBase::SearchOptions() +// This function allows the user to set the following prefs: +// - search in subfolders [yes / no] +// - search locally / on server +// After changing these prefs, it is necessary to reconstruct the attributes menu +// because a search on server usually doesn't offer as many criteria as a local search. +// So we call PopulateAttributesMenus() which will do a MSG_GetAttributesForSearchScopes(). +//----------------------------------- +{ + StDialogHandler theOptionsDialogHandler( resIDT_SearchOptionsDialog, dynamic_cast(this) ); + + // get the options dialog + LWindow* theOptionsDialog = theOptionsDialogHandler.GetDialog(); + AssertFail_( theOptionsDialog != nil ); + + int prefResult; + XP_Bool searchSubFolderPref = false; + XP_Bool searchServerPref = false; + + // set the default value for the subfolder search from the preferences + LGACheckbox* searchSubChkb + = dynamic_cast( theOptionsDialog->FindPaneByID( 'SUBF' ) ); + AssertFail_( searchSubChkb != nil ); + + prefResult = PREF_GetBoolPref( searchSubfolderPrefName, &searchSubFolderPref ); + if ( prefResult == PREF_ERROR ) searchSubFolderPref = true; // default from spec. + searchSubFolderPref ? searchSubChkb->SetValue( Button_On ) : searchSubChkb->SetValue( Button_Off ); + + // set the default value for the local/server search from the preferences + LGARadioButton* searchServerBtn + = dynamic_cast( theOptionsDialog->FindPaneByID( 'SRCS' ) ); + LGARadioButton* searchLocalBtn + = dynamic_cast( theOptionsDialog->FindPaneByID( 'SRCL' ) ); + AssertFail_( searchServerBtn != nil ); + + prefResult = PREF_GetBoolPref( searchServerPrefName, &searchServerPref ); + if ( prefResult == PREF_ERROR ) searchServerPref = true; // default from spec. + if( searchServerPref ) + { + searchServerBtn->SetValue( Button_On ); // Other radio will be turned off via LTabGroup + } + else + { + searchLocalBtn->SetValue( Button_On ); // ditto + } + + MessageT theMessage; + + while( true ) + { + theMessage = theOptionsDialogHandler.DoDialog(); + + if( theMessage == msg_OK ) + { + Boolean choice; + if( ( choice = searchSubChkb->IsSelected() ) != searchSubFolderPref ) + PREF_SetBoolPref( searchSubfolderPrefName, choice ); + if( ( choice = searchServerBtn->IsSelected() ) != searchServerPref ) + PREF_SetBoolPref( searchServerPrefName, choice ); + // See the comment in the function header + mSearchManager.PopulateAttributesMenus(GetWindowScope()); + break; + } + else + if( theMessage == msg_Cancel ) + break; + } +} + +//----------------------------------- +Boolean CSearchWindowBase::HandleKeyPress(const EventRecord &inKeyEvent) +//----------------------------------- +{ + Int16 theKey = inKeyEvent.message & charCodeMask; + + if ( ((theKey == char_Enter) || (theKey == char_Return)) && !mSearchManager.IsSearching() ) + { + mSearchButton->SimulateHotSpotClick(kControlButtonPart); + return true; + } + else if ( (((theKey == char_Escape) && ((inKeyEvent.message & keyCodeMask) == vkey_Escape)) || + UKeyFilters::IsCmdPeriod(inKeyEvent)) && mSearchManager.IsSearching() ) + { + + USearchHelper::FindViewControl(this, paneID_Stop)->SimulateHotSpotClick(kControlButtonPart); + return true; + } + + return Inherited::HandleKeyPress(inKeyEvent); +} + +//----------------------------------- +void CSearchWindowBase::SpendTime(const EventRecord &/*inMacEvent*/) +//----------------------------------- +{ + USearchHelper::EnableDisablePane(mSearchButton, mSearchManager.CanSearch()); +} + +//----------------------------------- +void CSearchWindowBase::Activate() +// Start repeating. +//----------------------------------- +{ + StopIdling(); + StartRepeating(); + +// CSearchTabGroup *theTabGroup = USearchHelper::FindWindowTabGroup(&mSubCommanders); +// Boolean couldRotate = theTabGroup->SetCanRotate(false); // Don't rotate when activating + + Inherited::Activate(); + +// theTabGroup->SetCanRotate(couldRotate); +} + +//----------------------------------- +void CSearchWindowBase::DeactivateSelf() +//----------------------------------- +{ + StopRepeating(); + StartIdling(); + + Inherited::DeactivateSelf(); +} + +//----------------------------------- +void CSearchWindowBase::DrawSelf() +//----------------------------------- +{ + Boolean doResultsTable = IsResultsTableVisible(); + Rect frame; + + if ( doResultsTable && mResultsTable->CalcPortFrameRect(frame) && + ::RectInRgn(&frame, mUpdateRgnH) ) + { + { + StExcludeVisibleRgn excludeRgn(mResultsTable); + Inherited::DrawSelf(); + } + + StColorPenState::Normalize(); + ::EraseRect(&frame); + } + else + { + Inherited::DrawSelf(); + } + + USearchHelper::RemoveSizeBoxFromVisRgn(this); +} + +//----------------------------------- +void CSearchWindowBase::SetDescriptor(ConstStr255Param inDescriptor) +//----------------------------------- +{ + if ( !::EqualString(inDescriptor, *((WindowPeek) GetMacPort())->titleHandle, true, true) ) + { + Inherited::SetDescriptor(inDescriptor); + } +} + +//----------------------------------- +void CSearchWindowBase::MessageSearchParamsResized(Int16 inResizeAmount) +// React to more message. +//----------------------------------- +{ + LPane *resultsView = USearchHelper::FindViewSubpane(this, paneID_ResultsEnclosure); + + if ( inResizeAmount > 0 ) { // Make refresh look good! + resultsView->ResizeFrameBy(0, -inResizeAmount, true); + resultsView->MoveBy(0, inResizeAmount, true); + } + else + { + resultsView->MoveBy(0, inResizeAmount, true); + resultsView->ResizeFrameBy(0, -inResizeAmount, true); + } + + if ( !IsResultsTableVisible() ) + { + Rect bounds; + CSaveWindowStatus::GetPaneGlobalBounds(this, &bounds); + bounds.bottom += inResizeAmount; + this->DoSetBounds(bounds); // Update window size + } + + RecalcMinMaxStdSizes(); +} + +//----------------------------------- +void CSearchWindowBase::MessageWindSearch() +//----------------------------------- +{ + AssertFail_(mSearchManager.CanSearch()); + + MSG_SearchAttribute attrib; + Boolean sortBackwards; + mResultsTable->GetSortParams(&attrib, &sortBackwards); + + mResultsTable->StartSearch(*mMailNewsContext, CMailNewsContext::GetMailMaster(), + attrib, sortBackwards); + + mSearchButton->Hide(); + USearchHelper::FindViewSubpane(this, paneID_Stop)->Show(); + USearchHelper::FindViewSubpane(this, paneID_ScopeEnclosure)->Disable(); + + ShowHideSearchResults(true); + UpdatePort(); +} // CSearchWindowBase::MessageWindSearch() + +//----------------------------------- +void CSearchWindowBase::RecalcMinMaxStdSizes() +// Recalculate the window's min/max and std sizes based on the current window state. +//----------------------------------- +{ + if ( IsResultsTableVisible() ) + { + mMinMaxSize.top = mMinVResultsSize; + mMinMaxSize.bottom = 16000; + mStandardSize.height = max_Int16; + } + else + { + Rect bounds; + CSaveWindowStatus::GetPaneGlobalBounds(this, &bounds); + mMinMaxSize.top = mMinMaxSize.bottom = mStandardSize.height = (bounds.bottom - bounds.top); + } +} + +//----------------------------------- +void CSearchWindowBase::ShowHideSearchResults(Boolean inDoShow) +// Show or hide the search results in this window by resizing the window vertically. +// If the results are being hidden, the last window vertical extension is stored in +// mResultsVertWindExtension, otherwise mResultsVertWindExtension is used for determining +// the amount to resize the window. +// +// If search results are hidden, all current results are deleted from the result table. +//----------------------------------- +{ + if ( IsResultsTableVisible() ) + return; + + Rect curBounds; + + CSaveWindowStatus::GetPaneGlobalBounds(this, &curBounds); + + Rect newBounds = curBounds; + + if ( inDoShow ) + { + if ( mResultsVertWindExtension < 0 ) mResultsVertWindExtension = -mResultsVertWindExtension; // Undo initialization + newBounds.bottom += mResultsVertWindExtension; + mResultsEnclosure->Show(); + if ( (newBounds.bottom - newBounds.top) < mMinVResultsSize ) + { + newBounds.bottom = newBounds.top + mMinVResultsSize; + } + } + else + { + Rect bounds2; + CSaveWindowStatus::GetPaneGlobalBounds(USearchHelper::FindViewSubpane(this, CSearchManager::paneID_ParamEncl), + &bounds2); + Int16 newBottom = (bounds2.left - newBounds.left) + bounds2.bottom; + if ( mResultsVertWindExtension >= 0 ) mResultsVertWindExtension = newBounds.bottom - newBottom; + newBounds.bottom = newBottom; + mResultsEnclosure->Hide(); + } + + mResultsTableVisible = inDoShow; + mMinMaxSize.bottom = max_Int16; // Set so that VerifyWindowBounds() can do its work correctly! + mMinMaxSize.top = 100; // Set so that VerifyWindowBounds() can do its work correctly! + + CSaveWindowStatus::VerifyWindowBounds(this, &newBounds); + if ( !::EqualRect(&newBounds, &curBounds) ) + { + this->DoSetBounds(newBounds); + } + + RecalcMinMaxStdSizes(); +} + +//----------------------------------- +void CSearchWindowBase::UpdateResultsVertWindExtension() +// Update the vertical window extension variable. +//----------------------------------- +{ + if ( IsResultsTableVisible() && (mResultsVertWindExtension >= 0) ) + { + Rect windBounds, enclBounds; + CSaveWindowStatus::GetPaneGlobalBounds(this, &windBounds); + CSaveWindowStatus::GetPaneGlobalBounds(USearchHelper::FindViewSubpane( + this, CSearchManager::paneID_ParamEncl), &enclBounds); + Int16 enclBottom = (enclBounds.left - windBounds.left) + enclBounds.bottom; + mResultsVertWindExtension = windBounds.bottom - enclBottom; + } +} + +//----------------------------------- +void CSearchWindowBase::ReadWindowStatus(LStream *inStatusData) +//----------------------------------- +{ + if ( inStatusData != nil ) + { + Rect bounds; + *inStatusData >> bounds; + + CSaveWindowStatus::MoveWindowTo(this, topLeft(bounds)); + + Int16 resultsVertWindExtension; + + *inStatusData >> resultsVertWindExtension; + if ( resultsVertWindExtension > 0 ) + { + mResultsVertWindExtension = -resultsVertWindExtension; // See the ShowHideSearchResults(), set to negative + // to mean initialization + } + } + else + { + CSaveWindowStatus::MoveWindowToAlertPosition(this); + } + + mSearchManager.ReadSavedSearchStatus(inStatusData); + if( inStatusData ) + mResultsTable->GetTableHeader()->ReadColumnState(inStatusData); +} + +//----------------------------------- +void CSearchWindowBase::WriteWindowStatus(LStream *outStatusData) +//----------------------------------- +{ + CSaveWindowStatus::WriteWindowStatus(outStatusData); + + UpdateResultsVertWindExtension(); + + *outStatusData << ((Int16) ((mResultsVertWindExtension > 0) ? mResultsVertWindExtension : + (-mResultsVertWindExtension))); + + mSearchManager.WriteSavedSearchStatus(outStatusData); + + mResultsTable->GetTableHeader()->WriteColumnState(outStatusData); +} + +//----------------------------------- +void CSearchWindowBase::UpdateTableStatusDisplay() +//----------------------------------- +{ + if ( !IsResultsTableVisible() ) return; + + AssertFail_(mResultsTable != nil); + + TableIndexT numItems, numSelectedItems; + + mResultsTable->GetTableSize(numItems, numSelectedItems); + numSelectedItems = mResultsTable->GetSelectedRowCount(); + + CStr255 messageString; + if ( numItems > 0 ) + { + CStr31 numString, selectedString; + ::NumToString(numItems, numString); + if ( numSelectedItems > 0 ) + { + ::NumToString(numSelectedItems, selectedString); + USearchHelper::AssignUString( + (numItems == 1) ? + USearchHelper::USearchHelper::uStr_OneMessageFoundSelected : + USearchHelper::uStr_MultipleMessagesSelected, + messageString); + } + else + USearchHelper::AssignUString((numItems == 1) ? USearchHelper::uStr_OneMessageFound : USearchHelper::uStr_MultipleMessagesFound, + messageString); + ::StringParamText(messageString, numString, selectedString); + + } + else + USearchHelper::AssignUString(USearchHelper::uStr_NoMessagesFound, messageString); + + mProgressBar->SetDescriptor(messageString); + +} + +//----------------------------------- +Boolean CSearchWindowBase::GetDefaultSearchTable( + CMailNewsWindow*& outMailNewsWindow, + CMailFlexTable*& outMailFlexTable) +// Using the current PP window hierarchy, determine if the current top regular window is +// a window that we can use to associate with our search window. This method is +// called just before the search window is brought to the front of the regular window +// hierarchy. If the method returns false, no window was found that could be associated +// with our search window and the output parameters are set to nil. If the method returns +// true, a window was found that has an active flex table, and both objects are returned +// in the output parameters. +//----------------------------------- +{ + outMailNewsWindow = nil; + outMailFlexTable = nil; + + // Get the current top regular window of the type we need + CMediatedWindow *theFoundWindow = nil; + CWindowIterator theIterator(WindowType_Any); + do + { + if (theIterator.Next(theFoundWindow)) + { + DataIDT windowType = theFoundWindow->GetWindowType(); + switch (windowType) + { + case WindowType_SearchMailNews: + continue; // Found ourselves. + + case WindowType_MailNews: + outMailNewsWindow = dynamic_cast(theFoundWindow); + break; + + case WindowType_MailThread: + case WindowType_Newsgroup: + outMailNewsWindow = dynamic_cast(theFoundWindow); + break; + + case WindowType_Message: + outMailNewsWindow = dynamic_cast(theFoundWindow); + break; + } + if (outMailNewsWindow) + break; // Found a MailNews window that can return a "search table" + } + else + theFoundWindow = nil; + } + while (theFoundWindow != nil); + + if (outMailNewsWindow) + { + // Is there an active target table? + outMailFlexTable = outMailNewsWindow->GetSearchTable(); + if (outMailFlexTable == nil) + outMailNewsWindow = nil; + + } + return (outMailFlexTable != nil); +} + + +//----------------------------------- +void CSearchWindowBase::MessageWindStop(Boolean inUserAborted) +//----------------------------------- +{ + if ( mSearchManager.IsSearching() ) + { + mSearchManager.StopSearch(inUserAborted ? ((MWContext *) *mMailNewsContext) : nil); + mSearchButton->Show(); + USearchHelper::FindViewSubpane(this, paneID_Stop)->Hide(); + USearchHelper::FindViewSubpane(this, paneID_ScopeEnclosure)->Enable(); + + // This call fixes a bug in the BE search row insertion + mResultsTable->ForceCurrentSort(); + } +} + +/*====================================================================================== + Return a display name for the specified folder name. +======================================================================================*/ + +CStr255& CSearchWindowBase::GetFolderDisplayName(const char *inFolderName, CStr255& outFolderName) +{ + outFolderName = inFolderName; + char* temp = (char*)outFolderName; + NET_UnEscape(temp); + outFolderName = temp; + return outFolderName; +} + + +/*====================================================================================== + Get the display text for the specified attribute and table row index. Return + kNumAttributes if the inCellDataType is invalid. +======================================================================================*/ + +MSG_SearchAttribute CSearchWindowBase::AttributeFromDataType(PaneIDT inCellDataType) +{ + MSG_SearchAttribute rtnVal; + + switch ( inCellDataType ) + { + // Message search columns + case kSubjectMessageColumn: rtnVal = attribSubject; break; + case kSenderMessageColumn: rtnVal = attribSender; break; + case kDateMessageColumn: rtnVal = attribDate; break; + case kStatusMessageLocation: rtnVal = attribLocation; break; + case kPriorityMessageColumn: rtnVal = attribPriority; break; + case kStatusMessageColumn: rtnVal = attribMsgStatus; break; + default: rtnVal = kNumAttributes; break; + } + + return rtnVal; +} + + + + +/*====================================================================================== + Return a sort data type from the specified sort command. Return 0L if the + inSortCommand is invalid. +======================================================================================*/ + +PaneIDT CSearchWindowBase::DataTypeFromSortCommand(CommandT inSortCommand) +{ + PaneIDT rtnVal; + + switch ( inSortCommand ) + { + case cmd_SortBySubject: rtnVal = kSubjectMessageColumn; break; + case cmd_SortBySender: rtnVal = kSenderMessageColumn; break; + case cmd_SortByDate: rtnVal = kDateMessageColumn; break; + case cmd_SortByLocation: rtnVal = kStatusMessageLocation; break; + case cmd_SortByPriority: rtnVal = kPriorityMessageColumn; break; + case cmd_SortByStatus: rtnVal = kStatusMessageColumn; break; + // Do we need these commands for column sorting in the LDAP search result pane? + default: rtnVal = cmd_Nothing; break; + } + + return rtnVal; +} + + +//----------------------------------- +void CSearchWindowBase::AddOneScopeMenuItem( + Int16 inStringIndex, + Int16 inAttrib + ) +//----------------------------------- +{ + CStr255 string; + USearchHelper::AssignUString(inStringIndex, string); + AddOneScopeMenuItem(string, inAttrib); +} + +//----------------------------------- +void CSearchWindowBase::AddOneScopeMenuItem( + const CStr255& inString, + Int16 inAttrib + ) +//----------------------------------- +{ + Assert_(mNumBasicScopeMenuItems < MAX_SEARCH_MENU_ITEMS); + if (mNumBasicScopeMenuItems < MAX_SEARCH_MENU_ITEMS) + { + MSG_SearchMenuItem& curItem = mSearchMenuItems[mNumBasicScopeMenuItems]; + LString::CopyPStr(inString, (UInt8*)curItem.name, sizeof(curItem.name)); + curItem.attrib = inAttrib; + curItem.isEnabled = true; + ++mNumBasicScopeMenuItems; + } +} + + +//----------------------------------- +void CSearchWindowBase::SetWinCSID(Int16 wincsid) +//----------------------------------- +{ + if(mMailNewsContext) + mMailNewsContext->SetWinCSID(wincsid); + if(mResultsTable) + mResultsTable->SetWinCSID(wincsid); + mSearchManager.SetWinCSID(wincsid); +} + diff --git a/mozilla/cmd/macfe/MailNews/CSearchWindowBase.h b/mozilla/cmd/macfe/MailNews/CSearchWindowBase.h new file mode 100644 index 00000000000..af289bffa92 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CSearchWindowBase.h @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CSearchWindowBase.h + +#pragma once + +#include "CMailNewsWindow.h" +#include "LPeriodical.h" +#include "LListener.h" +#include "msg_srch.h" +#include "PascalString.h" +#include "CSearchManager.h" +#include "MailNewsgroupWindow_Defines.h" + +class LGAPushButton; +class CPatternProgressCaption; +class CSearchTableView; + +const long MAX_SEARCH_MENU_ITEMS = 16; + +//====================================== +class CSearchWindowBase : public CMailNewsWindow, + public LPeriodical, + public LListener +//====================================== +{ + typedef CMailNewsWindow Inherited; + +public: + // IDs for panes in associated view, also messages that are broadcast to this object + + enum + { + paneID_Search = 'SRCH' // MSG_Pane *, search button + , paneID_Stop = 'STOP' // nil, stop button + , paneID_ResultsEnclosure = 'RENC' // Results enclosure + , paneID_ScopeEnclosure = 'SENC' // Scope enclosure + , paneID_ResultsTable = 'Tabl' // Results table + , paneID_ProgressBar = kMailNewsStatusPaneID // Progress bar + , paneID_SearchOptions = 'SOPT' // Search options dialog + }; + + // Stream version number + // This must be changed every time you change the streamed parameters + enum { eValidStreamVersion = 0x000b }; + + CSearchWindowBase(LStream *inStream, DataIDT inWindowType); + virtual ~CSearchWindowBase(); + virtual void SetUpBeforeSelecting() = 0; + + static MSG_SearchAttribute AttributeFromDataType(PaneIDT inCellDataType); + static PaneIDT DataTypeFromSortCommand(CommandT inSortCommand); + +protected: + + // Overriden methods + virtual MSG_ScopeAttribute GetWindowScope() const = 0; + + virtual void FinishCreateSelf(); + virtual Boolean ObeyCommand(CommandT inCommand, void *ioParam = nil); + virtual void FindCommandStatus(CommandT inCommand, Boolean &outEnabled, + Boolean &outUsesMark, Char16 &outMark, + Str255 outName); + virtual void ListenToMessage(MessageT inMessage, void *ioParam = nil); + virtual Boolean HandleKeyPress(const EventRecord &inKeyEvent); + virtual void SpendTime(const EventRecord &inMacEvent); + virtual void DrawSelf(); + virtual void SetDescriptor(ConstStr255Param inDescriptor); + virtual void Activate(); + virtual void DeactivateSelf(); + virtual void AboutToClose(); + virtual void SearchOptions() ; + + // Utility methods + + void RecalcMinMaxStdSizes(); + void ShowHideSearchResults(Boolean inDoShow); + void UpdateResultsVertWindExtension(); + Boolean IsResultsTableVisible() + { + return mResultsTableVisible; + } + Boolean GetDefaultSearchTable( CMailNewsWindow*& outWindow, CMailFlexTable*& outMailFlexTable); + + CStr255& GetFolderDisplayName(const char *inFolderName, CStr255& outFolderName); + + void MessageSearchParamsResized(Int16 inResizeAmount); + virtual void MessageWindSearch(); + virtual void MessageWindStop(Boolean inUserAborted); + + void AddOneScopeMenuItem( + Int16 inStringIndex, + Int16 inAttrib); + void AddOneScopeMenuItem( + const CStr255& inString, + Int16 inAttrib); + + virtual void ReadWindowStatus(LStream *inStatusData); + virtual void WriteWindowStatus(LStream *outStatusData); + virtual void UpdateTableStatusDisplay(); + + + virtual void SetWinCSID(Int16 wincsid); + virtual UInt16 GetValidStatusVersion() const; + + // Instance variables + + Int16 mMinVResultsSize; + Int16 mResultsVertWindExtension; + Boolean mResultsTableVisible; + LPane *mResultsEnclosure; + CSearchTableView *mResultsTable; + LGAPushButton *mSearchButton; + + Boolean mCanRotateTarget; + + CSearchManager mSearchManager; + LArray mSearchFolders; + + CPatternProgressCaption *mProgressBar; + Int16 mNumBasicScopeMenuItems; + Int16 mNumMenuItems; + MSG_SearchMenuItem mSearchMenuItems[MAX_SEARCH_MENU_ITEMS]; + + private: + + LGAPushButton *mSearchOptionsButton; + +}; // class CSearchWindowBase diff --git a/mozilla/cmd/macfe/MailNews/CSecurityButton.cp b/mozilla/cmd/macfe/MailNews/CSecurityButton.cp new file mode 100644 index 00000000000..f2083985a4e --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CSecurityButton.cp @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CSecurityButton.cp + +#include "CSecurityButton.h" +/* #include "CMailComposeWindow.h" */ +/* #include "msgcom.h" */ +#include "CComposeSession.h" +/* #include "CBrowserContext.h" */ +#include "CButton.h" +#include "LGAIconSuiteControl.h" + +#include "CMessageView.h" +#include "ssl.h" + +void USecurityIconHelpers::UpdateMailWindow( CMessageView *messageView ) +{ + if ( messageView ) + { + CMailNewsWindow* window = dynamic_cast(LWindow::FetchWindowObject( + messageView->GetMacPort())); + if ( !window ) + return; + MWContext* context = NULL; + context = (MWContext*)*messageView->GetContext(); + uint32 folderFlags = messageView->GetFolderFlags(); + XP_Bool isEncrypted = 0; + XP_Bool isSigned = 0; + + MIME_GetMessageCryptoState( context , 0 ,0, &isSigned, &isEncrypted ); + + Boolean isNewsGroup = ((folderFlags & MSG_FOLDER_FLAG_NEWSGROUP)!=0) ; + if ( isNewsGroup ) + { + // news message encryption depends if the News group is secure + int secLevel = XP_GetSecurityStatus( context ); + isEncrypted = (secLevel != SSL_SECURITY_STATUS_OFF); + } + if ( isSigned >1 || isSigned<0) + isSigned = 0; + if ( isEncrypted>1 || isEncrypted<0 ) + isEncrypted = 0; + + USecurityIconHelpers::ChangeToSecurityState( window, isEncrypted, isSigned ); + } +} + +void USecurityIconHelpers::UpdateComposeButton( CMailComposeWindow *window ) +{ + Assert_( window !=NULL ); + CComposeSession * session = window->GetComposeSession(); + XP_Bool isEncrypted = 0; + XP_Bool isSigned = 0; + isEncrypted = session->GetCompBoolHeader( MSG_ENCRYPTED_BOOL_HEADER_MASK ); + isSigned = session->GetCompBoolHeader( MSG_SIGNED_BOOL_HEADER_MASK ); + USecurityIconHelpers::ChangeToSecurityState( window, isEncrypted, isSigned ); +} + +void USecurityIconHelpers::SetNoMessageLoadedSecurityState( LWindow * window ) +{ + Assert_( window != NULL ); + USecurityIconHelpers::ChangeToSecurityState( window, false, false ); +} + +void USecurityIconHelpers::AddListenerToSmallButton(LWindow* inWindow, LListener* inListener) +{ + LGAIconSuiteControl* button = dynamic_cast(inWindow->FindPaneByID( eSmallEncryptedButton )); + if (button) + button->AddListener(inListener); + button = dynamic_cast(inWindow->FindPaneByID( eSmallSignedButton )); + if (button) + button->AddListener(inListener); +} + +void USecurityIconHelpers::ChangeToSecurityState( + LWindow* inWindow, + Boolean inEncrypted, + Boolean inSigned ) +{ + if (!inWindow) + return; + CButton* button = dynamic_cast(inWindow->FindPaneByID( eBigSecurityButton )); + if (button) + { + static const ResIDT bigSecurityIcons[4]={ 15439, 15435, 15447, 15443 }; + ResIDT newIconID = bigSecurityIcons[ inSigned + 2*inEncrypted ]; + if (button->GetGraphicID() != newIconID ) + { + button->SetGraphicID( newIconID); + button->Refresh(); + } + } + LGAIconSuiteControl* smallButton + = dynamic_cast(inWindow->FindPaneByID( eSmallEncryptedButton )); + if (smallButton) + { + ResIDT newIconID = inEncrypted ? 15336 : 15335; + if (smallButton->GetIconResourceID() != newIconID ) + { + smallButton->SetIconResourceID( newIconID); + smallButton->Refresh(); + } + } + smallButton + = dynamic_cast(inWindow->FindPaneByID( eSmallSignedButton )); + if (smallButton) + { + if (inSigned) + smallButton->Show(); + else + smallButton->Hide(); + } +} + +void CMailSecurityListener::ListenToMessage(MessageT inMessage, void* ioParam) +{ +#pragma unused (ioParam) + switch( inMessage ) + { + case msg_NSCAllConnectionsComplete: + USecurityIconHelpers::UpdateMailWindow( mMessageView); + break; + + default: + break; + } +} diff --git a/mozilla/cmd/macfe/MailNews/CSecurityButton.h b/mozilla/cmd/macfe/MailNews/CSecurityButton.h new file mode 100644 index 00000000000..b5edbfe9769 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CSecurityButton.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CSecurityButton.h + +#pragma once +#include +class CMailComposeWindow; +class CMailNewsWindow; +class CMessageView; +class LWindow; +// Modifies the Security button to reflect wether the message is +// signed/encrypted +class CMailSecurityListener : public LListener +{ +public: + CMailSecurityListener() : mMessageView(NULL) {}; + virtual void ListenToMessage(MessageT inMessage, void* ioParam); + void SetMessageView( CMessageView* view) { mMessageView = view;} +private: + CMessageView *mMessageView; +}; + +class USecurityIconHelpers +{ +public: + enum State { eNone, eSigned, eEncrypted, eSignedEncrypted}; + enum { eBigSecurityButton = 'Bsec', + eSmallEncryptedButton = 'SBsc', + eSmallSignedButton = 'SBsg' }; + // Large security buttons must be 'Bsec' + // Small security buttons must be 'SBsc' (encryption) or 'SBsg' (signed) + static void UpdateComposeButton( CMailComposeWindow *window ); + static void UpdateMailWindow( CMessageView *messageView); + static void SetNoMessageLoadedSecurityState( LWindow * window ); + static void AddListenerToSmallButton(LWindow* inWindow, LListener* inListener); +private: + static void ChangeToSecurityState( + LWindow* window, + Boolean inEncrypted, + Boolean inSigned ); +}; diff --git a/mozilla/cmd/macfe/MailNews/CSimpleFolderView.cp b/mozilla/cmd/macfe/MailNews/CSimpleFolderView.cp new file mode 100644 index 00000000000..4e20a2ac537 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CSimpleFolderView.cp @@ -0,0 +1,565 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CSimpleFolderView.cp + +/* + This class allows to get a list of containers: + - Mail servers & folders + - News servers & groups + + It was originally developed as part of CMessageFolderView + which implements the Message Center. Having a separate class + allows to implement folder lists in other places (Offline Picker). + + It inherits from CMailFlexTable and CStandardFlexTable. + It handles the selection and clicks on the twistee icons but + it doesn't handle any command, except for the Stop button. + + Mail and News folders are obtained from the Back-End by creating + a Folder Pane. + + + History: + ¥ In version 1.1 of this file, there was a mechanism to save/restore + the selection in case of MSG_NotifyScramble or MSG_NotifyAll (like + in CThreadView). This mechanism was removed because the folder view + does not support sort commands. +*/ + +#include "CSimpleFolderView.h" + +// Netscape Mac Libs +#include "resgui.h" + +// Mail/News Specific +#include "CMailNewsWindow.h" +#include "MailNewsgroupWindow_Defines.h" +#include "CProgressListener.h" +#include "CMessageFolder.h" +#include "CMailNewsContext.h" +#include "UMailFolderMenus.h" + +#define cmd_ExpandCellKludge 'Expd' // fake command number used in a + // kludge to avoid multiple redraws + + +//------------------------------------------------------------------------------ +// ¥ CSimpleFolderView +//------------------------------------------------------------------------------ +// +CSimpleFolderView::CSimpleFolderView(LStream *inStream) + : Inherited(inStream) + , mUpdateMailFolderMenusWhenComplete(false) + , mUpdateMailFolderMenusOnNextUpdate(false) + , mSelectHilitesWholeRow(false) +{ +} + + +//------------------------------------------------------------------------------ +// ¥ ~CSimpleFolderView +//------------------------------------------------------------------------------ +// +CSimpleFolderView::~CSimpleFolderView() +{ +} + + +#pragma mark - +//------------------------------------------------------------------------------ +// ¥ LoadFolderList +//------------------------------------------------------------------------------ +// Given a context, starts getting the list. Typically called in the +// FinishCreateSelf() of the parent window which is the one which creates the context. +// +void CSimpleFolderView::LoadFolderList(CNSContext* inContext) +{ + Assert_(inContext != NULL); + mContext = inContext; + if (GetMessagePane() == NULL) + { + SetMessagePane(MSG_CreateFolderPane(*inContext, CMailNewsContext::GetMailMaster())); + ThrowIfNULL_(GetMessagePane()); + MSG_SetFEData(GetMessagePane(), CMailCallbackManager::Get()); + } + + NotifySelfAll(); // notify ourselves that everything has changed +} + + +//------------------------------------------------------------------------------ +// ¥ GetSelectedFolder +//------------------------------------------------------------------------------ +// Public utility: return the ID of the selected folder. +// +MSG_FolderInfo* CSimpleFolderView::GetSelectedFolder() const +{ + TableIndexT row = 0; + if (GetMessagePane() && GetNextSelectedRow(row)) + return MSG_GetFolderInfo(GetMessagePane(), row - 1); + return NULL; +} + + +//------------------------------------------------------------------------------ +// ¥ SelectFirstFolderWithFlags +//------------------------------------------------------------------------------ +// Public utility: used to select the Inbox on "Window | Message Center" +// or the first news server on "Prefs | Launch Collabra Discussions". +// +void CSimpleFolderView::SelectFirstFolderWithFlags(uint32 inFlags) +{ + MSG_FolderInfo* folderInfo; + + ::MSG_GetFoldersWithFlag(CMailNewsContext::GetMailMaster(), inFlags, &folderInfo, 1); + SelectFolder(folderInfo); +} + + +//------------------------------------------------------------------------------ +// ¥ SelectFolder +//------------------------------------------------------------------------------ +// Public utility: can be used to select the parent folder of a Thread window. +// +void CSimpleFolderView::SelectFolder(const MSG_FolderInfo* inFolderInfo) +{ + if (inFolderInfo) + { + MSG_ViewIndex index = ::MSG_GetFolderIndexForInfo( + GetMessagePane(), (MSG_FolderInfo*)inFolderInfo, true); + ArrayIndexT lineNumber = 1 + index; + STableCell cellToSelect(lineNumber, 1); + + SetNotifyOnSelectionChange(false); + UnselectAllCells(); + SetNotifyOnSelectionChange(true); + + ScrollCellIntoFrame(cellToSelect); // show folder before selecting it + SelectCell(cellToSelect); + } + else + UnselectAllCells(); +} + + +//------------------------------------------------------------------------------ +// ¥ DeleteSelection +//------------------------------------------------------------------------------ +// Required by CStandardFlexTable. We don't want to implement that here: +// the Message Center will certainly stay the only place where a folder can be +// deleted. If this were to change, move the code from CMessageFolderView back +// to here along with the 'mWatchedFolder' hack. +// +void CSimpleFolderView::DeleteSelection() +{ +} + + +#pragma mark - +//------------------------------------------------------------------------------ +// ¥ GetIconID +//------------------------------------------------------------------------------ +// From CStandardFlexTable. Return the folder icon for that row. +// +ResIDT CSimpleFolderView::GetIconID(TableIndexT inRow) const +{ + CMessageFolder folder(inRow, GetMessagePane()); + return folder.GetIconID(); +} + + +//------------------------------------------------------------------------------ +// ¥ GetNestedLevel +//------------------------------------------------------------------------------ +// From CStandardFlexTable. Return the ident for that row. +// This method goes together with GetHiliteTextRect(). +// +UInt16 CSimpleFolderView::GetNestedLevel(TableIndexT inRow) const +{ + CMessageFolder folder(inRow, GetMessagePane()); + return folder.GetLevel() - 1; +} + + +//------------------------------------------------------------------------------ +// ¥ GetHiliteColumn +//------------------------------------------------------------------------------ +// From CStandardFlexTable. When a row is selected, we can hilite the +// whole row (default behavior of the base class) or just the folder name. +// This method goes together with GetHiliteTextRect(). +// +TableIndexT CSimpleFolderView::GetHiliteColumn() const +{ + if (mSelectHilitesWholeRow) + return Inherited::GetHiliteColumn(); + + return mTableHeader->ColumnFromID(kFolderNameColumn); +} + + +//------------------------------------------------------------------------------ +// ¥ GetHiliteTextRect +//------------------------------------------------------------------------------ +// From CStandardFlexTable. Pass back the rect to hilite for that row (either +// the whole row, either just the folder name). Return true if rect is not empty. +// This method goes together with GetHiliteColumn() and GetNestedLevel(). +// +Boolean CSimpleFolderView::GetHiliteTextRect( + const TableIndexT inRow, + Rect& outRect) const +{ + if (mSelectHilitesWholeRow) + return Inherited::GetHiliteTextRect(inRow, outRect); + + STableCell cell(inRow, GetHiliteColumn()); + if (!GetLocalCellRect(cell, outRect)) + return false; + + Rect iconRect; + GetIconRect(cell, outRect, iconRect); + outRect.left = iconRect.right; + char folderName[cMaxMailFolderNameLength + 1]; + GetHiliteText(inRow, folderName, sizeof(folderName), &outRect); + return true; +} + + +//------------------------------------------------------------------------------ +// ¥ ApplyTextStyle +//------------------------------------------------------------------------------ +// From CStandardFlexTable. Set the text style for that row. +// +void CSimpleFolderView::ApplyTextStyle(TableIndexT inRow) const +{ + CMessageFolder folder(inRow, GetMessagePane()); + // According to the latest UI spec (4/16/98), bold is used for folders with unread messages + int32 textStyle = normal; + if( folder.IsOpen() ) + { + if ( folder.CountUnseen()> 0 ) + textStyle = bold; + } + else + { + // if the folder is closed we want to bold the folder if any subfolder has unread messages + if ( folder.CountDeepUnseen()> 0 ) + textStyle = bold; + } + ::TextFace( textStyle ); +} + + +//------------------------------------------------------------------------------ +// ¥ DrawCellContents +//------------------------------------------------------------------------------ +// From CStandardFlexTable. Pass a cell and a rect: it draws it. +// It knows how to draw the name column (with a twistee if necessary + +// the icon corresponding to the item) and the 'Unread' and 'Total' +// columns. If you have additional columns, overide this method. +// +void CSimpleFolderView::DrawCellContents( + const STableCell& inCell, + const Rect& inLocalRect) +{ + PaneIDT cellType = GetCellDataType(inCell); + switch (cellType) + { + case kFolderNameColumn: + { + // Draw icons (item icon + twistee) + SInt16 iconRight = DrawIcons(inCell, inLocalRect); + + if (mRowBeingEdited != inCell.row || !mNameEditor) + { + + // Draw folder name + Rect textRect = inLocalRect; + textRect.left = iconRight; + char folderName[cMaxMailFolderNameLength + 1]; + if (GetHiliteText(inCell.row, folderName, sizeof(folderName), &textRect)) + { + DrawTextString(folderName, &mTextFontInfo, 0, textRect); + if (inCell.row == mDropRow) + ::InvertRect(&textRect); + } + } + break; + } + + case kFolderNumUnreadColumn: + { + CMessageFolder folder(inCell.row, GetMessagePane()); + if (folder.CanContainThreads()) + if (folder.CountMessages() != 0) + DrawCountCell(folder.CountUnseen(), inLocalRect); + break; + } + + case kFolderNumTotalColumn: + { + CMessageFolder folder(inCell.row, GetMessagePane()); + if (folder.CanContainThreads()) + if (folder.CountMessages() != 0) + DrawCountCell(folder.CountMessages(), inLocalRect); + break; + } + } +} + + +#pragma mark - +//------------------------------------------------------------------------------ +// ¥ CellHasDropFlag +//------------------------------------------------------------------------------ +// Check if a cell has a twistee icon and if the twistee is open. +// +Boolean CSimpleFolderView::CellHasDropFlag( + const STableCell& inCell, + Boolean& outIsExpanded) const +{ + CMessageFolder folder(inCell.row, GetMessagePane()); + if (GetCellDataType(inCell) == kFolderNameColumn && folder.CountSubFolders() != 0) + { + outIsExpanded = folder.IsOpen(); + return true; + } + return false; +} + + +//------------------------------------------------------------------------------ +// ¥ SetCellExpansion +//------------------------------------------------------------------------------ +// Open or close the twistee icon of a folder. +// +void CSimpleFolderView::SetCellExpansion( + const STableCell& inCell, + Boolean inExpand) +{ + // check current state + CMessageFolder folder(inCell.row, GetMessagePane()); + if (inExpand == folder.IsOpen()) + return; + + // kludge: slow down the status bar refresh rate + // to reduce flickers and improve performance + if (inExpand) + { + if (folder.IsNewsHost() || folder.IsIMAPMailFolder()) + { + CMailNewsWindow * myWindow = dynamic_cast + (LWindow::FetchWindowObject(GetMacPort())); + + if (myWindow) + myWindow->GetProgressListener()->SetLaziness( + CProgressListener::lazy_VeryButForThisCommandOnly); + } + } + + // kludge to avoid multiple redraws (see ChangeFinished()) + mContext->SetCurrentCommand(cmd_ExpandCellKludge); + + // toggle twistee + ToggleExpandAction(inCell.row); + folder.FolderInfoChanged(); +} + + +//------------------------------------------------------------------------------ +// ¥ GetMainRowText +//------------------------------------------------------------------------------ +// From CStandardFlexTable. Return the folder name for that row. +// Implemented here because the folder name is likely to stay +// the main text of the row for the users of the class. +// +void CSimpleFolderView::GetMainRowText( + TableIndexT inRow, + char* outText, + UInt16 inMaxBufferLength) const +{ + if (!outText || inMaxBufferLength == 0) + return; + + if (inMaxBufferLength < cMaxMailFolderNameLength + 1) + { + *outText = '\0'; + return; + } + + CMessageFolder folder(inRow, GetMessagePane()); + CMailFolderMixin::GetFolderNameForDisplay(outText, folder); + NET_UnEscape(outText); +} + + +#pragma mark - +//------------------------------------------------------------------------------ +// ¥ FindCommandStatus +//------------------------------------------------------------------------------ +// Enable Stop button when list is loading. +// +void CSimpleFolderView::FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +{ + if (mStillLoading) + { + if (inCommand == cmd_Stop) + { + outEnabled = true; + return; + } + } + else + { + Inherited::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); + } +} + + +//------------------------------------------------------------------------------ +// ¥ ListenToMessage +//------------------------------------------------------------------------------ +// When list is complete, update folder menus across the app. +// +void CSimpleFolderView::ListenToMessage(MessageT inMessage, void* ioParam) +{ + Inherited::ListenToMessage(inMessage, ioParam); + + if (inMessage == msg_NSCAllConnectionsComplete) + { + if (mUpdateMailFolderMenusWhenComplete) + { + mUpdateMailFolderMenusWhenComplete = false; + CMailFolderMixin::UpdateMailFolderMixins(); + } + mContext->SetCurrentCommand(cmd_Nothing); + } +} + + +#pragma mark - +//------------------------------------------------------------------------------ +// ¥ ChangeFinished +//------------------------------------------------------------------------------ +// A list of hacks to update our list + the folder menus across the app. +// Also restores the selection after a 'sort' command (see ChangeStarting()). +// +void CSimpleFolderView::ChangeFinished( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount) +{ + //-------- + // When opening a News host by toggling the twistee in the Message Center, + // we get one notification for each subscribed newsgroup and it generates + // a lot of flicker. + // + // The following is a kludge which just ignores the notifications because + // a News host info never changes anyway. However we're not guaranteed + // that it will always be the case in the future. A bug report (#79163) + // has been opened asking the Back-End folks to fix the problem and + // reassign the bug to a Mac engineer in order to remove that kludge. + // + if (inChangeCode == MSG_NotifyChanged) + { + if (inRowCount == 1) + { + CMessageFolder folder(inStartRow, GetMessagePane()); + folder.FolderInfoChanged(); + UInt32 folderFlags = folder.GetFolderFlags(); + if (((folderFlags & MSG_FOLDER_FLAG_NEWS_HOST) != 0) && + ((folderFlags & MSG_FOLDER_FLAG_DIRECTORY) != 0)) + inChangeCode = MSG_NotifyNone; + } + } + // + //-------- + + Inherited::ChangeFinished(inPane, inChangeCode, inStartRow, inRowCount); + + if (mMysticPlane < kMysticUpdateThreshHold) + { + switch (inChangeCode) + { + case MSG_NotifyScramble: + case MSG_NotifyAll: + UnselectAllCells(); + CMailFolderMixin::UpdateMailFolderMixins(); // This should really be somewhere else! + break; + + case MSG_NotifyInsertOrDelete: + CMailFolderMixin::UpdateMailFolderMixins(); + // When rows are inserted or deleted because the user has moved a folder hierarchy, + // then we have to invalidate the cache for the inserted rows, because + // the folder levels have possibly changed. This also applies to cmd_Clear, + // which (unless the option key is down) is a move in disguise. When expanding + // folders, we need to do this too, just to cover the case when a folder was + // moved into another folder and is now being displayed for the first time. + CommandT currentCommand = mContext->GetCurrentCommand(); + if ((currentCommand == cmd_MoveMailMessages + || currentCommand == cmd_Clear + || currentCommand == cmd_ExpandCellKludge) + && inRowCount > 0) + FoldersChanged(inStartRow, inRowCount); + break; + + case MSG_NotifyChanged: + if (mContext->GetCurrentCommand() == cmd_ExpandCellKludge) + mUpdateMailFolderMenusWhenComplete = true; + else + if (mUpdateMailFolderMenusOnNextUpdate) + { + mUpdateMailFolderMenusOnNextUpdate = false; + CMailFolderMixin::UpdateMailFolderMixins(); + } + else + { + FoldersChanged(inStartRow, inRowCount); + } + break; + } + } +} + + +//------------------------------------------------------------------------------ +// ¥ FoldersChanged +//------------------------------------------------------------------------------ +// Mark a range of folders as 'changed': invalidate the cached line for each. +// +void CSimpleFolderView::FoldersChanged( + TableIndexT inStartRow, + SInt32 inRowCount) const +{ + if (inRowCount < 0) + inRowCount = -inRowCount; + + for (SInt32 i = 0; i < inRowCount; i ++) + { + CMessageFolder folder(inStartRow + i, GetMessagePane()); + folder.FolderInfoChanged(); + } +} diff --git a/mozilla/cmd/macfe/MailNews/CSimpleFolderView.h b/mozilla/cmd/macfe/MailNews/CSimpleFolderView.h new file mode 100644 index 00000000000..a402cad4b6d --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CSimpleFolderView.h @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CSimpleFolderView.h + +#pragma once + +#include "CMailFlexTable.h" + +class CThreadView; +class CThreadWindow; +class CStr255; +class CMessageFolder; + + + +//------------------------------------------------------------------------------ +// ¥ CSimpleFolderView +//------------------------------------------------------------------------------ +// +class CSimpleFolderView : public CMailFlexTable +{ +private: + typedef CMailFlexTable Inherited; + +public: + enum + { class_ID = 'SFVw' + }; + + CSimpleFolderView(LStream *inStream); + virtual ~CSimpleFolderView(); + +protected: + + //----------------------------------- + // Public folder fun + //----------------------------------- +public: + void LoadFolderList(CNSContext* inContext); + + //----------------------------------- + // Command implementation + //----------------------------------- + MSG_FolderInfo* GetSelectedFolder() const; + void SelectFirstFolderWithFlags(uint32 inFlags); + void SelectFolder(const MSG_FolderInfo* inFolderInfo); + void SelectHilitesWholeRow(const Boolean inWholeRow) + { mSelectHilitesWholeRow = inWholeRow;}; + virtual void DeleteSelection(); + + //----------------------------------- + // Drawing (specials from CStandardFlexTable) + //----------------------------------- +protected: + virtual ResIDT GetIconID(TableIndexT inRow) const; + virtual UInt16 GetNestedLevel(TableIndexT inRow) const; + + virtual TableIndexT GetHiliteColumn() const; + virtual Boolean GetHiliteTextRect( + const TableIndexT inRow, + Rect& outRect) const; + + virtual void ApplyTextStyle(TableIndexT inRow) const; + virtual void DrawCellContents( + const STableCell &inCell, + const Rect &inLocalRect); + + //----------------------------------- + // Hierarchy + //----------------------------------- + virtual Boolean CellHasDropFlag(const STableCell& inCell, Boolean& outIsExpanded) const; + virtual void SetCellExpansion(const STableCell& inCell, Boolean inExpand); + virtual void GetMainRowText( + TableIndexT inRow, + char* outText, + UInt16 inMaxBufferLength) const; + + //----------------------------------- + // Commands + //----------------------------------- + virtual void FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName); +public: + + //----------------------------------- + // Messaging + //----------------------------------- + virtual void ListenToMessage(MessageT inMessage, void* ioParam); + + + //----------------------------------- + // Data change notification (callbacks from MSGlib) + //----------------------------------- + virtual void ChangeFinished( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount); + void FoldersChanged( + TableIndexT inStartRow, + SInt32 inRowCount) const; +protected: + void UpdateMailFolderMenusOnNextUpdate() + { mUpdateMailFolderMenusOnNextUpdate = true; } + + //----------------------------------- + // Data + //----------------------------------- + + Boolean mSelectHilitesWholeRow; // select whole row vs. just the name + + Boolean mUpdateMailFolderMenusWhenComplete; + Boolean mUpdateMailFolderMenusOnNextUpdate; +}; + diff --git a/mozilla/cmd/macfe/MailNews/CSubscribeView.cp b/mozilla/cmd/macfe/MailNews/CSubscribeView.cp new file mode 100644 index 00000000000..39e7f2e212d --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CSubscribeView.cp @@ -0,0 +1,1188 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CSubscribeView.cp + +#include "CSubscribeView.h" + +// MacOS +#include + +// PowerPlant +#include +#include + +// XP +#include "msgcom.h" + +// MacFE +#include "macutil.h" +#include "macgui.h" +#include "resgui.h" +#include "CPaneEnabler.h" + +// Mail/News Specific +#include "CMailNewsContext.h" +#include "CSubscribeWindow.h" +#include "MailNewsgroupWindow_Defines.h" +#include "UMailFolderMenus.h" +#include "UMailSelection.h" + +/* + +Overview: +--------- + + CSubscribeView: + It inherits from CMailFlexTable and displays the list of newsgroups. + It has 3 states (or MSG_SubscribeMode) which correspond to the 3 panels + of the CSubscribeWindow: All, Search and New. In each state, you can + select the newsgroups you want to subscribe to. + + CNewsgroup: + It corresponds to an entry in the CSubscribeView and allows + you to access to the properties of the entry. Its purpose + is more informational than functional. Most of the methods + are simple accessors. Some of them are not used but left + here for future generations. + + +Hints: +------ + + Loading of the newsgroup list is done in the idler because it's a synchronous + operation and we don't want it to be done before the window is displayed. It + could potentially take a long time. + + The methods you need to know about are RefreshList() and the usual + pair of FindCommandStatus() and ObeyCommand(). The rest is mostly for + internal use. + + Most of the commands handled by the CSubscribeView are of type cmd_NewsXXX + (cmd_NewsToggleSubscribe for instance) and they correspond to the different buttons + of the CSubscribeWindow. The commands which don't fit in that scheme (ie. for + which a higher and better level of abstraction might have been possible) are: + + * msg_EditField: sent by the editable field of the "All Groups" tab panel, + it allows to do a type-ahead search in the list of newsgroups. Enabled + here in FindCommandStatus() and processed by CSubscribeWindow. + + * cmd_OpenNewsHost: sent by the Add Server button. Enabled here in + FindCommandStatus() and processed by CSubscribeWindow. + + * msg_TabSelect: sent by the LTabGroup. Processed here. + + The communication with the news servers has two states. In the first one, + we download the list of newsgroup names: it happens the first time we + connect to the server (the list is then stored in a cache file), or + everytime we click the Get Groups button. In that state, the user is not + allowed to do anything except cancel or connect to another server. + In the second state, we download the number of postings in each newsgroup: + it happens everytime we switch to another tab panel or open a folder in + the list. Since it is much more common, it is done asynchronously and + all (or almost all) the commands must be available to the user. So, we have + two flags which indicate how busy we are. The first one, mStillLoading, + is inherited from CMailFlexTable and is set whenever we talk to the host. + The second flag, mStillLoadingFullList, is defined here and is set when + we downnload the newsgroup list. + + More info about the window in "CSubscribeWindow.cp". + +History: +-------- + November 97: + Added the mCommitState for IMAP public folders support. It is set + when the user clicks OK: while the BE talks to the IMAP server, + the dialog is put in a state where the only possible action is + to click the Stop button. +*/ + + +enum +{ + kNormalMessageFolderIconID = 15238 +, kNewsgroupIconID = 15231 +, kSubscribedIconID = 15237 +, kUnsubscribedIconID = 15235 + +}; + +#define kIconMargin 4 +#define kIndentPerLevel 16 +#define kIconWidth 16 + + +#pragma mark --- CNewsgroup + +//---------------------------------------------------------------------------- +// CNewsgroup +// +// Constructor +// Cache functions +//---------------------------------------------------------------------------- + +MSG_GroupNameLine CNewsgroup::sNewsgroupData; +MSG_ViewIndex CNewsgroup::sCachedIndex; + + +CNewsgroup::CNewsgroup(TableIndexT inRow, MSG_Pane* inNewsgroupList) + : mIndex(inRow - 1) + , mNewsgroupList(inNewsgroupList) +{ + InvalidateCache(); + UpdateCache(); +} + +void CNewsgroup::InvalidateCache() const +{ + sCachedIndex = LONG_MAX; +} + +void CNewsgroup::UpdateCache() const +{ + if (mIndex != sCachedIndex) + { + MSG_GetGroupNameLineByIndex(mNewsgroupList, mIndex, 1, &sNewsgroupData); + sCachedIndex = mIndex; + } +} + + +//---------------------------------------------------------------------------- +// CNewsgroup / Newsgroup info accessor functions +// +// GetNewsgroupLine +// +// GetName +// GetPrettyName +// CountPostings +// GetLevel +// CanContainThreads +// CountChildren +// GetIconID +//---------------------------------------------------------------------------- + +MSG_GroupNameLine* CNewsgroup::GetNewsgroupLine() const +{ + UpdateCache(); + return &sNewsgroupData; +} + + +char* CNewsgroup::GetName() const { return GetNewsgroupLine()->name;} +char* CNewsgroup::GetPrettyName() const { return GetNewsgroupLine()->name;} +Int32 CNewsgroup::CountPostings() const { return GetNewsgroupLine()->total;} +UInt32 CNewsgroup::GetLevel() const { return GetNewsgroupLine()->level;} +Boolean CNewsgroup::CanContainThreads() const { return GetLevel() > kRootLevel;} + + +UInt32 CNewsgroup::CountChildren() const +{ + if (HasChildren()) + return sNewsgroupData.total; + return 0; +} + + +ResIDT CNewsgroup::GetIconID() const +{ + //¥ TO DO ¥: deal with the "Open" state, the "read" state, etc. + if (this->HasChildren()) + return kNormalMessageFolderIconID; + else + return kNewsgroupIconID; +} + + +//---------------------------------------------------------------------------- +// CNewsgroup / Flags accessor functions +// +// GetNewsgroupFlags +// +// IsOpen +// HasChildren +// IsSubscribed +// IsNew +//---------------------------------------------------------------------------- + +UInt32 CNewsgroup::GetNewsgroupFlags() const +{ + UpdateCache(); + return sNewsgroupData.flags; +} + + +Boolean CNewsgroup::IsOpen() const +{ + return ((GetNewsgroupFlags() & MSG_GROUPNAME_FLAG_ELIDED) == 0); +} + + +Boolean CNewsgroup::HasChildren() const +{ + return ((GetNewsgroupFlags() & MSG_GROUPNAME_FLAG_HASCHILDREN) != 0); +} + + +Boolean CNewsgroup::IsSubscribed() const +{ + return ((GetNewsgroupFlags() & MSG_GROUPNAME_FLAG_SUBSCRIBED) != 0); +} + + +Boolean CNewsgroup::IsNew() const +{ + return ((GetNewsgroupFlags() & MSG_GROUPNAME_FLAG_NEW_GROUP) != 0); +} + + +#pragma mark --- CSubscribeView + +//---------------------------------------------------------------------------- +// CSubscribeView Constructor/Destructor +// +//---------------------------------------------------------------------------- + +CSubscribeView::CSubscribeView(LStream *inStream) +: Inherited(inStream), + mStillLoadingFullList(false), + mCommitState(kIdle) +{ +} + + +CSubscribeView::~CSubscribeView() +{ +// Warning: Don't call MSG_DestroyPane() here: +// CMailFlexTable calls SetMessagePane(NULL) in its destructor. +} + + +//---------------------------------------------------------------------------- +// CloseParentWindow +// +//---------------------------------------------------------------------------- +void CSubscribeView::CloseParentWindow() +{ + CSubscribeWindow * myWindow = + dynamic_cast(LWindow::FetchWindowObject(GetMacPort())); + if (myWindow) + myWindow->DoClose(); +} + + +//---------------------------------------------------------------------------- +// SetProgressBarLaziness +// +//---------------------------------------------------------------------------- +void CSubscribeView::SetProgressBarLaziness( + CProgressListener::ProgressBarLaziness inLaziness) +{ + CSubscribeWindow * myWindow = + dynamic_cast(LWindow::FetchWindowObject(GetMacPort())); + if (myWindow) + { + CProgressListener * myProgress = myWindow->GetProgressListener(); + if (myProgress) + myProgress->SetLaziness(inLaziness); + } +} + + +//---------------------------------------------------------------------------- +// ProcessKeyPress +// +// Update buttons depending on the selection when scrolling through the list +//---------------------------------------------------------------------------- + +Boolean CSubscribeView::ProcessKeyPress(const EventRecord &inKeyEvent) +{ + Boolean keyHandled = Inherited::ProcessKeyPress(inKeyEvent); + CPaneEnabler::UpdatePanes(); + return keyHandled; +} + + +//---------------------------------------------------------------------------- +// GetIconID +// +//---------------------------------------------------------------------------- + +ResIDT CSubscribeView::GetIconID(TableIndexT inRow) const +{ + CNewsgroup newsgroup(inRow, GetMessagePane()); + return newsgroup.GetIconID(); +} + +//---------------------------------------------------------------------------- +// DrawCell +// +//---------------------------------------------------------------------------- + +void CSubscribeView::DrawCell(const STableCell& inCell, const Rect& inLocalRect) +{ + // check if we are in the update region + RgnHandle updateRgn = GetLocalUpdateRgn(); + if (!updateRgn) + return; + + Rect updateRect = (**updateRgn).rgnBBox; + ::DisposeRgn(updateRgn); + + Rect intersection; + if (!::SectRect(&updateRect, &inLocalRect, &intersection)) + return; + + // draw the cell + StClipRgnState savedClip; + ::ClipRect(&intersection); + PaneIDT cellType = GetCellDataType(inCell); + CNewsgroup newsgroup(inCell.row, GetMessagePane()); + switch (cellType) + { + case kNewsgroupNameColumn: + SInt16 newLeft = DrawIcons(inCell, inLocalRect); + Rect textRect = inLocalRect; + textRect.left = newLeft; + char groupName[cMaxMailFolderNameLength+1]; + if (GetHiliteText(inCell.row, groupName, sizeof(groupName), &textRect)) + DrawTextString(groupName, &mTextFontInfo, 0, textRect); + break; + + case kNewsgroupSubscribedColumn: + if (CanSubscribeToNewsgroup(newsgroup)) + { + short iconID = (newsgroup.IsSubscribed() ? + kSubscribedIconID : kUnsubscribedIconID); + DrawIconFamily(iconID, 16, 16, 0, inLocalRect); + } + break; + + case kNewsgroupPostingsColumn: + if (CanSubscribeToNewsgroup(newsgroup)) + DrawCountCell(newsgroup.CountPostings(), inLocalRect); + break; + + } +} + + +//---------------------------------------------------------------------------- +// RefreshList +// +//---------------------------------------------------------------------------- + +void CSubscribeView::RefreshList(MSG_Host* newsHost, MSG_SubscribeMode subscribeMode) +{ + mNewsHost = newsHost; + mSubscribeMode = subscribeMode; + StartIdling(); +} + + +//---------------------------------------------------------------------------- +// SpendTime +// +//---------------------------------------------------------------------------- + +void CSubscribeView::SpendTime(const EventRecord &) +{ + // stop idling first (avoid infinite recursive calls if BE calls FE_Alert) + StopIdling(); + + Assert_(mContext != NULL); + if (mContext == NULL) + return; + + StSpinningBeachBallCursor beachBallCursor; + + // create the list + if (GetMessagePane() == NULL) + { + SetMessagePane(MSG_CreateSubscribePaneForHost( + *mContext, CMailNewsContext::GetMailMaster(), mNewsHost)); + ThrowIfNULL_(GetMessagePane()); + MSG_SetFEData(GetMessagePane(), CMailCallbackManager::Get()); + + // force a refresh of the postings count + SetProgressBarLaziness(CProgressListener::lazy_VeryButForThisCommandOnly); + MSG_Command(GetMessagePane(), MSG_UpdateMessageCount, NULL, NULL); + } + else + { + // display the list (it is read from the cache file) + SetProgressBarLaziness(CProgressListener::lazy_VeryButForThisCommandOnly); + MSG_SubscribeSetHost(GetMessagePane(), mNewsHost); + } + MSG_SubscribeSetMode(GetMessagePane(), mSubscribeMode); + + switch (mSubscribeMode) + { + case MSG_SubscribeAll: + MSG_GroupNameLine aLine; + // If the list (ie. the cache file) is empty, download it from the server. + if (! MSG_GetGroupNameLineByIndex(GetMessagePane(), 0, 1, &aLine)) + { + SetProgressBarLaziness(CProgressListener::lazy_JustABit); + MSG_Command(GetMessagePane(), MSG_FetchGroupList, NULL, NULL); + } + break; + + case MSG_SubscribeSearch: + // don't get the list yet: wait until user clicks Search button + break; + + case MSG_SubscribeNew: + // Always get the list of new newsgroups from the server + SetProgressBarLaziness(CProgressListener::lazy_VeryButForThisCommandOnly); + MSG_Command(GetMessagePane(), MSG_CheckForNew, NULL, NULL); + break; + } + + // notify ourselves + NotifySelfAll(); + + // Since fetching the group list is a synchronous operation, we may have a lot of + // unresponded to clicks and key strokes in the event queue. Flush them. + ::FlushEvents(mDownMask | mUpMask | keyDownMask | keyUpMask | autoKeyMask, 0); +} + + +//---------------------------------------------------------------------------- +// SearchForString +// +//---------------------------------------------------------------------------- +void CSubscribeView::SearchForString(const StringPtr searchStr) +{ + P2CStr(searchStr); + switch (MSG_SubscribeGetMode(GetMessagePane())) + { + case MSG_SubscribeAll: + MSG_ViewIndex theRow; + theRow = MSG_SubscribeFindFirst(GetMessagePane(), (const char *)searchStr); + STableCell theCell(theRow + 1, 1); + SetNotifyOnSelectionChange(false); + UnselectAllCells(); + SetNotifyOnSelectionChange(true); + SelectCell(theCell); + ScrollCellIntoFrame(theCell); + CPaneEnabler::UpdatePanes(); + break; + + case MSG_SubscribeSearch: + MSG_SubscribeFindAll(GetMessagePane(), (const char *)searchStr); + // beep when list is empty + MSG_GroupNameLine aLine; + if (! MSG_GetGroupNameLineByIndex(GetMessagePane(), 0, 1, &aLine)) + ::SysBeep(1); + break; + } +} + + +//---------------------------------------------------------------------------- +// SubscribeCancel +// +//---------------------------------------------------------------------------- +void CSubscribeView::SubscribeCancel() +{ + if (GetMessagePane() != NULL) + MSG_SubscribeCancel(GetMessagePane()); + CloseParentWindow(); +} + + +//---------------------------------------------------------------------------- +// SubscribeCommit +// +//---------------------------------------------------------------------------- +void CSubscribeView::SubscribeCommit() +{ + if (GetMessagePane() != NULL) + { + mContext->AddListener(this); + if (XP_IsContextBusy(*mContext)) + { + mCommitState = kInterrupting; + XP_InterruptContext(*mContext); + } + else + { + mCommitState = kCommitting; + MSG_SubscribeCommit(GetMessagePane()); + } + } + else + CloseParentWindow(); +} + + +//---------------------------------------------------------------------------- +// CellHasDropFlag +// +//---------------------------------------------------------------------------- + +Boolean CSubscribeView::CellHasDropFlag( + const STableCell& inCell, + Boolean& outIsExpanded) const +{ + PaneIDT cellType = GetCellDataType(inCell); + CNewsgroup newsgroup(inCell.row, GetMessagePane()); + if (cellType == kNewsgroupNameColumn && newsgroup.CountChildren() != 0) + { + outIsExpanded = newsgroup.IsOpen(); + return true; + } + return false; +} + + +//---------------------------------------------------------------------------- +// SetCellExpansion +// +//---------------------------------------------------------------------------- + +void CSubscribeView::SetCellExpansion( + const STableCell& inCell, + Boolean inExpand) +{ + CNewsgroup newsgroup(inCell.row, GetMessagePane()); + if (inExpand == newsgroup.IsOpen()) + return; + ToggleExpandAction(inCell.row); +} + +//------------------------------------------------------------------------------ +// GetNestedLevel +//------------------------------------------------------------------------------ +// From CStandardFlexTable. Return the ident for that row. +// This method goes together with GetHiliteTextRect(). +// +UInt16 CSubscribeView::GetNestedLevel(TableIndexT inRow) const +{ + CNewsgroup newsgroup(inRow, GetMessagePane()); + return newsgroup.GetLevel(); +} + +//------------------------------------------------------------------------------ +// GetHiliteColumn +//------------------------------------------------------------------------------ +// From CStandardFlexTable. When a row is selected, we can +// hilite the whole row or just the folder name. +// This method goes together with GetHiliteTextRect(). +// +TableIndexT CSubscribeView::GetHiliteColumn() const +{ + return mTableHeader->ColumnFromID(kNewsgroupNameColumn); +} + + +//------------------------------------------------------------------------------ +// GetHiliteTextRect +//------------------------------------------------------------------------------ +// From CStandardFlexTable. Pass back the rect to hilite for that row (either +// the whole row, either just the folder name). Return true if rect is not empty. +// This method goes together with GetHiliteColumn() and GetNestedLevel(). +// +Boolean CSubscribeView::GetHiliteTextRect( + const TableIndexT inRow, + Rect& outRect) const +{ + STableCell cell(inRow, GetHiliteColumn()); + if (!GetLocalCellRect(cell, outRect)) + return false; + + Rect iconRect; + GetIconRect(cell, outRect, iconRect); + outRect.left = iconRect.right; + char folderName[cMaxMailFolderNameLength + 1]; + GetHiliteText(inRow, folderName, sizeof(folderName), &outRect); + return true; +} + + +//------------------------------------------------------------------------------ +// GetMainRowText +//------------------------------------------------------------------------------ +// From CStandardFlexTable. Return the folder name for that row. +// +void CSubscribeView::GetMainRowText( + TableIndexT inRow, + char* outText, + UInt16 inMaxBufferLength) const +{ + if (!outText || inMaxBufferLength == 0) + return; + + if (inMaxBufferLength < cMaxMailFolderNameLength + 1) + { + *outText = '\0'; + return; + } + + CNewsgroup newsgroup(inRow, GetMessagePane()); + // ¥¥¥ FIX ME -> Get the BE to store the newsgroup name already unescaped + XP_STRCPY(outText, newsgroup.GetPrettyName()); + NET_UnEscape(outText); +} + + +//---------------------------------------------------------------------------- +// ChangeStarting +// +//---------------------------------------------------------------------------- + +void CSubscribeView::ChangeStarting( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount) +{ + switch ( inChangeCode ) + { + case MSG_NotifyScramble: + case MSG_NotifyAll: + mStillLoadingFullList = true; + break; + } + + Inherited::ChangeStarting(inPane, inChangeCode, inStartRow, inRowCount); +} + +//---------------------------------------------------------------------------- +// ChangeFinished +// +//---------------------------------------------------------------------------- + +void CSubscribeView::ChangeFinished( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount) +{ + switch ( inChangeCode ) + { + case MSG_NotifyScramble: + case MSG_NotifyAll: + mStillLoadingFullList = false; + break; + } + + Inherited::ChangeFinished(inPane, inChangeCode, inStartRow, inRowCount); + + if ( mMysticPlane < kMysticUpdateThreshHold ) + { + switch ( inChangeCode ) + { + case MSG_NotifyScramble: + case MSG_NotifyAll: + UnselectAllCells(); + break; + } + } +} + +//---------------------------------------------------------------------------- +// OpenRow +// +//---------------------------------------------------------------------------- +// Called on enter/return or double-click. +// Hack: don't do anything when multiple items are selected. +// +void CSubscribeView::OpenRow(TableIndexT inRow) +{ + if (inRow > 0) + { + CMailSelection selection; + if (selection.selectionSize <= 1) + { + STableCell aCell(inRow, 1); + Boolean outIsExpanded; + if (CellHasDropFlag(aCell, outIsExpanded)) + { + MSG_ToggleExpansion (GetMessagePane(), inRow - 1, NULL); + } + } + } +} + + +//---------------------------------------------------------------------------- +// ListenToMessage +// +//---------------------------------------------------------------------------- + +void CSubscribeView::ListenToMessage(MessageT inMessage, void* ioParam) +{ + if (! ObeyCommand(inMessage, ioParam)) // check button messages first + { + switch (inMessage) + { + case msg_NSCAllConnectionsComplete: + if (mCommitState == kInterrupting) + { + mCommitState = kCommitting; + MSG_SubscribeCommit(GetMessagePane()); + } + else if (mCommitState == kCommitting) + { + mCommitState = kIdle; + CloseParentWindow(); // the window is closed here... + return; // ...so exit now because I'm dead + } + /* no break; */ + + default: + Inherited::ListenToMessage(inMessage, ioParam); + break; + } + } +} + + +//---------------------------------------------------------------------------- +// FindCommandStatus +// +//---------------------------------------------------------------------------- + +void CSubscribeView::FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +{ + // The pane can be null when no server has been configured. + // In that case, we only enable the Add Server button. + if (GetMessagePane() == NULL) + { + outEnabled = (inCommand == cmd_OpenNewsHost); + return; + } + + // When committing, allow only the Stop button... + if (mCommitState == kCommitting) + { + outEnabled = (inCommand == cmd_Stop); + return; + } + else if (mCommitState == kInterrupting) + { + outEnabled = false; + return; + } + + // ...otherwise the Stop button is enabled whenever we talk to the server + if (inCommand == cmd_Stop) + { + outEnabled = (mStillLoading | mStillLoadingFullList); + return; + } + + // Handle basic commands as long as we're not + // downloading the full newsgroups list + if (! mStillLoadingFullList) + { + if (FindMessageLibraryCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName)) + return; + } + + switch (inCommand) + { + case msg_EditField: // similar in behaviour to cmd_NewsSearch + case cmd_OpenNewsHost: + case cmd_NewsHostChanged: + case cmd_NewsToggleSubscribe: + case cmd_NewsSetSubscribe: + case cmd_NewsClearSubscribe: + case cmd_NewsExpandGroup: + case cmd_NewsExpandAll: + case cmd_NewsCollapseGroup: + case cmd_NewsCollapseAll: + case cmd_NewsGetGroups: + case cmd_NewsSearch: + case cmd_NewsGetNew: + case cmd_NewsClearNew: + FindSubscribeCommandStatus(inCommand, outEnabled); + break; + + default: + Inherited::FindCommandStatus( + inCommand, outEnabled, outUsesMark, outMark, outName); + break; + } +} + + +//---------------------------------------------------------------------------- +// FindSubscribeCommandStatus +// +//---------------------------------------------------------------------------- +void CSubscribeView::FindSubscribeCommandStatus( + CommandT inCommand, + Boolean &outEnabled) +{ + Assert_(GetMessagePane() != NULL); + if (GetMessagePane() == NULL) + { + outEnabled = false; + return; + } + + // When downloading the newsgroups list, can't do + // anything but connect to a different server. + if (mStillLoadingFullList) + { + outEnabled = (inCommand == cmd_NewsHostChanged); + return; + } + + // When connected to an IMAP server, the Search and New tab panels + // are completely disabled except for connect to a different server. + if (MSG_IsIMAPHost(mNewsHost)) + { + switch (MSG_SubscribeGetMode(GetMessagePane())) + { + case MSG_SubscribeSearch: + case MSG_SubscribeNew: + outEnabled = (inCommand == cmd_NewsHostChanged); + return; // <-- note it + } + } + + switch (inCommand) + { + // Expand All is enabled if the selection + // contains a non-expanded folder and + // if the host is not an IMAP host + case cmd_NewsExpandAll: + { + if (! MSG_IsIMAPHost(mNewsHost)) + { + CMailSelection selection; + GetSelection(selection); + if (selection.selectionSize > 0) + { + STableCell aCell; + Boolean outIsExpanded; + for (long index = 0; index < selection.selectionSize; index ++) + { + aCell.SetCell(selection.GetSelectionList()[index] + 1, 1); + if (CellHasDropFlag(aCell, outIsExpanded) && (! outIsExpanded)) + { + outEnabled = true; + break; + } + } + } + } + } + break; + + // These commands require a selection. Note: Collapse All + // does not really require a selection but it is prettier + // when it is not activated just after the list is displayed. + case cmd_NewsToggleSubscribe: + case cmd_NewsExpandGroup: + case cmd_NewsCollapseGroup: + case cmd_NewsCollapseAll: + { + CMailSelection selection; + GetSelection(selection); + outEnabled = (selection.selectionSize > 0); + } + break; + + // These commands require a selection _and_ the subscribed/unsubscribed + // state of the selected items must match the command. + case cmd_NewsSetSubscribe: + case cmd_NewsClearSubscribe: + { + Boolean lookingForBool = (inCommand == cmd_NewsSetSubscribe ? false : true); + + CMailSelection selection; + if (GetSelection(selection)) + { + const MSG_ViewIndex* index = selection.GetSelectionList(); + for (int i = 0; i < selection.selectionSize; i ++, index++) + { + CNewsgroup newsgroup((*index + 1), GetMessagePane()); // add one to convert to TableIndexT + if (CanSubscribeToNewsgroup(newsgroup)) + { + if (lookingForBool == newsgroup.IsSubscribed()) + { + outEnabled = true; // found what I was looking for + break; + } + } + } + } + } + break; + + // Don't enable Get Groups when I'm downloading the number of postings. + // This is not required but forcing the user to click Stop before is + // a way to notify him that something is going on on the network. + case cmd_NewsGetGroups: + outEnabled = (! mStillLoading); + break; + + // This commands are always enabled, except when downloading + // the newsgroups list but this is checked above + case msg_EditField: // similar in behaviour to cmd_NewsSearch + case cmd_NewsSearch: + case cmd_NewsGetNew: + case cmd_NewsClearNew: + default: + outEnabled = true; + break; + } +} + + +//---------------------------------------------------------------------------- +// ObeyCommand +// +//---------------------------------------------------------------------------- + +Boolean CSubscribeView::ObeyCommand( + CommandT inCommand, + void *ioParam) +{ + Boolean result = false; + mContext->SetCurrentCommand(inCommand); + switch(inCommand) + { + //----------------------------------- + // TabGroup command + //----------------------------------- + case msg_TabSelect: + if (GetMessagePane() != NULL) + { + CMailSelection selection; + GetSelection(selection); + if (selection.selectionSize == 0) + { + // set a selection to activate the button enablers + STableCell cell(1,1); + SelectCell(cell); + } + } + result = true; + break; + + //----------------------------------- + // Button commands + //----------------------------------- + +// now handled by the window +// case cmd_NewsHostChanged: +// if (mStillLoading || mStillLoadingFullList) +// ObeyCommand(cmd_Stop, NULL); +// MSG_Host* newsHost = (MSG_Host*)(ioParam); +// RefreshList(newsHost, MSG_SubscribeGetMode(GetMessagePane())); +// result = true; +// break; + + case cmd_NewsExpandAll: + case cmd_NewsCollapseAll: + case cmd_NewsToggleSubscribe: + case cmd_NewsSetSubscribe: + case cmd_NewsClearSubscribe: + case cmd_NewsGetGroups: + case cmd_NewsGetNew: + case cmd_NewsClearNew: + MSG_CommandType theCommand; + switch (inCommand) + { + case cmd_NewsExpandAll: theCommand = MSG_ExpandAll; break; + case cmd_NewsCollapseAll: theCommand = MSG_CollapseAll; break; + case cmd_NewsToggleSubscribe: theCommand = MSG_ToggleSubscribed; break; + case cmd_NewsSetSubscribe: theCommand = MSG_SetSubscribed; break; + case cmd_NewsClearSubscribe: theCommand = MSG_ClearSubscribed; break; + case cmd_NewsGetGroups: theCommand = MSG_FetchGroupList; break; + case cmd_NewsGetNew: theCommand = MSG_CheckForNew; break; + case cmd_NewsClearNew: theCommand = MSG_ClearNew; break; + } + + if (inCommand == cmd_NewsExpandAll || inCommand == cmd_NewsCollapseAll) + UCursor::SetWatch(); + + CMailSelection selection; // not all commands require the selection... + GetSelection(selection); // ...in these cases, it will just be ignored + MSG_Command(GetMessagePane(), + theCommand, + (MSG_ViewIndex*)selection.GetSelectionList(), + selection.selectionSize + ); + + UCursor::SetArrow(); + result = true; + break; + + //----------------------------------- + // MSGLIB commands + //----------------------------------- + default: + if (inCommand == cmd_Stop) + { + if (mCommitState != kIdle) + { + mCommitState = kIdle; + mContext->RemoveListener(this); + } + } + result = (ObeyMessageLibraryCommand(inCommand, ioParam) + || Inherited::ObeyCommand(inCommand, ioParam)); + break; + } + return result; +} + +//---------------------------------------------------------------------------- +// ClickSelect +// +// Handle clicks in the Subscribe column. +//---------------------------------------------------------------------------- + +Boolean CSubscribeView::ClickSelect( + const STableCell &inCell, + const SMouseDownEvent &inMouseDown) +{ + PaneIDT cellType = GetCellDataType(inCell); + if (cellType == kNewsgroupSubscribedColumn) + return true; + else + return Inherited::ClickSelect(inCell, inMouseDown); +} + +//---------------------------------------------------------------------------- +// ClickCell +// +// Handle clicks in the Subscribe column. +//---------------------------------------------------------------------------- + +void CSubscribeView::ClickCell( + const STableCell &inCell, + const SMouseDownEvent &inMouseDown) + +{ + Boolean clickInSubscribeColumn = false; + + PaneIDT cellType = GetCellDataType(inCell); + if (cellType == kNewsgroupSubscribedColumn) + { + CNewsgroup newsgroup(inCell.row, GetMessagePane()); + if (CanSubscribeToNewsgroup(newsgroup)) + { + clickInSubscribeColumn = true; + Inherited::ClickCell(inCell, inMouseDown); + + MSG_ViewIndex viewIndex = inCell.row - 1; + MSG_Command(GetMessagePane(), MSG_ToggleSubscribed, &viewIndex, 1); + } + } + + if (! clickInSubscribeColumn) + Inherited::ClickCell(inCell, inMouseDown); +} + + +//---------------------------------------------------------------------------- +// GetQapRowText +// +// Return info for QA Partner +//---------------------------------------------------------------------------- + +#if defined(QAP_BUILD) +void CSubscribeView::GetQapRowText( + TableIndexT inRow, + char* outText, + UInt16 inMaxBufferLength) const +{ + if (!outText || inMaxBufferLength == 0) + return; + + cstring rowText(""); + short colCount = mTableHeader->CountVisibleColumns(); + CNewsgroup newsgroup(inRow, GetMessagePane()); + + CMailNewsWindow * myWindow = dynamic_cast(LWindow::FetchWindowObject(GetMacPort())); + if (!myWindow) return; + + for (short col = 1; col <= colCount; col ++) + { + STableCell aCell(inRow, col); + LTableHeader::SColumnData * colData = mTableHeader->GetColumnData(col); + if (!colData) break; + LPane * colPane = myWindow->FindPaneByID(colData->paneID); + if (!colPane) break; + + // get column name + CStr255 descriptor; + colPane->GetDescriptor(descriptor); + rowText += descriptor; + rowText += "=\042"; + + // add cell text + switch (PaneIDT dataType = GetCellDataType(aCell)) + { + + case kNewsgroupNameColumn: + if (newsgroup.CountChildren() != 0) + { + if (newsgroup.IsOpen()) + rowText += "-"; + else + rowText += "+"; + } + else + rowText += " "; + rowText += newsgroup.GetName(); + break; + + case kNewsgroupSubscribedColumn: + if (CanSubscribeToNewsgroup(newsgroup)) + if (newsgroup.IsSubscribed()) + rowText += "+"; + break; + + case kNewsgroupPostingsColumn: + int theNum = newsgroup.CountPostings(); + if (theNum >= 0) + { + char tempStr[32]; + sprintf(tempStr, "%d", theNum); + rowText += tempStr; + } + else + rowText += "?"; + break; + } + + if (col < colCount) + rowText += "\042 | "; + else + rowText += "\042\r"; + } + strncpy(outText, (char*)rowText, inMaxBufferLength); + outText[inMaxBufferLength - 1] = '\0'; +} // CSubscribeView::GetQapRowText +#endif //QAP_BUILD diff --git a/mozilla/cmd/macfe/MailNews/CSubscribeView.h b/mozilla/cmd/macfe/MailNews/CSubscribeView.h new file mode 100644 index 00000000000..5e94f918c4a --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CSubscribeView.h @@ -0,0 +1,228 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CSubscribeView.h + +#pragma once + +// PowerPlant +#include +#include + +// Mail/News FE +#include "CMailFlexTable.h" +#include "CProgressListener.h" + +#define kRootLevel 1 // news host, local mail etc. + +//---------------------------------------------------------------------------- +// CNewsgroup +// +//---------------------------------------------------------------------------- + +class CNewsgroup +{ +public: + CNewsgroup(TableIndexT inRow, MSG_Pane* inNewsgroupList); + + // Cache management + void InvalidateCache() const; + void UpdateCache() const; + + // misc. info accessors + MSG_GroupNameLine* GetNewsgroupLine() const; + char* GetName() const; + char* GetPrettyName() const; + Int32 CountPostings() const; // neg if unknown + UInt32 GetLevel() const; + Boolean CanContainThreads() const; + UInt32 CountChildren() const; + ResIDT GetIconID() const; + + // Flag property accessors: + UInt32 GetNewsgroupFlags() const; + Boolean IsOpen() const; + Boolean HasChildren() const; + Boolean IsSubscribed() const; + Boolean IsNew() const; + + + // Data +public: + MSG_ViewIndex mIndex; + +protected: + static MSG_GroupNameLine sNewsgroupData; + static MSG_ViewIndex sCachedIndex; + MSG_Pane* mNewsgroupList; +}; + + + +//---------------------------------------------------------------------------- +// CSubscribeView +// +//---------------------------------------------------------------------------- + +class CSubscribeView : public CMailFlexTable, + public LPeriodical +{ +private: + typedef CMailFlexTable Inherited; + +public: + CSubscribeView(LStream *inStream); + enum { class_ID = 'subV' }; + virtual ~CSubscribeView(); + + //----------------------------------- + // Public folder fun + //----------------------------------- + void SetContext(CNSContext* inContext) + {mContext = inContext;}; + + void RefreshList( + MSG_Host* newsHost, + MSG_SubscribeMode subscribeMode); + + void SearchForString(const StringPtr searchStr); + + virtual void SubscribeCancel(); + virtual void SubscribeCommit(); + + + //----------------------------------- + // Command implementation + //----------------------------------- + virtual void OpenRow(TableIndexT inRow); + + virtual Boolean ProcessKeyPress( + const EventRecord& inKeyEvent); + + virtual void DrawCell( + const STableCell &inCell, + const Rect &inLocalRect); + + //----------------------------------- + // Hierarchy + //----------------------------------- + virtual Boolean CellHasDropFlag( + const STableCell& inCell, + Boolean& outIsExpanded) const; + + virtual void SetCellExpansion( + const STableCell& inCell, + Boolean inExpand); + + virtual TableIndexT GetHiliteColumn() const; + virtual UInt16 GetNestedLevel(TableIndexT inRow) const; + virtual Boolean GetHiliteTextRect( + const TableIndexT inRow, + Rect& outRect) const; + virtual void GetMainRowText( + TableIndexT inRow, + char* outText, + UInt16 inMaxBufferLength) const; + virtual ResIDT GetIconID(TableIndexT inRow) const; + + + virtual void DeleteSelection(void) { /* do nothing */ }; + + + //----------------------------------- + // Commands + //----------------------------------- + virtual void FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName); + + + virtual Boolean ObeyCommand( + CommandT inCommand, + void *ioParam); + + virtual Boolean ClickSelect( + const STableCell &inCell, + const SMouseDownEvent &inMouseDown); + virtual void ClickCell( + const STableCell &inCell, + const SMouseDownEvent &inMouseDown); + + //----------------------------------- + // Messaging + //----------------------------------- +protected: + virtual void ListenToMessage( + MessageT inMessage, + void *ioParam); + + virtual void FindSubscribeCommandStatus( + CommandT inCommand, + Boolean &outEnabled); + + virtual void SpendTime(const EventRecord &); + + //----------------------------------- + // Other + //----------------------------------- + virtual void CloseParentWindow(); + void SetProgressBarLaziness( + CProgressListener::ProgressBarLaziness inLaziness); + Boolean CanSubscribeToNewsgroup(const CNewsgroup& newsgroup) const + { return ((newsgroup.CountChildren() == 0) || MSG_IsIMAPHost(mNewsHost));} + + + //----------------------------------- + // Data change notification + // Callbacks from MSGlib come here. + //----------------------------------- + virtual void ChangeStarting( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount); + + virtual void ChangeFinished( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount); + + // ------------------------------------------------------------ + // QA Partner support + // ------------------------------------------------------------ +#if defined(QAP_BUILD) +public: + virtual void GetQapRowText(TableIndexT inRow, char* outText, UInt16 inMaxBufferLength) const; +#endif + + //----------------------------------- + // Data + //----------------------------------- + Boolean mStillLoadingFullList; + +private: + MSG_Host* mNewsHost; + MSG_SubscribeMode mSubscribeMode; + enum { kIdle, kInterrupting, kCommitting } mCommitState; +}; diff --git a/mozilla/cmd/macfe/MailNews/CSubscribeWindow.cp b/mozilla/cmd/macfe/MailNews/CSubscribeWindow.cp new file mode 100644 index 00000000000..82edd046f9b --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CSubscribeWindow.cp @@ -0,0 +1,871 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CSubscribeWindow.cp + +#include "CSubscribeWindow.h" +#include "CSubscribeView.h" +#include "CMailNewsContext.h" +#include "CNewsSubscriber.h" +#include "CProgressListener.h" +#include "CPaneEnabler.h" +#include "CTabControl.h" +#include "URobustCreateWindow.h" +#include "UMenuUtils.h" +#include "UMailFolderMenus.h" + +#include +#include +#include +#include "CLargeEditField.h" +#include "macgui.h" +//#include "newshost.h" +#include "miconutils.h" +#include "MercutioAPI.h" + +extern "C" { + #include "asyncCursors.h" +} + +// Command Numbers +#include "resgui.h" +#include "MailNewsgroupWindow_Defines.h" + + +/* + +Overview: +--------- + + CSubscribeWindow: + This window displays the list of newsgroups available on the + different news servers and lets you select the newsgroups you + want to subscribe to. Technically speaking, the CSubscribeWindow + handles the interactions between a CSubscribeView and a small bunch + of buttons and edit fields, plus sother elements listed below. + + CSubscribeHostPopup: + Popup menu to select the news server you want to connect to. + + CNewsHostIterator: + Simple iterator to access the list of news servers. + Used by the CSubscribeHostPopup. + + +Hints: +------ + The newsgroup list (a CSubscribeView) should not be disposed when switching + panels in order to keep its state (ie. its Context) and not reload it + everytime from the cache file or from the server. The list is originally + attached to the window and when a panel is activated, a CPlaceHolderView + puts it inside the panel. The list is linked where it belongs in the commander + chain after the CTabControl broadcasts the activation of a tab panel. + + We have a similar problem with the CSubscribeHostPopup: it must keep its + state when switching panels. This is done by storing the last selected + host in a static variable of the CNewsSubscriber (which has been delegated + this role because it only contains static stuff). The same static variable + is used when bringing up the CSubscribeWindow in order to display the list + corresponding to the host currently selected in the Message Center. + + More info about the list in "CSubscribeView.cp". + +History: +-------- + November 97: + Converted BE calls to the new API in order to support IMAP public folders. + Added Mercutio callback to display imap/news server icons in the popup menu. + + March 98: + Disabled "Search" and "New" panels when an IMAP host is selected. It required to + put the host popup menu in a CPlaceHolderView too in order to avoid deleting + and recreating it when a switch from a News host to an IMAP host causes + the tab panel to automatically display "All Groups" (it's bad to delete a + control when it's broadcasting). +*/ + + +static const ResIDT resID_SubscribeTab1 = 14002; // PPob resources +static const ResIDT resID_SubscribeTab2 = 14003; +static const ResIDT resID_SubscribeTab3 = 14004; +static const ResIDT resID_SubscribeWindow = 14006; + +static const PaneIDT paneID_OkButton = 'BtOk'; // generic buttons +static const PaneIDT paneID_CancelButton = 'Canc'; +static const PaneIDT paneID_EditField = 'NuEd'; +static const PaneIDT paneID_FolderList = 'Slst'; +static const PaneIDT paneID_FolderView = 'SbVw'; +static const PaneIDT paneID_TabSwitcher = 'SbTb'; +static const PaneIDT paneID_TabControl = 'TabC'; +static const PaneIDT paneID_SubscribeButton = 'Subs'; +static const PaneIDT paneID_UnsubscribeButton = 'UnSb'; +static const PaneIDT paneID_ServerPopup = 'SPop'; +static const PaneIDT paneID_StopButton = 'Stop'; + +static const PaneIDT paneID_AllTabPanel = 'Sub1'; // tab #1 buttons +static const PaneIDT paneID_ExpandButton = 'Expn'; +static const PaneIDT paneID_CollapseButton = 'Coll'; +static const PaneIDT paneID_GetGroupsButton = 'GetG'; +static const PaneIDT paneID_AddServerButton = 'AddS'; + +static const PaneIDT paneID_SearchTabPanel = 'Sub2'; // tab #2 buttons +static const PaneIDT paneID_SearchButton = 'Srch'; + +static const PaneIDT paneID_NewTabPanel = 'Sub3'; // tab #3 buttons +static const PaneIDT paneID_GetNewButton = 'GetN'; +static const PaneIDT paneID_ClearNewButton = 'ClrN'; + + +//---------------------------------------------------------------------------- +// USubscribeUtilities +// +//---------------------------------------------------------------------------- + +//#define REGISTER_(letter,root) \ +// RegisterClass_(letter##root::class_ID, \ +// (ClassCreatorFunc)letter##root::Create##root##Stream); + +#define REGISTER_(letter,root) \ + RegisterClass_(letter##root); + +#define REGISTERC(root) REGISTER_(C,root) +#define REGISTERL(root) REGISTER_(L,root) + + +void USubscribeUtilities::RegisterSubscribeClasses() +{ + REGISTERC(SubscribeWindow) + REGISTERC(SubscribeHostPopup) + REGISTERC(TabContainer) // reusing CTabContainer from CMailComposeWindow.cp +} + + +#pragma mark - + +//---------------------------------------------------------------------------- +// CNewsHostIterator +// +//---------------------------------------------------------------------------- +MSG_Host* CNewsHostIterator::mHostList[CNewsHostIterator::kMaxNewsHosts]; +Int32 CNewsHostIterator::mHostCount = 0; + +CNewsHostIterator::CNewsHostIterator(MSG_Master* master) +{ + mHostCount = MSG_GetSubscribingHosts(master, + &mHostList[0], + CNewsHostIterator::kMaxNewsHosts); + mIndex = 0; +} + + +Boolean CNewsHostIterator::Current(MSG_Host*& outNewsHost) +{ + outNewsHost = nil; + if (mIndex < mHostCount) + outNewsHost = mHostList[mIndex]; + return (outNewsHost != nil); +} + + +Boolean CNewsHostIterator::Next(MSG_Host*& outNewsHost) +{ + Boolean valid = Current(outNewsHost); + ResetTo(GetCurrentIndex() + 1); + return valid; +} + + +Boolean CNewsHostIterator::FindIndex(const MSG_Host* inNewsHost, Int32& outIndex) +{ + Int32 index; + for (index = 0; index < mHostCount; index ++) + { + if (inNewsHost == mHostList[index]) + { + outIndex = index; + return true; + } + } + outIndex = 0; + return false; +} + + +#pragma mark - + + +//---------------------------------------------------------------------------- +// FindAndShow [static] +// Handle the menu command that creates/shows/selects the MailNews window. +// Currently there can only be one of these. +//---------------------------------------------------------------------------- + +CSubscribeWindow* CSubscribeWindow::FindAndShow(Boolean makeNew) +{ + CSubscribeWindow* result = NULL; + try + { + CWindowIterator iter(WindowType_SubscribeNews); + iter.Next(result); + if (!result && makeNew) + { + result = dynamic_cast + (URobustCreateWindow::CreateWindow( + res_ID, LCommander::GetTopCommander())); + ThrowIfNULL_(result); + } + if (result) + { + result->Show(); + result->Select(); + } + } + catch( ... ) + { + } + return result; +} + + +//------------------------------------------------------------------------------ +// DisplayDialog [static] +//------------------------------------------------------------------------------ + +Boolean CSubscribeWindow::DisplayDialog() +{ + StDialogHandler handler(res_ID, NULL); + + MessageT message; + do + { + message = handler.DoDialog(); + } while (message != paneID_OkButton && message != paneID_CancelButton); + + return (message == paneID_OkButton); +} + + +//---------------------------------------------------------------------------- +// CSubscribeWindow Constructor / Destructor +// +//---------------------------------------------------------------------------- + +CSubscribeWindow::CSubscribeWindow(LStream* inStream) : + CMailNewsWindow(inStream, WindowType_SubscribeNews), + mList(nil) +{ +} + +CSubscribeWindow::~CSubscribeWindow() +{ + stopAsyncCursors(); +} + + +//---------------------------------------------------------------------------- +// FinishCreateSelf +// +//---------------------------------------------------------------------------- + +void CSubscribeWindow::FinishCreateSelf() +{ + // Finish create context and other things + Inherited::FinishCreateSelf(); + + mProgressListener->SetLaziness(CProgressListener::lazy_JustABit); + + mList = dynamic_cast(GetActiveTable()); + if (mList) mList->SetContext((CMailNewsContext*)this->GetWindowContext()); + + // Hook up listeners + UReanimator::LinkListenerToControls(this, this, GetPaneID()); + + CTabControl* tabControl = dynamic_cast(FindPaneByID(paneID_TabControl)); + if (tabControl) tabControl->AddListener(this); + + // Initialize dialog to display first tab (hack because the CTabControl has + // already broadcasted its value message but nobody was listening at that time) + long msg = resID_SubscribeTab1; + ListenToMessage(msg_TabSwitched, &msg); + + // "Search" and "New" are not supported for IMAP + if (MSG_IsIMAPHost(CNewsSubscriber::GetHost())) + { + CTabControl* tabControl = dynamic_cast(FindPaneByID(paneID_TabControl)); + Assert_(tabControl); + tabControl->SetTabEnable(resID_SubscribeTab2, false); + tabControl->SetTabEnable(resID_SubscribeTab3, false); + } +} + + +//---------------------------------------------------------------------------- +// DoClose +// +//---------------------------------------------------------------------------- + +void CSubscribeWindow::DoClose() +{ + // Update the Mail Folder popup menu + CMailFolderMixin::UpdateMailFolderMixins(); + + // Update the Message Center + CMailNewsFolderWindow* messageCenter = NULL; + CWindowIterator iter(WindowType_MailNews); + iter.Next(messageCenter); + if (messageCenter) + { + messageCenter->Refresh(); + } + + // Close window + Inherited::DoClose(); +} + + +//---------------------------------------------------------------------------- +// GetActiveTable +// +// Get the currently active table in the window. The active table is +// the table in the window that the user considers to be receiving input. +//---------------------------------------------------------------------------- + +CMailFlexTable* CSubscribeWindow::GetActiveTable() +{ + CMailFlexTable* list = dynamic_cast + (FindPaneByID(paneID_FolderList)); + Assert_(list); + return list; +} + + +//---------------------------------------------------------------------------- +// CalcStandardBoundsForScreen +// +// Zoom in the vertical direction only. +//---------------------------------------------------------------------------- + +void CSubscribeWindow::CalcStandardBoundsForScreen( + const Rect &inScreenBounds, + Rect &outStdBounds) const +{ + LWindow::CalcStandardBoundsForScreen(inScreenBounds, outStdBounds); + Rect contRect = UWindows::GetWindowContentRect(mMacWindowP); + + outStdBounds.left = contRect.left; + outStdBounds.right = contRect.right; +} + + +//---------------------------------------------------------------------------- +// CommandDelegatesToSubscribeList +// +//---------------------------------------------------------------------------- + +Boolean CSubscribeWindow::CommandDelegatesToSubscribeList(CommandT inCommand) +{ + switch(inCommand) + { + case cmd_Stop: + + case cmd_OpenNewsHost: + case cmd_NewsHostChanged: + + case cmd_NewsToggleSubscribe: + case cmd_NewsSetSubscribe: + case cmd_NewsClearSubscribe: + case cmd_NewsExpandGroup: + case cmd_NewsExpandAll: + case cmd_NewsCollapseGroup: + case cmd_NewsCollapseAll: + case cmd_NewsGetGroups: + case cmd_NewsSearch: + case cmd_NewsGetNew: + case cmd_NewsClearNew: + return true; + } + return false; +} + + +//---------------------------------------------------------------------------- +// FindCommandStatus +// +// Pass down commands to the list (ie. the CSubscribeView) when another +// object (ie. the edit field) is the target +//---------------------------------------------------------------------------- + +void CSubscribeWindow::FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +{ + if (mList == nil) + { + outEnabled = (inCommand == paneID_CancelButton); + } + else + { + if (CommandDelegatesToSubscribeList(inCommand)) + { + mList->FindCommandStatus(inCommand, outEnabled, outUsesMark, + outMark, outName); + } + else + { + CMediatedWindow::FindCommandStatus(inCommand, outEnabled, outUsesMark, + outMark, outName); + } + } +} + + +//---------------------------------------------------------------------------- +// ObeyCommand +// +//---------------------------------------------------------------------------- + +Boolean CSubscribeWindow::ObeyCommand( + CommandT inCommand, + void *ioParam) +{ +#pragma unused (ioParam) + if (inCommand == cmd_Stop) + { + if (mList) + { + // Re-enable main controls in case we were committing + mList->Enable(); + LButton * btn; + btn = (LButton *)FindPaneByID(paneID_CancelButton); + if (btn) btn->Enable(); + btn = (LButton *)FindPaneByID(paneID_OkButton); + if (btn) btn->Enable(); + } + } + return false; +} + + +//---------------------------------------------------------------------------- +// ListenToMessage +// +//---------------------------------------------------------------------------- + +void CSubscribeWindow::ListenToMessage(MessageT inMessage, void* ioParam) +{ +#pragma unused (ioParam) + switch (inMessage) + { + // Selecting another host + case cmd_NewsHostChanged: + if (mList) + { + mList->ObeyCommand(cmd_Stop, NULL); + + MSG_Host* newsHost = (MSG_Host*)(ioParam); + MSG_SubscribeMode subscribeMode = MSG_SubscribeGetMode(mList->GetMessagePane()); + CTabControl* tabControl = dynamic_cast(FindPaneByID(paneID_TabControl)); + Assert_(tabControl); + + if (MSG_IsIMAPHost(newsHost)) + { + // "Search" and "New" are not supported for IMAP... + tabControl->SetTabEnable(resID_SubscribeTab2, false); + tabControl->SetTabEnable(resID_SubscribeTab3, false); + // ... so switch back to "All" + if (tabControl->GetValue() != 1) + tabControl->SetValue(1); // will refresh the list too + else + mList->RefreshList(newsHost, subscribeMode); + } + else + { + tabControl->SetTabEnable(resID_SubscribeTab2, true); + tabControl->SetTabEnable(resID_SubscribeTab3, true); + mList->RefreshList(newsHost, subscribeMode); + } + } + break; + + // Switching tabs + case msg_TabSwitched: + long paneID = *(long*)ioParam; + switch (paneID) + { + case resID_SubscribeTab1: HandleAllGroupsTabActivate(); break; + case resID_SubscribeTab2: HandleSearchGroupsTabActivate(); break; + case resID_SubscribeTab3: HandleNewGroupsTabActivate(); break; + } + UReanimator::LinkListenerToControls(mList, this, paneID); + break; + + // Search: paneID_EditField or paneID_SearchButton + case msg_EditField2: + case cmd_NewsSearch: + HandleSearchInList(); + break; + + // Add Server button: paneID_AddServerButton + case cmd_OpenNewsHost: + if (CNewsSubscriber::DoAddNewsHost()) + { + CSubscribeHostPopup* popup = dynamic_cast + (FindPaneByID(paneID_ServerPopup)); + Assert_(popup); + if (popup) popup->ReloadMenu(); + } + break; + + // OK/Cancel buttons + case paneID_CancelButton: + if (mList) + mList->SubscribeCancel(); + else + DoClose(); + break; + + case paneID_OkButton: + if (mList) + { + mList->Disable(); + LButton * btn; + btn = (LButton *)FindPaneByID(paneID_CancelButton); + if (btn) btn->Disable(); + btn = (LButton *)FindPaneByID(paneID_OkButton); + if (btn) btn->Disable(); + + startAsyncCursors(); + mList->SubscribeCommit(); + } + else + DoClose(); + break; + } +} + + +//---------------------------------------------------------------------------- +// HandleKeyPress +// +// Handle Escape and Cmd-Period (copied from LDialogBox) +// Handle Enter and Return in the edit fields +//---------------------------------------------------------------------------- + +Boolean CSubscribeWindow::HandleKeyPress( + const EventRecord &inKeyEvent) +{ + Boolean keyHandled = false; + PaneIDT keyButtonID = 0; + + switch (inKeyEvent.message & charCodeMask) + { + case char_Enter: + case char_Return: + if (mList == nil) + return false; + switch (MSG_SubscribeGetMode(mList->GetMessagePane())) + { + case MSG_SubscribeAll: + mList->OpenSelection(); + keyHandled = true; + break; + + case MSG_SubscribeSearch: + keyButtonID = paneID_SearchButton; + break; + } + break; + + case char_Escape: + if ((inKeyEvent.message & keyCodeMask) == vkey_Escape) + keyButtonID = paneID_CancelButton; + break; + + default: + if (UKeyFilters::IsCmdPeriod(inKeyEvent)) + keyButtonID = paneID_CancelButton; + else + keyHandled = LWindow::HandleKeyPress(inKeyEvent); + break; + } + + if (keyButtonID != 0) + { + LControl* keyButton = (LControl*)FindPaneByID(keyButtonID); + keyButton->SimulateHotSpotClick(kControlButtonPart); + keyHandled = true; + } + + return keyHandled; +} + + +//---------------------------------------------------------------------------- +// HandleAllGroupsTabActivate +// +//---------------------------------------------------------------------------- + +void CSubscribeWindow::HandleAllGroupsTabActivate() +{ + if (! mList) + return; + + LControl* addServerBtn = dynamic_cast(FindPaneByID(paneID_AddServerButton)); + if (addServerBtn) addServerBtn->AddListener(this); + + CSubscribeHostPopup* popup = dynamic_cast(FindPaneByID(paneID_ServerPopup)); + if (popup) popup->AddListener(this); + + CLargeEditFieldBroadcast* groupEdit = dynamic_cast + (FindPaneByID(paneID_EditField)); + if (groupEdit) + { + groupEdit->AddListener(this); + + // put the list in my LTabGroup... + mList->SetSuperCommander(groupEdit->GetSuperCommander()); + + // ... but make me the target + SwitchTarget(groupEdit); + LCommander::SetUpdateCommandStatus(true); + } + + // refresh the list + mList->RefreshList(CNewsSubscriber::GetHost(), MSG_SubscribeAll); +} + + +//---------------------------------------------------------------------------- +// HandleSearchGroupsTabActivate +// +//---------------------------------------------------------------------------- + +void CSubscribeWindow::HandleSearchGroupsTabActivate() +{ + if (! mList) + return; + + LControl* searchBtn = dynamic_cast(FindPaneByID(paneID_SearchButton)); + if (searchBtn) searchBtn->AddListener(this); + + CSubscribeHostPopup* popup = dynamic_cast(FindPaneByID(paneID_ServerPopup)); + if (popup) popup->AddListener(this); + + LEditField* searchEdit = dynamic_cast(FindPaneByID(paneID_EditField)); + if (searchEdit) + { + // put the list in my LTabGroup... + mList->SetSuperCommander(searchEdit->GetSuperCommander()); + + // ... but make me the target + SwitchTarget(searchEdit); + LCommander::SetUpdateCommandStatus(true); + } + + LGAPushButton * defaultBtn = dynamic_cast(FindPaneByID(paneID_SearchButton)); + if (defaultBtn) defaultBtn->SetDefaultButton(true, true); + + // refresh the list + mList->RefreshList(CNewsSubscriber::GetHost(), MSG_SubscribeSearch); +} + + +//---------------------------------------------------------------------------- +// HandleNewGroupsTabActivate +// +//---------------------------------------------------------------------------- + +void CSubscribeWindow::HandleNewGroupsTabActivate() +{ + if (! mList) + return; + + CSubscribeHostPopup* popup = dynamic_cast(FindPaneByID(paneID_ServerPopup)); + if (popup) popup->AddListener(this); + + SwitchTarget(mList); + LCommander::SetUpdateCommandStatus(true); + + // refresh the list + mList->RefreshList(CNewsSubscriber::GetHost(), MSG_SubscribeNew); +} + + +//---------------------------------------------------------------------------- +// HandleSearchInList +// +//---------------------------------------------------------------------------- + +void CSubscribeWindow::HandleSearchInList() +{ + if ((! mList) || (! mList->GetMessagePane())) + return; + + Str255 editStr; + LEditField* editField = dynamic_cast(FindPaneByID(paneID_EditField)); + Assert_(editField); + if (editField) + { + editField->GetDescriptor(editStr); + mList->SearchForString(editStr); + } +} + + +#pragma mark - + + +//---------------------------------------------------------------------------- +// CSubscribeHostPopup +// +//---------------------------------------------------------------------------- + +void* CSubscribeHostPopup::sMercutioCallback = nil; + +static Int16 cIMAPHostIconID = 15226; +static Int16 cNewsHostIconID = 15227; + +//----------------------------------- +static pascal void SubscribeMercutioCallback( + Int16 menuID, + Int16 previousModifiers, + RichItemDataYadaYada& inItemData) +//----------------------------------- +{ +#pragma unused (menuID) +#pragma unused (previousModifiers) + RichItemData& itemData = (RichItemData&)inItemData; + switch (itemData.cbMsg) + { + case cbBasicDataOnlyMsg: + itemData.flags |= + (ksameAlternateAsLastTime|kIconIsSmall|kHasIcon/*|kChangedByCallback*/|kdontDisposeIcon); + itemData.flags &= ~(kIsDynamic); + return; + + case cbIconOnlyMsg: + break; // look out below! + + default: + return; + } + + MSG_Host * host = CNewsHostIterator::GetHostList()[itemData.itemID - 1]; + Int16 iconID = 0; + if (MSG_IsNewsHost(host)) + iconID = cNewsHostIconID; + else if (MSG_IsIMAPHost(host)) + iconID = cIMAPHostIconID; + + itemData.hIcon = CIconList::GetIconSuite(iconID); + itemData.iconType = 'suit'; +} + +//-------------------------------------- +CSubscribeHostPopup::CSubscribeHostPopup(LStream * inStream) + : LGAPopup(inStream), + mNewsHostIterator(nil) +{ +} + +//----------------------------------------- +CSubscribeHostPopup::~CSubscribeHostPopup() +{ + delete mNewsHostIterator; +} + +//------------------------------------------ +void CSubscribeHostPopup::FinishCreateSelf() +{ + LGAPopup::FinishCreateSelf(); // line order... + MenuHandle menuH = GetMacMenuH(); // ...does matter + if (MDEF_IsCustomMenu(menuH)) + { + if (!sMercutioCallback) + sMercutioCallback = NewMercutioCallback(SubscribeMercutioCallback); + FailNIL_(sMercutioCallback); + MDEF_SetCallbackProc(menuH, (MercutioCallbackUPP)sMercutioCallback); + + // Items with 'condense' style will be drawn by the Mercutio callback + MenuPrefsRec myPrefs; + ::memset(&myPrefs, 0, sizeof(myPrefs)); + myPrefs.useCallbackFlag.s = condense; + MDEF_SetMenuPrefs(menuH, &myPrefs); + } + ReloadMenu(); +} + +//------------------------------------ +void CSubscribeHostPopup::ReloadMenu() +{ + // empty menu + for (Int32 index = 0; index < GetMaxValue(); index ++) { + ::DeleteMenuItem(GetMacMenuH(), 1); + } + + // reload menu + delete mNewsHostIterator; + mNewsHostIterator = new CNewsHostIterator(CMailNewsContext::GetMailMaster()); + MSG_Host* outNewsHost; + MenuHandle menuH = GetMacMenuH(); + while (mNewsHostIterator->Next(outNewsHost)) + { + // Add the menu item + LStr255 pstr(MSG_GetHostUIName(outNewsHost)); + Int16 itemIndex = UMenuUtils::AppendMenuItem(menuH, pstr, true); + + // Ask for a callback so we can set the icon + if (MDEF_IsCustomMenu(menuH)) + ::SetItemStyle(menuH, itemIndex, condense); + } + + // select the default host + Int32 hostIndex; + Boolean found = mNewsHostIterator->FindIndex(CNewsSubscriber::GetHost(), hostIndex); + if (! found) + { + mNewsHostIterator->ResetTo(hostIndex = 0); + + MSG_Host* outNewsHost; + mNewsHostIterator->Current(outNewsHost); + CNewsSubscriber::SetHost(outNewsHost); + } + + SetMaxValue(mNewsHostIterator->GetCount()); + mValue = -1; // force BroadcastValueMessage(); + SetValue(hostIndex + 1); // iterator is 0-based, menu is 1-based +} + + +void CSubscribeHostPopup::BroadcastValueMessage() +{ + MSG_Host* outNewsHost; + mNewsHostIterator->ResetTo(mValue - 1); + mNewsHostIterator->Current(outNewsHost); + CNewsSubscriber::SetHost(outNewsHost); + + if (mValueMessage != msg_Nothing) { + BroadcastMessage(mValueMessage, (void*)outNewsHost); + } +} + diff --git a/mozilla/cmd/macfe/MailNews/CSubscribeWindow.h b/mozilla/cmd/macfe/MailNews/CSubscribeWindow.h new file mode 100644 index 00000000000..ca33c539ea5 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CSubscribeWindow.h @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CSubscribeWindow.h + +#pragma once + +// C/C++ headers +//#include + +// PowerPlant +#include +#include +#include +#include +#include +#include + +// Mail/News MacFE stuff +#include "CBrowserContext.h" +#include "CComposeAddressTableView.h" + +// UI elements +#include "CMailNewsWindow.h" +#include "CMailComposeWindow.h" +#include "CPatternBevelView.h" +#include "CTabSwitcher.h" +#include "UMailFolderMenus.h" + +// Netscape stuff +#include "msgcom.h" +#include "mattach.h" +//#include "MailNewsGUI.h" + + +class CMailComposeWindow; +class CProgressListener; +class CComposeSession; +class CSubscribeView; + + +//---------------------------------------------------------------------------- +// USubscribeUtilities +// +//---------------------------------------------------------------------------- +class USubscribeUtilities +{ +public: + static void RegisterSubscribeClasses(); +}; + + +//---------------------------------------------------------------------------- +// CNewsHostIterator +// +//---------------------------------------------------------------------------- + +class CNewsHostIterator +{ + public: + enum { kMaxNewsHosts = 32 }; + + CNewsHostIterator(MSG_Master* master); + Boolean Current(MSG_Host*& outNewsHost); + Boolean Next(MSG_Host*& outNewsHost); + + Boolean FindIndex(const MSG_Host* inNewsHost, Int32& outIndex); + + void ResetTo(Int32 current) {mIndex = current;}; + Int32 GetCurrentIndex() {return mIndex;}; + static Int32 GetCount() {return mHostCount;}; + static MSG_Host** GetHostList() {return mHostList;}; + + protected: + static MSG_Host* mHostList[kMaxNewsHosts]; + static Int32 mHostCount; + Int32 mIndex; +}; + + +//---------------------------------------------------------------------------- +// CSubscribeHostPopup +// +//---------------------------------------------------------------------------- + +class CSubscribeHostPopup : public LGAPopup +{ +public: + enum { class_ID = 'SHpp' }; + + CSubscribeHostPopup(LStream* inStream); + virtual ~CSubscribeHostPopup(); + + virtual void FinishCreateSelf(); + virtual void ReloadMenu(); + +protected: + virtual void BroadcastValueMessage(); + +protected: + CNewsHostIterator* mNewsHostIterator; + static void* sMercutioCallback; +}; + + +//---------------------------------------------------------------------------- +// CSubscribeWindow +// +//---------------------------------------------------------------------------- + +class CSubscribeWindow : public CMailNewsWindow, + public LBroadcaster, + public LListener +{ +private: + typedef CMailNewsWindow Inherited; // trick suggested by the ANSI committee. + +public: + enum { class_ID = 'Subs', res_ID = 14006 }; + +protected: + virtual ResIDT GetStatusResID(void) const { return res_ID; } + virtual UInt16 GetValidStatusVersion(void) const { return 0x0112; } + +public: + static CSubscribeWindow* FindAndShow(Boolean makeNew); + static Boolean DisplayDialog(); + + CSubscribeWindow(LStream* inStream); + virtual ~CSubscribeWindow(); + + virtual void FinishCreateSelf(); + virtual void DoClose(); + + CMailFlexTable* GetActiveTable(); + + virtual void CalcStandardBoundsForScreen( + const Rect &inScreenBounds, + Rect &outStdBounds) const; + + virtual void ListenToMessage(MessageT inMessage, void* ioParam); + + virtual void FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName); + + virtual Boolean ObeyCommand( + CommandT inCommand, + void *ioParam); + + virtual Boolean HandleKeyPress(const EventRecord &inKeyEvent); + +protected: + Boolean CommandDelegatesToSubscribeList(CommandT inCommand); + + void HandleAllGroupsTabActivate(); + void HandleSearchGroupsTabActivate(); + void HandleNewGroupsTabActivate(); + void HandleSearchInList(); + + CSubscribeView* mList; +}; diff --git a/mozilla/cmd/macfe/MailNews/CThreadMessageController.cp b/mozilla/cmd/macfe/MailNews/CThreadMessageController.cp new file mode 100644 index 00000000000..ca7a9a87e4f --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CThreadMessageController.cp @@ -0,0 +1,657 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CThreadMessageController.cp + +#include "CThreadMessageController.h" + +#include "CMessageView.h" +#include "CMailNewsWindow.h" +#include "CThreadView.h" +#include "CBrowserContext.h" +#include "CProgressListener.h" +#include "UMailSelection.h" + +#include "macutil.h" +#include "secnav.h" +#include "prefapi.h" + +// Command numbers +#include "resgui.h" +#include "MailNewsgroupWindow_Defines.h" + +const Int16 kBottomBevelHeight = 0; + +#define THREE_PANE 1 + +#include "CKeyStealingAttachment.h" + +#pragma mark - + +//----------------------------------- +void CThreadWindowExpansionData::ReadStatus(LStream* inStream) +//----------------------------------- +{ + if (!inStream) return; +// *inStream >> mHeight >> mExpandoHeight; +} // CThreadWindowExpansionData::ReadStatus + +//----------------------------------- +void CThreadWindowExpansionData::WriteStatus(LStream* inStream) +//----------------------------------- +{ + if (!inStream) return; +// *inStream << mHeight << mExpandoHeight; +} // CThreadWindowExpansionData::WriteStatus + +#pragma mark - + +//----------------------------------- +CThreadMessageController::CThreadMessageController( + CExpandoDivider* inExpandoDivider +, CThreadView* inThreadView +, CMessageView* inMessageView +, CMessageAttachmentView* inAttachmentView +#if !ONECONTEXTPERWINDOW +, LListener** inMessageContextListeners + // Null-terminated list of pointers, owned by + // this controller henceforth. +#endif +) +//----------------------------------- +: CExpandoListener(&mClosedData, &mOpenData) +, mExpandoDivider(inExpandoDivider) +, mThreadView(inThreadView) +, mMessageView(inMessageView) +, mAttachmentView(inAttachmentView) +, mMessageContext(nil) +, mStoreStatusEnabled(true) +, mThreadKeyStealingAttachment(nil) +#if !ONECONTEXTPERWINDOW +, mMessageContextListeners(inMessageContextListeners) +#endif +{ + Assert_(mExpandoDivider); + Assert_(mThreadView); + Assert_(mMessageView); +} // CThreadMessageController::CThreadMessageController + +//----------------------------------- +CThreadMessageController::~CThreadMessageController() +//----------------------------------- +{ +#if !ONECONTEXTPERWINDOW + delete [] mMessageContextListeners; +#endif +} // CThreadMessageController::~CThreadMessageController + +//----------------------------------- +void CThreadMessageController::InitializeDimensions() +//----------------------------------- +{ + // In case this is the first instantiation, set up the defaults + StoreCurrentDimensions(); // get the closed state from PPOb + // Set the default open state depending on the initial closed state + *(CThreadWindowExpansionData*)mStates[open_state] + = *(CThreadWindowExpansionData*)mStates[closed_state]; +// ExpandoHeight(open_state) -= kBottomBevelHeight; +} // CThreadMessageController::InitializeDimensions + +//----------------------------------- +void CThreadMessageController::FinishCreateSelf() +//----------------------------------- +{ + mMessageView->SetMasterCommander(mThreadView); + LWindow* win = FindOwningWindow(); + LCaption* statisticsCaption = (LCaption*)win->FindPaneByID('Tstt'); + // Initialize the format string. + if (statisticsCaption) + { + statisticsCaption->GetDescriptor(mThreadStatisticsFormat); + statisticsCaption->Hide(); + } +} // CThreadMessageController::FinishCreateSelf + +//----------------------------------- +void CThreadMessageController::ReadStatus(LStream *inStatusData) +//----------------------------------- +{ + mExpandoDivider->ReadStatus(inStatusData); + CExpandable::ReadStatus(inStatusData); +} // CThreadMessageController::ReadWindowStatus + +//----------------------------------- +void CThreadMessageController::WriteStatus(LStream *inStatusData) +//----------------------------------- +{ + mExpandoDivider->WriteStatus(inStatusData); + CExpandable::WriteStatus(inStatusData); +} // CThreadMessageController::WriteWindowStatus + +//----------------------------------- +LWindow* CThreadMessageController::FindOwningWindow() const +//----------------------------------- +{ + return LWindow::FetchWindowObject(mMessageView->GetMacPort()); +} + +//----------------------------------- +void CThreadMessageController::SetExpandState(ExpandStateT inExpand) +//----------------------------------- +{ + if (!mExpandoDivider) return; + if (mStoreStatusEnabled) // don't do this during the reading-in phase + StoreCurrentDimensions(); + mExpandState = inExpand; + Rect myRect; + mExpandoDivider->CalcPortFrameRect(myRect); // relative is fine + ::EraseRect(&myRect); + LWindow* win = FindOwningWindow(); + win->Refresh(); + if (inExpand) + { + // Ordering is important! If we resize the expando pane + // when in the closed position, we change the first frame size. So + // always do the "recall" step when the expando pane has the "open" + // bindings. + mExpandoDivider->SetExpandState(inExpand); // set expando properties + RecallCurrentDimensions(); // resize myself and the expando pane + } + else + { + // Ordering is important! + RecallCurrentDimensions(); // resize myself and the expando pane + mExpandoDivider->SetExpandState(inExpand); + } + InstallMessagePane(inExpand); + + if (inExpand) + { + // Since "expanding" means that the threadview gets smaller, it often happens + // that the user's selection is out of the frame after "expansion". + mThreadView->ScrollSelectionIntoFrame(); + } + + // Set the fancy double-click behavior, which is only needed for the expanded state + mThreadView->SetFancyDoubleClick(inExpand); + + // Set the "select after deletion" behavior of the thread view. The correct macui + // behavior is NOT to do this. But when the thread view is controlling the message + // view, it makes sense to go along with the "Netscape" behavior, ie the wintel + // behavior. + Boolean doSelect = inExpand; // (As I just said) + // ... but some big customer may want the Macintosh behavior in both cases, so check + // for the special preference. + XP_Bool noNextRowSelection; + if (PREF_GetBoolPref("mail.no_select_after_delete", &noNextRowSelection) == PREF_NOERROR + && noNextRowSelection) + doSelect = false; // no matter what. + mThreadView->SetSelectAfterDelete(doSelect); + // The kludge to fix the bottom of the message view has moved to CExpandoDivider. +} // CThreadMessageController::SetExpandState + +//----------------------------------- +void CThreadMessageController::StoreDimensions(CExpansionData&) +//----------------------------------- +{ +} // CThreadMessageController::StoreDimensions() + +//----------------------------------- +void CThreadMessageController::RecallDimensions(const CExpansionData&) +//----------------------------------- +{ +} // CThreadMessageController::RecallDimensions() + +//----------------------------------- +cstring CThreadMessageController::GetCurrentURL() const +//----------------------------------- +{ + if (mMessageContext) + return mMessageContext->GetCurrentURL(); + else + return cstring(""); +} + +//----------------------------------- +void CThreadMessageController::InstallMessagePane( + Boolean inInstall) +//----------------------------------- +{ + // Disable the message view to prevent a crash in layout code, + // called from CHTMLView::AdjustCursorSelf() + Assert_(mMessageView); + mMessageView->Disable(); + OSErr err = noErr; + CMailNewsWindow* win = dynamic_cast(FindOwningWindow()); + ThrowIfNil_(win); +#ifndef THREE_PANE + SwitchTarget(nil); // necessary paranoia if closing window. + // Do before changing the supercommander chain, so that powerplant will correctly + // take the current chain off duty. +#endif + if (inInstall) + { + Try_ + { +#if ONECONTEXTPERWINDOW + mMessageContext = dynamic_cast(win->GetWindowContext()); + ThrowIfNil_(mMessageContext); + ((MWContext*)*mMessageContext)->type = MWContextMailMsg; +#else + mMessageContext = new CBrowserContext(MWContextMailMsg); +#endif + StSharer theShareLock(mMessageContext); + mMessageContext->AddUser(this); + //mMessageContext->AddListener(this); + mMessageView->SetContext(mMessageContext); + // This call links up the model object hierarchy for any potential + // sub model that gets created within the scope of the html view. + if (win) + mMessageView->SetFormElemBaseModel(win); // ? Probably wrong. + mThreadView->AddListener((CExpandoListener*)this); // for msg_SelectionChanged + // When the message view is installed, we still want the thread view to + // have first crack at all the commands, and then to pass to message view if + // not handled. So we insert the message view between us and the thread view, + // and set the target to the thread view. +#ifndef THREE_PANE + mThreadView->SetSuperCommander(mMessageView); + mMessageView->SetSuperCommander(this); +#endif + CNSContext* context = mThreadView->GetContext(); + if (context) + mMessageView->SetDefaultCSID(context->GetDefaultCSID()); + // Add the attachment that will divert certain key events to the message pane. + // This is added first so that it is executed first, and can disable normal + // response to keypresses. + mThreadKeyStealingAttachment = new CKeyStealingAttachment(mMessageView); + mThreadView->AddAttachmentFirst(mThreadKeyStealingAttachment); +#ifndef THREE_PANE + mThreadKeyStealingAttachment->StealKey(char_Home); + mThreadKeyStealingAttachment->StealKey(char_End); + mThreadKeyStealingAttachment->StealKey(char_PageUp); + mThreadKeyStealingAttachment->StealKey(char_PageDown); +#endif // THREE_PANE + mThreadKeyStealingAttachment->StealKey(' '); + + // Tell message view about its attachment pane and hide it + if (mAttachmentView) + { + mMessageView->SetAttachmentView(mAttachmentView); + //mAttachmentView->Remove(); + } + // Tell the thread view its selection changed, so it will broadcast. + // If a message was selected, this will cause it to display in the message pane. + mThreadView->SelectionChanged(); + mMessageView->Enable(); +#if !ONECONTEXTPERWINDOW + LListener** nextListener = mMessageContextListeners; + while (*nextListener) + mMessageContext->AddListener(*nextListener++); +#endif + mSecurityListener.SetMessageView( mMessageView ); + mMessageContext->AddListener( &mSecurityListener); + } + Catch_(localErr) + { + inInstall = false; + } + EndCatch_ + } + if (!inInstall) // called with de-install, or threw above... + { + if (mThreadView) + { + mThreadView->RemoveListener((CMailCallbackListener*)mMessageView); // for msg_SelectionChanged + // While the message view is installed, it is inserted in the command chain + // between this and the message view. +#ifndef THREE_PANE + mThreadView->SetSuperCommander(this); +#endif + // If mThreadView is nil, we have deleted it and the attachment will be deleted, too. + delete mThreadKeyStealingAttachment; //... which will remove itself as an attachment + mThreadKeyStealingAttachment = nil; + } + // If a message is loading when we de-install the pane we want to stop the load + if (mMessageContext) + XP_InterruptContext(*mMessageContext); + + if (mMessageView) + { + mMessageView->SetFormElemBaseModel(nil); // ? Probably wrong. + mMessageView->SetContext(nil); + } + if (mMessageContext) + { +#if !ONECONTEXTPERWINDOW + LListener** nextListener = mMessageContextListeners; + while (*nextListener) + mMessageContext->RemoveListener(*nextListener++); +#endif + mSecurityListener.SetMessageView( NULL ); + mMessageContext->RemoveListener( &mSecurityListener); + USecurityIconHelpers::SetNoMessageLoadedSecurityState( win ); + + ((MWContext*)*mMessageContext)->type = MWContextMail; // see hack in mimemoz.c + mMessageContext->RemoveUser(this); // and delete. + mMessageContext = nil; + } + } +#ifndef THREE_PANE + if (mThreadView) + SwitchTarget(mThreadView); +#endif +} // CThreadMessageController::InstallMessagePane + +//----------------------------------- +void CThreadMessageController::ListenToMessage(MessageT inMessage, void *ioParam) +//----------------------------------- +{ + switch (inMessage) + { + case CStandardFlexTable::msg_SelectionChanged: + if (!mThreadView) + return; + LWindow* win = FindOwningWindow(); + USecurityIconHelpers::SetNoMessageLoadedSecurityState(win); + if (GetExpandState() == open_state) + { + if (mMessageView) + { + CMailSelection selection; + mThreadView->GetSelection(selection); + MSG_FolderInfo* folder = mThreadView->GetOwningFolder(); + if (folder && selection.selectionSize == 1) + { + LCommander::SetUpdateCommandStatus(true); + CMessage message(*selection.GetSelectionList() + 1, selection.xpPane); + // (add one to convert MSG_ViewIndex to TableIndexT) + MessageKey id = message.GetMessageKey(); + mMessageView->ShowMessage( + CMailNewsContext::GetMailMaster(), + folder, + id, + false); // load on next idle. + + if (MSG_GetBacktrackState(mThreadView->GetMessagePane()) == MSG_BacktrackIdle) + MSG_AddBacktrackMessage(mThreadView->GetMessagePane(), folder, id); + else + MSG_SetBacktrackState(mThreadView->GetMessagePane(), MSG_BacktrackIdle); + } + else + { + // blank out the message view. + mMessageView-> + ShowMessage( + CMailNewsContext::GetMailMaster(), + folder, MSG_MESSAGEKEYNONE, false); // But on next idle! #107826 + } + } + } +#if ONECONTEXTPERWINDOW + else +#endif // with multiple contexts, always do this. If shared, only when message pane not there. + mThreadView->UpdateHistoryEntry(); + break; + case CMailCallbackManager::msg_ChangeStarting: + case CMailCallbackManager::msg_ChangeFinished: + case CMailCallbackManager::msg_PaneChanged: + if (mThreadView != nil) + { + // Set the pane in order to receive XP messages. + // Note: SetPane() can't be called in FinishCreateSelf(), instead we + // have to wait until we are notified that a folder is first loaded into + // the mThreadView. Well, we just chose to call it on every notification... + CMailCallbackListener::SetPane(mThreadView->GetMessagePane()); + if (IsMyPane(ioParam)) + { + // Check if the notification is about _my_ folder + SPaneChangeInfo * callbackInfo = reinterpret_cast(ioParam); + MSG_FolderInfo * folderInfo = reinterpret_cast(callbackInfo->value); + if (mThreadView->GetOwningFolder() == folderInfo) + CMailCallbackListener::ListenToMessage(inMessage, ioParam); + } + } + break; + + default: + CExpandoListener::ListenToMessage(inMessage, ioParam); + break; + } +} // CThreadMessageController::ListenToMessage + +//----------------------------------- +void CThreadMessageController::ChangeStarting( + MSG_Pane* , + MSG_NOTIFY_CODE , + TableIndexT , + SInt32 /*inRowCount*/) +//----------------------------------- +{ +} + +//----------------------------------- +void CThreadMessageController::ChangeFinished( + MSG_Pane* , + MSG_NOTIFY_CODE , + TableIndexT , + SInt32 /*inRowCount*/) +//----------------------------------- +{ +} + +//----------------------------------- +void CThreadMessageController::PaneChanged( + MSG_Pane* , + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 /*value*/) +//----------------------------------- +{ + switch (inNotifyCode) + { + case MSG_PaneNotifyMessageLoaded: + case MSG_PaneNotifyMessageDeleted: + case MSG_PaneNotifyLastMessageDeleted: + // nothing to do for now + break; + case MSG_PaneNotifyFolderDeleted: +#if !THREE_PANE + LWindow* win = FindOwningWindow(); + win->AttemptClose(); +#endif // THREE_PANE + return; // duh! + } + if (inNotifyCode != MSG_PaneNotifyMessageDeleted) + { + // Refresh the statistics on the location bar. + LWindow* win = FindOwningWindow(); + if (win) + { + LCaption* statisticsCaption = (LCaption*)win->FindPaneByID('Tstt'); + if (statisticsCaption && mThreadView) + { + CStr255 tempString = mThreadStatisticsFormat; + CMessageFolder folder(mThreadView->GetOwningFolder()); + SInt32 total = folder.CountMessages(), unread = folder.CountUnseen(); + if (total >= 0 && unread >= 0) + ::StringParamText(tempString, total, unread, 0, 0); + else + ::GetIndString(tempString, 7099, 8); // " Message counts unavailable" + statisticsCaption->SetDescriptor(tempString); + statisticsCaption->Show(); + } + } + } +} // CThreadMessageController::PaneChanged + +//----------------------------------- +Boolean CThreadMessageController::CommandDelegatesToMessagePane(CommandT inCommand) +//----------------------------------- +{ + Boolean isOneLineSelected = mThreadView && mThreadView->GetSelectedRowCount() == 1; + if (GetExpandState() == open_state && isOneLineSelected) + switch (inCommand) + { + case cmd_Copy: + case cmd_Print: + case cmd_PrintOne: + case cmd_SecurityInfo: + case cmd_MarkReadByDate: + case cmd_Stop: + case cmd_LoadImages: + case cmd_Reload: + case cmd_ViewSource: + case cmd_DocumentInfo: + case cmd_WrapLongLines: + case cmd_Find: + case cmd_FindAgain: + return true; + + // Plus Commands msglib can tell us about. + case cmd_Back: + case cmd_GoForward: + case cmd_NextMessage: + case cmd_NextUnreadMessage: + case cmd_PreviousMessage: + case cmd_PreviousUnreadMessage: + + case cmd_NextUnreadThread: + case cmd_NextUnreadGroup: + case cmd_NextFolder: + case cmd_FirstFlagged: + case cmd_PreviousFlagged: + case cmd_NextFlagged: + + case cmd_GetMoreMessages: + case cmd_Clear: + case cmd_ReplyToSender: + case cmd_PostReply: + case cmd_PostAndMailReply: + case cmd_PostNew: + case cmd_ReplyToAll: + case cmd_ForwardMessage: + case cmd_ForwardMessageQuoted: + + case cmd_ShowMicroMessageHeaders: + case cmd_ShowSomeMessageHeaders: + case cmd_ShowAllMessageHeaders: + + case cmd_AttachmentsAsLinks: + case cmd_AttachmentsInline: + return true; + } + return false; +} // CThreadMessageController::CommandDelegatesToMessagePane + +//----------------------------------- +void CThreadMessageController::FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +// The message pane, if it exists, is never the target (the thread pane is). +// Therefore, some commands must be explicitly passed down to the message pane +// by this, the window object. +//----------------------------------- +{ +#if 0 + switch (inCommand) + { + case cmd_SelectSelection: + outEnabled = true; + return; + } +#endif // 0 + if (CommandDelegatesToMessagePane(inCommand)) + { + if (mMessageView) + { + // Set the super commander of the message view to nil, because otherwise + // when it calls the inherited method, we'll get called here again, with + // infinite recursion! + LCommander* savedCommander = mMessageView->GetSuperCommander(); + mMessageView->SetSuperCommander(nil); + mMessageView->FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); + mMessageView->SetSuperCommander(savedCommander); + } + return; + } + LCommander::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); +} // CThreadMessageController::FindCommandStatus + +//----------------------------------- +Boolean CThreadMessageController::ObeyCommand( + CommandT inCommand, + void *ioParam) +//----------------------------------- +{ +#if 0 + switch (inCommand) + { + case cmd_RelocateViewToFolder: + // Command was not (could not be) handled by the ThreadView, + // so we leave the window as it is and just update the Location popup. + UpdateFilePopupCurrentItem(); + return true; + + case cmd_SelectSelection: + // This command comes from the context menu, as the default in the thread view. + LControl* twistie = dynamic_cast(FindPaneByID(kTwistieID)); + if (twistie) + twistie->SetValue(true); + // and nature will take its course... + return true; + } +#endif // 0 + if (CommandDelegatesToMessagePane(inCommand)) + { + if (mMessageView) + { + // Set the super commander of the message view to nil, because otherwise + // when it calls the inherited method, we'll get called here again, with + // infinite recursion! + LCommander* savedCommander = mMessageView->GetSuperCommander(); + mMessageView->SetSuperCommander(nil); + LCommander* savedMasterCommander = mMessageView->GetMasterCommander(); + mMessageView->SetMasterCommander(nil); + Boolean result = mMessageView->ObeyCommand(inCommand, ioParam); + if (result) + { + // This doesn't work, because the load does not complete. + STableCell cell(mMessageView->GetCurMessageViewIndex() + 1,1); + mThreadView->SetNotifyOnSelectionChange(false); + mThreadView->UnselectAllCells(); + mThreadView->SetNotifyOnSelectionChange(true); + mThreadView->SelectCell(cell); + } + mMessageView->SetMasterCommander(savedMasterCommander); + mMessageView->SetSuperCommander(savedCommander); + return result; + } + } + if ( inCommand == cmd_SecurityInfo ) + { +// MWContext* context = *GetWindowContext(); + MWContext* context = *mThreadView->GetContext(); + SECNAV_SecurityAdvisor(context, NULL ); + return true; + } + return LCommander::ObeyCommand(inCommand, ioParam); +} // CThreadMessageController::ObeyCommand diff --git a/mozilla/cmd/macfe/MailNews/CThreadMessageController.h b/mozilla/cmd/macfe/MailNews/CThreadMessageController.h new file mode 100644 index 00000000000..be11d582c11 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CThreadMessageController.h @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CThreadMessageController.h + +#pragma once + +#include "CExpandoDivider.h" +#include "MailNewsCallbacks.h" +#include "CSecurityButton.h" +#include "cstring.h" + +class CThreadView; +class CMessageView; +class CMessageAttachmentView; +class CExpandoDivider; +class CSpinningN; +class CBrowserContext; +class CProgressListener; +class CKeyStealingAttachment; + +//====================================== +class CThreadWindowExpansionData : public CExpansionData +//====================================== +{ +public: + virtual void ReadStatus(LStream* inStream); + virtual void WriteStatus(LStream* inStream); +}; // class CThreadWindowExpansionData + +//====================================== +class CThreadMessageController + : public CExpandoListener + , public CMailCallbackListener + , public LCommander +// This manages the thread and message panes in the thread window. In 4.0x, this +// was all handled by CThreadWindow. This code moved here on 97/09/30. +//====================================== +{ +public: +// Construction/Destruction + CThreadMessageController( + CExpandoDivider* inExpandoDivider + , CThreadView* inThreadView + , CMessageView* inMessageView + , CMessageAttachmentView* inAttachmentView + #if !ONECONTEXTPERWINDOW + , LListener** inMessageContextListeners + // Null-terminated list of pointers, owned by + // this controller henceforth. + #endif + ); + virtual ~CThreadMessageController(); + + void ReadStatus(LStream *inStatusData); + void WriteStatus(LStream *inStatusData); + void InitializeDimensions(); + void FinishCreateSelf(); + void InstallMessagePane( + Boolean inInstall); + LWindow* FindOwningWindow() const; + +// Accessors/Mutators + CMessageView* GetMessageView() const { return mMessageView; } + cstring GetCurrentURL() const; + void SetStoreStatusEnabled(Boolean inEnabled) + { mStoreStatusEnabled = inEnabled; } + void SetThreadView(CThreadView* inThreadView) + { mThreadView = inThreadView; } + +// CMailCallbackListener overrides: +protected: + virtual void ChangeStarting( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount); + virtual void ChangeFinished( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount); + virtual void PaneChanged( + MSG_Pane* inPane, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value); + +// LCommander Overrides +protected: + virtual void FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName); + virtual Boolean ObeyCommand( + CommandT inCommand, + void *ioParam); +// command helpers +protected: + Boolean CommandDelegatesToMessagePane(CommandT inCommand); + +// CExpandoListener overrides: +protected: + virtual void ListenToMessage(MessageT inMessage, void *ioParam); + +// CExpandable overrides: +protected: + virtual void SetExpandState(ExpandStateT inExpanded); + virtual void StoreDimensions(CExpansionData&); + virtual void RecallDimensions(const CExpansionData&); + +// Data +protected: + CExpandoDivider* mExpandoDivider; + CThreadView* mThreadView; + CMessageView* mMessageView; + CMessageAttachmentView* mAttachmentView; +#if !ONECONTEXTPERWINDOW + LListener** mMessageContextListeners; +#endif + CThreadWindowExpansionData mClosedData, mOpenData; + Boolean mStoreStatusEnabled; + CBrowserContext* mMessageContext; + CMailSecurityListener mSecurityListener; + CKeyStealingAttachment* mThreadKeyStealingAttachment; // installed when window expanded. + Str255 mThreadStatisticsFormat; // format string +}; // class CThreadMessageController diff --git a/mozilla/cmd/macfe/MailNews/CThreadView.cp b/mozilla/cmd/macfe/MailNews/CThreadView.cp new file mode 100644 index 00000000000..53db865296b --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CThreadView.cp @@ -0,0 +1,3261 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +// Created 3/25/96 - Tim Craycroft + +#include "CThreadView.h" + +// App specific +#include "resgui.h" +#include "macutil.h" +#include "Netscape_Constants.h" +#include "CBrowserContext.h" + +#include "libi18n.h" + +// Mail/News Specific +#include "CNewsSubscriber.h" +#include "Netscape_Constants.h" +#include "CMailNewsContext.h" +#include "CMessageWindow.h" +#include "CMessageView.h" +#include "MailNewsgroupWindow_Defines.h" +#include "LTableViewHeader.h" +#include "CThreadWindow.h" +#include "UMailFolderMenus.h" +#include "CMailFolderButtonPopup.h" +#include "UMessageLibrary.h" +#include "CMailProgressWindow.h" +#include "CProgressListener.h" +#include "UMailSelection.h" + +// utility +#include "UPropFontSwitcher.h" +#include "UUTF8TextHandler.h" +#include "SearchHelpers.h" +#include "CDrawingState.h" +#include "URobustCreateWindow.h" +#include "CApplicationEventAttachment.h" +// remove next line when we move the PlaceUTF8TextInRect to utility classes +#include "UGraphicGizmos.h" +#include "CTargetedUpdateMenuRegistry.h" +#include "CProxyDragTask.h" +#include "UNewFolderDialog.h" +#include "UDeferredTask.h" + +// XP +#include "ntypes.h" +#include "msg_srch.h" // for MSG_GetPriorityName, MSG_GetStatusName + +// application +#include "uapp.h" +#include "prefapi.h" +#include "secnav.h" +#include "shist.h" + +// PowerPlant +#include +#include +#include +#include +#include +#include + +// Mac +#include + +// ANSI +#include +#include + +#define kPriorityMenuID 10528 + +enum { + kThreadedIcon = 10501, + kUnthreadedIcon = 10502 +}; + +static const UInt16 kMaxSubjectLength = 256; + +#pragma mark - + +MSG_MessageLine CMessage::sMessageLineData; +MSG_Pane* CMessage::sCacheMessageList = NULL; +MSG_ViewIndex CMessage::sCacheIndex = 0; + +//---------------------------------------------------------------------------------------- +CMessage::CMessage(MSG_ViewIndex inIndex, MSG_Pane* inMessageList) +//---------------------------------------------------------------------------------------- +: mIndex(inIndex-1) +, mMessageList(inMessageList) +{ +} + +//---------------------------------------------------------------------------------------- +CMessage::~CMessage() +//---------------------------------------------------------------------------------------- +{ +} + +//---------------------------------------------------------------------------------------- +Boolean CMessage::UpdateMessageCache() const +//---------------------------------------------------------------------------------------- +{ + if (mMessageList && (mIndex != sCacheIndex || mMessageList != sCacheMessageList)) + { + if (!::MSG_GetThreadLineByIndex(mMessageList, mIndex, 1, &sMessageLineData)) + { + InvalidateCache(); + return false; + } + sCacheIndex = mIndex; + sCacheMessageList = mMessageList; + } + return true; +} // CMessage::UpdateMessageCache + +//---------------------------------------------------------------------------------------- +void CMessage::InvalidateCache() +//---------------------------------------------------------------------------------------- +{ + sCacheMessageList = NULL; +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessage::TestXPFlag(UInt32 inMask) const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + return ((sMessageLineData.flags & inMask) != 0); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessage::IsOffline() const // db has offline news article +//---------------------------------------------------------------------------------------- +{ + return TestXPFlag(MSG_FLAG_OFFLINE); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessage::IsDeleted() const +//---------------------------------------------------------------------------------------- +{ + return TestXPFlag(MSG_FLAG_IMAP_DELETED); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessage::IsTemplate() const +//---------------------------------------------------------------------------------------- +{ + return TestXPFlag(MSG_FLAG_TEMPLATE); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessage::HasBeenRead() const +//---------------------------------------------------------------------------------------- +{ + return TestXPFlag(MSG_FLAG_READ); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessage::IsFlagged() const +//---------------------------------------------------------------------------------------- +{ + return TestXPFlag(MSG_FLAG_MARKED); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessage::IsThread() const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + return (sMessageLineData.numChildren > 0); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessage::HasAttachments() const +//---------------------------------------------------------------------------------------- +{ + return TestXPFlag(MSG_FLAG_ATTACHMENT); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessage::IsOpenThread() const +//---------------------------------------------------------------------------------------- +{ + Assert_(IsThread()); + return !TestXPFlag(MSG_FLAG_ELIDED); +} + +//---------------------------------------------------------------------------------------- +inline Boolean CMessage::HasBeenRepliedTo() const +//---------------------------------------------------------------------------------------- +{ + return TestXPFlag(MSG_FLAG_REPLIED); +} + +//---------------------------------------------------------------------------------------- +inline MessageId CMessage::GetThreadID() const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + return sMessageLineData.threadId; +} + +//---------------------------------------------------------------------------------------- +MessageKey CMessage::GetMessageKey() const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + return sMessageLineData.messageKey; +} + +//---------------------------------------------------------------------------------------- +uint16 CMessage::GetNumChildren() const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + return sMessageLineData.numChildren; +} + +//---------------------------------------------------------------------------------------- +uint16 CMessage::GetNumNewChildren() const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + return sMessageLineData.numNewChildren; +} + +//---------------------------------------------------------------------------------------- +/* static */ const char* CMessage::GetSubject( + MSG_MessageLine* data, + char* buffer, + UInt16 bufSize) +//---------------------------------------------------------------------------------------- +{ + *buffer = 0; + if ((data->flags & MSG_FLAG_HAS_RE) != 0) + { + const CString** sh = (const CString**)GetString(REPLY_FORM_RESID); + if (sh) + { + Assert_(bufSize > (**sh).Length()); + XP_SAFE_SPRINTF(buffer, bufSize - 1, (const char*)(**sh), data->subject); + ::ReleaseResource((Handle)sh); + return buffer; + } + } + return XP_STRNCPY_SAFE(buffer, data->subject, bufSize); +} + +//---------------------------------------------------------------------------------------- +const char* CMessage::GetSubject(char* buffer, UInt16 bufSize) const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + return GetSubject(&sMessageLineData, buffer, bufSize); +} + +//---------------------------------------------------------------------------------------- +const char* CMessage::GetSubject() const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + return sMessageLineData.subject; +} + +//---------------------------------------------------------------------------------------- +inline MSG_PRIORITY CMessage::GetPriority() const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + return sMessageLineData.priority; +} + +//---------------------------------------------------------------------------------------- +void CMessage::GetPriorityColor(MSG_PRIORITY priority, RGBColor& priorityColor) /* static */ +//---------------------------------------------------------------------------------------- +{ + // Initialize to black + memset(&priorityColor, 0, sizeof(RGBColor)); + switch (priority) + { + case MSG_LowestPriority: // green + priorityColor.green = 0x7FFF; + break; + case MSG_LowPriority: // blue + priorityColor.blue = 0xFFFF; + break; + case MSG_HighPriority: // purple + priorityColor.red = 0x7FFF; + priorityColor.blue = 0x7FFF; + break; + case MSG_HighestPriority: // red + priorityColor.red = 0xFFFF; + break; + case MSG_NormalPriority: + case MSG_PriorityNotSet: + case MSG_NoPriority: + default: + break; + } +} // CMessage::GetPriorityColor + +//---------------------------------------------------------------------------------------- +void CMessage::GetPriorityColor(RGBColor& priorityColor) const +//---------------------------------------------------------------------------------------- +{ + // Don't draw deleted messages in color. + MSG_PRIORITY priority = IsDeleted() ? MSG_NormalPriority : GetPriority(); + GetPriorityColor(priority, priorityColor); +} // CMessage::GetPriorityColor + +//---------------------------------------------------------------------------------------- +inline Int16 CMessage::PriorityToMenuItem(MSG_PRIORITY inPriority) /* static */ +//---------------------------------------------------------------------------------------- +{ + return (inPriority > MSG_NoPriority) ? inPriority - 1 : 3; +} + +//---------------------------------------------------------------------------------------- +inline MSG_PRIORITY CMessage::MenuItemToPriority(Int16 inMenuItem) /* static */ +//---------------------------------------------------------------------------------------- +{ + return (MSG_PRIORITY)(inMenuItem + 1); +} + +//---------------------------------------------------------------------------------------- +inline const char* CMessage::GetSender() const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + return sMessageLineData.author; +} + +//---------------------------------------------------------------------------------------- +inline const char* CMessage::GetDateString() const +//---------------------------------------------------------------------------------------- +{ +// UpdateMessageCache(); + return MSG_FormatDate(mMessageList, GetDate()); +} + +//---------------------------------------------------------------------------------------- +inline const char* CMessage::GetAddresseeString() const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + return sMessageLineData.author; +} + +//---------------------------------------------------------------------------------------- +const char * CMessage::GetSizeStr() const +// ¥ FIX ME: Is there an XP call for constructing the size string ? +//---------------------------------------------------------------------------------------- +{ + static char sizeString[32]; + UInt32 messageSize = GetSize(); + CStr255 formatString; + if (messageSize > (1024*1000)) + { + ::GetIndString(formatString, 7099, 4); + float floatSize = (float)messageSize / (1024*1000); + sprintf(sizeString, formatString, floatSize); + } + else + { + if (messageSize > 1024) + { + ::GetIndString(formatString, 7099, 5); + messageSize /= 1024; + } + else + { + ::GetIndString(formatString, 7099, 6); + } + sprintf(sizeString, formatString, messageSize); + } + return sizeString; +} + +//---------------------------------------------------------------------------------------- +const char * CMessage::GetPriorityStr() const +//---------------------------------------------------------------------------------------- +{ + static char buffer[256]; + buffer[0] = '\0'; + MSG_PRIORITY priority = GetPriority(); + if (priority == MSG_PriorityNotSet + || priority == MSG_NoPriority + || priority == MSG_NormalPriority) + return buffer; + MSG_GetPriorityName(priority, buffer, sizeof(buffer)-1); + return buffer; +} + + +//---------------------------------------------------------------------------------------- +const char * CMessage::GetStatusStr() const +//---------------------------------------------------------------------------------------- +{ + static char buffer[256]; + MSG_GetStatusName(GetStatus(), buffer, sizeof(buffer)-1); + return buffer; +} + +//---------------------------------------------------------------------------------------- +inline time_t CMessage::GetDate() const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + return sMessageLineData.date; +} + +//---------------------------------------------------------------------------------------- +inline UInt32 CMessage::GetSize() const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + return sMessageLineData.messageLines; // bytes for mail, lines for news. Oh well! +} + +//---------------------------------------------------------------------------------------- +inline UInt32 CMessage::GetStatus() const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + return sMessageLineData.flags; + // actually, not all bits are relevant, but there's no mask in msgcom.h. +} + +//---------------------------------------------------------------------------------------- +inline int8 CMessage::GetThreadLevel() const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + return sMessageLineData.level; +} + +//====================================== +// Icon suite IDs +//====================================== +#pragma mark +enum IconTable +{ + kMessageReadIcon = 15234 // icon to mark a message as read +, kFlagIcon = 15236 // icon to mark a message as read + + // Icons for representing mail messages +, kMailMessageIcon = 15228 +, kReadMailMessageIcon = 15229 +, kMailMessageWithAttachmentIcon = 10511 +, kFlaggedMailMessageIcon = 10512 +, kFlaggedMailMessageWithAttachmentIcon = 10513 + + // News articles +, kNewsArticleMessageIcon = 15231 +, kReadNewsArticleMessageIcon = 15232 +, kNewsArticleReadFlaggedMessageIcon = 15232 +, kNewsArticleFlaggedMessageIcon = 15234 +}; + +static ResIDT gIconIDTable[] = { +//------------------------------------------------------------------------------------------------------------- +// NAME UNREAD READ NEW (UNUSED) +// LOCAL ONLINE LOCAL ONLINE LOCAL ONLINE LOCAL ONLINE +//------------------------------------------------------------------------------------------------------------- +/* POP */ 15228 , 0 , 15229 , 0 , 15407 , 0 , 0 , 0 , +/* IMAP */ 15228 , 15226 , 15229 , 15226 , 15407 , 15407 , 15523 , 15523 , +/* Art */ 15518 , 15233 , 15519 , 15396 , 0 , 0 , 0 , 0 , +/* Drft */ 15230 , 15230 , 15230 , 15230 , 15230 , 15230 , 0 , 0 , +}; //---------------------------------------------------------------------------------------------------- + +enum { + kOnline = 1 // offset +, kRead = 2 // offset +, kNew = 4 // offset +, kDeleted = 6 // offset +, kKindsPerRow = 8 +, kMessageIx = 0 +, kIMAPIx = 1 * kKindsPerRow +, kArticleIx = 2 * kKindsPerRow +, kDraftIx = 3 * kKindsPerRow +}; + +//---------------------------------------------------------------------------------------- +ResIDT CMessage::GetIconID(UInt16 inFolderFlags) const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + return GetIconID(inFolderFlags, sMessageLineData.flags); +} // CMessage::GetIconID + +//---------------------------------------------------------------------------------------- +ResIDT CMessage::GetIconID(UInt16 inFolderFlags, UInt32 inMessageFlags) +//---------------------------------------------------------------------------------------- +{ +#define IMAP_FOLDER (MSG_FOLDER_FLAG_IMAPBOX | MSG_FOLDER_FLAG_MAIL) + short iconIndex = 0; + // Special case of pop folder because MSG_FLAG_OFFLINE is not correct for pop folders. + Boolean isPopFolder = (inFolderFlags & IMAP_FOLDER) == MSG_FOLDER_FLAG_MAIL; + Boolean isNews = (inFolderFlags & MSG_FOLDER_FLAG_NEWSGROUP) == MSG_FOLDER_FLAG_NEWSGROUP; + + // First, what kind of folder are we in (determines the basic icon type, the row in + // the table above) + if ((inFolderFlags & IMAP_FOLDER) == IMAP_FOLDER) + iconIndex = kIMAPIx; + else if (inFolderFlags & MSG_FOLDER_FLAG_MAIL) + iconIndex = kMessageIx; // FIXME: How do we tell whether it has attachments? + else if (inFolderFlags & MSG_FOLDER_FLAG_NEWSGROUP) + iconIndex = kArticleIx; + else if (inFolderFlags & MSG_FOLDER_FLAG_DRAFTS) + iconIndex = kDraftIx; + + // Next, which of the four major columns are we in. + if (!isNews && !isPopFolder && (inMessageFlags & MSG_FLAG_IMAP_DELETED) != 0) + iconIndex += kDeleted; + else if (!isNews && (inMessageFlags & MSG_FLAG_NEW) != 0) + iconIndex += kNew; + else if ((inMessageFlags & MSG_FLAG_READ) != 0) + iconIndex += kRead; + + // Finally, choose the local/offline column. + // Note: pop does not support the "offline" flag bit, it should always be set + // (but isn't) + if (!isPopFolder && (inMessageFlags & MSG_FLAG_OFFLINE) == 0) + iconIndex += kOnline; + + return gIconIDTable[iconIndex]; +} // CMessage::GetIconID + +//====================================== +// Thread Icon IDs +//====================================== +enum ThreadIconTable +{ + // Icons for representing threaded messages + kThreadIcon = 15399 +, kNewThreadIcon = 15401 +, kWatchedThreadIcon = 15403 +, kWatchedNewThreadIcon = 15404 +, kIgnoredThreadIcon = 15429 + +}; + +static ResIDT gThreadIconIDTable[] = { +//----------------------------------------------------------- +// NAME UNWATCHED WATCHED +// OLD UNREAD OLD UNREAD +//----------------------------------------------------------- +/* Thread */ 15399 , 15401 , 15403 , 15404 +}; //-------------------------------------------------- + +enum { + kUnread = 1 // offset +, kWatched = 2 // offset +}; + +//---------------------------------------------------------------------------------------- +ResIDT CMessage::GetThreadIconID() const +//---------------------------------------------------------------------------------------- +{ + UpdateMessageCache(); + short iconIndex = 0; + if ((sMessageLineData.flags & MSG_FLAG_IGNORED) != 0) + return kIgnoredThreadIcon; + if (GetNumNewChildren() > 0) + iconIndex += kUnread; + if ((sMessageLineData.flags & MSG_FLAG_WATCHED) != 0) + iconIndex += kWatched; + return gThreadIconIDTable[iconIndex]; +} // CMessage::GetIconID + +#pragma mark - + +//======================================================================================== +class CDeferredLoadFolderTask +//======================================================================================== +: public CDeferredTask +{ + public: + CDeferredLoadFolderTask( + CThreadView* inView, + CNSContext* inContext, + CMessageFolder inFolder); + protected: + virtual ExecuteResult ExecuteSelf(); +// data + protected: + CThreadView* mThreadView; + CNSContext* mContextToLoadAtIdle; + CMessageFolder mFolderToLoadAtIdle; + UInt32 mEarliestExecuteTime; +}; // class CDeferredLoadFolderTask + +//---------------------------------------------------------------------------------------- +CDeferredLoadFolderTask::CDeferredLoadFolderTask( + CThreadView* inView, + CNSContext* inContext, + CMessageFolder inFolder) +//---------------------------------------------------------------------------------------- +: mThreadView(inView) +, mContextToLoadAtIdle(inContext) +, mFolderToLoadAtIdle(inFolder) +, mEarliestExecuteTime(::TickCount() + GetDblTime()) +{ +} + +//---------------------------------------------------------------------------------------- +CDeferredTask::ExecuteResult CDeferredLoadFolderTask::ExecuteSelf() +//---------------------------------------------------------------------------------------- +{ + // Wait for the earliest execute time. It's expensive to load a folder, and if the + // user is nervously clicking on multiple folders, we only want to load the last one. + if (::TickCount() < mEarliestExecuteTime) + return eWaitStayFront; + if (CMailProgressWindow::GetModal()) + return eWaitStayFront; // wait until other modal tasks are done. + mThreadView->LoadMessageFolder(mContextToLoadAtIdle, mFolderToLoadAtIdle, true); + return eDoneDelete; +} // CDeferredLoadFolderTask::ExecuteSelf + +#pragma mark - + +//======================================================================================== +class CDeferredUndoTask +//======================================================================================== +: public CDeferredTask +{ + public: + CDeferredUndoTask( + CThreadView* inView); + void SetUndoStatus(UndoStatus inStatus) + { + mUndoStatus = inStatus; + } + protected: + virtual ExecuteResult ExecuteSelf(); +// data + protected: + CThreadView* mThreadView; + UndoStatus mUndoStatus; +}; // class CDeferredUndoTask + +//---------------------------------------------------------------------------------------- +CDeferredUndoTask::CDeferredUndoTask( + CThreadView* inView) +//---------------------------------------------------------------------------------------- +: mThreadView(inView) +, mUndoStatus(UndoInProgress) +{ +} + +//---------------------------------------------------------------------------------------- +CDeferredTask::ExecuteResult CDeferredUndoTask::ExecuteSelf() +//---------------------------------------------------------------------------------------- +{ + if (mUndoStatus == UndoComplete) + return eDoneDelete; // remove from queue + else if (mUndoStatus == UndoInProgress) + { + mUndoStatus = MSG_GetUndoStatus(mThreadView->GetMessagePane()); + switch (mUndoStatus) + { + case UndoComplete: + MSG_FolderInfo* finfo; + MessageKey key; + MSG_Pane* pane = mThreadView->GetMessagePane(); + if (MSG_GetUndoMessageKey(pane, &finfo, &key)) + { + if (finfo && finfo != mThreadView->GetOwningFolder()) + { + mThreadView->LoadMessageFolder(mThreadView->GetContext(), finfo, false); + } + mThreadView->SelectMessageWhenReady(key); + } + break; + case UndoInProgress: + return eWaitStayFront; // block, leave in queue + default: + return eDoneDelete; + } // switch + } + return eDoneDelete; // remove from queue +} // CDeferredUndoTask::ExecuteSelf + +#pragma mark - + +//======================================================================================== +class CDeferredSelectRowTask +//======================================================================================== +: public CDeferredTask +{ + public: + CDeferredSelectRowTask( + CThreadView* inView, + TableIndexT inRow); + protected: + virtual ExecuteResult ExecuteSelf(); +// data + protected: + CThreadView* mThreadView; + TableIndexT mRow; +}; // class CDeferredSelectRowTask + +//---------------------------------------------------------------------------------------- +CDeferredSelectRowTask::CDeferredSelectRowTask( + CThreadView* inView, + TableIndexT inRow) +//---------------------------------------------------------------------------------------- +: mThreadView(inView) +, mRow(inRow) +{ +} + +//---------------------------------------------------------------------------------------- +CDeferredTask::ExecuteResult CDeferredSelectRowTask::ExecuteSelf() +//---------------------------------------------------------------------------------------- +{ + mThreadView->SelectAfterDelete(mRow); + return eDoneDelete; // remove from queue +} // CDeferredSelectRowTask::ExecuteSelf + +#pragma mark - + +//======================================================================================== +class CScrollToNewMailTask +//======================================================================================== +: public CDeferredTask +{ + public: + CScrollToNewMailTask( + CThreadView* inView); + protected: + virtual ExecuteResult ExecuteSelf(); +// data + protected: + CThreadView* mThreadView; +}; // class CScrollToNewMailTask + +//---------------------------------------------------------------------------------------- +CScrollToNewMailTask::CScrollToNewMailTask( + CThreadView* inView) +//---------------------------------------------------------------------------------------- +: mThreadView(inView) +{ +} + +//---------------------------------------------------------------------------------------- +CDeferredTask::ExecuteResult CScrollToNewMailTask::ExecuteSelf() +//---------------------------------------------------------------------------------------- +{ + mThreadView->ScrollToGoodPlace(); + return eDoneDelete; // remove from queue +} // CScrollToNewMailTask::ExecuteSelf + +#pragma mark - + +//======================================================================================== +class CDeferredSelectKeyTask +//======================================================================================== +: public CDeferredTask +{ + public: + CDeferredSelectKeyTask( + CThreadView* inView, + MessageKey inKey); + protected: + virtual ExecuteResult ExecuteSelf(); +// data + protected: + CThreadView* mThreadView; + MessageKey mPendingMessageKeyToSelect; // Hack to support ShowSearchMessage() +}; // class CDeferredSelectKeyTask + +//---------------------------------------------------------------------------------------- +CDeferredSelectKeyTask::CDeferredSelectKeyTask( + CThreadView* inView, + MessageKey inKey) +//---------------------------------------------------------------------------------------- +: mThreadView(inView) +, mPendingMessageKeyToSelect(inKey) +{ +} + +//---------------------------------------------------------------------------------------- +CDeferredTask::ExecuteResult CDeferredSelectKeyTask::ExecuteSelf() +//---------------------------------------------------------------------------------------- +{ + mThreadView->SelectMessageWhenReady(mPendingMessageKeyToSelect); + return eDoneDelete; +} // CDeferredSelectKeyTask::ExecuteSelf + +#pragma mark - + +//======================================================================================== +class CDeferredThreadViewCommand +//======================================================================================== +: public CDeferredCommand +{ + private: + typedef CDeferredCommand Inherited; + public: + CDeferredThreadViewCommand( + CThreadView* inView, + CommandT inCommand, + void* ioParam); + protected: + virtual ExecuteResult ExecuteSelf(); +// data + protected: + CThreadView* mThreadView; +}; // class CDeferredThreadViewCommand + +//---------------------------------------------------------------------------------------- +CDeferredThreadViewCommand::CDeferredThreadViewCommand( + CThreadView* inView, + CommandT inCommand, + void* ioParam) +//---------------------------------------------------------------------------------------- +: CDeferredCommand(inView, inCommand, ioParam) +, mThreadView(inView) +{ +} + +//---------------------------------------------------------------------------------------- +CDeferredTask::ExecuteResult CDeferredThreadViewCommand::ExecuteSelf() +//---------------------------------------------------------------------------------------- +{ + if (mCommand == cmd_GetNewMail) + { + if (mThreadView->IsStillLoading()) + return eWaitStayFront; // don't stop idling +// && ((mXPFolder.GetFolderFlags() & MSG_FOLDER_FLAG_INBOX) != 0) && // if this is an inbox +// ((mXPFolder.GetFolderFlags() & MSG_FOLDER_FLAG_IMAPBOX) == 0) // and our server is pop + } + return Inherited::ExecuteSelf(); +} // CDeferredThreadViewCommand::ExecuteSelf + +#pragma mark - + +//---------------------------------------------------------------------------------------- +CThreadView::CThreadView(LStream *inStream) +//---------------------------------------------------------------------------------------- +: Inherited(inStream) +, mXPFolder(NULL) +, mSavedSelection(nil) +, mExpectingNewMail(false) +, mGotNewMail(false) +, mRowToSelect(0) +, mSelectAfterDelete(false) // This new "hidden" pref was a demand from a big customer +, mMotionPendingCommand((MSG_MotionType)-1) +, mUndoTask(nil) +, mScrollToShowNew(false) +{ + XP_Bool noNextRowSelection; + if (PREF_GetBoolPref("mail.no_select_after_delete", &noNextRowSelection) == PREF_NOERROR + && noNextRowSelection) + SetSelectAfterDelete(false); +} + +//---------------------------------------------------------------------------------------- +CThreadView::~CThreadView() +//---------------------------------------------------------------------------------------- +{ + SaveSelectedMessage(); + delete mSavedSelection; +} + +//---------------------------------------------------------------------------------------- +void CThreadView::ApplyTextStyle(TableIndexT inRow) const +//---------------------------------------------------------------------------------------- +{ + CMessage message(inRow, GetMessagePane()); + // Set bold if new + if (!message.HasBeenRead()) + ::TextFace(bold); + else if (message.IsThread() && !message.IsOpenThread() && message.GetNumNewChildren() > 0) + ::TextFace(underline); + else + ::TextFace(normal); + ::TextMode(message.IsDeleted() ? grayishTextOr : srcOr); +} // CMessageFolderView::ApplyTextStyle + +//---------------------------------------------------------------------------------------- +void CThreadView::ApplyTextColor(TableIndexT inRow) const +//---------------------------------------------------------------------------------------- +{ + CMessage message(inRow, GetMessagePane()); + // Set up the text color based on priority + RGBColor priorityColor; + message.GetPriorityColor(priorityColor); + ::RGBForeColor(&priorityColor); +} // CMessageFolderView::ApplyTextStyle + +//---------------------------------------------------------------------------------------- +void CThreadView::DrawCellContents( + const STableCell &inCell, + const Rect &inLocalRect) +//---------------------------------------------------------------------------------------- +{ + ApplyTextColor(inCell.row); + + CMessage message(inCell.row, GetMessagePane()); + PaneIDT cellType = GetCellDataType(inCell); + switch (cellType) + { + case kThreadMessageColumn: + { + // draw a "thread dragger" /*if we're sorted by thread and*/ this + // message is a thread header... or if we are watched/ignored + //if (GetSortedColumn() == kThreadMessageColumn && message.IsThread()) + + ResIDT iconID = message.GetThreadIconID(); + if( /*GetSortedColumn() == kThreadMessageColumn &&*/ ( message.IsThread() || iconID >= kWatchedThreadIcon ) ) + DrawIconFamily( iconID, 16, 16, 0, inLocalRect); + break; + + } + case kMarkedReadMessageColumn: + { + if (!message.HasBeenRead()) + DrawIconFamily(kMessageReadIcon, 16, 16, 0, inLocalRect); + break; + } + case kFlagMessageColumn: + if (message.IsFlagged()) + DrawIconFamily(kFlagIcon, 16, 16, 0, inLocalRect); + break; + case kSubjectMessageColumn: + SInt16 newLeft = DrawIcons(inCell, inLocalRect); + Rect subjectRect = inLocalRect; + subjectRect.left = newLeft; + DrawMessageSubject(inCell.row, subjectRect); + break; + case kSenderMessageColumn: + { + const char* raw = message.GetSender(); + char* conv = IntlDecodeMimePartIIStr(raw, mContext->GetWinCSID(), FALSE); + + if(mContext->GetWinCSID() == CS_UTF8) + DrawUTF8TextString((conv ? conv : raw), &mTextFontInfo, 2, inLocalRect); + else + DrawTextString((conv ? conv : raw), &mTextFontInfo, 2, inLocalRect); + + if(conv) + XP_FREE(conv); + } + break; + case kDateMessageColumn: + DrawTextString(message.GetDateString(), &mTextFontInfo, 2, inLocalRect, teFlushLeft, true, truncEnd); + break; + case kPriorityMessageColumn: + DrawMessagePriority(message, inLocalRect); + break; + case kSizeMessageColumn: + DrawMessageSize(message, inLocalRect); + break; + case kStatusMessageColumn: + DrawMessageStatus(message, inLocalRect); + break; + case kAddresseeMessageColumn: + { + const char* raw = message.GetAddresseeString(); + char* conv = IntlDecodeMimePartIIStr(raw, mContext->GetWinCSID(), FALSE); + if(mContext->GetWinCSID() == CS_UTF8) + DrawUTF8TextString((conv ? conv : raw), &mTextFontInfo, 2, inLocalRect); + else + DrawTextString((conv ? conv : raw), &mTextFontInfo, 2, inLocalRect); + DrawTextString((conv ? conv : raw), &mTextFontInfo, 2, inLocalRect); + if(conv) + XP_FREE(conv); + } + break; + case kTotalMessageColumn: + if (GetSortedColumn() == kThreadMessageColumn && message.IsThread()) + DrawCountCell(message.GetNumChildren(), inLocalRect); + break; + case kUnreadMessageColumn: + if (GetSortedColumn() == kThreadMessageColumn && message.IsThread()) + DrawCountCell(message.GetNumNewChildren(), inLocalRect); + break; + default: + Assert_(false); + break; + } + // The selection of a new message after one is removed is handled by a timer, and is keyed + // off mRowToSelect. This data member is set when we are notified that a row has been removed. + // We used to start this timer immediately on initiating the delete operation (in DeleteSelection) + // but this ended up firing before the refresh occurred, resulting in two rows selected, which + // was ugly. So we turn on the idler here, now, to make sure that the row isn't selected until + // after the refresh is done. 98/01/14 + if (mRowToSelect) + { + CDeferredTaskManager::Post1(new CDeferredSelectRowTask(this, mRowToSelect), this); + mRowToSelect = LArray::index_Bad; + } + if (mScrollToShowNew) + { + CDeferredTaskManager::Post1(new CScrollToNewMailTask(this), this); + mScrollToShowNew = false; + } +} // CThreadView::DrawCellContents + +//---------------------------------------------------------------------------------------- +void CThreadView::DrawIconsSelf( + const STableCell& inCell, + IconTransformType inTransformType, + const Rect& inIconRect) const +//---------------------------------------------------------------------------------------- +{ + const ResIDT kPaperClipBackLayer = 15555; + const ResIDT kPaperClipFrontLayer = 15556; + CMessage message(inCell.row, GetMessagePane()); + Boolean hasAttachments = message.HasAttachments(); + if (hasAttachments) + DrawIconFamily(kPaperClipBackLayer, 16, 16, inTransformType, inIconRect); + Inherited::DrawIconsSelf(inCell, inTransformType, inIconRect); + if (hasAttachments) + DrawIconFamily(kPaperClipFrontLayer, 16, 16, inTransformType, inIconRect); +} // CThreadView::DrawIconsSelf + +//---------------------------------------------------------------------------------------- +Boolean CThreadView::GetDragCopyStatus( + DragReference inDragRef +, const CMailSelection& inSelection +, Boolean& outCopy) +//---------------------------------------------------------------------------------------- +{ + SInt16 modifiers; + ::GetDragModifiers(inDragRef, NULL, &modifiers, NULL); + outCopy = ((modifiers & optionKey) != 0); // user wants copy if possible. + MSG_DragEffect effect = outCopy ? MSG_Require_Copy : MSG_Default_Drag; + MSG_FolderInfo* destFolder = GetOwningFolder(); + if (!destFolder) + return false; // eg, when the pane is blank because a server is selected. + // Ask the back end for guidance on what is possible: + effect = ::MSG_DragMessagesIntoFolderStatus( + inSelection.xpPane, + inSelection.GetSelectionList(), + inSelection.selectionSize, + destFolder, + effect); + if (effect == MSG_Drag_Not_Allowed && outCopy) + { + // Well, maybe a move is ok + effect = ::MSG_DragMessagesIntoFolderStatus( + inSelection.xpPane, + inSelection.GetSelectionList(), + inSelection.selectionSize, + destFolder, + MSG_Default_Drag); + } + outCopy = (effect & MSG_Require_Copy) != 0; + return (effect != MSG_Drag_Not_Allowed); // this looks ok as a drop operation. +} // CThreadView::GetSelectionAndCopyStatusFromDrag + +//---------------------------------------------------------------------------------------- +void CThreadView::EnterDropArea( + DragReference inDragRef, + Boolean inDragHasLeftSender) +// CStandardFlexTable overrides the LDropArea base method, because it assumes a drop-on-row +// scenario +//---------------------------------------------------------------------------------------- +{ + LDragAndDrop::EnterDropArea(inDragRef, inDragHasLeftSender); +} + +//---------------------------------------------------------------------------------------- +void CThreadView::LeaveDropArea(DragReference inDragRef) +// CStandardFlexTable overrides the LDropArea base method, because it assumes a drop-on-row +// scenario +//---------------------------------------------------------------------------------------- +{ + LDragAndDrop::LeaveDropArea(inDragRef); + mDropRow = 0; + mIsDropBetweenRows = false; + mIsInternalDrop = false; +} + +//---------------------------------------------------------------------------------------- +Boolean CThreadView::ItemIsAcceptable( + DragReference inDragRef, + ItemReference /*inItemRef*/ ) +//---------------------------------------------------------------------------------------- +{ + Boolean dropOK = false; + Boolean doCopy = false; + + CMailSelection selection; + if (GetSelectionFromDrag(inDragRef, selection) + && selection.xpPane != GetMessagePane() + && GetDragCopyStatus(inDragRef, selection, doCopy)) + dropOK = true; + if (dropOK && doCopy) + { + CursHandle copyDragCursor = ::GetCursor(curs_CopyDrag); + if (copyDragCursor != nil) + ::SetCursor(*copyDragCursor); + } + else + ::SetCursor(&qd.arrow); + return dropOK; +} // CThreadView::ItemIsAcceptable + +//---------------------------------------------------------------------------------------- +void CThreadView::ReceiveDragItem( + DragReference inDragRef, + DragAttributes /*inDragAttrs*/, + ItemReference inItemRef, + Rect& /*inItemBounds*/) +//---------------------------------------------------------------------------------------- +{ + Assert_(inItemRef == CMailFlexTable::eMailNewsSelectionDragItemRefNum); + + // What are the items being dragged? + CMailSelection selection; + if (!GetSelectionFromDrag(inDragRef, selection)) + return; + + // Don't do anything if the user aborted by dragging back into the same view. + if (selection.xpPane == GetMessagePane()) + return; + + Boolean copyMessages; + if (!GetDragCopyStatus(inDragRef, selection, copyMessages)) + return; + + // display modal dialog + short stringID = (copyMessages ? 19 : 15); // Copying / Moving Messages + CStr255 commandString; + ::GetIndString(commandString, 7099, stringID); + CMailProgressWindow* progressWindow = CMailProgressWindow::CreateModalParasite( + CMailProgressWindow::res_ID_modal, + selection.xpPane, + commandString); + if (progressWindow) + progressWindow->SetDelay(0); + + // copy / move messages + if (! copyMessages) + ::MSG_MoveMessagesIntoFolder( + selection.xpPane, + selection.GetSelectionList(), + selection.selectionSize, + mXPFolder.GetFolderInfo() + ); + else + ::MSG_CopyMessagesIntoFolder( + selection.xpPane, + selection.GetSelectionList(), + selection.selectionSize, + mXPFolder.GetFolderInfo() + ); + // Close any windows associated with these messages, if the preferences + // say we should. + CMessageWindow::NoteSelectionFiledOrDeleted(selection); + mUndoCommand = cmd_Undo; +} // CThreadView::ReceiveDragItem + +//---------------------------------------------------------------------------------------- +void CThreadView::DrawMessageSize( + const CMessage& inMessage, + const Rect& inLocalRect ) +//---------------------------------------------------------------------------------------- +{ + DrawTextString(inMessage.GetSizeStr(), + &mTextFontInfo, 2, inLocalRect, teFlushLeft, true, truncEnd); +} // CThreadView::DrawMessageSize + +//---------------------------------------------------------------------------------------- +void CThreadView::DrawMessagePriority( + const CMessage& inMessage, + const Rect& inLocalRect) +//---------------------------------------------------------------------------------------- +{ + DrawTextString(inMessage.GetPriorityStr(), + &mTextFontInfo, 0, inLocalRect); +} // CThreadView::DrawMessagePriority + +//---------------------------------------------------------------------------------------- +void CThreadView::DrawMessageStatus( + const CMessage& inMessage, + const Rect& inLocalRect) +//---------------------------------------------------------------------------------------- +{ + DrawTextString(inMessage.GetStatusStr(), + &mTextFontInfo, 0, inLocalRect); +} //CThreadView::DrawMessageStatus + +//---------------------------------------------------------------------------------------- +ResIDT CThreadView::GetIconID(TableIndexT inRow) const +//---------------------------------------------------------------------------------------- +{ + CMessage message(inRow, GetMessagePane()); + return message.GetIconID(mXPFolder.GetFolderFlags()); +} // CMessageFolderView::GetIconID + +//---------------------------------------------------------------------------------------- +UInt16 CThreadView::GetNestedLevel(TableIndexT inRow) const +//---------------------------------------------------------------------------------------- +{ + CMessage message(inRow, GetMessagePane()); + return message.GetThreadLevel(); +} // CThreadView::GetNestedLevel + +//---------------------------------------------------------------------------------------- +Boolean CThreadView::GetHiliteTextRect( + const TableIndexT inRow, + Rect& outRect) const +//---------------------------------------------------------------------------------------- +{ + STableCell cell(inRow, GetHiliteColumn()); + if (!GetLocalCellRect(cell, outRect)) + return false; + Rect iconRect; + GetIconRect(cell, outRect, iconRect); + outRect.left = iconRect.right; + char subject[kMaxSubjectLength]; + GetHiliteText(inRow, subject, sizeof(subject), &outRect); + return true; +} // CThreadView::GetHiliteRect + +//---------------------------------------------------------------------------------------- +void CThreadView::GetDropFlagRect( + const Rect& inLocalCellRect, + Rect& outFlagRect ) const +//---------------------------------------------------------------------------------------- +{ + Inherited::GetDropFlagRect(inLocalCellRect, outFlagRect); + if (GetSortedColumn() != kThreadMessageColumn) + { + // Return a rectangle of zero width. + outFlagRect.left = inLocalCellRect.left; + outFlagRect.right = outFlagRect.left; + } +} // CThreadView::GetDropFlagRect + +//---------------------------------------------------------------------------------------- +void CThreadView::GetMainRowText( + TableIndexT inRow, + char* outText, + UInt16 inMaxBufferLength) const +// Calculate the text and (if ioRect is not passed in as null) a rectangle that fits it. +// Return result indicates if any of the text is visible in the cell +//---------------------------------------------------------------------------------------- +{ + if (!outText || inMaxBufferLength < 2) + return; + CMessage message(inRow, GetMessagePane()); + const char* raw = message.GetSubject(outText, inMaxBufferLength - 1); + char* conv = IntlDecodeMimePartIIStr(raw, mContext->GetWinCSID(), FALSE); + if (conv) + { + XP_STRNCPY_SAFE(outText, conv, inMaxBufferLength); + XP_FREE(conv); + } +} // CThreadView::GetMainRowText + +//---------------------------------------------------------------------------------------- +void CThreadView::DrawMessageSubject( + TableIndexT inRow, + const Rect& inLocalRect) +// Draw the message drop flag, icon and subject +//---------------------------------------------------------------------------------------- +{ + // draw a drop flag if we're sorted by thread and this + // message is a thread header... + char subject[kMaxSubjectLength]; + Rect textRect = inLocalRect; + if (GetHiliteText(inRow, subject, sizeof(subject), &textRect)) + { + if (mContext->GetWinCSID() == CS_UTF8) + { + textRect.right = inLocalRect.right; // calculation will have been wrong. + DrawUTF8TextString(subject, &mTextFontInfo, 0, textRect); + } + else + DrawTextString(subject, &mTextFontInfo, 0, textRect); +// if (inRow == mDropRow) +// ::InvertRect(&textRect); + } +} // CThreadView::DrawMessageSubject + +//---------------------------------------------------------------------------------------- +Boolean CThreadView::CellSelects(const STableCell& inCell) const +//---------------------------------------------------------------------------------------- +{ + PaneIDT cellType = GetCellDataType(inCell); + switch (cellType) + { + case kThreadMessageColumn: + { + CMessage message(inCell.row, GetMessagePane()); + return (message.IsThread()); + } + case kMarkedReadMessageColumn: + case kFlagMessageColumn: + return false; + default: + return true; + } +} // CThreadView::CellSelects + +//----------------------------------- +void CThreadView::SetRowCount() +// Queries the back end pane and sets the number of rows. +// Overridden to call MSG_GetThreadLineByIndex, which forces +// authentication if the preference specifies it. A result of false +// means that the folder is password protected. +//----------------------------------- +{ + MSG_MessageLine ignored; + MSG_Pane* pane = GetMessagePane(); + if (!pane || !::MSG_GetThreadLineByIndex(pane, 0, 1, &ignored)) + { + // Access denied! + TableIndexT rows, cols; + GetTableSize(rows, cols); + if (rows > 0) + RemoveRows(rows, 1, false); + return; + } + Inherited::SetRowCount(); +} // CThreadView::SetRowCount() + + +//---------------------------------------------------------------------------------------- +Boolean CThreadView::CellWantsClick ( const STableCell& inCell ) const +// Overloaded to ensure that certain columns get the mouse click +//---------------------------------------------------------------------------------------- +{ + PaneIDT cellType = GetCellDataType(inCell); + switch (cellType) + { +// case kThreadMessageColumn: + case kMarkedReadMessageColumn: + case kFlagMessageColumn: + case kPriorityMessageColumn: + return true; + + default: + return false; + } + +} // CThreadView::CellWantsClick + + +//---------------------------------------------------------------------------------------- +void CThreadView::ClickCell( + const STableCell &inCell, + const SMouseDownEvent &inMouseDown) +// Handles marking message read/unread +//---------------------------------------------------------------------------------------- +{ + CMailProgressWindow::RemoveModalParasite(); + PaneIDT cellType = GetCellDataType(inCell); + switch (cellType) + { + case kThreadMessageColumn: + { + if (GetSortedColumn() == kThreadMessageColumn) + { + CMessage message(inCell.row, GetMessagePane()); + if (message.IsThread()) + { + if (! message.IsOpenThread()) + ToggleExpandAction(inCell.row); + DoSelectThread(inCell.row); + CMessage::InvalidateCache(); // Because BE has changed the message state + } + } + break; + } + // toggle the message's read/unread state + case kMarkedReadMessageColumn: + { + MSG_ViewIndex index = inCell.row - 1; + MSG_Command(GetMessagePane(), MSG_ToggleMessageRead, &index, 1); + CMessage::InvalidateCache(); // Because BE has changed the message state + RefreshCell(inCell); + break; + } + case kFlagMessageColumn: + { + CMessage message(inCell.row, GetMessagePane()); + MSG_CommandType cmd = message.IsFlagged() ? MSG_UnmarkMessages : MSG_MarkMessages; + MSG_ViewIndex index = inCell.row - 1; + MSG_Command(GetMessagePane(), cmd, &index, 1); + CMessage::InvalidateCache(); // Because BE has changed the message state + RefreshCell(inCell); + break; + } + case kPriorityMessageColumn: + { + if ((mXPFolder.GetFolderFlags() & MSG_FOLDER_FLAG_NEWSGROUP) != 0) + break; // no priority for news. + + // calculate popup location + FocusDraw(); + Point where = inMouseDown.wherePort; + PortToGlobalPoint(where); + Rect cellRect; + if (GetLocalCellRect(inCell, cellRect)) + { + where = topLeft(cellRect); + ::LocalToGlobal(&where); + } + + // get popup default value + CMessage message(inCell.row, GetMessagePane()); + MSG_PRIORITY oldPriority = message.GetPriority(); + Int16 defaultChoice = CMessage::PriorityToMenuItem(oldPriority); + + // display popup + Int16 menuItem = 0; + LMenu * thePopup = new LMenu(kPriorityMenuID); + + { // copied from HandlePopupMenuSelect() + + MenuHandle menuH = thePopup->GetMacMenuH(); + +// we decided that all the context menus should look the same and +// this meant drawing them in the system font (at least for 4.0) +// to re-enable drawing in another font just take out my #ifdef's +// and #endif's +#ifdef USE_SOME_OTHER_FONT_FOR_THE_PRIORITY_MENU + Int16 saveFont = ::LMGetSysFontFam(); + Int16 saveSize = ::LMGetSysFontSize(); + + StMercutioMDEFTextState theMercutioMDEFTextState; +#endif + try { +#ifdef USE_SOME_OTHER_FONT_FOR_THE_PRIORITY_MENU + + // Reconfigure the system font so that the menu will be drawn in our desired + // font and size + if ( mTextTraitsID ) { + FocusDraw(); + TextTraitsH traitsH = UTextTraits::LoadTextTraits(mTextTraitsID); + if ( traitsH ) { + // Bug #64133 + // If setting to application font, get the application font for current script + if((**traitsH).fontNumber == 1) + ::LMSetSysFontFam ( ::GetScriptVariable(::FontToScript(1), smScriptAppFond) ); + else + ::LMSetSysFontFam ( (**traitsH).fontNumber ); + ::LMSetSysFontSize((**traitsH).size); + ::LMSetLastSPExtra(-1L); + } + } +#endif + // Handle the actual insertion into the hierarchical menubar + ::InsertMenu(menuH, hierMenu); + + // Call PopupMenuSelect and wait for it to return + Int32 result = ::PopUpMenuSelect(menuH, where.v, where.h, defaultChoice); + + // Extract the values from the returned result. + menuItem = LoWord(result); + } + catch(...) { + // Ignore errors + } + +// this is the last one of these that you'd want to take out in order +// to re-enable the ability to use some font (other than the system font) +// to draw the priority context menu +#ifdef USE_SOME_OTHER_FONT_FOR_THE_PRIORITY_MENU + // Restore the system font + ::LMSetSysFontFam(saveFont); + ::LMSetSysFontSize(saveSize); + ::LMSetLastSPExtra(-1L); +#endif + // Finally get the menu removed + ::DeleteMenu(kPriorityMenuID); + } + + if (menuItem) + { + MSG_PRIORITY newPriority = CMessage::MenuItemToPriority(menuItem); + if (newPriority != oldPriority) + { + // BE bug? We're not getting told that a change happened. So + // do it ourselves. If this gets fixed later, we can remove these + // change calls. + ChangeStarting(GetMessagePane(), MSG_NotifyChanged, inCell.row, 1); + MSG_SetPriority(GetMessagePane(), message.GetMessageKey(), newPriority); + ChangeFinished(GetMessagePane(), MSG_NotifyChanged, inCell.row, 1); + CMessage::InvalidateCache(); // Because BE has changed the message state + } + } + break; + } + default: + break; + } +} // CThreadView::ClickCell + +//---------------------------------------------------------------------------------------- +TableIndexT CThreadView::GetHiliteColumn() const +//---------------------------------------------------------------------------------------- +{ + return mTableHeader->ColumnFromID(kSubjectMessageColumn); +} // CThreadView::GetHiliteColumn + +//---------------------------------------------------------------------------------------- +void CThreadView::SetCellExpansion( + const STableCell& inCell, + Boolean inExpanded) +//---------------------------------------------------------------------------------------- +{ + Boolean currentlyExpanded; + if (!CellHasDropFlag(inCell, currentlyExpanded) || (inExpanded == currentlyExpanded)) + return; + ToggleExpandAction(inCell.row); + CMessage::InvalidateCache(); // Because BE has changed the message state +} // CThreadView::SetCellExpansion + +//---------------------------------------------------------------------------------------- +Boolean CThreadView::CellInitiatesDrag(const STableCell& inCell) const +//---------------------------------------------------------------------------------------- +{ + PaneIDT cellType = GetCellDataType(inCell); + return (cellType == kSubjectMessageColumn + || (cellType == kThreadMessageColumn && GetSortedColumn() == kThreadMessageColumn)); +} // CThreadView::CellInitiatesDrag + +//---------------------------------------------------------------------------------------- +Boolean CThreadView::CellHasDropFlag( + const STableCell& inCell, + Boolean& outExpanded) const +//---------------------------------------------------------------------------------------- +{ + PaneIDT cellType = GetCellDataType(inCell); + + // Only show drop flags when we're sorted by thread + if (cellType == kSubjectMessageColumn && GetSortedColumn() == kThreadMessageColumn) + { + CMessage message(inCell.row, GetMessagePane()); + if (message.IsThread()) + { + outExpanded = message.IsOpenThread(); + return true; + } + } + return false; +} // CThreadView::CellHasDropFlag + +//---------------------------------------------------------------------------------------- +TableIndexT CThreadView::CountExtraRowsControlledByCell(const STableCell& inCell) const +//---------------------------------------------------------------------------------------- +{ + PaneIDT cellType = GetCellDataType(inCell); + // Only show drop flags when we're sorted by thread + if (cellType != kThreadMessageColumn || GetSortedColumn() != kThreadMessageColumn) + return 0; + int32 msgExpansionDelta = MSG_ExpansionDelta(GetMessagePane(), inCell.row - 1); + if (msgExpansionDelta >= 0) + return 0; + return -msgExpansionDelta; +} // CThreadView::CountExtraRowsControlledByCell + +//---------------------------------------------------------------------------------------- +void CThreadView::OpenRow(TableIndexT inRow) +// Open the message into its own window. +//---------------------------------------------------------------------------------------- +{ + Assert_(mContext); + + // Check the application heap. + if (Memory_MemoryIsLow() || ::MaxBlock() < 100*1024) + throw memFullErr; + // Check the Mozilla allocator + void* temp = XP_ALLOC(20*1024); + if (!temp) + throw memFullErr; + XP_FREE(temp); + + ::SetCursor(*::GetCursor(watchCursor)); + Boolean multipleSelection = GetSelectedRowCount() > 1; + CMessage message(inRow, GetMessagePane()); + // if a Draft, template, or Queue message open the compose window + if ( + (mXPFolder.GetFolderFlags() + & (MSG_FOLDER_FLAG_DRAFTS | MSG_FOLDER_FLAG_QUEUE | MSG_FOLDER_FLAG_TEMPLATES)) + || message.IsTemplate() + ) + { + ::MSG_OpenDraft(GetMessagePane() , mXPFolder.GetFolderInfo(), message.GetMessageKey()); + return; + } + // First see if there's an open window open to this message. If there is, + // bring it to the front. + CMessageWindow* messageWindow = CMessageWindow::FindAndShow(message.GetMessageKey()); + if (messageWindow) + { + messageWindow->GetMessageView()->SetDefaultCSID(mContext->GetDefaultCSID()); + // Found it. Bring it to the front. + messageWindow->Select(); + return; + } + // First, if recycling a window is indicated, ask for any existing one. + if (mContext->GetCurrentCommand() != cmd_OpenSelectionNewWindow) + { + // With a multiple selection, we never recycle! + XP_Bool prefReuseWindow = 0; // recycle any message window + PREF_GetBoolPref("mailnews.reuse_message_window", &prefReuseWindow); + if (!multipleSelection && + (CApplicationEventAttachment::CurrentEventHasModifiers(optionKey) ^ prefReuseWindow)) + { + messageWindow = CMessageWindow::FindAndShow(0); + } + } + // If we couldn't (or shouldn't) recycle one, make a new one. + if (!messageWindow) + { + try + { + messageWindow = + (CMessageWindow*)URobustCreateWindow::CreateWindow( + CMessageWindow::res_ID, + LCommander::GetTopCommander()); + CBrowserContext* theContext = new CBrowserContext(MWContextMailMsg); + StSharer theShareLock(theContext); // if we throw, theContext will have no users & die + messageWindow->SetWindowContext(theContext); + } + catch(...) + { + delete messageWindow; + messageWindow = NULL; + throw; + } + } + // Whether it's a new one or an old one, load the message now. + if (messageWindow) + { + try + { + messageWindow->GetMessageView()->ShowMessage( + CMailNewsContext::GetMailMaster(), + mXPFolder.GetFolderInfo(), + message.GetMessageKey()); // pass flags to tell what sort of message it is + messageWindow->GetMessageView()->SetDefaultCSID(mContext->GetDefaultCSID()); + messageWindow->Select(); +// messageWindow->Show(); + // The window will now show itself when a successful load occurs + } + catch(...) + { + delete messageWindow; + messageWindow = NULL; + throw; + } + } + CMessage::InvalidateCache(); // So that the "read" flag gets changed. +} // CThreadView::OpenRow + +#define THREE_PANE 1 + +//---------------------------------------------------------------------------------------- +void CThreadView::SaveSelectedMessage() +//---------------------------------------------------------------------------------------- +{ + MSG_Pane* curPane = GetMessagePane(); + if (curPane) + { + MSG_FolderInfo* curFolder = ::MSG_GetThreadFolder(curPane); + if (curFolder) + { + if (GetSelectedRowCount() == 1) + { + TableIndexT curRow = mTableSelector->GetFirstSelectedRow(); + CMessage message(curRow, curPane); + MessageKey curMessage = message.GetMessageKey(); + ::MSG_SetLastMessageLoaded(curFolder, curMessage); + } + else + ::MSG_SetLastMessageLoaded(curFolder, MSG_MESSAGEKEYNONE); + } + } +} // CThreadView::SaveSelectedMessage + +//---------------------------------------------------------------------------------------- +Boolean CThreadView::RestoreSelectedMessage() +// Called on FolderLoaded pane notification. We probably should check the preference. +//---------------------------------------------------------------------------------------- +{ + XP_Bool remember = true; + if (PREF_NOERROR != PREF_GetBoolPref("mailnews.remember_selected_message", &remember)) + return false; + if (!remember) + return false; + MSG_Pane* curPane = GetMessagePane(); + if (curPane) + { + MSG_FolderInfo* curFolder = ::MSG_GetThreadFolder(curPane); + if (curFolder) + { + MessageKey savedMessage = ::MSG_GetLastMessageLoaded(curFolder); + if (savedMessage != MSG_MESSAGEKEYNONE) + { + MSG_ViewIndex index = ::MSG_GetMessageIndexForKey(curPane, savedMessage, true); + if (index != MSG_VIEWINDEXNONE) + { + mRowToSelect = index + 1; + return true; + } + } + } + } + return false; +} // CThreadView::RestoreSelectedMessage + +//---------------------------------------------------------------------------------------- +void CThreadView::LoadMessageFolder( + CNSContext* inContext, // My window's context + const CMessageFolder& inFolder, + Boolean inLoadNow) +// Load a given message folder into the message table view. +//---------------------------------------------------------------------------------------- +{ + Assert_(inContext); + // Load the folder into the message list. We'll get a PaneChanged message + // when loading is complete, and another one to say that the folder info + // has changed. When these callbacks occur, we'll call SetFolder(). However, + // the first time the window is initialized, the title needs to look nice + // even during the load process. Hence this call, which will be made twice: + SetFolder(inFolder); + if (!inLoadNow) + { + CDeferredLoadFolderTask* task + = new CDeferredLoadFolderTask(this, inContext, inFolder); + CDeferredTaskManager::Post1(task, this); + return; + } + try + { + ::SetCursor(*::GetCursor(watchCursor)); + SaveSelectedMessage(); + UnselectAllCells(); + // Else it crashes, because the messageview (if displaying a message), doesn't + // get cleaned up properly. #81060 + mContext = inContext; +#if !ONECONTEXTPERWINDOW + SetMessagePane(nil); // release the memory first. +#endif + // A null inFolder is possible, and the result should be to clear the thread area. + // This behavior is new in 5.0 with the 3-pane window - 97/10/06 + if (!inFolder.GetFolderInfo()) + { + RemoveAllRows( true ); + SetMessagePane(nil); + return; + } +#if ONECONTEXTPERWINDOW + if (!GetMessagePane()) +#endif + { + SetMessagePane(::MSG_CreateThreadPane(*inContext, CMailNewsContext::GetMailMaster())); + ThrowIfNULL_(GetMessagePane()); + // plug into the view so we get notified when the data changes + ::MSG_SetFEData(GetMessagePane(), CMailCallbackManager::Get()); + } + if (inFolder.GetFolderInfo() == MSG_GetThreadFolder(GetMessagePane())) + return; + +#if USING_MODAL_FOLDER_LOAD + CStr255 commandString; + ::GetIndString(commandString, 7099, 16); // "Loading Folder" + CMailProgressWindow::CreateModalParasite( + CMailProgressWindow::res_ID_modal, + GetMessagePane(), + commandString); +#endif + // Close any other windows showing this folder + CWindowIterator iter(WindowType_MailThread); + CThreadWindow* thisWindow + = dynamic_cast(LWindow::FetchWindowObject(GetMacPort())); + + CThreadWindow* window; + for (iter.Next(window); window; iter.Next(window)) + { + window = dynamic_cast(window); + if (window && window != thisWindow) + { + CMessageView* messageView = window->GetMessageView(); + if (inFolder.GetFolderInfo() == messageView->GetFolderInfo()) + window->AttemptClose(); + } + } +#if 1 + // IMAP mailboxes do "get new mail" automatically. So set up the flags to expect new + // mail. + if ((inFolder.GetFolderFlags() & MSG_FOLDER_FLAG_IMAPBOX) == MSG_FOLDER_FLAG_IMAPBOX) + ExpectNewMail(); +#else + // IMAP inbox does "get new mail" automatically. So set up the flags to expect new + // mail. + #define IMAP_INBOX (MSG_FOLDER_FLAG_INBOX | MSG_FOLDER_FLAG_IMAPBOX) + if ((inFolder.GetFolderFlags() & IMAP_INBOX) == IMAP_INBOX) + ExpectNewMail(); +#endif + // Now finally, load the folder + ThrowIfError_(::MSG_LoadFolder(GetMessagePane(), inFolder.GetFolderInfo())); + + #ifndef THREE_PANE + SwitchTarget(this); + #endif + + // Change the i18n encoding and font to match the new folder: + int foldercsid = ::MSG_GetFolderCSID(inFolder.GetFolderInfo()); + if ((CS_UNKNOWN == foldercsid) || (CS_DEFAULT == foldercsid)) + { + foldercsid = INTL_DefaultDocCharSetID(0); + ::MSG_SetFolderCSID(inFolder.GetFolderInfo(), foldercsid); + } + SetDefaultCSID(foldercsid); + ResetTextTraits(); // May need to change the font when we change folder + } + catch(...) + { + } +} // CThreadView::LoadMessageFolder + +//---------------------------------------------------------------------------------------- +Boolean CThreadView::RelocateViewToFolder(const CMessageFolder& inFolder) +// Retarget the view to the specified BE folder. +//---------------------------------------------------------------------------------------- +{ + if (!inFolder.GetFolderInfo() || inFolder == mXPFolder) + return false; + + if (CThreadWindow::FindAndShow(inFolder.GetFolderInfo()) == nil) + { + MSG_Master* master = CMailNewsContext::GetMailMaster(); + LoadMessageFolder(mContext, inFolder); + return true; + } + return false; +} // CThreadWindow::RelocateViewToFolder + + +//---------------------------------------------------------------------------------------- +void CThreadView::FileMessagesToSelectedPopupFolder(const char *inFolderName, + Boolean inMoveMessages) +// File selected messages to the specified BE folder. If inMoveMessages is true, move +// the messages, otherwise copy them. +//---------------------------------------------------------------------------------------- +{ + if (!inFolderName || !*inFolderName) + return; + CMailSelection selection; + if ( !GetSelection(selection) ) { + Assert_(false); // Method should not be called in this case! + return; // Nothing selected! + } + +#ifdef Debug_Signal + const char *curName = ::MSG_GetFolderNameFromID(GetOwningFolder()); + Assert_(strcasecomp(curName, inFolderName) != 0); // Specified folder should not be the same as this folder +#endif // Debug_Signal + + CStr255 commandString; + ::GetIndString(commandString, 7099, (inMoveMessages ? 15 : 19)); // "Moving/Copying Messages" + CMailProgressWindow* progressWindow = CMailProgressWindow::CreateModalParasite( + CMailProgressWindow::res_ID_modal, + GetMessagePane(), + commandString); + if (progressWindow) + progressWindow->SetDelay(0); + if ( inMoveMessages ) + ::MSG_MoveMessagesInto(selection.xpPane, selection.GetSelectionList(), selection.selectionSize, + inFolderName); + else + ::MSG_CopyMessagesInto(selection.xpPane, selection.GetSelectionList(), selection.selectionSize, + inFolderName); + mUndoCommand = cmd_Undo; +} // CThreadWindow::FileMessagesToSelectedPopupFolder + +//---------------------------------------------------------------------------------------- +Boolean CThreadView::ScrollToGoodPlace() +//---------------------------------------------------------------------------------------- +{ + if (mRows == 0) + return false; + MessageKey id; + MSG_ViewIndex index; + MSG_ViewIndex threadIndex; + MSG_ViewNavigate(GetMessagePane(), MSG_FirstNew, 0, &id, &index, &threadIndex, NULL); + if (index != MSG_VIEWINDEXNONE) + { + ScrollRowIntoFrame(mRows); // show bottom cell... + ScrollRowIntoFrame(index + 1); // ... then show first unread. + return true; + } + return false; +} // CThreadView::ScrollToGoodPlace + +//---------------------------------------------------------------------------------------- +void CThreadView::NoteSortByThreadColumn(Boolean isThreaded) const +//---------------------------------------------------------------------------------------- +{ + if (isThreaded) + GetTableHeader()->ChangeIconOfColumn(kThreadMessageColumn, kThreadedIcon); + else + GetTableHeader()->ChangeIconOfColumn(kThreadMessageColumn, kUnthreadedIcon); +} + +//---------------------------------------------------------------------------------------- +void CThreadView::ChangeSort(const LTableHeader::SortChange* inSortChange) +//---------------------------------------------------------------------------------------- +{ + CStandardFlexTable::ChangeSort(inSortChange); + if (GetMessagePane() != NULL) + { + MSG_CommandType sortCommand; + switch (inSortChange->sortColumnID) + { + case kDateMessageColumn: sortCommand = MSG_SortByDate; break; + case kSubjectMessageColumn: sortCommand = MSG_SortBySubject; break; + case kThreadMessageColumn: sortCommand = MSG_SortByThread; break; + case kSenderMessageColumn: + case kAddresseeMessageColumn: sortCommand = MSG_SortBySender; break; + + case kMarkedReadMessageColumn: sortCommand = MSG_SortByUnread; break; + case kPriorityMessageColumn: sortCommand = MSG_SortByPriority; break; + case kSizeMessageColumn: sortCommand = MSG_SortBySize; break; + case kStatusMessageColumn: sortCommand = MSG_SortByStatus; break; + case kFlagMessageColumn: sortCommand = MSG_SortByFlagged; break; + case kHiddenOrderReceivedColumn: sortCommand = MSG_SortByMessageNumber; break; + //case kUnreadMessageColumn: sortCommand = + //case kTotalMessageColumn: sortCommand = + + default: sortCommand = (MSG_CommandType) 0; + } + if (sortCommand != 0) + { + CMailProgressWindow::RemoveModalParasite(); + ::MSG_Command(GetMessagePane(), sortCommand, NULL, 0); + // Make sure the menus and headers reflect the new state of affairs + SyncSortToBackend(); + } + } +} // CThreadView::ChangeSort + +//---------------------------------------------------------------------------------------- +void CThreadView::DeleteSelection() +// Delete all selected messages +//---------------------------------------------------------------------------------------- +{ + try + { + CMailSelection selection; + if (GetSelection(selection)) + { +#if 0 + // Don't do this any more! Interrupting IMAP will cause a crash if the timing + // is just right. 98/03/31 + CThreadWindow* threadWindow + = dynamic_cast(LWindow::FetchWindowObject(GetMacPort())); + XP_ASSERT(threadWindow); + CMessageView* messageView + = dynamic_cast(threadWindow-> + FindPaneByID(CMessageView::class_ID)); + XP_ASSERT(messageView); + messageView->ObeyCommand(cmd_Stop, NULL); +#endif + // Try to close any open windows on these messages + const MSG_ViewIndex* index = selection.GetSelectionList(); + for (int i = 0; i < selection.selectionSize; i++,index++) + { + CMessage message((*index + 1), GetMessagePane()); + // (Add one to convert to TableIndexT) + CMessageWindow::CloseAll(message.GetMessageKey()); + } + MSG_CommandType cmd = + ((mXPFolder.GetFolderFlags() & MSG_FOLDER_FLAG_NEWSGROUP) == 0) + ? UMessageLibrary::GetMSGCommand(cmd_Clear) + : MSG_CancelMessage; // cancel my posting, if possible. + CMailProgressWindow::RemoveModalParasite(); + MSG_Command( + GetMessagePane(), + cmd, + (MSG_ViewIndex*)selection.GetSelectionList(), + selection.selectionSize); + UnselectAllCells(); + } + // The selection of a new message after one is removed is handled by a deferred task. + // We used to start this timer immediately on initiating the delete operation (here) + // but this ended up firing before the refresh occurred, resulting in two rows + // selected, which was ugly. So we moved the posting call to DrawCellContents, + // to make sure that the row isn't selected until after the refresh is done. 98/01/14 + } + catch(...) {} +} // CThreadView::DeleteSelection() + +//---------------------------------------------------------------------------------------- +void CThreadView::ChangeStarting( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount) +//---------------------------------------------------------------------------------------- +{ + if (mMysticPlane <= kMysticUpdateThreshHold + && (inChangeCode == MSG_NotifyScramble || inChangeCode == MSG_NotifyAll)) + { + try + { + // Get the selection, convert it to non-volatile MSG_FolderInfo... + CMailSelection selection; + if (GetSelection(selection)) + { + delete mSavedSelection; + mSavedSelection = new CPersistentMessageSelection(selection); + } + } + catch (...) {} + } + Inherited::ChangeStarting(inPane, inChangeCode, inStartRow, inRowCount); +} // CThreadView::ChangeStarting + +//---------------------------------------------------------------------------------------- +void CThreadView::ChangeFinished( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount) +//---------------------------------------------------------------------------------------- +{ + Inherited::ChangeFinished(inPane, inChangeCode, inStartRow, inRowCount); + if (mMysticPlane <= kMysticUpdateThreshHold) + { + switch (inChangeCode) + { + case MSG_NotifyScramble: + case MSG_NotifyAll: + { + SetNotifyOnSelectionChange(false); + try + { + UnselectAllCells(); + if (mSavedSelection) + { + const MSG_ViewIndex* index = mSavedSelection->GetSelectionList(); + MSG_ViewIndex count = mSavedSelection->selectionSize; + for (MSG_ViewIndex i = 0; i < count; i++, index++) + { + if (*index != MSG_VIEWINDEXNONE) + SelectRow(1 + *index); + } + } + } + catch (...) {} + SetNotifyOnSelectionChange(true); + SelectionChanged(); + delete mSavedSelection; + mSavedSelection = NULL; + } + break; + case MSG_NotifyInsertOrDelete: + if (CMailProgressWindow::GettingMail()) // could happen from the folder pane etc. + mExpectingNewMail = true; + if (mExpectingNewMail || inRowCount > 0) + mGotNewMail = true; + // If we're deleting the only selected row, we may need to select + // something later. Don't do this as a result of "get new mail", though. + // The intention here is that this behavior is only to happen as a result + // of an explicit user delete, on the current machine (Note that you may + // log on to another machine and delete the message that is currently + // selected. Note also that, during folder load, filters can cause a + // "row-deleted" message here.) + if (inRowCount == -1 // one row was deleted + && mSelectAfterDelete // the preference to do this is on + && GetSelectedRowCount() == 0 // there is nothing selected now + && inStartRow <= mRows + 1 // range check (+1 if deleting last row) + && !mExpectingNewMail) // this is not a folder load, or get mail + mRowToSelect = inStartRow; + break; + } + CMessage::InvalidateCache(); // most changes will make the index invalid. + } +} // CThreadView::ChangeFinished + +//---------------------------------------------------------------------------------------- +void CThreadView::SetFolder(const CMessageFolder& inFolder) +// Set the window title to the folder name. +//---------------------------------------------------------------------------------------- +{ + mXPFolder = inFolder; + + Boolean isNewsgroup = ((mXPFolder.GetFolderFlags() & MSG_FOLDER_FLAG_NEWSGROUP) != 0); + + char newName[255]; + const char* nameToUse = (inFolder.GetPrettyName() ? + inFolder.GetPrettyName() : inFolder.GetName()); + if (nameToUse) + { + strcpy(newName, nameToUse); + NET_UnEscape(newName); + } + else + *newName = '\0'; + + CThreadWindow* threadWindow + = dynamic_cast(LWindow::FetchWindowObject(GetMacPort())); + if (threadWindow) + threadWindow->SetFolderName(newName, isNewsgroup); +} // CThreadView::SetFolderName + +// ------------------------- +void CThreadView::SyncSortToBackend() +//--------------------------- +{ + // This is ugly it would be better if we did this in FindCommandStatus. + // since the BE could provide us the needed info. The difficutly would be in keeping + // the headers in sync. If done properly would help simplify FindCommandStatus + // and eliminate all this extra stuff + // Something to think about for 5.0 + MSG_Pane* inPane = GetMessagePane(); + XP_Bool selectable; + MSG_COMMAND_CHECK_STATE checkedState; + const char* display_string = nil; + MSG_CommandType cmd; + XP_Bool plural; + for (cmd = MSG_SortByDate; + cmd <= MSG_SortByUnread; + cmd = MSG_CommandType( int(cmd) + 1)) + { + ::MSG_CommandStatus( + inPane, cmd, nil, 0, &selectable, &checkedState, &display_string, &plural ); + if (checkedState == MSG_Checked) + break; + } + PaneIDT pane = 0; + switch ( cmd ) + { + case MSG_SortByDate: pane = kDateMessageColumn; break; + case MSG_SortBySubject: pane = kSubjectMessageColumn; break; + case MSG_SortBySender: pane = kSenderMessageColumn; break; + case MSG_SortByUnread: pane = kMarkedReadMessageColumn;break; + case MSG_SortByPriority: pane = kPriorityMessageColumn; break; + case MSG_SortBySize: pane = kSizeMessageColumn; break; + case MSG_SortByStatus: pane = kStatusMessageColumn; break; + case MSG_SortByFlagged: pane = kFlagMessageColumn; break; + case MSG_SortByMessageNumber: pane = kHiddenOrderReceivedColumn; break; + default: + case MSG_SortByThread: pane = kThreadMessageColumn; break; + } + NoteSortByThreadColumn(cmd == MSG_SortByThread); + UInt16 col = mTableHeader->ColumnFromID( pane ); + ::MSG_CommandStatus( + inPane, MSG_SortBackward, nil, 0, &selectable, &checkedState, + &display_string, &plural); + Boolean backwards = checkedState == MSG_Checked; + mTableHeader->SetWithoutSort( col, backwards, true ); + // Make sure the sort submenus on the menu bar are showing the correct state. + UpdateSortMenuCommands(); +} // CThreadView::SyncSortToBackend() + +//---------------------------------------------------------------------------------------- +void CThreadView::PaneChanged( + MSG_Pane* inPane, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value) +//---------------------------------------------------------------------------------------- +{ + switch (inNotifyCode) + { + case MSG_PaneNotifyFolderLoaded: + if (value > 0) + { + LWindow* win = LWindow::FetchWindowObject(GetMacPort()); + if (win) + win->Show(); + SetFolder(CMessageFolder((MSG_FolderInfo*)value)); + CMessage::InvalidateCache(); + SetRowCount(); + Refresh(); + } + // This is the first opportunity to do stuff that needs the folder etc. + // Update history entry and load an empty message into the message view. + // Calling SelectionChanged will do these things. + + SelectionChanged(); + + EnableStopButton(false); + if (mMotionPendingCommand != (MSG_MotionType)-1) + { + ObeyMotionCommand( mMotionPendingCommand ); + mMotionPendingCommand = (MSG_MotionType)-1; + } +#ifdef PREFERENCE_IMPLEMENTED + if (!RestoreSelectedMessage()) + ScrollToGoodPlace(); +#else + RestoreSelectedMessage(); // select the last selected message AND + mScrollToShowNew = true; + //ScrollToGoodPlace(); // scroll to show anything new. +#endif + DontExpectNewMail(); + // break; NO! Fall through, because we need to update the rowcount and sort order. + case MSG_PaneNotifyFolderInfoChanged: + if (mXPFolder == (MSG_FolderInfo*)value) + { + SyncSortToBackend(); + CMessage::InvalidateCache(); + } + mXPFolder.FolderInfoChanged(); + break; + case MSG_PaneProgressDone: // comes from progress window + if ((MessageT)value == msg_NSCAllConnectionsComplete) + { + if (GotNewMail()) // ie in THIS folder in THIS operation + { + CMessage::InvalidateCache(); + SetRowCount(); + Refresh(); + ScrollToGoodPlace(); + } + if (CMailProgressWindow::GettingMail()) + DontExpectNewMail(); + } + break; + case MSG_PaneNotifyFolderDeleted: + if ((MSG_FolderInfo*)value == mXPFolder.GetFolderInfo()) + { + RemoveAllRows( false ); + ::MSG_LoadFolder(GetMessagePane(), nil); + mXPFolder.SetFolderInfo(nil); // prevent crashes + } + break; + default: + Inherited::PaneChanged(inPane, inNotifyCode, value); + } +} // CThreadView::PaneChanged + +//---------------------------------------------------------------------------------------- +void CThreadView::ResizeFrameBy( + Int16 inWidthDelta, + Int16 inHeightDelta, + Boolean inRefresh) +//---------------------------------------------------------------------------------------- +{ + Inherited::ResizeFrameBy(inWidthDelta, inHeightDelta, inRefresh); + ScrollSelectionIntoFrame(); +} // CThreadView::ResizeFrameBy + +//---------------------------------------------------------------------------------------- +void CThreadView::FindSortCommandStatus(CommandT inCommand, Int16& outMark) +//---------------------------------------------------------------------------------------- +{ + PaneIDT paneID = GetSortedColumn(); + outMark = 0; + switch (inCommand) + { + case cmd_SortByDate: + if (paneID == kDateMessageColumn) + outMark = checkMark; + break; + case cmd_SortBySubject: + if (paneID == kSubjectMessageColumn) + outMark = checkMark; + break; + case cmd_SortBySender: + if (paneID == kSenderMessageColumn || paneID == kAddresseeMessageColumn) + outMark = checkMark; + break; + case cmd_SortByThread: + if (paneID == kThreadMessageColumn) + outMark = checkMark; + break; + case cmd_SortByPriority: + if (paneID == kPriorityMessageColumn) + outMark = checkMark; + break; + case cmd_SortBySize: + if (paneID == kSizeMessageColumn) + outMark = checkMark; + break; + case cmd_SortByReadness: + if (paneID == kMarkedReadMessageColumn) + outMark = checkMark; + break; + case cmd_SortByStatus: + if (paneID == kStatusMessageColumn) + outMark = checkMark; + break; + case cmd_SortByFlagged: + if (paneID == kFlagMessageColumn) + outMark = checkMark; + break; + case cmd_SortByOrderReceived: + if (paneID == kHiddenOrderReceivedColumn) + outMark = checkMark; + break; + case cmd_SortAscending: + case cmd_SortDescending: + outMark = + ((inCommand == cmd_SortDescending) == IsSortedBackwards()) ? + checkMark : 0; + break; + } +} // CThreadView::FindSortCommandStatus + +// ------------------------- +void CThreadView::UpdateSortMenuCommands() const +//--------------------------- +{ + list commandsToUpdate; + commandsToUpdate.push_front(cmd_SortByDate); + commandsToUpdate.push_front(cmd_SortBySubject); + commandsToUpdate.push_front(cmd_SortBySender); + commandsToUpdate.push_front(cmd_SortByThread); + commandsToUpdate.push_front(cmd_SortByPriority); + commandsToUpdate.push_front(cmd_SortBySize); + commandsToUpdate.push_front(cmd_SortByReadness); + commandsToUpdate.push_front(cmd_SortByStatus); + commandsToUpdate.push_front(cmd_SortByFlagged); + commandsToUpdate.push_front(cmd_SortByOrderReceived); + commandsToUpdate.push_front(cmd_SortAscending); + commandsToUpdate.push_front(cmd_SortDescending); + CTargetedUpdateMenuRegistry::SetCommands(commandsToUpdate); + CTargetedUpdateMenuRegistry::UpdateMenus(); +} // CThreadView::UpdateSortMenuCommands + +//---------------------------------------------------------------------------------------- +Boolean CThreadView::ObeySortCommand(CommandT inCommand) +//---------------------------------------------------------------------------------------- +{ + PaneIDT oldColumnID = GetSortedColumn(); + Boolean oldReverse = IsSortedBackwards(); + PaneIDT newColumnID; + Boolean newReverse = oldReverse; + switch (inCommand) + { + case cmd_SortByDate: + newColumnID = kDateMessageColumn; + break; + case cmd_SortBySubject: + newColumnID = kSubjectMessageColumn; + break; + case cmd_SortBySender: + newColumnID = kSenderMessageColumn; + break; + case cmd_SortByThread: + newColumnID = kThreadMessageColumn; + break; + case cmd_SortByPriority: + newColumnID = kPriorityMessageColumn; + break; + case cmd_SortBySize: + newColumnID = kSizeMessageColumn; + break; + case cmd_SortByReadness: + newColumnID = kMarkedReadMessageColumn; + break; + case cmd_SortByStatus: + newColumnID = kStatusMessageColumn; + break; + case cmd_SortByFlagged: + newColumnID = kFlagMessageColumn; + break; + case cmd_SortByOrderReceived: + newColumnID = kHiddenOrderReceivedColumn; + break; + case cmd_SortAscending: + case cmd_SortDescending: + newColumnID = GetSortedColumn(); + newReverse = (cmd_SortDescending == inCommand); + break; + default: + return false; + } + if (oldColumnID != newColumnID || oldReverse != newReverse) + { + mTableHeader->SetSortedColumn(mTableHeader->ColumnFromID(newColumnID), newReverse, true); + } + return true; +} // CThreadView::ObeySortCommand + +//---------------------------------------------------------------------------------------- +void CThreadView::FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +//---------------------------------------------------------------------------------------- +{ + outEnabled =false; // This is probably unnecessary, I think PP does it before calling. + if (::StillDown()) + { + // Assume this is a call from the context menu attachment. + Point whereLocal; + ::GetMouse(&whereLocal); + SPoint32 imagePoint; + LocalToImagePoint(whereLocal, imagePoint); + STableCell hitCell; + if (GetCellHitBy(imagePoint, hitCell)) + { + PaneIDT cellType = GetCellDataType(hitCell); + if (cellType == kPriorityMessageColumn) + return; // disable all commands, because we do it ourselves. + // Fix me: there's no reason why the priority popup can't be done + // using the context menu mechanism now. + } + } + if (inCommand == cmd_Stop && mStillLoading) + { + outEnabled = true; // stop button on, nothing else. + return; + // ... otherwise, fall through and pass it up to the window + } + // Note: for cmd_Stop, the window may also be able to handle the command, if + // a message pane exists. + CMailSelection selection; + GetSelection(selection); + switch (inCommand) + { + // Single-selection items + case cmd_SelectThread: + outEnabled = selection.selectionSize == 1; + return; + // Items always available + case cmd_SubscribeNewsgroups: + outEnabled = true; + return; + case cmd_AddToBookmarks: + outEnabled = (mXPFolder.GetFolderFlags() & MSG_FOLDER_FLAG_IMAPBOX) == 0; + return; + case cmd_SelectMarkedMessages: + case cmd_MarkReadByDate: + case cmd_NewFolder: + outEnabled = true; + outUsesMark = false; + return; + case cmd_GetMoreMessages: + CStr255 cmdStr; + CStr255 numStr; + ::GetIndString(cmdStr, kMailNewsMenuStrings, kNextChunkMessagesStrID); + ::NumToString(CPrefs::GetLong(CPrefs::NewsArticlesMax), numStr); + cmdStr += numStr; + memcpy(outName, (StringPtr)cmdStr, cmdStr.Length() + 1); + break; + } + // Now check for message library commands. Remember (###), some of these are + // handled by CMessageView(), and that might be our super commander, so + // return here only if msglib enables the item for the thread pane. + // Also don't want the message pane to only deal with cases where 1 message is selected + if (FindMessageLibraryCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName) + && ( outEnabled || selection.selectionSize != 1) ) + return; + MSG_MotionType cmd = UMessageLibrary::GetMotionType(inCommand); + if (UMessageLibrary::IsValidMotion(cmd)) + { + XP_Bool selectable; + outEnabled = ( + GetMessagePane() + && MSG_NavigateStatus( + GetMessagePane(), + cmd, + selection.GetSelectionList() ? *selection.GetSelectionList() : MSG_VIEWINDEXNONE, + &selectable, + NULL) == 0 + && selectable + ); + outUsesMark = false; + if (outEnabled) // ONLY in this case. See the previous comment (###). + return; + } + if ( (inCommand == cmd_MoveMailMessages) || (inCommand == cmd_CopyMailMessages) || + CMailFolderSubmenu::IsMailFolderCommand(&inCommand) ) { + // Mail folder commands + outEnabled = (GetSelectedRowCount() > 0); + return; + } + // Default for if and switch: fall through into second switch + switch (inCommand) + { + case cmd_GetInfo: + outEnabled = false; // FIXME: implement it. + break; + case cmd_SortByDate: + case cmd_SortBySubject: + case cmd_SortBySender: + case cmd_SortByThread: + case cmd_SortByPriority: + case cmd_SortBySize: +// case cmd_SortByTotal: + case cmd_SortByReadness: + case cmd_SortByStatus: + case cmd_SortByFlagged: + case cmd_SortByOrderReceived: + case cmd_SortAscending: + case cmd_SortDescending: + if (!mStillLoading) + { + outUsesMark = true; + outEnabled = true; + FindSortCommandStatus(inCommand, outMark); + } + break; + default: + if (inCommand >= ENCODING_BASE && inCommand < ENCODING_CEILING) + { + if (mContext) + { + outUsesMark = true; + outEnabled = true; + + int16 csid = CPrefs::CmdNumToDocCsid( inCommand ); + outMark = (csid == mContext->GetDefaultCSID()) ? 0x12 : ' '; + } + } + else + { + // if (inCommand == cmd_OpenSelection) + // outEnabled = false; + // else + Inherited::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); + } + } +} // CThreadView::FindCommandStatus + +//---------------------------------------------------------------------------------------- +void CThreadView::SelectAfterDelete(TableIndexT row) +// Called from ObeyCommand after command completion. +// In "expanded mode", the user probably wants a new message to +// be loaded into the message pane after a deletion. +// So detect the case when a message was showing and the message has +// been moved from the folder. We want to display a new message. +//---------------------------------------------------------------------------------------- +{ + SetUpdateCommandStatus(true); // Always do this. + if (row > 0) + SelectRow(row > mRows ? mRows : row); +} // CThreadView::SelectAfterDelete + +//---------------------------------------------------------------------------------------- +Boolean CThreadView::ObeyMotionCommand(MSG_MotionType cmd) +//---------------------------------------------------------------------------------------- +{ + if (!GetMessagePane()) return false; + Assert_(UMessageLibrary::IsValidMotion(cmd)); + try + { + CMailSelection selection; + GetSelection(selection); + MSG_ViewIndex resultIndex; + MessageKey resultKey = MSG_MESSAGEKEYNONE; + MSG_FolderInfo* finfo; + if (MSG_ViewNavigate( + GetMessagePane(), + cmd, + selection.GetSelectionList() ? *selection.GetSelectionList() : MSG_VIEWINDEXNONE, + &resultKey, + &resultIndex, + NULL, + &finfo) == 0) + { + if (resultKey != MSG_MESSAGEKEYNONE) + { + SetNotifyOnSelectionChange(false); + UnselectAllCells(); + SetNotifyOnSelectionChange(true); + if (finfo && finfo != GetOwningFolder()) + RelocateViewToFolder(finfo); + SelectMessageWhenReady(resultKey); + } + else if (finfo) + { + switch( cmd ) + { + case MSG_NextFolder: + case MSG_NextMessage: + mMotionPendingCommand = MSG_FirstMessage; + break; + case MSG_NextUnreadMessage: + case MSG_NextUnreadThread: + case MSG_NextUnreadGroup: + case MSG_LaterMessage: + case (MSG_MotionType)MSG_ToggleThreadKilled: + mMotionPendingCommand = MSG_NextUnreadMessage; + break; + default: + break; + } + RelocateViewToFolder( finfo ); + } + return true; + } + mUndoCommand = cmd_Undo; + } + catch (...) + { + SysBeep(1); + } + return false; +} // CThreadView::ObeyMotionCommand + +//---------------------------------------------------------------------------------------- +void CThreadView::SelectionChanged() +//---------------------------------------------------------------------------------------- +{ + Inherited::SelectionChanged(); +} // CThreadView::SelectionChanged + +//---------------------------------------------------------------------------------------- +void CThreadView::SelectMessageWhenReady(MessageKey inKey) +//---------------------------------------------------------------------------------------- +{ + if (mStillLoading || (!GetMessagePane())) + CDeferredTaskManager::Post1(new CDeferredSelectKeyTask(this, inKey), this); + else + SelectMessage(inKey); +} // CThreadView::SelectMessageWhenReady + +//---------------------------------------------------------------------------------------- +void CThreadView::SelectMessage(MessageKey inKey) +//---------------------------------------------------------------------------------------- +{ + Assert_(GetMessagePane()); + if (GetMessagePane()) + { + MSG_ViewIndex index = ::MSG_GetMessageIndexForKey(GetMessagePane(), inKey, true); + if (index != MSG_VIEWINDEXNONE) + SelectRow(index + 1); + } +} // CThreadView::SelectMessage + +//---------------------------------------------------------------------------------------- +void CThreadView::UpdateHistoryEntry() +//---------------------------------------------------------------------------------------- +{ + TableIndexT rowCount = GetSelectedRowCount(); + MSG_Pane* pane = GetMessagePane(); + if (!pane) + return; // e.g., during window creation + URL_Struct* url = nil; + char entryName[64]; + entryName[0] = '\0'; + if (rowCount == 1) + { +#if 0 // Auto-scroll should not be done here (+ it was redundant) + if (!CApplicationEventAttachment::CurrentEventHasModifiers(cmdKey) && + !CApplicationEventAttachment::CurrentEventHasModifiers(shiftKey)) + { + STableCell cell = GetFirstSelectedCell(); + if (IsValidCell(cell)) + ScrollCellIntoFrame(cell); + } +#endif + CMailSelection selection; + GetSelection( selection ); + CMessage message(*selection.GetSelectionList() + 1, selection.xpPane); + MessageKey id = message.GetMessageKey(); + url = ::MSG_ConstructUrlForMessage(pane, id); + message.GetSubject(entryName, sizeof(entryName)); + } + else + { + // zero selection or multiple selection - use folder for history entry + url = CreateURLForProxyDrag(entryName); + } + if (url && *entryName) + { + LO_DiscardDocument(*mContext); + History_entry* theNewEntry = ::SHIST_CreateHistoryEntry( + url, + entryName); + ::SHIST_AddDocument(*mContext, theNewEntry); + } + XP_FREEIF(url); +} // CThreadView::UpdateHistoryEntry + +//---------------------------------------------------------------------------------------- +Boolean CThreadView::ObeyMarkReadByDateCommand() +//---------------------------------------------------------------------------------------- +{ + StDialogHandler handler(10525, NULL); + LWindow* dlog = handler.GetDialog(); + if (! dlog) + return true; + + CSearchDateField* dateField = dynamic_cast + (dlog->FindPaneByID(CSearchDateField::class_ID)); + if (dateField) + { + dateField->SetToToday(); + MessageT message; + do + { + message = handler.DoDialog(); + } while (message != msg_OK && message != msg_Cancel); + if (message == msg_OK) + { + Int16 outYear; UInt8 outMonth, outDay; + dateField->GetDate(&outYear, &outMonth, &outDay); + + tm time; + time.tm_sec = 1; + time.tm_min = 1; + time.tm_hour = 1; + time.tm_mday = outDay; + time.tm_mon = outMonth - 1; + time.tm_year = outYear - 1900; + time.tm_wday = -1; + time.tm_yday = -1; + time.tm_isdst = -1; + + time_t endDate = ::mktime(&time); + MSG_MarkReadByDate(GetMessagePane(), 0, endDate); + } + } + return true; +} // CThreadView::ObeyMarkReadByDateCommand + +//---------------------------------------------------------------------------------------- +void CThreadView::DoSelectThread(TableIndexT inSelectedRow) +// Select all messages belonging to the same thread as this row. +//---------------------------------------------------------------------------------------- +{ + CMessage message(inSelectedRow, GetMessagePane()); + MSG_ViewIndex threadIndex = MSG_ThreadIndexOfMsg(GetMessagePane(), message.GetMessageKey()); + if (threadIndex == MSG_VIEWINDEXNONE) + return; + TableIndexT first = 1 + threadIndex; + STableCell firstCell(first, 1); + TableIndexT last = first + CountExtraRowsControlledByCell(firstCell); + Assert_(first < last || first == inSelectedRow); + if (LTableRowSelector * selector = dynamic_cast(mTableSelector)) + { + // Powerplant's ClickSelect has already been called, so use the sense of the + // clicked cell and turn all other cells in the thread on or off to match. + STableCell clickedCell(inSelectedRow, 1); + Boolean doSelect = selector->CellIsSelected(clickedCell); + SetNotifyOnSelectionChange(false); + try + { + if (!CApplicationEventAttachment::CurrentEventHasModifiers(shiftKey|cmdKey)) + UnselectAllCells(); + for (TableIndexT i = first; i <= last; i++) + selector->DoSelect(i, doSelect, true, false); + } + catch (...) {} + SetNotifyOnSelectionChange(true); + SelectionChanged(); + } +} // CThreadView::DoSelectThread + +//---------------------------------------------------------------------------------------- +void CThreadView::DoSelectFlaggedMessages() +//---------------------------------------------------------------------------------------- +{ + SetNotifyOnSelectionChange(false); + try + { + if (!CApplicationEventAttachment::CurrentEventHasModifiers(shiftKey|cmdKey)) + UnselectAllCells(); + for (TableIndexT i = 1; i <= mRows; i++) + { + CMessage message(i, GetMessagePane()); + if (message.IsFlagged()) + SelectCell(STableCell(i, 1)); + } + } + catch (...) {} + SetNotifyOnSelectionChange(true); + SelectionChanged(); +} // CThreadView::DoSelectFlaggedMessages + +//---------------------------------------------------------------------------------------- +Boolean CThreadView::ObeyCommand( + CommandT inCommand, + void *ioParam) +//---------------------------------------------------------------------------------------- +{ + if (!mContext) + return false; + + if (inCommand == cmd_UnselectAllCells) + { + UnselectAllCells(); + return true; + } + + if (inCommand != cmd_Stop && XP_IsContextBusy((MWContext*)(*mContext))) + return LCommander::GetTopCommander()->ObeyCommand(inCommand, ioParam); // global commands OK. + if (inCommand == msg_TabSelect) + return (GetOwningFolder() != nil); // Allow selection only if a folder is loaded. + + Boolean commandHandled = false; + CommandT originalCommand = mContext->GetCurrentCommand(); + CNSContext* originalContext = mContext; // in case we close the window & delete it! + mContext->SetCurrentCommand(inCommand); + if (inCommand == cmd_GetNewMail || inCommand == cmd_GetMoreMessages) + { + ExpectNewMail(); + + // getting new messages: slow down the status bar + // to reduce flickers and improve performance + CThreadWindow* threadWindow + = dynamic_cast(LWindow::FetchWindowObject(GetMacPort())); + if (threadWindow) + threadWindow->GetProgressListener()->SetLaziness( + CProgressListener::lazy_VeryButForThisCommandOnly); + } + // Don't let msgLib do the deletion since we want(?) to close open mail windows + if ( inCommand == cmd_Clear ) + { + DeleteSelection(); + return true; + } + if ( inCommand == cmd_NewFolder ) + { + UFolderDialogs::ConductNewFolderDialog(GetOwningFolder()); + return true; + } + + // If you're reading a news group, and you want to compose a new message, the new message + // is, by default, addressed to that news group... this test and reassignment are necessary + // to make this happen + if ( (inCommand == cmd_NewMailMessage) && (GetFolderFlags() & MSG_FOLDER_FLAG_NEWSGROUP) ) { + inCommand = cmd_PostNew; + } + + // For msglib commands, we have to be careful to check whether the command + // can be handled for THIS pane, because the message pane might have + // enabled the menu item. Failing to check again here leads to a nasty + // crash. + Boolean enabled; Boolean usesMark; Char16 mark; Str255 name; + if (FindMessageLibraryCommandStatus(inCommand, enabled, usesMark, mark, name) + && enabled) + { + commandHandled = ObeyMessageLibraryCommand(inCommand, ioParam); + if (inCommand == cmd_Undo || inCommand == cmd_Redo) + { + UnselectAllCells(); + mUndoTask = new CDeferredUndoTask(this); + CDeferredTaskManager::Post1(mUndoTask, this); + } + else if (mUndoTask) + CDeferredTaskManager::Remove(mUndoTask, this); + } + if (!commandHandled) + { + MSG_MotionType mcmd = UMessageLibrary::GetMotionType(inCommand); + if (UMessageLibrary::IsValidMotion(mcmd)) + commandHandled = ObeyMotionCommand(mcmd); + if (!commandHandled) + { + // Mail folder commands. We come here either from a button (broadcast) or + // from a menu command (synthetic). + const char* folderPath = nil; + if (inCommand == cmd_MoveMailMessages || inCommand == cmd_CopyMailMessages) + { + // Button case + if ( ioParam ) // The BE dereferences this ASAP + folderPath = ::MSG_GetFolderNameFromID((MSG_FolderInfo*)ioParam); + } + else if (!CMailFolderSubmenu::IsMailFolderCommand(&inCommand, &folderPath)) // menu case + { + folderPath = nil; // any other case. + } + if (folderPath && *folderPath) + { + FileMessagesToSelectedPopupFolder( + folderPath, + inCommand == cmd_MoveMailMessages); + commandHandled = true; + } + } + } + if (!commandHandled) switch(inCommand) + { + case cmd_SubscribeNewsgroups: + MSG_Host* selectedHost = MSG_GetHostForFolder(GetOwningFolder()); + CNewsSubscriber::DoSubscribeNewsGroup(selectedHost); + break; + case cmd_MarkReadByDate: + ObeyMarkReadByDateCommand(); + commandHandled = true; + break; +#if 0 + case cmd_RelocateViewToFolder: + // Don't handle this in the three-pane view. The folder pane has to change, too, + // so pass it up to CThreadWindow and let it delegate. + commandHandled = RelocateViewToFolder((MSG_FolderInfo*)ioParam); + break; +#endif // 0 + case cmd_SortByDate: + case cmd_SortBySubject: + case cmd_SortBySender: + case cmd_SortByThread: + case cmd_SortByPriority: + case cmd_SortBySize: + case cmd_SortByStatus: + case cmd_SortByFlagged: + case cmd_SortByOrderReceived: + case cmd_SortByReadness: + case cmd_SortAscending: + case cmd_SortDescending: + if (ObeySortCommand(inCommand)) + commandHandled = true; + break; + case cmd_SelectThread: + CMailSelection selection; + GetSelection( selection ); + TableIndexT selectedRow = *selection.GetSelectionList() + 1; + DoSelectThread(selectedRow); + break; + case cmd_SelectMarkedMessages: + DoSelectFlaggedMessages(); + break; + } // switch + if (!commandHandled && inCommand > ENCODING_BASE && inCommand < ENCODING_CEILING) + { + Int16 default_csid = CPrefs::CmdNumToDocCsid(inCommand); + SetDefaultCSID(default_csid); + LCommander::SetUpdateCommandStatus(true); // bug #80474 + commandHandled = true; + } + if (!commandHandled) + commandHandled = Inherited::ObeyCommand(inCommand, ioParam); + //----------------------------------- + // Cleanup + //----------------------------------- + // The following test against originalContext protects against the cases (quit, close) + // when the object has been deleted. The test against cmdHandled protects against + // re-entrant calls to ListenToMessage. + if (mContext == originalContext) + if (commandHandled) + mContext->SetCurrentCommand(cmd_Nothing); // watch out for re-entrant broadcast msgs. + else + { + // It wasn't a command, so restore damage done if we're processing a broadcast. + mContext->SetCurrentCommand(originalCommand); + } + return commandHandled; +} // CThreadView::ObeyCommand + +//---------------------------------------------------------------------------------------- +void CThreadView::ResetTextTraits() +//---------------------------------------------------------------------------------------- +{ + Int16 wincsid = mContext->GetWinCSID(); + ResIDT newTextTraitsID; + // ## Begin Hacky Code copy from Akbar (v3.0) mnews.cp + // This will make everyone happy. + // If the window is MAC_ROMAN, it will use the default mnews font. + // Otherwise, get it from the Font preference + if ( wincsid == INTL_CharSetNameToID(INTL_ResourceCharSet()) ) + newTextTraitsID = 130; + else + newTextTraitsID = CPrefs::GetTextFieldTextResIDs(wincsid); + // ## End of Hacky Code copy from Akbar (v3.0) mnews.cp + + if (newTextTraitsID != mTextTraitsID ) + { + SetTextTraits(newTextTraitsID); + Refresh(); + } +} // CThreadView::ResetTextTraits + +//---------------------------------------------------------------------------------------- +Int16 CThreadView::DefaultCSIDForNewWindow(void) +//---------------------------------------------------------------------------------------- +{ + if (mContext) + return mContext->GetDefaultCSID(); + return 0; +} // CThreadView::DefaultCSIDForNewWindow + +//---------------------------------------------------------------------------------------- +void CThreadView::SetDefaultCSID(Int16 default_csid) +//---------------------------------------------------------------------------------------- +{ + // Set the csid in the context + mContext->SetDefaultCSID(default_csid); + mContext->SetWinCSID(INTL_DocToWinCharSetID(default_csid)); + + // Set the csid for the folder + MSG_SetFolderCSID(GetOwningFolder(), default_csid); + + // We need to set the csid for the view with the window + // Ask the CThreadWindow to do it. + CThreadWindow* threadWindow + = dynamic_cast(LWindow::FetchWindowObject(GetMacPort())); + if (threadWindow) + threadWindow->SetDefaultCSID(default_csid); + + // We need to change the text info and redraw all the header + ResetTextTraits(); + + // If we have Message Window appear, we need to set the csid for it. + try { + // Find the all the MessageWindow which view the some folder + // and set the default csid for its view. + + CWindowIterator iter(WindowType_Message); + CMessageWindow* window; + for (iter.Next(window); window; iter.Next(window)) + { + window = dynamic_cast(window); + if (window) + { + CMessageView* messageView = window->GetMessageView(); + if (GetOwningFolder() == messageView->GetFolderInfo()) + { + messageView->SetDefaultCSID(default_csid); + } + } + } + } + catch( ... ) + { + } + +} // CThreadView::SetDefaultCSID + +// ¥¥ÊFix me, this should go into some utility classes +//---------------------------------------------------------------------------------------- +void CThreadView::DrawUTF8TextString( + const char* inText, + const FontInfo* inFontInfo, + SInt16 inMargin, + const Rect& inBounds, + SInt16 inJustification, + Boolean inDoTruncate, + TruncCode inTruncWhere) +//---------------------------------------------------------------------------------------- +{ + Rect r = inBounds; + + r.left += inMargin; + r.right -= inMargin; + + PlaceUTF8TextInRect(inText, + strlen(inText), + r, + inJustification, + teCenter, + inFontInfo, + inDoTruncate, + inTruncWhere ); +} + +// ¥¥ÊFix me, this should go into some utility classes +//---------------------------------------------------------------------------------------- +void CThreadView::PlaceUTF8TextInRect( + const char* inText, + Uint32 inTextLength, + const Rect &inRect, + Int16 inHorizJustType, + Int16 inVertJustType, + const FontInfo* /*inFontInfo*/, + Boolean inDoTruncate, + TruncCode /*inTruncWhere*/) +//---------------------------------------------------------------------------------------- +{ + FontInfo utf8FontInfo; + UFontSwitcher *fs; + UMultiFontTextHandler *th; + th = UUTF8TextHandler::Instance(); + fs = UPropFontSwitcher::Instance(); + th->GetFontInfo(fs, &utf8FontInfo); + + const char* text = inText; + short length = inTextLength; + if (inDoTruncate) + { + // ¥¥ Fix ME: Don't know how to do text truncation for UTF8 now. + } + Point thePoint = UGraphicGizmos::CalcStringPosition( + inRect, + th->TextWidth(fs, (char*)text, length), + inHorizJustType, + inVertJustType, + &utf8FontInfo); + ::MoveTo(thePoint.h, thePoint.v); + th->DrawText(fs, (char*)text ,length); +} // CThreadView::PlaceUTF8TextInRect + +//---------------------------------------------------------------------------------------- +void CThreadView::ListenToMessage( + MessageT inCommand, + void *ioParam) +//---------------------------------------------------------------------------------------- +{ + // Check ObeyCommand first, for a button message, but ONLY IF WE'RE ON DUTY. + if (!IsOnDuty() || !ObeyCommand((CommandT)inCommand, ioParam)) // button message? + { + Inherited::ListenToMessage(inCommand, ioParam); + switch (inCommand) + { + case msg_NSCAllConnectionsComplete: + { +// if (mPendingCommand) +// { +// CommandT cmd = mPendingCommand; +// mPendingCommand = 0; +// ObeyCommand(cmd, nil); +// +// } +// else +#ifdef REMOVED_870427 + if (GotNewMail()) + { + ScrollToGoodPlace(); + DontExpectNewMail(); + } + break; +#endif + if (GetContext()->GetCurrentCommand() == cmd_GetNewMail) + DontExpectNewMail(); + } + } // switch + } +} // CThreadView::ListenToMessage + +//---------------------------------------------------------------------------------------- +void CThreadView::ActivateSelf() +//---------------------------------------------------------------------------------------- +{ + Inherited::ActivateSelf(); +} // CThreadView::ActivateSelf + +//---------------------------------------------------------------------------------------- +void CThreadView::ObeyCommandWhenReady(CommandT inCommand) +//---------------------------------------------------------------------------------------- +{ + if (inCommand != cmd_Nothing) + CDeferredTaskManager::Post(new CDeferredThreadViewCommand(this, inCommand, nil), this); +} + +//---------------------------------------------------------------------------------------- +URL_Struct* CThreadView::CreateURLForProxyDrag(char* outTitle) +//---------------------------------------------------------------------------------------- +{ + MSG_Pane* pane = GetMessagePane(); + if (!pane) + return nil; + MSG_FolderInfo* folderInfo = GetOwningFolder(); + if (!folderInfo) + return nil; + CMessageFolder folder(folderInfo); + if (outTitle) + { + strcpy(outTitle, folder.GetName()); + NET_UnEscape(outTitle); + } + return ::MSG_ConstructUrlForFolder(pane, folderInfo); +} // CThreadView::CreateURLForProxyDrag + +#if defined(QAP_BUILD) +//---------------------------------------------------------------------------------------- +void CThreadView::GetQapRowText( + TableIndexT inRow, + char* outText, + UInt16 inMaxBufferLength) const +// Calculate the text and (if ioRect is not passed in as null) a rectangle that fits it. +// Return result indicates if any of the text is visible in the cell +//---------------------------------------------------------------------------------------- +{ + if (!outText || inMaxBufferLength == 0) + return; + + cstring rowText(""); + short colCount = mTableHeader->CountVisibleColumns(); + CMessage message(inRow, GetMessagePane()); + + CMailNewsWindow * myWindow = dynamic_cast(LWindow::FetchWindowObject(GetMacPort())); + if (!myWindow) return; + + for (short col = 1; col <= colCount; col ++) + { + STableCell aCell(inRow, col); + LTableHeader::SColumnData * colData = mTableHeader->GetColumnData(col); + if (!colData) break; + LPane * colPane = myWindow->FindPaneByID(colData->paneID); + if (!colPane) break; + + // get column name + CStr255 descriptor; + switch (GetCellDataType(aCell)) + { + case kThreadMessageColumn: descriptor = "Thread"; break; + case kMarkedReadMessageColumn: descriptor = "MarkRead"; break; + case kFlagMessageColumn: descriptor = "Flag"; break; + default: + colPane->GetDescriptor(descriptor); + break; + } + rowText += descriptor; + rowText += "=\042"; + + // add cell text + switch (PaneIDT dataType = GetCellDataType(aCell)) + { + case kThreadMessageColumn: + if (message.IsThread()) + { + if (message.IsOpenThread()) + rowText += "-"; + else + rowText += "+"; + } + else + rowText += " "; + break; + + // note: no intl conversions (yet?) for subject, sender and addressee + case kMarkedReadMessageColumn: rowText += (message.HasBeenRead() ? "+" : " "); break; + case kFlagMessageColumn: rowText += (message.IsFlagged() ? "+" : " "); break; + case kSubjectMessageColumn: rowText += message.GetSubject(); break; + case kSenderMessageColumn: rowText += message.GetSender(); break; + case kDateMessageColumn: rowText += message.GetDateString(); break; + case kPriorityMessageColumn: rowText += message.GetPriorityStr(); break; + case kSizeMessageColumn: rowText += message.GetSizeStr(); break; + case kStatusMessageColumn: rowText += message.GetStatusStr(); break; + case kAddresseeMessageColumn: rowText += message.GetAddresseeString(); break; + + case kTotalMessageColumn: + case kUnreadMessageColumn: + if (GetSortedColumn() == kThreadMessageColumn && message.IsThread()) + { + int theNum = (dataType == kTotalMessageColumn + ? message.GetNumChildren() + : message.GetNumNewChildren()); + char tempStr[32]; + sprintf(tempStr, "%d", theNum); + rowText += tempStr; + } + break; + } + if (col < colCount) + rowText += "\042 | "; + else + rowText += "\042\r"; + } + strncpy(outText, (char*)rowText, inMaxBufferLength); + outText[inMaxBufferLength - 1] = '\0'; +} // CThreadView::GetQapRowText +#endif //QAP_BUILD diff --git a/mozilla/cmd/macfe/MailNews/CThreadView.h b/mozilla/cmd/macfe/MailNews/CThreadView.h new file mode 100644 index 00000000000..870dc890fb1 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CThreadView.h @@ -0,0 +1,387 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CThreadView.h + +#pragma once + +// PowerPlant +#include +#include +#include + +#include "msgcom.h" + +// Mac Netscape Lib +#include "CMailFlexTable.h" +#include "LTableHeader.h" +#include "NetscapeDragFlavors.h" + +// MacFE +#include "CMailNewsContext.h" +#include "CMessageFolder.h" + +class CMessageWindow; +class CDeferredUndoTask; +class CPersistentMessageSelection; + +//====================================== +class CMessage +//====================================== +{ +public: + CMessage(TableIndexT inIndex, MSG_Pane* mMessageList); + virtual ~CMessage(); + + // Sometimes the new mMessageList has the same + // address as the sCacheMessageList. So, when we are + // loading a new message folder, call InvalidateCache + static void InvalidateCache(); + + MessageId GetThreadID() const; + MessageKey GetMessageKey() const; + + Boolean HasBeenRead() const; + Boolean IsFlagged() const; + Boolean HasBeenRepliedTo() const; + + const char* GetSubject(char* buffer, UInt16 bufSize) const; + const char* GetSubject() const; + const char* GetSender() const; + const char* GetDateString() const; + const char* GetAddresseeString() const; + const char* GetSizeStr() const; + const char* GetPriorityStr() const; + const char* GetStatusStr() const; + void GetPriorityColor(RGBColor&) const; + static void GetPriorityColor(MSG_PRIORITY inPriority, RGBColor& outColor); + static Int16 PriorityToMenuItem(MSG_PRIORITY inPriority); + static MSG_PRIORITY MenuItemToPriority(Int16 inMenuItem); + static const char* GetSubject(MSG_MessageLine* data, char* buffer, UInt16 bufSize); + + inline time_t GetDate() const; + inline UInt32 GetSize() const; + MSG_PRIORITY GetPriority() const; + inline UInt32 GetStatus() const; + int8 GetThreadLevel() const; + uint16 GetNumChildren() const; + uint16 GetNumNewChildren() const; + + Boolean HasAttachments() const; + Boolean IsThread() const; + Boolean IsOpenThread() const; + Boolean IsOffline() const; // db has offline news or IMAP msg body + Boolean IsDeleted() const; + Boolean IsTemplate() const; + + ResIDT GetIconID(UInt16 inFolderFlags) const; + static ResIDT GetIconID(UInt16 inFolderFlags, UInt32 inMessageFlags); + ResIDT GetThreadIconID() const; + + Boolean UpdateMessageCache() const; + +protected: + + Boolean TestXPFlag(UInt32 inMask) const; + + MSG_ViewIndex mIndex; + MSG_Pane* mMessageList; + + + static MSG_MessageLine sMessageLineData; + static MSG_Pane* sCacheMessageList; + static MSG_ViewIndex sCacheIndex; +}; // class CMessage + +//====================================== +class CThreadView : public CMailFlexTable +//====================================== +{ + friend class CThreadMessageController; + friend class CFolderThreadController; +private: + typedef CMailFlexTable Inherited; +public: + enum {class_ID = 'msTb'}; + + // ------------------------------------------------------------ + // Construction + // ------------------------------------------------------------ + CThreadView(LStream *inStream); + virtual ~CThreadView(); + void LoadMessageFolder( + CNSContext* inContext, + const CMessageFolder& inFolder, + Boolean loadNow = false); + + void FileMessagesToSelectedPopupFolder(const char *ioFolderName, + Boolean inMoveMessages);//¥¥TSM + + MSG_FolderInfo* GetOwningFolder() const + { + return mXPFolder.GetFolderInfo(); + } + + uint32 GetFolderFlags() const + { + return mXPFolder.GetFolderFlags(); + } + + + // ------------------------------------------------------------ + // Data change notification + // Callbacks from MSGlib come here. + // ------------------------------------------------------------ + virtual void ChangeStarting( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount + ); + virtual void ChangeFinished( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount + ); + virtual void PaneChanged( + MSG_Pane* inPane, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value); + +protected: + void SetFolder(const CMessageFolder& inFolder); + void SetFolder(const MSG_FolderInfo* inFolderLine); + + // ------------------------------------------------------------ + // Clicking + // ------------------------------------------------------------ +protected: + virtual void ClickCell( + const STableCell &inCell, + const SMouseDownEvent &inMouseDown); + virtual Boolean CellSelects(const STableCell& inCell) const; + virtual Boolean CellWantsClick( const STableCell& inCell ) const; + + // ------------------------------------------------------------ + // Drawing + // ------------------------------------------------------------ +protected: + + virtual void DrawCellContents( + const STableCell &inCell, + const Rect &inLocalRect); + void DrawMessageSubject( + TableIndexT inRow, + const Rect& inLocalRect); + void DrawMessageSize( + const CMessage& inMessage, + const Rect& inLocalRect ); + void DrawMessagePriority( + const CMessage& inMessage, + const Rect& inLocalRect ); + void DrawMessageStatus( + const CMessage& inMessage, + const Rect& inLocalRect ); + + // Specials from CStandardFlexTable + virtual TableIndexT GetHiliteColumn() const; + virtual ResIDT GetIconID(TableIndexT inRow) const; + virtual UInt16 GetNestedLevel(TableIndexT inRow) const; + virtual void GetDropFlagRect( const Rect& inLocalCellRect, + Rect& outFlagRect) const; + virtual void GetMainRowText( + TableIndexT inRow, + char* outText, + UInt16 inMaxBufferLength) const; + virtual Boolean GetHiliteTextRect( + const TableIndexT inRow, + Rect& outRect) const; + virtual void ApplyTextStyle(TableIndexT inRow) const; + virtual void ApplyTextColor(TableIndexT inRow) const; + virtual void DrawIconsSelf( + const STableCell& inCell, + IconTransformType inTransformType, + const Rect& inIconRect) const; + + // ------------------------------------------------------------ + // Mail and news + // ------------------------------------------------------------ +public: + void ExpectNewMail() { mExpectingNewMail = true; mGotNewMail = false; } + void DontExpectNewMail() { mExpectingNewMail = false; mGotNewMail = false; } + Boolean GotNewMail() { return mExpectingNewMail && mGotNewMail; } + void SaveSelectedMessage(); + Boolean RestoreSelectedMessage(); + + // ------------------------------------------------------------ + // Drag and Drop + // ------------------------------------------------------------ +public: + URL_Struct* CreateURLForProxyDrag(char* outTitle); + +protected: + virtual Boolean ItemIsAcceptable(DragReference inDragRef, ItemReference inItemRef); + //virtual void InsideDropArea(DragReference inDragRef); + virtual void EnterDropArea(DragReference inDragRef, Boolean inDragHasLeftSender); + virtual void LeaveDropArea(DragReference inDragRef); + virtual void ReceiveDragItem( + DragReference inDragRef + , DragAttributes inDragAttrs + , ItemReference inItemRef + , Rect& inItemBounds); + Boolean GetDragCopyStatus( + DragReference inDragRef + , const CMailSelection& inSelection + , Boolean& outCopy); + + // ------------------------------------------------------------ + // Row Expansion/Collapsing + // ------------------------------------------------------------ +protected: + virtual void SetCellExpansion(const STableCell &inCell, Boolean inExpanded); + virtual Boolean CellHasDropFlag( + const STableCell& inCell, + Boolean& outExpanded) const; + virtual Boolean CellInitiatesDrag(const STableCell& inCell) const; + virtual TableIndexT CountExtraRowsControlledByCell(const STableCell& inCell) const; + + //----------------------------------- + // Commands + //----------------------------------- +public: + enum {cmd_UnselectAllCells = 'UnSl'}; + + virtual Boolean ObeyCommand( + CommandT inCommand, + void *ioParam); + + void ObeyCommandWhenReady(CommandT inCommand); + +protected: + virtual void FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName); + void FindSortCommandStatus(CommandT inCommand, Int16& inOutMark); + Boolean ObeySortCommand(CommandT inCommand); + Boolean ObeyMotionCommand(MSG_MotionType inCommand); + Boolean ObeyMarkReadByDateCommand(); + + virtual void DeleteSelection(); + virtual void ListenToMessage( + MessageT inCommand, + void *ioParam); + + // ------------------------------------------------------------ + // Miscellany - PP + // ------------------------------------------------------------ +public: + virtual void ActivateSelf(); + +protected: + void NoteSortByThreadColumn(Boolean isThreaded) const; + virtual void OpenRow(TableIndexT inRow); + + // ------------------------------------------------------------ + // Miscellany - Selection management + // ------------------------------------------------------------ +public: + Boolean ScrollToGoodPlace(); // as, to first unread message + virtual void SelectionChanged(); // overridden to scroll into view + + void SelectMessageWhenReady(MessageKey inKey); + void SetSelectAfterDelete(Boolean inDoSelect) { mSelectAfterDelete = inDoSelect; } + void SelectAfterDelete(TableIndexT inRow); + +protected: + void SelectMessage(MessageKey inKey); + virtual void ResizeFrameBy( + Int16 inWidthDelta, + Int16 inHeightDelta, + Boolean inRefresh); + virtual void SetRowCount(); + + // ------------------------------------------------------------ + // Miscellany - i18n support + // ------------------------------------------------------------ +public: + void SetDefaultCSID(Int16 default_csid); + virtual Int16 DefaultCSIDForNewWindow(); + + // The following two should really go into utility class + static void DrawUTF8TextString( const char* inText, + const FontInfo* inFontInfo, + SInt16 inMargin, + const Rect& inBounds, + SInt16 inJustification = teFlushLeft, + Boolean doTruncate = true, + TruncCode truncWhere = truncMiddle); + static void PlaceUTF8TextInRect( + const char* inText, + Uint32 inTextLength, + const Rect &inRect, + Int16 inHorizJustType = teCenter, + Int16 inVertJustType = teCenter, + const FontInfo* inFontInfo = NULL, + Boolean inDoTruncate = false, + TruncCode inTruncWhere = truncMiddle); +protected: + void ResetTextTraits(); + +protected: + virtual void ChangeSort(const LTableHeader::SortChange* inSortChange); + void SyncSortToBackend(); + void UpdateSortMenuCommands() const; + Boolean RelocateViewToFolder(const CMessageFolder& inFolder); + // Retarget the view to the specified BE folder. + void UpdateHistoryEntry(); // For bookmark support. + void DoSelectThread(TableIndexT inSelectedRow); + void DoSelectFlaggedMessages(); + + // ------------------------------------------------------------ + // QA Partner support + // ------------------------------------------------------------ +#if defined(QAP_BUILD) +public: + virtual void GetQapRowText(TableIndexT inRow, char* outText, UInt16 inMaxBufferLength) const; +#endif + + // ------------------------------------------------------------ + // Data + // ------------------------------------------------------------ + +protected: + CMessageFolder mXPFolder; // The unique id of the folder we are viewing. Owned by backend. + + CPersistentMessageSelection* mSavedSelection; // Used while the window is being rearranged. + + Boolean mExpectingNewMail; + Boolean mGotNewMail; + Boolean mIsIdling; + Boolean mSelectAfterDelete; // This was a demand from a big customer + TableIndexT mRowToSelect; // hack to help coordinate thread/message panes. + Boolean mScrollToShowNew; + MSG_MotionType mMotionPendingCommand; + CDeferredUndoTask* mUndoTask; + +}; // class CThreadView diff --git a/mozilla/cmd/macfe/MailNews/CThreadWindow.cp b/mozilla/cmd/macfe/MailNews/CThreadWindow.cp new file mode 100644 index 00000000000..b4e2d3c28b7 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CThreadWindow.cp @@ -0,0 +1,1093 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CThreadWindow.cp +// This is the 3-pane mail window + +#include "CThreadWindow.h" + +#include "CMessageFolder.h" +#include "CGrayBevelView.h" +#include "CProgressListener.h" +#include "CBrowserContext.h" +#include "CThreadView.h" +#include "CMessageView.h" +#include "CMessageFolderView.h" +#include "CMailFolderButtonPopup.h" +#include "CSpinningN.h" +#include "CApplicationEventAttachment.h" +#include "CMailProgressWindow.h" +#include "CProxyPane.h" +#include "CProxyDragTask.h" +#include "LTableViewHeader.h" +#include "CTargetFramer.h" + +#include "resgui.h" +#include "MailNewsgroupWindow_Defines.h" + +#include "msg_srch.h" +//#include "secnav.h" + +#include "uapp.h" +#include "macutil.h" +#include "StSetBroadcasting.h" +#include "earlmgr.h" +#include "prefapi.h" +#include +#include +#include "CMessageAttachmentView.h" +#include "CThreadMessageController.h" +#include "CFolderThreadController.h" +#include "UDeferredTask.h" +#include "UMailSelection.h" + +#define kTwistieID 'Twst' +static const PaneIDT paneID_ThreadFileButton = 'Bfil'; +static const PaneIDT paneID_ThreadReplyButton = 'Brep'; +static const PaneIDT paneID_ThreadGetMailButton = 'Bget'; // mail +static const PaneIDT paneID_ThreadGetNewsButton = 'Bmor'; // news +static const PaneIDT paneID_ThreadComposeButton = 'Bcmp'; // mail +static const PaneIDT paneID_ThreadPostNewButton = 'Bpst'; // news +static const PaneIDT paneID_ThreadPrintButton = 'Bprn'; // mail +static const PaneIDT paneID_ThreadBackButton = 'Bbck'; // news +static const PaneIDT paneID_ThreadDeleteButton = 'Bdel'; // mail +static const PaneIDT paneID_ThreadMarkButton = 'Bmrk'; // news + +const PaneIDT paneID_ThreadRelocateButton = 'MfPM'; + + +//----------------------------------- +CThreadWindow::CThreadWindow(LStream *inStream) +//----------------------------------- +: Inherited(inStream, WindowType_MailThread) +, mStatusResID(res_ID) +, mThreadView(nil) +, mFolderContext(nil) +, mFolderView(nil) +, mFolderThreadController(nil) +, mThreadMessageController(nil) +{ +} // CThreadWindow::CThreadWindow + +//----------------------------------- +CThreadWindow::~CThreadWindow() +//----------------------------------- +{ + if (mFolderContext) + { + mFolderContext->RemoveUser(this); + mFolderContext = nil; + } +} // CThreadWindow::~CThreadWindow + +#define kButtonBarResID 10513 + // Common included button bar for thread windows. + // This has the RIdl for the buttons. + +//----------------------------------- +void CThreadWindow::FinishCreateSelf() +//----------------------------------- +{ + CNetscapeWindow::FinishCreateSelf(); // SKIP CMailNewsWindow, and roll our own! + // should be in resource, but there's a resource freeze. + CExpandoDivider* threadMessageExpandoView = dynamic_cast(FindPaneByID('Expo')); + + // Initialize our stored view pointers. + CMessageView* messageView = GetMessageView(); + Assert_(messageView); + GetThreadView(); + Assert_(mThreadView); // Else you're deadybones! + + CMessageAttachmentView* attachmentView = + dynamic_cast( FindPaneByID('MATv') ); + mFolderView = dynamic_cast(FindPaneByID('Flst')); + ThrowIfNil_(mFolderView); + + //----------- + // The following code is analogous to + // CMailNewsWindow::FinishCreateSelf() except it deals with mFolderContext + // as well as mMailNewsContext + //----------- + // Let there be a thread context + mMailNewsContext = CreateContext(); + mMailNewsContext->AddUser(this); + + // Let there be a progress listener, placed in my firmament, + // which shall listen to both contexts + mProgressListener = new CProgressListener(this, mMailNewsContext); + + // Let there be a folder context + mFolderContext = new CMailNewsContext(MWContextMail); + StSharer theLock(mFolderContext); + mFolderContext->AddUser(this); + // The progress listener must also listen to the folder context + mFolderContext->AddListener(mProgressListener); + +#if !ONECONTEXTPERWINDOW + // Make a listener list for the message context + typedef LListener* SListenerPointer; + const UInt8 kNumberOfListeners = 2; + LListener** messageContextListeners = new SListenerPointer[kNumberOfListeners + 1]; + // The progress listener must also listen to the message context. + messageContextListeners[0] = mProgressListener; +#endif + + // The progress listener is "just a bit" lazy during network activity and + // "not at all" at idle time to display the URLs pointed by the mouse cursor. + mProgressListener->SetLaziness(CProgressListener::lazy_NotAtAll); + + // The spinning N must listen to all three contexts + CSpinningN* theN = dynamic_cast(FindPaneByID(CSpinningN::class_ID)); + if (theN) + { + mFolderContext->AddListener(theN); + mMailNewsContext->AddListener(theN); +#if !ONECONTEXTPERWINDOW + messageContextListeners[1] = theN; +#endif + } + +#if !ONECONTEXTPERWINDOW + // Terminate the listener list + messageContextListeners[kNumberOfListeners] = 0; +#endif + + mThreadMessageController = new CThreadMessageController( + threadMessageExpandoView + , mThreadView + , messageView + , attachmentView +#if !ONECONTEXTPERWINDOW + , messageContextListeners +#endif + ); + + mThreadMessageController->InitializeDimensions(); + + // ThreadMessageController is a CExpandoListener, so let it do some listening! + LControl* twistie = dynamic_cast(FindPaneByID(kTwistieID)); + if (twistie) twistie->AddListener((CExpandoListener*)mThreadMessageController); + + //----------- + // The next part of this code code is analogous to + // CMailNewsFolderWindow::FinishCreateSelf() except it deals with mFolderContext + // instead of mMailNewsContext + //----------- + mFolderView->LoadFolderList(mFolderContext); + UReanimator::LinkListenerToControls(mFolderView, this, kButtonBarResID); + + // Make the Folder/Thread Controller + LDividedView* folderThreadExpandoView + = dynamic_cast(FindPaneByID('ExCt')); + mFolderThreadController = new CFolderThreadController( + folderThreadExpandoView, + mMailNewsContext, + mFolderView, + mThreadView); + + SetLatentSub(mFolderView); + mMailNewsContext->AddListener(mThreadView); // listen for all connections complete. + mFolderContext->AddListener(mFolderView); // listen for all connections complete. + + mThreadMessageController->FinishCreateSelf(); + // The folder-thread controller is a tabgroup, tabbing between the folder, thread, + // and message views. We have to add the message view explicitly to the folder- + // thread controller here, because it doesn't know about the message view. And we have to + // do that after the folder-thread controller adds the folder and thread subcommanders. + mFolderThreadController->FinishCreateSelf(); + messageView->SetSuperCommander(mFolderThreadController); + CTargetFramer* framer = new CTargetFramer(); + messageView->AddAttachment(framer); + + // make the message view listen to buttons + UReanimator::LinkListenerToControls((CMessageView::Inherited *)messageView, this, kButtonBarResID); + + // If the folder-thread controller can't handle a command (because the wrong pane + // is the target), let it pass it up to the thread-message controller, which has a + // mechanism for delegating to the message view. + mFolderThreadController->SetSuperCommander(mThreadMessageController); + mThreadMessageController->SetSuperCommander(this); + + // The 2-pane and 3-pane thread view have the same pane controls, so: + UReanimator::LinkListenerToControls(mThreadView, this, kButtonBarResID); + USecurityIconHelpers::AddListenerToSmallButton( + this /*LWindow**/, + mThreadView /*LListener**/); + + LGAIconSuiteControl* offlineButton + = dynamic_cast(FindPaneByID(kOfflineButtonPaneID)); + if (offlineButton) + offlineButton->AddListener(CFrontApp::GetApplication()); + + LControl* stopButton + = dynamic_cast(FindPaneByID('Bstp')); + if (stopButton) + stopButton->AddListener((CHTMLView*)messageView); + + // Create the new window just like the frontmost one of the same type, if possible + CWindowIterator iter(WindowType_MailThread); + CMediatedWindow* window; + CThreadWindow* templateWindow = nil; + for (iter.Next(window); window; iter.Next(window)) + { + templateWindow = dynamic_cast(window); + if (!templateWindow || templateWindow != this) + break; + } + if (templateWindow && templateWindow != this) + { + // Use the template window + CSaveWindowStatus::FinishCreateWindow(templateWindow); + } + else + { + //Get it from disk + CSaveWindowStatus::FinishCreateWindow(); + } + ReadGlobalDragbarStatus(); + AdjustStagger(WindowType_MailThread); + + // And behold, he saw that it was good. + SetLatentSub(mThreadView); + SwitchTarget(mThreadView); // only one of these will work. +} // CThreadWindow::FinishCreateSelf + +//----------------------------------- +void CThreadWindow::AboutToClose() +//----------------------------------- +{ + CSaveWindowStatus::AttemptCloseWindow(); // Do this first: uses table and calls the window's WriteWindowStatus() method + WriteGlobalDragbarStatus(); + mFolderView = nil; + // This is special: + mFolderContext->RemoveListener(mProgressListener); + if (mFolderView) + { + if (mFolderContext) + mFolderContext->RemoveListener(mFolderView); // don't listen for all connections complete. + mFolderView = nil; + } + mThreadView = nil; + // This is special: + mMailNewsContext->RemoveListener(mProgressListener); + if (mThreadView) + { + if (mMailNewsContext) + mMailNewsContext->RemoveListener(mThreadView); // don't listen for all connections complete. + mThreadView = nil; + } + + if (mThreadMessageController) + { + mThreadMessageController->InstallMessagePane(false); + mThreadMessageController->SetThreadView(nil); +// delete mThreadMessageController; No, it's a subcommander and will be deleted when we are. + mThreadMessageController = nil; + } + + // InstallMessagePane(false) can call InterruptContext, which in turn can show the window + // so we must call Hide() here. Hiding is necessary so that views in the window are not + // the current target while we are deleting them here. + if (IsVisible()) + Hide(); + + // Delete the folder-thread controller now, thus deleting all the msglib panes. We have + // to do that first before deleting the context, because the destructor of the pane + // references the context. Caution: Commanders delete all their subcommanders + // when they die. So detach it before we delete it, so that our destructor doesn't + // perform a double-deletion. + + //SwitchTarget(this); + + mFolderThreadController->SetSuperCommander(nil); + delete mFolderThreadController; + mFolderThreadController = nil; + + CSpinningN* theN = dynamic_cast(FindPaneByID(CSpinningN::class_ID)); + if (theN) + mFolderContext->RemoveListener(theN); +} // CThreadWindow::AboutToClose + +//---------------------------------------------------------------------------------------- +CNSContext* CThreadWindow::CreateContext() const +//---------------------------------------------------------------------------------------- +{ +#if ONECONTEXTPERWINDOW + CNSContext* result = new CBrowserContext(MWContextMailMsg); + FailNIL_(result); + return result; +#else + return Inherited::CreateContext(); +#endif +} //CMailNewsWindow::CreateContext + +//---------------------------------------------------------------------------------------- +static void InterruptTableContext(CMailFlexTable* inTable) +//---------------------------------------------------------------------------------------- +{ + if (inTable) + { + CNSContext* context = inTable->GetContext(); + if (context) + XP_InterruptContext(*context); + } +} + +//---------------------------------------------------------------------------------------- +static Boolean IsTableContextBusy(CMailFlexTable* inTable) +//---------------------------------------------------------------------------------------- +{ + if (inTable) + { + CNSContext* context = inTable->GetContext(); + if (context) + return XP_IsContextBusy(*context); + } + return false; +} + +//---------------------------------------------------------------------------------------- +void CThreadWindow::StopAllContexts() +//---------------------------------------------------------------------------------------- +{ + InterruptTableContext(GetFolderView()); + InterruptTableContext(GetThreadView()); +#if !ONECONTEXTPERWINDOW + CMessageView* mv = GetMessageView(); + if (mv) + { + CBrowserContext* context = mv->GetContext(); + if (context) + XP_InterruptContext(*context); + } +#endif +} + +//---------------------------------------------------------------------------------------- +Boolean CThreadWindow::IsAnyContextBusy() +//---------------------------------------------------------------------------------------- +{ + Boolean busy = IsTableContextBusy(GetFolderView()); + if (!busy) + busy = IsTableContextBusy(GetThreadView()); +#if !ONECONTEXTPERWINDOW + if (!busy) + { + CMessageView* mv = GetMessageView(); + if (mv) + { + CBrowserContext* context = mv->GetContext(); + if (context) + return XP_IsContextBusy(*context); + } + } +#endif + return busy; +} + +//----------------------------------- +void CThreadWindow::UpdateFilePopupCurrentItem() +//----------------------------------- +{ + CThreadView *threadView = GetThreadView(); + MSG_FolderInfo* folderInfo = nil; + if (threadView != nil) + folderInfo = threadView->GetOwningFolder(); + + CMailFolderPatternTextPopup *popup2 = + dynamic_cast(FindPaneByID(paneID_ThreadRelocateButton)); + if ( popup2 != nil && threadView->GetOwningFolder()) + { + StSetBroadcasting setBroadcasting(popup2, false); // Don't broadcast anything here + popup2->MSetSelectedFolder(folderInfo); + } + + CMailFolderSubmenu::SetSelectedFolder(folderInfo); +} // CThreadWindow::UpdateFilePopupCurrentItem + +//----------------------------------- +void CThreadWindow::ActivateSelf() +//----------------------------------- +{ + Inherited::ActivateSelf(); +#if 0 + // Don't do this for the 3-pane UI + // make sure the message view isn't target + CMailFlexTable* threadView = GetThreadView(); + if (threadView) + { + SwitchTarget(threadView); + } +#endif + UpdateFilePopupCurrentItem(); // Is this really necessary? +} // CThreadWindow::ActivateSelf + + +//----------------------------------- +void CThreadWindow::AdaptToolbarToFolder() +//----------------------------------- +{ + CThreadView* threadView = GetThreadView(); + if (threadView == nil) + return; + + // show/hide buttons depending on the window type (News vs Mail) + LControl * aControl; + Boolean isNewsWindow = ((threadView->GetFolderFlags() & MSG_FOLDER_FLAG_NEWSGROUP) != 0); + static const short kBtnCount = 3; + + PaneIDT mailBtn[kBtnCount] = + { + paneID_ThreadGetMailButton, + paneID_ThreadComposeButton, + paneID_ThreadDeleteButton // update kBtnCount if you add a btn + }; + + PaneIDT newsBtn[kBtnCount] = + { + paneID_ThreadGetNewsButton, + paneID_ThreadPostNewButton, + paneID_ThreadMarkButton // update kBtnCount if you add a btn + }; + + + for (short btnIndex = 0; btnIndex < kBtnCount; btnIndex ++) + { + if (isNewsWindow) + { + aControl = dynamic_cast(FindPaneByID(mailBtn[btnIndex])); + if (aControl != nil) aControl->Hide(); + aControl = dynamic_cast(FindPaneByID(newsBtn[btnIndex])); + if (aControl != nil) aControl->Show(); + } + else + { + aControl = dynamic_cast(FindPaneByID(newsBtn[btnIndex])); + if (aControl != nil) aControl->Hide(); + aControl = dynamic_cast(FindPaneByID(mailBtn[btnIndex])); + if (aControl != nil) aControl->Show(); + } + } + + // other changes depending on the window type + if (isNewsWindow) + { + aControl = dynamic_cast(FindPaneByID(paneID_ThreadReplyButton)); + if (aControl != nil) aControl->SetValueMessage(cmd_PostReply); // quick-click default + } + else + { + aControl = dynamic_cast(FindPaneByID(paneID_ThreadReplyButton)); + if (aControl != nil) aControl->SetValueMessage(cmd_ReplyToSender); // quick-click default + } + CMessageFolder owningFolder(threadView->GetOwningFolder()); + ResIDT iconID = owningFolder.GetIconID(); + + LIconPane* proxyIcon = dynamic_cast(FindPaneByID('poxy')); + if (proxyIcon) + proxyIcon->SetIconID(iconID); + + CProxyPane* newProxy = dynamic_cast(FindPaneByID(CProxyPane::class_ID)); + if (newProxy) + { + newProxy->SetIconIDs(iconID, iconID); + } + +} // CThreadWindow::AdaptToolbarToFolder + +//----------------------------------- +CMailFlexTable* CThreadWindow::GetActiveTable() +// Get the currently active table in the window. The active table is the table in +// the window that the user considers to be receiving input. +//----------------------------------- +{ + CMailFlexTable* result = GetFolderView(); + if (result && result->IsOnDuty()) + return result; + return GetThreadView(); +} // CThreadWindow::GetActiveTable + +//----------------------------------- +CMailFlexTable* CThreadWindow::GetSearchTable() +// Get the table which should be used to set the search base node. The base class +// returns GetActiveTable() for this, but now life is more complicated... +// The fix is to return the folder view, even if it is not currently the target. Otherwise, +// if we try to return the thread view and it's not currently displaying a folder, bad things +// can happen. +//----------------------------------- +{ + CMailFlexTable* result = GetFolderView(); + if (result) + return result; + result = GetThreadView(); + if (::MSG_GetCurFolder(result->GetMessagePane())) + return result; + return nil; +} // CThreadWindow::GetActiveTable + +//----------------------------------- +CMessageFolderView* CThreadWindow::GetFolderView() +//----------------------------------- +{ + return mFolderView; +} // CThreadWindow::GetFolderView + +//----------------------------------- +CThreadView* CThreadWindow::GetThreadView() +//----------------------------------- +{ + if (!mThreadView) + mThreadView = dynamic_cast(FindPaneByID('Tabl')); + return mThreadView; +} // CThreadWindow::GetThreadView + + +//----------------------------------- +CMessageView* CThreadWindow::GetMessageView() +//----------------------------------- +{ + return dynamic_cast(FindPaneByID(CMessageView::class_ID)); +} + +//----------------------------------- +void CThreadWindow::ShowMessageKey(MessageKey inKey) +//----------------------------------- +{ + CThreadView* threadView = GetThreadView(); + Assert_(threadView); + + threadView->SelectMessageWhenReady(inKey); + +} // CThreadWindow::ShowSearchMessage + +//----------------------------------- +CThreadWindow* CThreadWindow::FindOrCreate( + const MSG_FolderInfo* inFolderInfo, + CommandT inCommand) +//----------------------------------- +{ + // First see if there's an open window open to this folder. + ThrowIf_(Memory_MemoryIsLow()); + CWindowIterator iter(WindowType_MailThread); + CMediatedWindow* window; + CThreadWindow* threadWindow = nil; + for (iter.Next(window); window; iter.Next(window)) + { + threadWindow = dynamic_cast(window); + ThrowIfNULL_(threadWindow); + CThreadView* threadView = threadWindow->GetThreadView(); + ThrowIfNULL_(threadView); + if (threadView->GetOwningFolder() == inFolderInfo) + { + if (inCommand != cmd_Nothing) + threadView->ObeyCommand(inCommand, nil); + return threadWindow; + } + } + + // Window not found: let's create it + return FindAndShow(inFolderInfo, true, inCommand, true); + +} // CThreadWindow::FindOrCreate + +//----------------------------------- +CThreadWindow* CThreadWindow::FindAndShow( + const MSG_FolderInfo* inFolderInfo, + Boolean inMakeNew, + CommandT inCommand, + Boolean forceNewWindow) +//----------------------------------- +{ + // NOTE: all references to 'categories' have been removed from + // this function in v1.63 on Tuesday 97/06/03. + + // First see if there's an open window open to this folder. + // If there is, bring it to the front. + ThrowIf_(Memory_MemoryIsLow()); + CWindowIterator iter(WindowType_MailThread, false); // false, don't want hidden windows. + CMediatedWindow* window; + CThreadWindow* existingThreadWindow = nil; + CThreadWindow* threadWindow = nil; + for (iter.Next(window); window; iter.Next(window)) + { + existingThreadWindow = dynamic_cast(window); + ThrowIfNULL_(existingThreadWindow); + CThreadView* tempThreadView = existingThreadWindow->GetThreadView(); + ThrowIfNULL_(tempThreadView); + if (tempThreadView->GetOwningFolder() == inFolderInfo) + { + threadWindow = existingThreadWindow; // Found it! + break; + } + } + if (!threadWindow && !inMakeNew) + return nil; + + // Re-use another Thread window, if possible... + if (!threadWindow && !forceNewWindow) + { + XP_Bool prefReuseWindow; + PREF_GetBoolPref("mailnews.reuse_thread_window", &prefReuseWindow); + if ( CApplicationEventAttachment::CurrentEventHasModifiers(optionKey) ) + prefReuseWindow = !prefReuseWindow; + if (prefReuseWindow) + { + CWindowIterator iter(WindowType_MailThread); + iter.Next(threadWindow); // use the frontmost open thread window + } + } + + // ...Otherwise create a new window + CMessageFolder folder(inFolderInfo); + if (!threadWindow) + { + try + { + ::SetCursor(*::GetCursor(watchCursor)); + SetDefaultCommander(LCommander::GetTopCommander()); + SetDefaultAttachable(nil); + threadWindow = (CThreadWindow*) UReanimator::ReadObjects('PPob', res_ID); +#if 0 + // FIX ME: for CSaveWindowStatus, use one resID for inbox, and 2-pane resid + // for all other thread panes. We will not use staggering for inbox. + threadWindow->SetStatusResID(folder.IsInbox() ? res_ID : res_ID_Alt); +#endif + threadWindow->SetStatusResID(res_ID); + threadWindow->FinishCreate(); + } + catch (...) + { + delete threadWindow; + threadWindow = nil; + throw; + } + } + + CThreadView* messageList = threadWindow->GetThreadView(); + ThrowIfNULL_(messageList); + + // Load in the Mail messages or News postings + if (messageList->GetOwningFolder() != inFolderInfo) + { + try + { + ::SetCursor(*::GetCursor(watchCursor)); + if (threadWindow->mProgressListener) + threadWindow->mProgressListener->SetLaziness( + CProgressListener::lazy_VeryButForThisCommandOnly); + messageList->EnableStopButton(true); + CMessageFolderView* folderView = threadWindow->GetFolderView(); + if (folderView) + folderView->SelectFolder(inFolderInfo); + else + messageList->LoadMessageFolder( + (CMailNewsContext*)threadWindow->GetWindowContext(), // thread view's own context + folder); // loadNow = false. + } + catch(...) + { + // Note, threadView has timer that will delete window. + throw; + } + } + + if (inCommand == cmd_GetNewMail) + { + // only do a get new mail here if were are executing the command on an existing window + // (because the user must have really wanted it if the command was passed in) + // or if we are online and the folder is pop (because IMAP gets new mail automatically + // if the folder is loaded for the first time). + if (threadWindow == existingThreadWindow + || (!NET_IsOffline() && !(folder.GetFolderFlags() & MSG_FOLDER_FLAG_IMAPBOX))) + { + // A thread window has two panes that can handle this command. If the thread + // pane is busy, why not give the folder pane something to do? + Boolean handled = false; + CNSContext* context = messageList->GetContext(); + if (context && XP_IsContextBusy(*context)) + { + CMessageFolderView* fv = threadWindow->GetFolderView(); + if (fv) + { + context = fv->GetContext(); + if (context && !XP_IsContextBusy(*context)) + { + CDeferredTaskManager::Post(new CDeferredCommand(fv, inCommand, nil), fv); + handled = true; + } + } + } + if (!handled) + messageList->ObeyCommandWhenReady(inCommand); + } + } + else if (inCommand != cmd_Nothing) + messageList->ObeyCommandWhenReady(inCommand); + + threadWindow->Show(); + threadWindow->Select(); + + // HACK TO SUPPORT OPENING OF MORE THAN ONE FOLDER. + // Otherwise, the BE doesn't initialize itself completely and the second window + // fails. + EventRecord ignoredEvent = {0}; + TheEarlManager.SpendTime(ignoredEvent); + return threadWindow; +} // CThreadWindow::FindAndShow + +//----------------------------------- +CThreadWindow* CThreadWindow::OpenFromURL(const char* url) +//----------------------------------- +{ + CThreadWindow* threadWindow = nil; + MSG_FolderInfo* folderInfo = MSG_GetFolderInfoFromURL( + CMailNewsContext::GetMailMaster(), + url, true); + if (!folderInfo) + return nil; + if (!CThreadWindow::FindAndShow(folderInfo, CThreadWindow::kDontMakeNew)) + threadWindow + = CThreadWindow::FindAndShow(folderInfo, CThreadWindow::kMakeNew); + return threadWindow; +} + +//----------------------------------- +cstring CThreadWindow::GetCurrentURL() const +//----------------------------------- +{ + if (mThreadMessageController) + return mThreadMessageController->GetCurrentURL(); + else + return cstring(""); +} + +//----------------------------------- +void CThreadWindow::SetFolderName(const char* inFolderName, Boolean inIsNewsgroup) +//----------------------------------- +{ + UpdateFilePopupCurrentItem(); + AdaptToolbarToFolder(); + + if (!inFolderName) + return; +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=mac68k +#else +#error "There'll probably be a bug here." +#endif + struct WindowTemplate { + Rect boundsRect; + short procID; + Boolean visible; + Boolean filler1; + Boolean goAwayFlag; + Boolean filler2; + long refCon; + Str255 title; + }; +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=reset +#endif + // Name of window in PPob is "Netscape ^0 Ò^1Ó + WindowTemplate** wt = (WindowTemplate**)GetResource('WIND', CThreadWindow::res_ID); + Assert_(wt); + if (wt) + { + StHandleLocker((Handle)wt); + CStr255 windowTitle((*wt)->title); + ReleaseResource((Handle)wt); + CStr255 typeString; + // Get "folder" or "newsgroup" as appropriate + GetIndString(typeString, 7099, 1 + inIsNewsgroup); + ::StringParamText(windowTitle, (const char*)typeString, inFolderName); + SetDescriptor(windowTitle); + CProxyPane* proxy = dynamic_cast(FindPaneByID(CProxyPane::class_ID)); + if (proxy) + proxy->ListenToMessage(msg_NSCDocTitleChanged, (char*)windowTitle); + } +} // CThreadWindow::SetFolderName + +//----------------------------------- +void CThreadWindow::CloseAll(const MSG_FolderInfo* inFolderInfo) +//----------------------------------- +{ + CThreadWindow* win; + do { + win = CThreadWindow::FindAndShow(inFolderInfo); + if (win) + win->AttemptClose(); + } while (win); +} // CThreadWindow::CloseAll + +//----------------------------------- +CThreadWindow* CThreadWindow::ShowInbox(CommandT inCommand) +//----------------------------------- +{ + CThreadWindow* result = nil; + if (!CMailProgressWindow::GettingMail()) + { + CMailNewsContext::ThrowIfNoLocalInbox(); // prefs have not been set up. + + MSG_FolderInfo* inboxFolderInfo = nil; + int32 numberOfInboxes = MSG_GetFoldersWithFlag( + CMailNewsContext::GetMailMaster(), + MSG_FOLDER_FLAG_INBOX, + &inboxFolderInfo, + 1); + + if (!numberOfInboxes || inboxFolderInfo == nil) + CMailNewsContext::AlertPrefAndThrow(PREF_PopHost); + + result = FindAndShow(inboxFolderInfo, true, inCommand); + } + return result; +} // CThreadWindow::ShowInbox + +//----------------------------------- +UInt16 CThreadWindow::GetValidStatusVersion() const +//----------------------------------- +{ + return 0x0132; + // to be updated whenever the form of the saved window data changes +} // CThreadWindow::GetValidStatusVersion() + +//----------------------------------- +void CThreadWindow::ReadWindowStatus(LStream *inStatusData) +//----------------------------------- +{ + //LCommander::SwitchTarget(mFolderView); // make sure it's the active table + + Inherited::ReadWindowStatus(inStatusData); + + if (inStatusData) + { + if (mFolderView) + mFolderView->ReadSavedTableStatus(inStatusData); + + if (mThreadView) + mThreadView->ReadSavedTableStatus(inStatusData); + + mThreadMessageController->ReadStatus(inStatusData); + mFolderThreadController->ReadStatus(inStatusData); + + // We are currently closed, whatever the value of mExpandState may say (after + // all, we only just read in the value). So we set mExpandState back to + // closed_state, and bang the twistie to set everything the right way. + ExpandStateT state = mThreadMessageController->GetExpandState(); + mThreadMessageController->NoteExpandState(closed_state); + LControl* twistie = dynamic_cast(FindPaneByID(kTwistieID)); + mThreadMessageController->SetStoreStatusEnabled(false); + if (twistie) + twistie->SetValue(state); + mThreadMessageController->SetStoreStatusEnabled(true); + } +} // CThreadWindow::ReadWindowStatus + +//----------------------------------- +void CThreadWindow::WriteWindowStatus(LStream *outStatusData) +// Overridden to stagger in the default case. +//----------------------------------- +{ + // don't mess with targets when closing windows any more + //LCommander::SwitchTarget(mFolderView); // make sure it's the active table + + Inherited::WriteWindowStatus(outStatusData); // write the drag bar status and window location + + if (outStatusData) + { + if (mFolderView) + mFolderView->WriteSavedTableStatus(outStatusData); + + if (mThreadView) + mThreadView->WriteSavedTableStatus(outStatusData); + + mThreadMessageController->WriteStatus(outStatusData); // write expando status + mFolderThreadController->WriteStatus(outStatusData); // write divided view status + } +} // CThreadWindow::WriteWindowStatus + +//----------------------------------- +ResIDT CThreadWindow::GetStatusResID() const +//----------------------------------- +{ + return mStatusResID; +} // CThreadWindow::GetStatusResID() + +#if 0 +//----------------------------------- +Boolean CThreadWindow::ObeyMotionCommand(MSG_ViewIndex index, MSG_MotionType cmd) +//----------------------------------- +{ + if (GetExpandState() == closed_state) return false; + Try_ + { + CMessageView* messageView = GetMessageView(); + if (messageView && messageView->ObeyMotionCommand(cmd)) + { + CThreadView* threadView = GetThreadView(); + STableCell cell(resultIndex + 1,1); + threadView->UnselectAllCells(); + threadView->SelectCell(cell); + } + else + Throw_(noErr); + } + Catch_ (err) + { + SysBeep(1); + } + EndCatch_ + return false; +} // CThreadWindow::ObeyMotionCommand +#endif // 0 + +//----------------------------------- +Boolean CThreadWindow::ObeyCommand( + CommandT inCommand, + void *ioParam) +//----------------------------------- +{ + switch (inCommand) + { + case cmd_RelocateViewToFolder: + // Command was not (could not be) handled by the ThreadView, + // so we leave the window as it is and just update the Location popup. + UpdateFilePopupCurrentItem(); + return true; + + case cmd_SelectSelection: + // This command comes from the context menu, as the default in the thread view. + LControl* twistie = dynamic_cast(FindPaneByID(kTwistieID)); + if (twistie) + twistie->SetValue(true); + // and nature will take its course... + return true; + } + return Inherited::ObeyCommand(inCommand, ioParam); +} // CThreadWindow::ObeyCommand + +//----------------------------------- +void CThreadWindow::FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +//----------------------------------- +{ + switch (inCommand) + { + case cmd_SelectSelection: + outEnabled = true; + return; + } + Inherited::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); +} // CThreadWindow::FindCommandStatus + +//----------------------------------- +Int16 CThreadWindow::DefaultCSIDForNewWindow() +//----------------------------------- +{ + Int16 csid = 0; + CMessageView* messageView = GetMessageView(); + if (messageView) + csid = messageView->DefaultCSIDForNewWindow(); + if( 0 == csid) + { + CThreadView* threadView = GetThreadView(); + if(threadView) + csid = threadView->DefaultCSIDForNewWindow(); + } + return csid; +} // CThreadWindow::DefaultCSIDForNewWindow + +//----------------------------------- +void CThreadWindow::SetDefaultCSID(Int16 default_csid) +//----------------------------------- +{ + CMessageView* messageView = GetMessageView(); + if (messageView) + messageView->SetDefaultCSID(default_csid, true); // force repagination +} // CThreadWindow::SetDefaultCSID + +//----------------------------------- +URL_Struct* CThreadWindow::CreateURLForProxyDrag(char* outTitle) +//----------------------------------- +{ + return mThreadView->CreateURLForProxyDrag(outTitle); +} // CThreadWindow::CreateURLForProxyDrag + +//---------------------------------------------------------------------------------------- +const char* CThreadWindow::GetLocationBarPrefName() const +//---------------------------------------------------------------------------------------- +{ + if (mFolderThreadController) + { + const char* result = mFolderThreadController->GetLocationBarPrefName(); + if (result) + return result; + } + return Inherited::GetLocationBarPrefName(); +} + +//---------------------------------------------------------------------------------------- +CExtraFlavorAdder* CThreadWindow::CreateExtraFlavorAdder() const +//---------------------------------------------------------------------------------------- +{ + class ThreadWindowFlavorAdder : public CExtraFlavorAdder + { + public: + ThreadWindowFlavorAdder(MSG_Pane* inFolderPane, MSG_FolderInfo* inFolderInfo) + : mFolderPane(inFolderPane) + , mFolderInfo(inFolderInfo) + { + } + virtual void AddExtraFlavorData(DragReference inDragRef, ItemReference inItemRef) + { + // Pass a selection, as if this is done from a folder view. + mSelection.xpPane = mFolderPane; + if (!mSelection.xpPane) + return; // drat. This is a real drag. + MSG_ViewIndex viewIndex = ::MSG_GetFolderIndexForInfo( + mFolderPane, + mFolderInfo, + true /*expand*/); + if (viewIndex == MSG_VIEWINDEXNONE) + return; + mSelection.SetSingleSelection(viewIndex); + ::AddDragItemFlavor( + inDragRef, + inItemRef, + kMailNewsSelectionDragFlavor, + &mSelection, + sizeof(mSelection), + 0); + } + private: + MSG_Pane* mFolderPane; + MSG_FolderInfo* mFolderInfo; + CMailSelection mSelection; + }; + + CThreadView* threadView = const_cast(this)->GetThreadView(); + MSG_FolderInfo* folderInfo = threadView->GetOwningFolder(); + MSG_Pane* folderPane = ::MSG_FindPane(nil, MSG_FOLDERPANE); + return new ThreadWindowFlavorAdder(folderPane, folderInfo); +} // CMessageWindow::CreateExtraFlavorAdder diff --git a/mozilla/cmd/macfe/MailNews/CThreadWindow.h b/mozilla/cmd/macfe/MailNews/CThreadWindow.h new file mode 100644 index 00000000000..f545a9f7ea7 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/CThreadWindow.h @@ -0,0 +1,135 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// CThreadWindow.h + +#pragma once +// Mac UI Lib +#include "CMailNewsWindow.h" +#include "msgcom.h" +#include "cstring.h" + +class CMessageView; +class CThreadView; +class CMessageFolderView; +class CThreadMessageController; +class CFolderThreadController; + +typedef struct MSG_ResultElement MSG_ResultElement; + +//====================================== +class CThreadWindow + : public CMailNewsWindow +// This window can have up to three contexts (folder, thread, message). The +// one managed by the base class is the THREAD context. +//====================================== +{ +private: + typedef CMailNewsWindow Inherited; +public: + enum { class_ID = 'thWN', res_ID = 10509, res_ID_Alt = 10508 }; + + + CThreadWindow(LStream *inStream); + virtual ~CThreadWindow(); + virtual void FinishCreateSelf(); + + virtual void AboutToClose(); // place to put common code from [Attempt|Do]Close() + + static CThreadWindow* ShowInbox(CommandT inCommand); + + enum { kDontMakeNew = false, kMakeNew = true }; + static CThreadWindow* FindAndShow( + const MSG_FolderInfo* inFolderInfo, + Boolean makeNew = kDontMakeNew, + CommandT inCommand = cmd_Nothing, + Boolean forceNewWindow = false); + + static CThreadWindow* FindOrCreate( + const MSG_FolderInfo* inFolderInfo, + CommandT inCommand = cmd_Nothing); + + static CThreadWindow* OpenFromURL(const char* url); + cstring GetCurrentURL() const; + static void CloseAll(const MSG_FolderInfo* inFolderInfo); + virtual CNSContext* CreateContext() const; + virtual void StopAllContexts(); + virtual Boolean IsAnyContextBusy(); + virtual CMailFlexTable* GetActiveTable(); + // Return the currently active table in the window, nil if none + virtual CMailFlexTable* GetSearchTable(); + CThreadView* GetThreadView(); + CMessageFolderView* GetFolderView(); + CMessageView* GetMessageView(); + // Return the message view. Note: there may be no MSG_Pane in it! + void SetFolderName(const char* inFolderName, Boolean inIsNewsgroup); + +// CSaveWindowStatus Overrides: + virtual void ReadWindowStatus(LStream *inStatusData); + // Overridden to stagger in the default case. + virtual void WriteWindowStatus(LStream *inStatusData); + virtual UInt16 GetValidStatusVersion() const; + virtual ResIDT GetStatusResID() const; + + // I18N stuff + virtual Int16 DefaultCSIDForNewWindow(void); + void SetDefaultCSID(Int16 default_csid); + void ShowMessageKey(MessageKey inKey); + +protected: + + void SetStatusResID(ResIDT id) { mStatusResID = id; } + + void UpdateFilePopupCurrentItem(); + virtual void ActivateSelf(void); + + virtual void AdaptToolbarToFolder(void); + +// LCommander overrides +protected: + virtual void FindCommandStatus( + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName); + virtual Boolean ObeyCommand( + CommandT inCommand, + void *ioParam); + +// CNetscapeWindow overrides +protected: + virtual URL_Struct* CreateURLForProxyDrag(char* outTitle = nil); + virtual CExtraFlavorAdder* CreateExtraFlavorAdder() const; + +// CMailNewsWindow overrides +protected: + virtual const char* GetLocationBarPrefName() const; + +//DATA: +protected: + ResIDT mStatusResID; + CThreadView* mThreadView; + CThreadMessageController* mThreadMessageController; + CMessageFolderView* mFolderView; + CMailNewsContext* mFolderContext; + CFolderThreadController* mFolderThreadController; +}; // class CThreadWindow + diff --git a/mozilla/cmd/macfe/MailNews/LGABox_fixes.cp b/mozilla/cmd/macfe/MailNews/LGABox_fixes.cp new file mode 100644 index 00000000000..46fdacb6c2f --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/LGABox_fixes.cp @@ -0,0 +1,159 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +// LGABox_fixes.cp + +/*====================================================================================*/ + #pragma mark INCLUDE FILES +/*====================================================================================*/ + +#include "LGABox_fixes.h" + + +#pragma mark - +/*====================================================================================*/ + #pragma mark CLASS IMPLEMENTATIONS +/*====================================================================================*/ + + +/*====================================================================================== + Turds be gone! Assumes entire frame is visible through superview. +======================================================================================*/ + +void LGABox_fixes::ResizeFrameBy(Int16 inWidthDelta, Int16 inHeightDelta, + Boolean inRefresh) { + + + if ( mHasBorder && (mBorderStyle != borderStyleGA_NoBorder) && !mRefreshAllWhenResized ) { + Rect borderRect; + CalcBorderRect(borderRect); + Point beforeBotRight = botRight(borderRect); + Rect invalRect; + CalcPortFrameRect(invalRect); + Point portBeforeBotRight = botRight(invalRect); + + inherited::ResizeFrameBy(inWidthDelta, inHeightDelta, inRefresh); + + CalcPortFrameRect(invalRect); + CalcBorderRect(borderRect); + Point portAfterBotRight = botRight(invalRect); + + if ( inWidthDelta != 0 ) { + if ( beforeBotRight.h < borderRect.right ) { + ::SetRect(&invalRect, beforeBotRight.h - mContentOffset.right, + borderRect.top, portBeforeBotRight.h, portBeforeBotRight.v); + } else { + ::SetRect(&invalRect, borderRect.right - mContentOffset.right, + borderRect.top, portAfterBotRight.h, portAfterBotRight.v); + } + LocalToPortPoint(topLeft(invalRect)); + LocalToPortPoint(botRight(invalRect)); + InvalPortRect(&invalRect); + } + if ( inHeightDelta != 0 ) { + if ( beforeBotRight.v < borderRect.bottom ) { + ::SetRect(&invalRect, borderRect.left, beforeBotRight.v - mContentOffset.bottom, + portBeforeBotRight.h, portBeforeBotRight.v); + } else { + ::SetRect(&invalRect, borderRect.left, borderRect.bottom - mContentOffset.bottom, + portAfterBotRight.h, portAfterBotRight.v); + } + LocalToPortPoint(topLeft(invalRect)); + LocalToPortPoint(botRight(invalRect)); + InvalPortRect(&invalRect); + } + } else { + inherited::ResizeFrameBy(inWidthDelta, inHeightDelta, inRefresh); + } +} + + +/*====================================================================================== + Fix bug where everything was not refreshed correctly. +======================================================================================*/ + +void LGABox_fixes::RefreshBoxBorder(void) { + + Rect theFrame; + if ( IsVisible() && CalcLocalFrameRect(theFrame) && (mSuperView) ) { + Rect revealed; + mSuperView->GetRevealedRect(revealed); + Point delta = topLeft(revealed); + PortToLocalPoint(topLeft(revealed)); + PortToLocalPoint(botRight(revealed)); + delta.h -= revealed.left; delta.v -= revealed.top; + if ( ::SectRect(&theFrame, &revealed, &revealed) ) { + RgnHandle borderRgn = GetBoxBorderRegion(revealed); + ::OffsetRgn(borderRgn, delta.h, delta.v); + InvalPortRgn(borderRgn); + ::DisposeRgn(borderRgn); + } + } +} + + +/*====================================================================================== + Fix bug where everything was not refreshed correctly. +======================================================================================*/ + +void LGABox_fixes::RefreshBoxTitle(void) { + + Rect theFrame; + if ( IsVisible() && CalcPortFrameRect(theFrame) && (mSuperView) ) { + Rect revealed; + mSuperView->GetRevealedRect(revealed); + if ( ::SectRect(&theFrame, &revealed, &theFrame) ) { + Rect titleRect; + CalcTitleRect(titleRect); + LocalToPortPoint(topLeft(titleRect)); + LocalToPortPoint(botRight(titleRect)); + if ( ::SectRect(&titleRect, &revealed, &titleRect) ) { + InvalPortRect(&titleRect); + } + } + } +} + + +/*====================================================================================== + Fix bug where everything was not refreshed correctly. +======================================================================================*/ + +void LGABox_fixes::Disable(void) { + + LView::Disable(); + + RefreshBoxBorder(); + RefreshBoxTitle(); +} + + +/*====================================================================================== + Get rid of the flicker. +======================================================================================*/ + +void LGABox_fixes::Enable(void) { + + LView::Enable(); + + RefreshBoxBorder(); + RefreshBoxTitle(); +} + + diff --git a/mozilla/cmd/macfe/MailNews/LGABox_fixes.h b/mozilla/cmd/macfe/MailNews/LGABox_fixes.h new file mode 100644 index 00000000000..525d90e9c1f --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/LGABox_fixes.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +// LGABox_fixes.h + +#pragma once + +/*====================================================================================*/ + #pragma mark INCLUDE FILES +/*====================================================================================*/ + +#include + + +#pragma mark - +/*====================================================================================*/ + #pragma mark TYPEDEFS +/*====================================================================================*/ + + +#pragma mark - +/*====================================================================================*/ + #pragma mark CONSTANTS +/*====================================================================================*/ + + +#pragma mark - +/*====================================================================================*/ + #pragma mark EXTERNAL FUNCTION PROTOTYPES +/*====================================================================================*/ + + +#pragma mark - +/*====================================================================================*/ + #pragma mark CLASS DECLARATIONS +/*====================================================================================*/ + +class LGABox_fixes : public LGABox { + +#if !defined(__MWERKS__) || (__MWERKS__ >= 0x2000) + typedef LGABox inherited; +#endif + +public: + + enum { class_ID = 'Gbo_' }; + LGABox_fixes(LStream *inStream) : + LGABox(inStream) { + + SetRefreshAllWhenResized(false); + } + + virtual void ResizeFrameBy(Int16 inWidthDelta, Int16 inHeightDelta, + Boolean inRefresh); + virtual void RefreshBoxBorder(void); + virtual void RefreshBoxTitle(void); + +protected: + + virtual void Disable(void); + virtual void Enable(void); +}; + diff --git a/mozilla/cmd/macfe/MailNews/MailNewsAddressBook.cp b/mozilla/cmd/macfe/MailNews/MailNewsAddressBook.cp new file mode 100644 index 00000000000..a6ee65a82f0 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/MailNewsAddressBook.cp @@ -0,0 +1,3915 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// MailNewsAddressBook.cp + +/*====================================================================================*/ + #pragma mark INCLUDE FILES +/*====================================================================================*/ + +#define DEBUGGER_ASSERTIONS + + +#include "ABCom.h" +#ifndef MOZ_NEWADDR +#include "MailNewsAddressBook.h" +#include "SearchHelpers.h" +#include "CSizeBox.h" +#include "MailNewsgroupWindow_Defines.h" +#include "Netscape_Constants.h" +#include "resgui.h" +#include "CMailNewsWindow.h" +#include "CMailNewsContext.h" +#include "UGraphicGizmos.h" +#include "LFlexTableGeometry.h" +#include "CProgressBroadcaster.h" +#include "CPatternProgressBar.h" +#include "CThreadWindow.h" +#include "CGAStatusBar.h" +#include "uprefd.h" +#include "ufilemgr.h" +#include "uerrmgr.h" +#include "PascalString.h" +#include "CGATabBox.h" +#include "CGAStatusBar.h" +#include "URobustCreateWindow.h" +#include "UMailSelection.h" // Couldn't detect any order here, just stuck it in - jrm +#include "CMouseDragger.h" +#include "UStdDialogs.h" +#include "macutil.h" +#include "UStClasses.h" +#include "CComposeAddressTableView.h" +#include "msgcom.h" + +#include +#include +#include +#include "divview.h" +#include "CTargetFramer.h" +// get string constants +#define WANT_ENUM_STRING_IDS +#include "allxpstr.h" +#undef WANT_ENUM_STRING_IDS +#include "MailNewsSearch.h" +#include "addrbook.h" +#include "msgcom.h" +#include "msg_srch.h" +#include "abdefn.h" +#include "dirprefs.h" +#include "prefapi.h" +#include "UProcessUtils.h" +#include "CAppleEventHandler.h" + +#include "secnav.h" +#include "CTableKeyAttachment.h" +#include "intl_csi.h" +#include "xp_help.h" +#include "CLDAPQueryDialog.h" + +class CNamePropertiesWindow; +class CListPropertiesWindow; +class CAddressBookListTableView; +class CAddressBookController; + +const ResIDT kConferenceExampleStr = 8901; +const ResIDT kAddressbookErrorStrings = 8902; +#pragma mark - +/*====================================================================================*/ + #pragma mark TYPEDEFS +/*====================================================================================*/ + + +#pragma mark - +/*====================================================================================*/ + #pragma mark CONSTANTS +/*====================================================================================*/ + +// Save window status version + +static const UInt16 cAddressSaveWindowStatusVersion = 0x0218; +static const UInt16 cNamePropertiesSaveWindowStatusVersion = 0x0200; +static const UInt16 cListPropertiesSaveWindowStatusVersion = 0x0200; + + +#pragma mark - +/*====================================================================================*/ + #pragma mark INTERNAL FUNCTION PROTOTYPES +/*====================================================================================*/ + +#define ASSERT_ADDRESS_ERROR(err) Assert_(!(err) || ((err) == 1)) + + +#pragma mark - +/*====================================================================================*/ + #pragma mark INTERNAL CLASS DECLARATIONS +/*====================================================================================*/ + + +// CAddressBookPane + + +class CAddressBookPane : public CMailFlexTable { + +private: + typedef CMailFlexTable Inherited; + +public: + + CAddressBookPane(LStream *inStream) : + Inherited(inStream), + + mProgressBar(nil) { + SetRefreshAllWhenResized(false); + } + + enum EColType { // Sort column header ids + eColType = 'Type' + , eColName = 'Name' + , eColEmail = 'Emal' + , eColCompany = 'Comp' + , eColLocality = 'Loca' + , eColNickname = 'Nick' + , eColWorkPhone ='Phon' + }; + + enum { // Command sort + cmd_SortAscending = 'Ascd' + , cmd_SortDescending = 'Dscd' + }; + + enum { eInvalidCachedRowIDType = 0x7FFFFFFF, eNewEntryID = 0x7FFFFFFF, eInvalidEntryID = 0 }; + + enum { + paneID_ProgressBar = 'Prog' + }; + + // Unimplemented string IDs + + enum { + uStr_OneEntryFoundSelected = 8906 + , uStr_NoEntriesFound = 8907 + , uStr_MultipleEntriesFound = 8908 + , uStr_OneEntryFound = 8909 + , uStr_MultipleEntriesSelected = 8910 + }; + + void GetEntryIDAndType(TableIndexT inRow, ABID *outID, ABID *outType); + ABID GetEntryID(TableIndexT inRow); + TableIndexT GetRowOfEntryID(ABID inEntryID) { + Assert_(GetMessagePane() != nil); + return (AB_GetIndexOfEntryID( (AddressPane*) GetMessagePane(), inEntryID) + 1); + } + + void GetFullAddress( TableIndexT inRow, char** inName ) + { + ABID id; + ABID type; + ABook* pABook = CAddressBookManager::GetAddressBook(); + GetEntryIDAndType( inRow, &id, &type ); + //pABook->GetFullAddress( sCurrentBook, id, inName ); + AB_GetExpandedName(sCurrentBook, pABook, id, inName); + } + + + static UInt32 SortTypeFromColumnType(EColType inColType); + static AB_CommandType SortCommandFromColumnType(EColType inColType); + + static Int32 GetBENumEntries(ABID inType, ABID inListID = eInvalidEntryID); + virtual + + + void ComposeMessage(); + void ShowEditWindowByEntryID(ABID inEntryID, Boolean inOptionKeyDown); + void ShowEditWindow(ABID inID, ABID inType); + static DIR_Server* GetCurrentBook(){ return sCurrentBook; } +protected: + + enum { // Icon resource IDs + ics8_Person = 15260 + , ics8_List = 15263 + }; + + void DestroyMessagePane(MSG_Pane* inPane); + virtual void FinishCreateSelf(); + + virtual void SetUpTableHelpers(); + + virtual void DrawCellContents(const STableCell &inCell, const Rect &inLocalRect); + + virtual void AddSelectionToDrag(DragReference inDragRef, RgnHandle inDragRgn); + virtual void AddRowDataToDrag(TableIndexT inRow, DragReference inDragRef); + + void GetSortParams(UInt32 *outSortKey, Boolean *outSortBackwards); + + // Backend methods + static void GetDisplayData(EColType inColType, ABID inEntryID, ABID inType, + CStr255 *outDisplayText, ResIDT *outIconID); + + // Instance variables ========================================================== + + CGAStatusBar *mProgressBar; + + static DIR_Server *sCurrentBook; // Current address book server +}; + + +// CAddressBookChildWindow + +class CAddressBookChildWindow : public CMediatedWindow, + public LListener, + public LBroadcaster, + public CSaveWindowStatus { + +private: + typedef CMediatedWindow Inherited; + +public: + + CAddressBookChildWindow(LStream *inStream, DataIDT inWindowType) : + CMediatedWindow(inStream, inWindowType), + LListener(), + LBroadcaster(), + CSaveWindowStatus(this), + mEntryID(CAddressBookPane::eNewEntryID) + { + SetRefreshAllWhenResized(false); + } + virtual ~CAddressBookChildWindow() { + } + void FinishCreateSelf(); + ABID GetEntryID() const { + return mEntryID; + } + virtual void UpdateBackendToUI(DIR_Server *inDir, ABook *inABook) = 0L; + virtual void UpdateUIToBackend(DIR_Server *inDir, ABook *inABook, + ABID inEntryID) = 0L; + + + virtual void DoClose() { + Inherited::AttemptClose(); + } + virtual Boolean HandleKeyPress( const EventRecord &inKeyEvent); + +protected: + + // Overriden methods + + virtual void ListenToMessage(MessageT inMessage, void *ioParam = nil); + virtual void UpdateTitle()=0; + // Instance variables + + ABID mEntryID; // BE ID of the property that this window is editing + // CAddressBookPane::eInvalidEntryID if none + +}; + + +// CAddressBookTableView + +class CAddressBookTableView : public CAddressBookPane { + +private: + typedef CAddressBookPane Inherited; + +public: + + enum { class_ID = 'AbTb' }; + enum { // Command IDs + cmd_NewAddressCard = 'NwCd' // New Card + , cmd_NewAddressList = 'NwLs' // New List + , cmd_HTMLDomains = 'HtDm' // domains dialog. + , cmd_EditProperties = 'EdPr' // Edit properties for a list or card + , cmd_DeleteEntry = 'DeLe' // Delete list or card + , cmd_ComposeMailMessage = 'Cmps' // Compose a new mail message + , cmd_ConferenceCall = 'CaLL' + , cmd_ViewMyCard = 'vmyC' + }; + CAddressBookTableView(LStream *inStream) : + CAddressBookPane(inStream) { + SetRefreshAllWhenResized(false); + } + virtual ~CAddressBookTableView(); + + void ButtonMessageToCommand( MessageT inMessage, void* ioParam = NULL ); + + Boolean LoadAddressBook( DIR_Server *inDir = nil); + + void NewCard(PersonEntry* pPerson = nil); + void NewList(); + void SaveAs(); + virtual void ChangeSort(const LTableHeader::SortChange *inSortChange); + virtual void DeleteSelection(); + + static Boolean CurrentBookIsPersonalBook() { + return (sCurrentBook == CAddressBookManager::GetPersonalBook()); + } + void ImportAddressBook(); + + void UpdateToTypedownText(Str255 inTypedownText); + void ConferenceCall( ); + virtual void PaneChanged(MSG_Pane *inPane, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value); + void SetColumnHeaders(DIR_Server* server); + void ShowMyAddressCard(); + void SetModalWindow( Boolean value) { mModalWindow = value; } + + + virtual Boolean ObeyCommand(CommandT inCommand, void *ioParam = nil); + virtual void FindCommandStatus(CommandT inCommand, Boolean &outEnabled, + Boolean &outUsesMark, Char16 &outMark, + Str255 outName); +protected: + virtual void OpenRow(TableIndexT inRow); + virtual Boolean CellInitiatesDrag(const STableCell &/*inCell*/) const { + return true; + } + + void CloseEditWindow(ABID inID, ABID inType); + + void ForceCurrentSort(); + + //void DisposeAddressBookPane(Boolean inRefresh = false); + + Boolean IsLDAPSearching(); + + // Backend methods + + virtual void ChangeFinished(MSG_Pane *inPane, MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, SInt32 inRowCount); + + virtual Int32 GetBENumRows() { + return Inherited::GetBENumEntries(ABTypeAll); + } +private: + Boolean mModalWindow; +}; + + +class CListPropertiesWindow : public CAddressBookChildWindow { + +private: + typedef CAddressBookChildWindow Inherited; + +public: + + enum { class_ID = 'LpWn', pane_ID = class_ID, res_ID = 8940 }; + + // IDs for panes in associated view, also messages that are broadcast to this object + + enum { + paneID_Name = 'NAME' + , paneID_Nickname = 'NICK' + , paneID_Description = 'DESC' + , paneID_AddressBookListTable = 'Tabl' // Address book list table + }; + + enum { // Broadcast messages + msg_ListPropertiesHaveChanged = 'lpCH' // this + }; + + // Unimplemented string IDs + + enum { + uStr_ListWindowTitle = 8912 + , uStr_ListDefaultName = 8914 + , uStr_ListEmptyWindowTitle = 8916 + }; + + // Stream creator method + + CListPropertiesWindow(LStream *inStream); + virtual ~CListPropertiesWindow(); + + virtual ResIDT GetStatusResID() const { return res_ID; } + + virtual void UpdateBackendToUI(DIR_Server *inDir, ABook *inABook); + virtual void UpdateUIToBackend(DIR_Server *inDir, ABook *inABook, + ABID inEntryID); +protected: + + // Overriden methods + + virtual void FinishCreateSelf(); + virtual void DrawSelf(); + virtual UInt16 GetValidStatusVersion() const { + return cListPropertiesSaveWindowStatusVersion; + } + virtual void UpdateTitle(); + virtual Boolean ObeyCommand(CommandT inCommand, void *ioParam = nil); + + // Instance variables + + CAddressBookListTableView *mAddressBookListTable; + LBroadcasterEditField *mTitleField; +}; + + +// CAddressBookListTableView + +//====================================== +class CAddressBookListTableView : public CAddressBookPane +//====================================== +{ + +private: + typedef CAddressBookPane Inherited; + +public: + + enum { class_ID = 'AlTb' }; + + enum { // Broadcast messages + msg_EntriesAddedToList = 'EnAd' // this + , msg_EntriesRemovedFromList ='EnRe' + }; + CAddressBookListTableView(LStream *inStream) : + CAddressBookPane(inStream), + mNumItemsDragAdded(0) { + SetRefreshAllWhenResized(false); + } + virtual ~CAddressBookListTableView(); + + void LoadAddressBookList(ABID &ioEntryListID); + //Drag Support +protected: + virtual Boolean DragIsAcceptable(DragReference inDragRef); + virtual Boolean ItemIsAcceptable(DragReference inDragRef, ItemReference inItemRef); + virtual void ReceiveDragItem(DragReference inDragRef, DragAttributes inDragAttrs, + ItemReference inItemRef, Rect &inItemBounds); + + virtual Boolean RowCanAcceptDropBetweenAbove( DragReference /*inDragRef*/, TableIndexT /*inDropRow*/) + { return true; } + Boolean CanInsertDragItem( DragReference inDragRef, ItemReference inItemRef, + SAddressDragInfo *outInfo); + Boolean RowCanAcceptDrop( DragReference /*inDragRef*/, TableIndexT /*inDropRow*/) + { return true; } +protected: + + virtual void FinishCreateSelf(); + + virtual void DeleteSelection(); + virtual void OpenRow(TableIndexT inRow); + + virtual Boolean CellInitiatesDrag(const STableCell &/*inCell*/) const { + return CAddressBookTableView::CurrentBookIsPersonalBook(); + } + + // Backend methods + + virtual Int32 GetBENumRows() { + return Inherited::GetBENumEntries(ABTypeAll, ((CListPropertiesWindow *) + USearchHelper::GetPaneWindow(this))->GetEntryID()); + } + + void DisposeAddressListPane(Boolean inRefresh = false); + + // Instance variables ========================================================== + + TableIndexT mNumItemsDragAdded; +}; + +class CAddressBookWindow : public CMailNewsWindow + { +private: + typedef CMailNewsWindow Inherited; +public: + + enum { class_ID = 'AbWn', pane_ID = class_ID, res_ID = 8900 }; + + // IDs for panes in associated view, also messages that are broadcast to this object + + enum { + paneID_DirServers = 'DRSR' // CDirServersPopupMenu *, this + , paneID_Search = 'SRCH' // MSG_Pane *, search button + , paneID_Stop = 'STOP' // nil, stop button + , paneID_AddressBookTable = 'Tabl' // Address book table + , paneID_TypedownName = 'TYPE' // Typedown name search edit field in window + , paneID_SearchEnclosure = 'SCHE' // Enclosure for search items + , paneID_AddressBookController = 'AbCn' + }; + #if 0 + enum { // Command IDs + cmd_NewAddressCard = 'NwCd' // New Card + , cmd_NewAddressList = 'NwLs' // New List + , cmd_HTMLDomains = 'HtDm' // domains dialog. + , cmd_EditProperties = 'EdPr' // Edit properties for a list or card + , cmd_DeleteEntry = 'DeLe' // Delete list or card + , cmd_ComposeMailMessage = 'Cmps' // Compose a new mail message + , cmd_ConferenceCall = 'CaLL' + , cmd_ViewMyCard = 'vmyC' + }; + #endif + // Stream creator method + + CAddressBookWindow(LStream *inStream) : + CMailNewsWindow(inStream, WindowType_Address), + mAddressBookController(nil) + { + SetPaneID(pane_ID); + SetRefreshAllWhenResized(false); + } + virtual ~CAddressBookWindow(); + + virtual ResIDT GetStatusResID() const { return res_ID; } + + + static Boolean CloseAddressBookChildren(); + static MWContext *GetMailContext(); + + + void ShowEditWindowByEntryID(ABID inEntryID, Boolean inOptionKeyDown); + + //CAddressBookTableView * GetAddressBookTable() { return mAddressBookTable; } + CAddressBookController * GetAddressBookController() { return mAddressBookController;} +protected: + + // Overriden methods + + virtual void FinishCreateSelf(); + + // Utility methods + + virtual void ReadWindowStatus(LStream *inStatusData); + virtual void WriteWindowStatus(LStream *outStatusData); + virtual UInt16 GetValidStatusVersion() const { return cAddressSaveWindowStatusVersion; } + + // Instance variables + CAddressBookController *mAddressBookController; +}; + + +class CNamePropertiesWindow : public CAddressBookChildWindow { + +private: + typedef CAddressBookChildWindow Inherited; + +public: + + enum { class_ID = 'NpWn', pane_ID = class_ID, res_ID = 8930 }; + + // IDs for panes in associated view, also messages that are broadcast to this object + + enum { + paneID_GeneralView = 'GNVW' // General preferences view + , paneID_FirstName = 'FNAM' + , paneID_LastName = 'LNAM' + , paneID_EMail = 'EMAL' + , paneID_Nickname = 'NICK' + , paneID_Notes = 'NOTE' + , paneID_PrefersHTML = 'HTML' + , paneID_ContactView = 'CNVW' // Contact preferences view + , paneID_Company = 'COMP' + , paneID_Title = 'TITL' + , paneID_Address = 'ADDR' + , paneID_City = 'CITY' + , paneID_State = 'STAT' + , paneID_ZIP = 'ZIP ' + , paneID_Country = 'Coun' + , paneID_WorkPhone = 'WPHO' + , paneID_FaxPhone = 'FPHO' + , paneID_HomePhone = 'HPHO' + , paneID_SecurityView = 'SCVW' // Security preferences view + , paneID_CooltalkView = 'CLVW' // Cooltalk preferences view + , paneID_ConferenceAddress = 'CAED', + paneID_ConferenceServer = 'CnPu' + }; + + enum { // Broadcast messages + paneID_ConferencePopup ='CoPU' // conference pop up button + }; + + + // Stream creator method + + CNamePropertiesWindow(LStream *inStream) : + CAddressBookChildWindow(inStream, WindowType_AddressPerson), + mFirstNameField(nil), + mLastNameField(nil) { + + SetPaneID(pane_ID); + + } + + virtual ResIDT GetStatusResID() const { return res_ID; } + virtual void UpdateBackendToUI(DIR_Server *inDir, ABook *inABook ); + void UpdateUIToPerson( PersonEntry* person , ABID inEntryID ); + void UpdateUIToBackend(DIR_Server *inDir, ABook *inABook, ABID inEntryID); + void SetConferenceText( ); +protected: + + // Overriden methods + + virtual void FinishCreateSelf(); + virtual void ListenToMessage(MessageT inMessage, void *ioParam = nil); + virtual UInt16 GetValidStatusVersion() const { return cNamePropertiesSaveWindowStatusVersion; } + + virtual void UpdateTitle(); + + // Instance variables + + LBroadcasterEditField *mFirstNameField; + LBroadcasterEditField *mLastNameField; +}; + + +// StPersonEntry + +class StPersonEntry { + +public: + + StPersonEntry(); + ~StPersonEntry() { + XP_FREEIF(mPerson); + } + + PersonEntry *GetEntry() { + return mPerson; + } +private: + PersonEntry *mPerson; +}; + + +// StMailingListEntry + +class StMailingListEntry { + +public: + + StMailingListEntry(); + ~StMailingListEntry() { + XP_FREEIF(mMailingList); + } + + MailingListEntry *GetEntry() { + return mMailingList; + } +private: + MailingListEntry *mMailingList; +}; + + +// The CAddressBookController handles the interaction between the container pane, CAddressBookTableView, +// and the typedown edit field + +class CAddressBookController:public LView, LCommander, public LListener, public LPeriodical +{ +private: + typedef LView Inherited; +public: + enum { class_ID = 'AbCn' }; + enum { eDontCheckTypedown = 0xFFFFFFFF, eCheckTypedownInterval = 20 /* 1/3 sec */ }; + + enum { + paneID_DirServers = 'DRSR' // CDirServersPopupMenu *, this + , paneID_Search = 'SRCH' // MSG_Pane *, search button + , paneID_Stop = 'STOP' // nil, stop button + , paneID_AddressBookTable = 'Tabl' // Address book table + , paneID_TypedownName = 'TYPE' // Typedown name search edit field in window + , paneID_SearchEnclosure = 'SCHE' // Enclosure for search items + , paneID_DividedView = 'A2Vw' // the divided view + }; + + + + CAddressBookController(LStream* inStream ):LView( inStream), + mNextTypedownCheckTime(eDontCheckTypedown), + mTypedownName(nil), mSearchButton(nil),mStopButton(nil) + {}; + ~CAddressBookController(){ }; +protected: + virtual void ListenToMessage(MessageT inMessage, void *ioParam); + +// LCommander overrides: +protected: + + virtual Boolean ObeyCommand(CommandT inCommand, void *ioParam); + virtual Boolean HandleKeyPress(const EventRecord &inKeyEvent); + + virtual void ActivateSelf() { + StartRepeating(); + Inherited::ActivateSelf(); + } + + virtual void DeactivateSelf() { + StopRepeating(); + Inherited::DeactivateSelf(); + } + +// LPeriodical + virtual void SpendTime(const EventRecord &inMacEvent); + +// CSaveWindowStatus helpers: +public: + void ReadStatus(LStream *inStatusData); + void WriteStatus(LStream *outStatusData); + +// Specials +public: + void FinishCreateSelf(); + void UpdateToDirectoryServers(); + void PopulateDirectoryServers(); + void SelectDirectoryServer(DIR_Server *inServer, Int32 inServerIndex); + Boolean IsLDAPSearching() { + return ((mStopButton != nil) ? mStopButton->IsVisible() : false); + } + Int32 GetServerIndexFromPopup(); + void SetPopupFromServerIndex(Int32 inServerIndex); + CAddressBookTableView* GetAddressBookTable(){ + Assert_( mAddressBookTable ); + return mAddressBookTable; + } + +protected: + void MessageWindSearch( char* text = NULL); + void MessageWindStop(Boolean inUserAborted); + +// Data +protected: + CSearchEditField* mTypedownName; + LDividedView* mDividedView; + CAddressBookTableView* mAddressBookTable; + //CAddressBookContainerView* mABContainerView; + UInt32 mNextTypedownCheckTime; + + LGAPushButton *mSearchButton; + LGAPushButton *mStopButton; + CPatternProgressCaption *mProgressCaption; + CMailNewsContext *mAddressBookContext; + +}; + +class UAddressBookUtilites +{ +public: + enum { invalid_command = 0xFFFFFFFF }; + static AB_CommandType GetABCommand( CommandT inCommand ); + static void ABCommandStatus( + CMailFlexTable* inTable, CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName); +}; + +AB_CommandType UAddressBookUtilites::GetABCommand( CommandT inCommand ) +{ + switch ( inCommand ) + { + + case cmd_NewMailMessage: + case CAddressBookTableView::cmd_ComposeMailMessage: return AB_NewMessageCmd; + case cmd_ImportAddressBook: return AB_ImportCmd; + case cmd_SaveAs: return AB_SaveCmd; +// case cmd_Close: return AB_CloseCmd; + +// case return AB_NewAddressBook +// case return AB_NewLDAPDirectory + + //case return AB_UndoCmd + + //case return AB_RedoCmd + + case CAddressBookTableView::cmd_DeleteEntry: return AB_DeleteCmd; + +// case return AB_LDAPSearchCmd, + + case CAddressBookPane::eColType: return AB_SortByTypeCmd; + case CAddressBookPane::eColName: return AB_SortByFullNameCmd; + case CAddressBookPane::eColLocality: return AB_SortByLocality; + case CAddressBookPane::eColNickname: return AB_SortByNickname; + case CAddressBookPane::eColEmail: return AB_SortByEmailAddress; + case CAddressBookPane::eColCompany: return AB_SortByCompanyName; + case CAddressBookPane::cmd_SortAscending: return AB_SortAscending; + case CAddressBookPane::cmd_SortDescending: return AB_SortDescending; + + case CAddressBookTableView::cmd_NewAddressCard: return AB_AddUserCmd; + case CAddressBookTableView::cmd_NewAddressList: return AB_AddMailingListCmd; + case CAddressBookTableView::cmd_EditProperties: return AB_PropertiesCmd; + case CAddressBookTableView::cmd_ConferenceCall: return AB_CallCmd; + // case return AB_ImportLdapEntriesCmd + default: return AB_CommandType( invalid_command ); + } +} + +void UAddressBookUtilites::ABCommandStatus( + CMailFlexTable* inTable, + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +{ + AB_CommandType abCommand = AB_CommandType(inCommand); + if( abCommand != UAddressBookUtilites::invalid_command ) + { + CMailSelection selection; + inTable->GetSelection(selection); + + XP_Bool selectable; + MSG_COMMAND_CHECK_STATE checkedState; + const char* display_string = nil; + XP_Bool plural; + + + if ( AB_CommandStatus( + (ABPane*)inTable->GetMessagePane(), + abCommand, + (MSG_ViewIndex*)selection.GetSelectionList(), + selection.selectionSize, + &selectable, + &checkedState, + &display_string, + &plural) + >= 0) + { + + outEnabled = (Boolean)selectable; + outUsesMark = true; + if (checkedState == MSG_Checked) + outMark = checkMark; + else + outMark = 0; + + if (display_string && *display_string) + *(CStr255*)outName = display_string; + } + } +} + + +#pragma mark - +/*====================================================================================*/ +#pragma mark Address Picker class +/*====================================================================================*/ +static const UInt16 cAddressPickerWindowStatusVersion = 0x004; +class CAddressPickerWindow : public CAddressBookWindow, public LListener +{ +public: + enum { class_ID = 'ApWn', pane_ID = class_ID, res_ID = 8990 }; + enum { eOkayButton = 'OkBt', eCancelButton ='CnBt', eHelp ='help'}; + // Buttons to enable and disable + enum { eToButton = 'ToBt', eCcButton = 'CcBt', eBccButton = 'BcBt',ePropertiesButton = 'PrBt' }; + + CAddressPickerWindow(LStream *inStream): CAddressBookWindow( inStream ), LListener(), + mInitialTable( nil ), mPickerAddresses(nil), mAddressBookTable(nil) {}; + void Setup( CComposeAddressTableView* initialTable ); + virtual void FinishCreateSelf(); + + +protected: + virtual void ListenToMessage(MessageT inMessage, void *ioParam); + + virtual ResIDT GetStatusResID() const { return res_ID; } + virtual UInt16 GetValidStatusVersion() const { return cAddressPickerWindowStatusVersion; } +private: + void AddSelection( EAddressType inAddressType ); + CAddressBookTableView * mAddressBookTable; + CComposeAddressTableView* mInitialTable; + CComposeAddressTableView* mPickerAddresses; +}; + + +#pragma mark - +/*====================================================================================*/ + #pragma mark BE CALLBACKS +/*====================================================================================*/ + +/*====================================================================================== + Get the global address book database. +======================================================================================*/ + +XP_List *FE_GetDirServers() { + return CAddressBookManager::GetDirServerList(); +} + + +/*====================================================================================== + Get the global address book database. +======================================================================================*/ + +ABook *FE_GetAddressBook(MSG_Pane *pane) { +#pragma unused (pane) + return CAddressBookManager::GetAddressBook(); +} + + +/*====================================================================================== + Returns the address book context, creating it if necessary. A mail pane is passed in, + on the unlikely chance that it might be useful for the FE. If "viewnow" is TRUE, + then present the address book window to the user; otherwise, don't (unless it is + already visible). +======================================================================================*/ + +MWContext *FE_GetAddressBookContext(MSG_Pane *pane, XP_Bool viewnow) { +#pragma unused (pane) + + if ( viewnow ) { + CAddressBookManager::ShowAddressBookWindow(); + } + + return CAddressBookWindow::GetMailContext(); +} + +/**************************************************************************** +This is a callback into the FE to bring up a modal property sheet for modifying +an existing entry or creating a new one from a person structure. If +entryId != MSG_MESSAGEIDNONE then it is the entryID of the entry to modify. +Each FE should Return TRUE if the user hit ok return FALSE if they hit cancel +and return -1 if there was a problem creating the window or something +****************************************************************************/ +int FE_ShowPropertySheetFor (MWContext* context, ABID entryID, PersonEntry* pPerson) +{ +#pragma unused (context) + CAddressBookWindow * addressWindow = CAddressBookManager::ShowAddressBookWindow(); + + addressWindow->GetAddressBookController()->SelectDirectoryServer(CAddressBookManager::GetPersonalBook(), 0); + + if (entryID == MSG_MESSAGEIDNONE) + { + addressWindow->GetAddressBookController()->GetAddressBookTable()->NewCard(pPerson); + } + else + { + // Not implemented yet const Boolean optionKeyDown = USearchHelper::KeyIsDown(USearchHelper::char_OptionKey); + const Boolean optionKeyDown = false; + AB_ModifyUser(CAddressBookManager::GetPersonalBook(), CAddressBookManager::GetAddressBook(), entryID, pPerson); + addressWindow->GetAddressBookController()->GetAddressBookTable()->ShowEditWindowByEntryID(entryID, optionKeyDown); + } + return true; +} + + +#pragma mark - +/*====================================================================================*/ + #pragma mark CLASS IMPLEMENTATIONS +/*====================================================================================*/ + +XP_List *CAddressBookManager::sDirServerList = nil; +ABook *CAddressBookManager::sAddressBook = nil; +Boolean CAddressBookManager::sDirServerListChanged = false; + +/*====================================================================================== + Open the address book at application startup. This method sets the initial address + book to the local personal address book for the user (creates one if it doesn't + exist already). +======================================================================================*/ + +void CAddressBookManager::OpenAddressBookManager() { + + AssertFail_(sDirServerList == nil); + + RegisterAddressBookClasses(); + + char fileName[64]; + fileName[63] = '\0'; + char *oldName = nil; + Try_ { + // Get the default address book path + FSSpec spec = CPrefs::GetFilePrototype(CPrefs::MainFolder); + // The real, new way! + CStr255 pfilename; + ::GetIndString(pfilename, 300, addressBookFile); // Get address book name from prefs + strncpy( fileName,(char*)pfilename, 63) ; + // Create directory servers + Int32 error = DIR_GetServerPreferences(&sDirServerList, fileName); + // No listed return error codes, who knows what they are! + FailOSErr_(error); + AssertFail_(sDirServerList != nil); + + // Get the akbar address book. It's in HTML format (addressbook.html) + spec = CPrefs::GetFilePrototype(CPrefs::MainFolder); + ::GetIndString(spec.name, 300, htmlAddressBook); // Get address book name from prefs + oldName = CFileMgr::GetURLFromFileSpec(spec); + FailNIL_(oldName); + + CAddressBookManager::FailAddressError( + AB_InitializeAddressBook(GetPersonalBook(), &sAddressBook, + oldName + XP_STRLEN("file://")) + ); + XP_FREE(oldName); + } + Catch_(inErr) { + CAddressBookManager::CloseAddressBookManager(); + XP_FREEIF(oldName); + } + EndCatch_ + + PREF_RegisterCallback("ldap_1", DirServerListChanged, NULL); +} + + +/*====================================================================================== + Closes any open resources used by the address book manager. +======================================================================================*/ + +void CAddressBookManager::CloseAddressBookManager() { + + if ( sAddressBook != nil ) { + Int32 error = AB_CloseAddressBook(&sAddressBook); + sAddressBook = nil; + ASSERT_ADDRESS_ERROR(error); + } + + if ( sDirServerList != nil ) { + Int32 error = DIR_DeleteServerList(sDirServerList); + sDirServerList = nil; + Assert_(!error); + } +} + + +/*====================================================================================== + Stupid comment that explains something that doesn't occur till after the comment. +======================================================================================*/ + +void CAddressBookManager::ImportLDIF(const FSSpec& inFileSpec) { + + char* path = CFileMgr::EncodedPathNameFromFSSpec(inFileSpec, true /*wantLeafName*/); + if (path) + { + int result = AB_ImportData( + nil, // no container, create new + path, + strlen(path), + AB_Filename // FIX ME: need the bit that tells the backend to delete the file. + ); + XP_FREE(path); + } +} + + +/*====================================================================================== + Show the search dialog by bringing it to the front if it is not already. Create it + if needed. +======================================================================================*/ + +CAddressBookWindow * CAddressBookManager::ShowAddressBookWindow() { + + // Find out if the window is already around + CAddressBookWindow *addressWindow = dynamic_cast( + CWindowMediator::GetWindowMediator()->FetchTopWindow(WindowType_Address)); + + if ( addressWindow == nil ) { + // Search dialog has not yet been created, so create it here and display it. + addressWindow = dynamic_cast( + URobustCreateWindow::CreateWindow(CAddressBookWindow::res_ID, + LCommander::GetTopCommander())); + AssertFail_(addressWindow != nil); + } + + // Select the window + + addressWindow->Show(); + addressWindow->Select(); + return addressWindow; +} + + +/*====================================================================================== + Return the list of directory servers for the application. Why are this + method and variable here? Can also call the BE method FE_GetDirServers() to return + the same result. +======================================================================================*/ + +XP_List *CAddressBookManager::GetDirServerList() { + + AssertFail_(sDirServerList != nil); + + if (sDirServerListChanged) { + // directory list may have been updated via a remote admin file + sDirServerListChanged = false; + XP_List* newList = nil; + CStr255 pfilename; + ::GetIndString(pfilename, 300, addressBookFile); + + if ( DIR_GetServerPreferences(&newList, pfilename) == 0 && newList ) { + SetDirServerList(newList, false); + } + } + + return sDirServerList; +} + + +/*====================================================================================== + This method should be called to set the current directory servers list. The old list + is destroyed and is replaced with inList; the caller does NOT dispose of inList, since + it is managed by this class after calling this method. This method also calls the + BE method DIR_SaveServerPreferences() if inSavePrefs is true. +======================================================================================*/ + +void CAddressBookManager::SetDirServerList(XP_List *inList, Boolean inSavePrefs) { + + AssertFail_(inList != nil); + + XP_List *tempDirServerList = sDirServerList; + sDirServerList = inList; // This needs to be set correctly for the callback to work. + if ( inSavePrefs ) { + Int32 error = DIR_SaveServerPreferences(inList); // No listed return error codes, + // who know what they are! + if (error) + { + sDirServerList = tempDirServerList; // Put this back if there are problems. + } + FailOSErr_(error); + } + + if ( tempDirServerList != nil ) { + Int32 error = DIR_DeleteServerList(tempDirServerList); + Assert_(!error); + } +} + + +/*====================================================================================== + Return the local personal address book. +======================================================================================*/ + +DIR_Server *CAddressBookManager::GetPersonalBook() { + + AssertFail_((sDirServerList != nil)); + DIR_Server *personalBook = nil; + DIR_GetPersonalAddressBook(sDirServerList, &personalBook); + AssertFail_(personalBook != nil); + return personalBook; +} + + +/*====================================================================================== + Return the address book database. Can also call the BE method FE_GetAddressBook() to + return the same result. +======================================================================================*/ + +ABook *CAddressBookManager::GetAddressBook() { + + AssertFail_(sAddressBook != nil); + return sAddressBook; +} + +void CAddressBookManager::FailAddressError(Int32 inError) +{ + if ( inError == 0 ) return; + switch ( inError ) + { + case MK_MSG_NEED_FULLNAME: + case MK_MSG_NEED_GIVENNAME: + Throw_( MK_MSG_NEED_FULLNAME ); + break; + case MK_OUT_OF_MEMORY: + Throw_(memFullErr); + break; + case MK_ADDR_LIST_ALREADY_EXISTS: + case MK_ADDR_ENTRY_ALREADY_EXISTS: + Throw_( inError ); + break; + case MK_UNABLE_TO_OPEN_FILE: + case XP_BKMKS_CANT_WRITE_ADDRESSBOOK: + Throw_(ioErr); + break; + case XP_BKMKS_NICKNAME_ALREADY_EXISTS: + Throw_(XP_BKMKS_INVALID_NICKNAME); + break; + default: + Assert_(false); + Throw_(32000); // Who knows? + break; + } +} + + +/*====================================================================================== + Register all classes associated with the address book window. +======================================================================================*/ + +void CAddressBookManager::RegisterAddressBookClasses() { + + RegisterClass_(CAddressBookWindow); + RegisterClass_(CAddressBookTableView); + RegisterClass_(CAddressBookListTableView); + RegisterClass_(CAddressPickerWindow); + RegisterClass_(CSearchPopupMenu); + + RegisterClass_(CNamePropertiesWindow); + RegisterClass_(CListPropertiesWindow); + + RegisterClass_(CAddressBookController); + RegisterClass_( CLDAPQueryDialog ); + CGATabBox::RegisterTabBoxClasses(); +} + +/*====================================================================================== + Find an open instance of the name properties window with the specified ID. +======================================================================================*/ + +CNamePropertiesWindow *CAddressBookManager::FindNamePropertiesWindow(ABID inEntryID) { + + CNamePropertiesWindow *propertiesWindow = nil; + + // Try to locate the specific window + CMediatedWindow *theWindow; + CWindowIterator theIterator(WindowType_AddressPerson); + + while ( theIterator.Next(theWindow) ) { + propertiesWindow = dynamic_cast(theWindow); + AssertFail_(propertiesWindow != nil); + if ( propertiesWindow->GetEntryID() == inEntryID ) { + break; // We found our window! + } else { + propertiesWindow = nil; + } + } + + return propertiesWindow; +} + + +/*====================================================================================== + Return an instance of the name properties window. If inEntryID is not equal to + CAddressBookPane::eNewEntryID, try to locate the window with the specified entry + ID; otherwise, try to locate the first top window. If inDoCreate is true, create the + window if it is not found, otherwise return the found window or nil. The caller should + always call Show() and Select() on the window. +======================================================================================*/ + +CNamePropertiesWindow *CAddressBookManager::GetNamePropertiesWindow(ABID inEntryID, Boolean inOptionKeyDown) { + + CNamePropertiesWindow *propertiesWindow = nil; + + // Try to locate a window with the specified ID + + if ( inEntryID != CAddressBookPane::eNewEntryID ) { + propertiesWindow = FindNamePropertiesWindow(inEntryID); + } + + if ( propertiesWindow == nil ) { + // Couldn't find a window with the specified ID + if ( inOptionKeyDown ) { + // Try to locate the top window + propertiesWindow = dynamic_cast( + CWindowMediator::GetWindowMediator()->FetchTopWindow(WindowType_AddressPerson)); + } + if ( propertiesWindow == nil ) { + // Create a new window + CAddressBookWindow *addressWindow = dynamic_cast( + CWindowMediator::GetWindowMediator()->FetchTopWindow(WindowType_Address)); + AssertFail_(addressWindow != nil); // Must be around to create name properties window + + // Create the window + propertiesWindow = dynamic_cast( + URobustCreateWindow::CreateWindow(CNamePropertiesWindow::res_ID, + addressWindow->GetSuperCommander())); + AssertFail_(propertiesWindow != nil); + } + } + + return propertiesWindow; +} + + +/*====================================================================================== + Find an open instance of the list properties window with the specified ID. +======================================================================================*/ + +CListPropertiesWindow *CAddressBookManager::FindListPropertiesWindow(ABID inEntryID) { + + CListPropertiesWindow *propertiesWindow = nil; + + // Try to locate the specific window + CMediatedWindow *theWindow; + CWindowIterator theIterator(WindowType_AddressList); + + while ( theIterator.Next(theWindow) ) { + propertiesWindow = dynamic_cast(theWindow); + AssertFail_(propertiesWindow != nil); + if ( propertiesWindow->GetEntryID() == inEntryID ) { + break; // We found our window! + } else { + propertiesWindow = nil; + } + } + + return propertiesWindow; +} + +//----------------------------------- +CListPropertiesWindow *CAddressBookManager::GetListPropertiesWindow(ABID inEntryID, Boolean inOptionKeyDown) +// Return an instance of the list properties window. If inEntryID is not equal to +// CAddressBookPane::eNewEntryID, try to locate the window with the specified entry +// ID; otherwise, try to locate the first top window. If inDoCreate is true, create the +// window if it is not found, otherwise return the found window or nil. The caller should +// always call Show() and Select() on the window. +//----------------------------------- +{ + + CListPropertiesWindow *propertiesWindow = nil; + + // Try to locate a window with the specified ID + + if ( inEntryID != CAddressBookPane::eNewEntryID ) { + propertiesWindow = FindListPropertiesWindow(inEntryID); + } + + if ( propertiesWindow == nil ) { + // Couldn't find a window with the specified ID + if ( inOptionKeyDown ) { + // Try to locate the top window + propertiesWindow = dynamic_cast( + CWindowMediator::GetWindowMediator()->FetchTopWindow(WindowType_AddressList)); + } + if ( propertiesWindow == nil ) { + // Create a new window + CAddressBookWindow *addressWindow = dynamic_cast( + CWindowMediator::GetWindowMediator()->FetchTopWindow(WindowType_Address)); + AssertFail_(addressWindow != nil); // Must be around to create name properties window + + // Create the window + propertiesWindow = dynamic_cast( + URobustCreateWindow::CreateWindow(CListPropertiesWindow::res_ID, + addressWindow->GetSuperCommander())); + AssertFail_(propertiesWindow != nil); + } + } + + return propertiesWindow; +} // CAddressBookWindow::GetListPropertiesWindow +#pragma mark - +/*====================================================================================== + Stop any current search. +======================================================================================*/ + +CAddressBookWindow::~CAddressBookWindow() { + + // Loop though current visible windows to close any name properties or list view windows + + CloseAddressBookChildren(); +#if 0 // Don't know why this code exists + Boolean canRotate = false; + if ( IsVisible() ) { + USearchHelper::FindWindowTabGroup(&mSubCommanders)->SetRotateWatchValue(&canRotate); + } +#endif // 0 + +} + + +/*====================================================================================== + Try to close the address book children windows. Return true if all windows were closed + successfully, or false if the user cancelled closing an open window. +======================================================================================*/ + +Boolean CAddressBookWindow::CloseAddressBookChildren() { + + // Loop though current visible windows to close any name properties or list view windows + + CWindowMediator *mediator = CWindowMediator::GetWindowMediator(); + Assert_(mediator != nil); + + CMediatedWindow *theWindow; + + Boolean rtnVal = true; + + Try_ { + + while ( ((theWindow = mediator->FetchTopWindow(WindowType_AddressPerson)) != nil) || + ((theWindow = mediator->FetchTopWindow(WindowType_AddressList)) != nil) ) { + + theWindow->DoClose(); + } + } + Catch_(inErr) { + rtnVal = false; + } + EndCatch_ + + return rtnVal; +} + + +/*====================================================================================== + Get the mail context for the address book. We must have an address book window to + access the context, so this method creates the window (but doesn't show it) if the + window is not yet around. +======================================================================================*/ + +MWContext *CAddressBookWindow::GetMailContext() { + + CAddressBookWindow *addressWindow = dynamic_cast( + CWindowMediator::GetWindowMediator()->FetchTopWindow(WindowType_Address)); + + if ( addressWindow == nil ) { + // Search dialog has not yet been created, so create it here and display it. + addressWindow = dynamic_cast( + URobustCreateWindow::CreateWindow(CAddressBookWindow::res_ID, + LCommander::GetTopCommander())); + AssertFail_(addressWindow != nil); + } + + return *(addressWindow->GetWindowContext()); +} + + +//----------------------------------- +void CAddressBookWindow::FinishCreateSelf() +//----------------------------------- +{ + + mAddressBookController = dynamic_cast< CAddressBookController* > + (FindPaneByID( paneID_AddressBookController) ) ; + FailNILRes_(mAddressBookController); + + CMediatedWindow::FinishCreateSelf(); // Call CMediatedWindow for now since we need to + // create a different context than CMailNewsWindow + + UReanimator::LinkListenerToControls(mAddressBookController, this,CAddressBookWindow:: res_ID); + + // Create context and and progress listener + + mMailNewsContext = new CMailNewsContext(MWContextAddressBook); + FailNIL_(mMailNewsContext); + mMailNewsContext->AddUser(this); + mMailNewsContext->AddListener(mAddressBookController); + + + mMailNewsContext->SetWinCSID(INTL_DocToWinCharSetID(mMailNewsContext->GetDefaultCSID())); + + + CMediatedWindow::FinishCreateSelf(); // Call CMediatedWindow for now since we need to + // create a different context than CMailNewsWindow + + CStr255 windowTitle; + GetUserWindowTitle(4, windowTitle); + SetDescriptor(windowTitle); + + // Call inherited method + FinishCreateWindow(); +} + + + +//----------------------------------- +void CAddressBookWindow::ReadWindowStatus(LStream *inStatusData) +// Adjust the window to stored preferences. +//----------------------------------- +{ + Int32 directoryIndex = 1; + Inherited::ReadWindowStatus(inStatusData); + if ( inStatusData != nil ) + { + + *inStatusData >> directoryIndex; + mAddressBookController->ReadStatus( inStatusData ); + } + + // The popup is set up to item 1, and setting the value does nothing if the value + // is the same as the existing value. Hence this special case. + if (directoryIndex == 1) + mAddressBookController->SelectDirectoryServer(nil, directoryIndex); + else + mAddressBookController->SetPopupFromServerIndex(directoryIndex); + +} + +//----------------------------------- +void CAddressBookWindow::WriteWindowStatus(LStream *outStatusData) +// Write window stored preferences. +//----------------------------------- +{ + + Inherited::WriteWindowStatus(outStatusData); + *outStatusData << mAddressBookController->GetServerIndexFromPopup(); + mAddressBookController->WriteStatus(outStatusData); + +} + +#pragma mark - + +DIR_Server *CAddressBookPane::sCurrentBook = nil; + +//----------------------------------- +void CAddressBookPane::FinishCreateSelf() +//----------------------------------- +{ + LPane *pane = USearchHelper::GetPaneWindow(this)->FindPaneByID(paneID_ProgressBar); + mProgressBar = dynamic_cast(pane); + Inherited::FinishCreateSelf(); +} + +//----------------------------------- +void CAddressBookPane::DrawCellContents(const STableCell &inCell, const Rect &inLocalRect) +// Draw the table view cell. +//----------------------------------- +{ + CStr255 displayText; + ResIDT iconID; + + ABID entryID, type; + GetEntryIDAndType(inCell.row, &entryID, &type); + + GetDisplayData((EColType) GetCellDataType(inCell), entryID, type, &displayText, &iconID); + + Rect cellDrawFrame; + + if ( iconID != 0 ) { + ::SetRect(&cellDrawFrame, 0, 0, 16, 16); + UGraphicGizmos::CenterRectOnRect(cellDrawFrame, inLocalRect); + } else { + cellDrawFrame = inLocalRect; + ::InsetRect(&cellDrawFrame, 2, 0); // For a nicer look between cells + } + + Boolean cellIsSelected = CellIsSelected(inCell); + + StSectClipRgnState saveSetClip(&cellDrawFrame); + + //if ( cellIsSelected && DrawHiliting() ) { + if ( iconID != 0 ) { + // Display an icon + ::PlotIconID(&cellDrawFrame, ttNone, ttNone, iconID); + } else { + DrawTextString( displayText, &mTextFontInfo, 0, + cellDrawFrame, teFlushLeft, + true, truncMiddle); + } + #if 0 + } else { + if ( iconID != 0 ) { + // Display an icon + ::PlotIconID(&cellDrawFrame, ttNone, ttNone, iconID); + } else { + // Display text + if ( displayText.Length() > 0 ) { + UGraphicGizmos::PlaceTextInRect((char *) &displayText[1], displayText[0], + cellDrawFrame, teFlushLeft, teCenter, + &mTextFontInfo, true, truncMiddle); + } + } + + } + #endif +} // CAddressBookPane::DrawCell + + +//----------------------------------- +void CAddressBookPane::SetUpTableHelpers() +//----------------------------------- +{ + + SetTableGeometry(new LFlexTableGeometry(this, mTableHeader)); + SetTableSelector(new LTableRowSelector(this)); + + AddAttachment( new CTableRowKeyAttachment(this) ); // need for arrow naviagation + // We don't need table storage. It's safe to have a null one. + AssertFail_(mTableStorage == nil); +} + +//----------------------------------- +void CAddressBookPane::GetEntryIDAndType(TableIndexT inRow, ABID *outID, ABID *outType) +// Get the entry ID and data type for the specified row. +//----------------------------------- +{ + + Assert_(GetMessagePane() != nil); + Assert_(sCurrentBook != nil); + + *outID = AB_GetEntryIDAt((AddressPane*) GetMessagePane(), inRow - 1); + Int32 error = AB_GetType(sCurrentBook, CAddressBookManager::GetAddressBook(), + *outID, outType); + ASSERT_ADDRESS_ERROR(error); +} + +//----------------------------------- +ABID CAddressBookPane::GetEntryID(TableIndexT inRow) +//----------------------------------- +{ + Assert_(GetMessagePane() != nil); + Assert_((inRow <= mRows) && (inRow > 0)); + if (inRow > mRows || inRow == 0) + return MSG_MESSAGEIDNONE; + + ABID id = AB_GetEntryIDAt( (AddressPane*) GetMessagePane(), inRow - 1); + Assert_(id != MSG_MESSAGEIDNONE); + + return id; +} + + +//----------------------------------- +void CAddressBookPane::GetDisplayData(EColType inColType, ABID inEntryID, ABID inType, + CStr255 *outDisplayText, ResIDT *outIconID) +//----------------------------------- +{ + *outDisplayText = CStr255::sEmptyString; + *outIconID = 0; + + switch ( inColType ) { + + case eColType: { + if ( inType == ABTypeList ) { + *outIconID = ics8_List; + } else { + *outIconID = ics8_Person; + } + } + break; + + case eColName: { + Int32 error = AB_GetFullName(sCurrentBook, CAddressBookManager::GetAddressBook(), inEntryID, + (char *) ((UInt8 *) &(*outDisplayText)[0])); + ASSERT_ADDRESS_ERROR(error); + } + break; + + case eColEmail: { + Int32 error = AB_GetEmailAddress(sCurrentBook, CAddressBookManager::GetAddressBook(), inEntryID, + (char *) ((UInt8 *) &(*outDisplayText)[0])); + ASSERT_ADDRESS_ERROR(error); + } + break; + + case eColCompany: { + Int32 error = AB_GetCompanyName(sCurrentBook, CAddressBookManager::GetAddressBook(), inEntryID, + (char *) ((UInt8 *) &(*outDisplayText)[0])); + ASSERT_ADDRESS_ERROR(error); + } + break; + + case eColLocality: { + Int32 error = AB_GetLocality(sCurrentBook, CAddressBookManager::GetAddressBook(), inEntryID, + (char *) ((UInt8 *) &(*outDisplayText)[0])); + ASSERT_ADDRESS_ERROR(error); + } + break; + + case eColNickname: { + Int32 error = AB_GetNickname(sCurrentBook, CAddressBookManager::GetAddressBook(), inEntryID, + (char *) ((UInt8 *) &(*outDisplayText)[0])); + ASSERT_ADDRESS_ERROR(error); + } + break; + case eColWorkPhone: + Int32 error = AB_GetWorkPhone(sCurrentBook, CAddressBookManager::GetAddressBook(), inEntryID, + (char *) ((UInt8 *) &(*outDisplayText)[0])); + ASSERT_ADDRESS_ERROR(error); + break; + + + default: + Assert_(false); // Should never get here! + break; + } + + if ( outDisplayText->Length() > 0 ) { + // We have a c string, convert to pascal + C2PStr((char *) ((UInt8 *) &(*outDisplayText)[0])); + } +} + + +/*====================================================================================== + Get the current sort params for the address book data. +======================================================================================*/ + +void CAddressBookPane::GetSortParams(UInt32 *outSortKey, Boolean *outSortBackwards) { + + AssertFail_(mTableHeader != nil); + PaneIDT sortColumnID; + mTableHeader->GetSortedColumn(sortColumnID); + *outSortKey = SortTypeFromColumnType((EColType) sortColumnID); + *outSortBackwards = mTableHeader->IsSortedBackwards(); +} + + +/*====================================================================================== + Call CStandardFlexTable's method and add actual ABIDs instead. +======================================================================================*/ + +void CAddressBookPane::AddSelectionToDrag(DragReference inDragRef, RgnHandle inDragRgn) { + + CStandardFlexTable::AddSelectionToDrag(inDragRef, inDragRgn); +} + + +/*====================================================================================== + Add the actual ABID. +======================================================================================*/ + +void CAddressBookPane::AddRowDataToDrag(TableIndexT inRow, DragReference inDragRef) { + + + AssertFail_(mDragFlavor == kMailAddresseeFlavor); + + SAddressDragInfo info; + info.id = GetEntryID(inRow); + info.dir = sCurrentBook; + + OSErr err = ::AddDragItemFlavor(inDragRef, inRow, mDragFlavor, + &info, sizeof(info), flavorSenderOnly); + FailOSErr_(err); + + + // temporary while the old address book is still being used. + char* fullName = NULL; + + GetFullAddress( inRow, &fullName); + + Size dataSize = XP_STRLEN ( fullName ); + err = ::AddDragItemFlavor(inDragRef, inRow, 'TEXT', fullName, dataSize, 0 ); + + XP_FREE(fullName); + + FailOSErr_(err); + +} + + +/*====================================================================================== + Return a valid BE sort type from the specified column type. +======================================================================================*/ + +UInt32 CAddressBookPane::SortTypeFromColumnType(EColType inColType) { + + UInt32 rtnVal; + + switch ( inColType ) { + case eColType: rtnVal = ABTypeEntry; break; + case eColName: rtnVal = ABFullName; break; + case eColEmail: rtnVal = ABEmailAddress; break; + case eColCompany: rtnVal = ABCompany; break; + case eColLocality: rtnVal = ABLocality; break; + case eColNickname: rtnVal = ABNickname; break; + default: rtnVal = ABFullName; break; + } + + return rtnVal; +} + + +/*====================================================================================== + Return a valid BE sort type from the specified column type. +======================================================================================*/ + +AB_CommandType CAddressBookPane::SortCommandFromColumnType(EColType inColType) { + + AB_CommandType rtnVal; + + switch ( inColType ) { + case eColType: rtnVal = AB_SortByTypeCmd; break; + case eColName: rtnVal = AB_SortByFullNameCmd; break; + case eColEmail: rtnVal = AB_SortByEmailAddress; break; + case eColCompany: rtnVal = AB_SortByCompanyName; break; + case eColLocality: rtnVal = AB_SortByLocality; break; + case eColNickname: rtnVal = AB_SortByNickname; break; + default: rtnVal = AB_SortByFullNameCmd; break; + } + + return rtnVal; +} + + +/*====================================================================================== + Get the number of entries for the specified entry. +======================================================================================*/ + +Int32 CAddressBookPane::GetBENumEntries(ABID inType, ABID inListID) { + + UInt32 numRows; + Int32 error = AB_GetEntryCount(sCurrentBook, CAddressBookManager::GetAddressBook(), + &numRows, inType, inListID); + ASSERT_ADDRESS_ERROR(error); + return numRows; +} + + +/*====================================================================================== + Compose a mail message with the selected items. +======================================================================================*/ + +void CAddressBookPane::ComposeMessage() { + + AssertFail_(GetMessagePane() != nil); + + CMailSelection selection; + if ( GetSelection(selection) ) + { + Try_ { + + CAddressBookManager::FailAddressError(AB_Command((ABPane *) GetMessagePane(), + AB_NewMessageCmd, + (MSG_ViewIndex*)selection.GetSelectionList(), + selection.selectionSize)); + } + Catch_(inErr) { + Throw_(inErr); + } + EndCatch_ + } +} + +/*====================================================================================== + Open and show an edit window for the specified address book entry (list or person). +======================================================================================*/ + +void CAddressBookPane::ShowEditWindowByEntryID(ABID inEntryID, Boolean inOptionKeyDown) { +#pragma unused (inOptionKeyDown) + ABID type; + Int32 error = AB_GetType(sCurrentBook, CAddressBookManager::GetAddressBook(), inEntryID, &type); + ASSERT_ADDRESS_ERROR(error); + + ShowEditWindow(inEntryID, type); +} + + +#pragma mark - + +/*====================================================================================== + Adjust the window to stored preferences. +======================================================================================*/ + +CAddressBookTableView::~CAddressBookTableView() { + + SetMessagePane( NULL ); +} + + +/*====================================================================================== + Dispose of the address book pane. +======================================================================================*/ + +void CAddressBookPane::DestroyMessagePane(MSG_Pane* inPane) { + +// if ( inRefresh && (mRows > 0) ) RemoveRows(mRows, 1, true); + + if ( inPane != nil ) { + ::SetCursor(*::GetCursor(watchCursor)); // Could take forever + Int32 error = AB_CloseAddressBookPane((ABPane **) &inPane); + + ASSERT_ADDRESS_ERROR(error); + // sCurrentBook = nil; + } + + +} + + +//----------------------------------- +Boolean CAddressBookTableView::LoadAddressBook(DIR_Server *inDir) +// Load and display the specified address book. A reference is stored to the specified +// DIR_Server, so it should not be deleted without deleting the pane. If the method +// returns true, the address book was able to be loaded; if the method returns false, +// the address book could not be loaded because the user decided to continue modifying +// the current one. +//----------------------------------- +{ + AssertFail_(inDir != nil); + +// if ( inDir == sCurrentBook ) return true; // Already set! + + + TableIndexT rows, cols; + GetTableSize(rows, cols); + if (rows > 0) + RemoveRows(rows, 1, false); + + sCurrentBook = inDir; + + Try_ { + ::SetCursor(*::GetCursor(watchCursor)); // Could take forever + if ( GetMessagePane() != nil ) + { + CAddressBookManager::FailAddressError(AB_ChangeDirectory((ABPane *) GetMessagePane(), inDir)); + mTableHeader->SetSortOrder(false); + // Big hack to avoid having to fix some notification + SetRowCount(); + Refresh(); + } + else + { + MWContext *context = CAddressBookWindow::GetMailContext(); + UInt32 sortKey; + Boolean sortBackwards; + GetSortParams(&sortKey, &sortBackwards); + // Sorting on Keys other than ABFullname, ABEmailAddress, ABNickname + // is slow ( see bug #81275 ) + switch ( sortKey ) + { + case ABFullName: + case ABEmailAddress: + case ABNickname: + break; + default: + sortKey = ABFullName; + mTableHeader->SetWithoutSort( + mTableHeader->ColumnFromID( eColName ), sortBackwards, true ); + } + MSG_Pane* pane; + CAddressBookManager::FailAddressError( + AB_CreateAddressBookPane( + (ABPane **) &pane, + context, + CMailNewsContext::GetMailMaster()) + ); + SetMessagePane( pane ); + sCurrentBook = inDir; + MSG_SetFEData((MSG_Pane *) GetMessagePane(), CMailCallbackManager::Get()); + CAddressBookManager::FailAddressError( + AB_InitializeAddressBookPane( + (ABPane *) GetMessagePane(), + inDir, + CAddressBookManager::GetAddressBook(), + sortKey, + !sortBackwards) + ); + } + + } + Catch_(inErr) { + SetMessagePane( NULL ); + Throw_(inErr); + } + EndCatch_ + + return true; +} // CAddressBookTableView::LoadAddressBook + +//------------------------------------ +void CAddressBookTableView::ButtonMessageToCommand( MessageT inMessage, void* ioParam ) +//------------------------------------ +{ + ObeyCommand( inMessage, ioParam ); +} // CAddressBookTableView::ButtonMessageToCommand + + +//---------------------------------- +Boolean CAddressBookTableView::ObeyCommand(CommandT inCommand, void *ioParam) +//----------------------------------- +{ + Boolean rtnVal = true; + + switch ( inCommand ) { + + case CAddressBookPane::eColType: + case CAddressBookPane::eColName: + case CAddressBookPane::eColEmail: + case CAddressBookPane::eColCompany: + case CAddressBookPane::eColLocality: + case CAddressBookPane::eColNickname: + GetTableHeader()->SimulateClick(inCommand); + rtnVal = true; + break; + + case CAddressBookPane::cmd_SortAscending: + case CAddressBookPane::cmd_SortDescending: + GetTableHeader()->SetSortOrder(inCommand == CAddressBookPane::cmd_SortDescending); + break; + + case cmd_NewAddressCard: + NewCard(); + break; + + case cmd_NewAddressList: + NewList(); + break; + + case cmd_HTMLDomains: + MSG_DisplayHTMLDomainsDialog( CAddressBookWindow::GetMailContext() ); + break; + + case cmd_EditProperties: + OpenSelection(); + break; + + case cmd_DeleteEntry: + DeleteSelection(); + break; + + case cmd_ImportAddressBook: + ImportAddressBook(); + break; + + case cmd_NewMailMessage: + case cmd_ComposeMailMessage: + ComposeMessage(); + break; + + + case cmd_SaveAs: + SaveAs(); + break; + case cmd_SecurityInfo: + MWContext * context = CAddressBookWindow::GetMailContext(); + SECNAV_SecurityAdvisor( context, NULL ); + return true; + + case cmd_ViewMyCard: + ShowMyAddressCard(); + break; + + default: + rtnVal = Inherited::ObeyCommand(inCommand, ioParam); + break; + } + + return rtnVal; +} + +//----------------------------------- +void CAddressBookTableView:: FindCommandStatus(CommandT inCommand, Boolean &outEnabled, + Boolean &outUsesMark, Char16 &outMark, + Str255 outName) +//----------------------------------- +{ + + switch ( inCommand ) { + + case CAddressBookPane::eColType: + case CAddressBookPane::eColName: + case CAddressBookPane::eColEmail: + case CAddressBookPane::eColCompany: + case CAddressBookPane::eColLocality: + case CAddressBookPane::eColNickname: + outEnabled = IsColumnVisible(inCommand); + outUsesMark = true; + if ( outEnabled ) { + outMark = (GetSortedColumn() == inCommand) ? checkMark : noMark; + } else { + outMark = noMark; + } + break; + + case CAddressBookPane::cmd_SortAscending: + case CAddressBookPane::cmd_SortDescending: + outEnabled = true; + outUsesMark = true; + if ( inCommand == CAddressBookPane::cmd_SortAscending ) { + outMark = (IsSortedBackwards() ? noMark : checkMark); + } else { + outMark = (IsSortedBackwards() ? checkMark : noMark); + } + break; + + case cmd_NewAddressCard: + case cmd_NewAddressList: + case cmd_ImportAddressBook: + case cmd_SaveAs: + case cmd_ViewMyCard: + case cmd_ConferenceCall: + outEnabled = CAddressBookTableView::CurrentBookIsPersonalBook(); + break; + + case cmd_HTMLDomains: + outEnabled = true; + break; + + case cmd_EditProperties: + case cmd_DeleteEntry: + if ( GetSelectedRowCount() > 0 ) { + outEnabled = CAddressBookTableView::CurrentBookIsPersonalBook(); + } + break; + + case cmd_ComposeMailMessage: + outEnabled = true; + break; + + default: + Inherited::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); + break; + } +} +//----------------------------------- +void CAddressBookTableView::DeleteSelection() +// Delete all selected items. +//----------------------------------- +{ + if ( !CAddressBookTableView::CurrentBookIsPersonalBook() ) return; + + AssertFail_(GetMessagePane() != nil); + + TableIndexT *indices = nil; + + CMailSelection selection; + + if ( GetSelection( selection ) ) + { + TableIndexT count = selection.selectionSize; + indices = (MSG_ViewIndex*)selection.GetSelectionList(); + Try_ { + + { // Close all open windows to delete + ABID id; + ABID type; + + for( Int32 index = 0; index < count; index ++ ) + { + GetEntryIDAndType( indices[ index ]+1 , &id, &type); + CloseEditWindow(id, type); + } + } + + CAddressBookManager::FailAddressError(AB_Command((ABPane *) GetMessagePane(), + AB_DeleteCmd, indices, count)); + + } + Catch_(inErr) { + + Throw_(inErr); + } + EndCatch_ + } +} + + +/*====================================================================================== + Import an address book and append it to the local address book. +======================================================================================*/ + +void CAddressBookTableView::ImportAddressBook() { + + AssertFail_(GetMessagePane() != nil); + AssertFail_(sCurrentBook == CAddressBookManager::GetPersonalBook()); + + CAddressBookManager::FailAddressError(AB_ImportFromFile((ABPane *) GetMessagePane(), + CAddressBookWindow::GetMailContext())); +} + +// Save As + +void CAddressBookTableView::SaveAs() +{ + CAddressBookManager::FailAddressError( + AB_ExportToFile( (ABPane *) GetMessagePane(),(MWContext*) CAddressBookWindow::GetMailContext() ) ); +} + + + + +/*====================================================================================== + Create a new card. +======================================================================================*/ + +void CAddressBookTableView::NewCard(PersonEntry* pPerson) { + + if ( !CAddressBookTableView::CurrentBookIsPersonalBook() ) return; + + ABID entryID = CAddressBookPane::eNewEntryID; + + CNamePropertiesWindow *namePropertiesWindow = + CAddressBookManager::GetNamePropertiesWindow(entryID, false); + AssertFail_(namePropertiesWindow != nil); // Must be there! + + namePropertiesWindow->AddListener(this); + + // Update the window properties + if( pPerson ) + namePropertiesWindow->UpdateUIToPerson( pPerson, entryID ); + namePropertiesWindow->SetConferenceText( ); + namePropertiesWindow->Show(); + namePropertiesWindow->Select(); +} + + +/*====================================================================================== + Create a new list. +======================================================================================*/ + +void CAddressBookTableView::NewList() { + + if ( !CAddressBookTableView::CurrentBookIsPersonalBook() ) return; + ABID entryID = CAddressBookPane::eNewEntryID; + #if HOTEDITWINDOWS + { + StMailingListEntry listEntry; + CAddressBookManager::FailAddressError(AB_AddMailingList(sCurrentBook, CAddressBookManager::GetAddressBook(), + listEntry.GetEntry(), &entryID)); + } + #endif // HOTEDITWINDOWS + ShowEditWindow(entryID, ABTypeList); +} + + +/*====================================================================================== + Open the selected message into its own window. +======================================================================================*/ + +void CAddressBookTableView::OpenRow(TableIndexT inRow) { + + if ( !CAddressBookTableView::CurrentBookIsPersonalBook() ) return; + + ABID id; + ABID type; + GetEntryIDAndType(inRow, &id, &type); + + ShowEditWindow(id, type); +} + + +/*====================================================================================== + Open the selected person or list into its own window. +======================================================================================*/ + +void CAddressBookPane::ShowEditWindow(ABID inID, ABID inType) { + + // Find out if the option key is down + +// Not implemented yet const Boolean optionKeyDown = USearchHelper::KeyIsDown(USearchHelper::char_OptionKey); + const Boolean optionKeyDown = false; + + LWindow *theWindow; + + if ( inType == ABTypeList ) { + CListPropertiesWindow *listPropertiesWindow = + CAddressBookManager::GetListPropertiesWindow(inID, optionKeyDown); + AssertFail_(listPropertiesWindow != nil); // Must be there! + + + // Update the window properties + listPropertiesWindow->UpdateUIToBackend(sCurrentBook, CAddressBookManager::GetAddressBook(), inID); + theWindow = listPropertiesWindow; + } else { + Assert_(inType == ABTypePerson); + CNamePropertiesWindow *namePropertiesWindow = + CAddressBookManager::GetNamePropertiesWindow(inID, optionKeyDown); + AssertFail_(namePropertiesWindow != nil); // Must be there! + + + // Update the window properties + + namePropertiesWindow->UpdateUIToBackend(sCurrentBook, CAddressBookManager::GetAddressBook(), inID); + theWindow = namePropertiesWindow; + } + + theWindow->Show(); + theWindow->Select(); +} + +// Call the selected person +void CAddressBookTableView::ConferenceCall( ) +{ + TableIndexT selectedRow=0; + GetNextSelectedRow( selectedRow ); + if ( !CAddressBookTableView::CurrentBookIsPersonalBook() ) + return; + + ABID id; + ABID type; + GetEntryIDAndType( selectedRow, &id, &type ); + char emailAddress[kMaxEmailAddressLength]; + char ipAddress[kMaxCoolAddress]; + short serverType; + AB_GetUseServer( sCurrentBook, CAddressBookManager::GetAddressBook(), id, &serverType ); + AB_GetEmailAddress( sCurrentBook, CAddressBookManager::GetAddressBook(), id, emailAddress ); + AB_GetCoolAddress( sCurrentBook, CAddressBookManager::GetAddressBook(), id, ipAddress ); + + // Check to see if we have all the data we need + if ( (serverType==kSpecificDLS || serverType==kHostOrIPAddress ) && !XP_STRLEN( ipAddress )) + { + FE_Alert( CAddressBookWindow::GetMailContext(), XP_GetString(MK_MSG_CALL_NEEDS_IPADDRESS)); + return; + } + if ( (serverType==kSpecificDLS || serverType==kDefaultDLS) && !XP_STRLEN( emailAddress )) + { + FE_Alert( CAddressBookWindow::GetMailContext(), XP_GetString(MK_MSG_CALL_NEEDS_EMAILADDRESS)); + return; + } + char *ipAddressDirect = NULL; + char *serverAddress = NULL; + char *address = NULL; + switch( serverType) + { + case kDefaultDLS: + address = &emailAddress[0]; + break; + case kSpecificDLS: + address = &emailAddress[0]; + serverAddress = &ipAddress[0]; + break; + case kHostOrIPAddress: + ipAddressDirect = &ipAddress[0]; + break; + default: + break; + } + // And now the AE fun begins + AppleEvent event,reply; + OSErr error; + ProcessSerialNumber targetPSN; + Boolean isAppRunning; + static const OSType kConferenceAppSig = 'Ncq¹'; // This needs to be in a header file + // so that uapp and us share it + isAppRunning = UProcessUtils::ApplicationRunning( kConferenceAppSig, targetPSN ); + if( !isAppRunning) + { + ObeyCommand(cmd_LaunchConference, NULL); + isAppRunning = UProcessUtils::ApplicationRunning( kConferenceAppSig, targetPSN ); + } + + if( !isAppRunning ) // for some reason we were unable to open app + return; + Try_ + { + error = AEUtilities::CreateAppleEvent( 'VFON', 'CALL', event, targetPSN ); + ThrowIfOSErr_(error); + if( ipAddressDirect) + { + error = ::AEPutParamPtr(&event, '----', typeChar, ipAddressDirect, XP_STRLEN(ipAddressDirect) ); + ThrowIfOSErr_(error); + } + if( address) + { + error = ::AEPutParamPtr(&event, 'MAIL', typeChar, address, XP_STRLEN(address) ); + ThrowIfOSErr_(error); + } + if( serverAddress ) + { + error = ::AEPutParamPtr(&event, 'SRVR', typeChar, serverAddress, XP_STRLEN(serverAddress) ); + ThrowIfOSErr_(error); + } + + // NULL reply parameter + reply.descriptorType = typeNull; + reply.dataHandle = nil; + + error = AESend(&event,&reply,kAENoReply + kAENeverInteract + + kAECanSwitchLayer+kAEDontRecord,kAENormalPriority,-1,nil,nil); + ThrowIfOSErr_(error); + + UProcessUtils::PullAppToFront( targetPSN ); + + } + Catch_(inError) // in case of errors do nothing + { + } + AEDisposeDesc(&reply); + AEDisposeDesc(&event); + +} +/*====================================================================================== + Close the selected person or list properties window if there is one. +======================================================================================*/ + +void CAddressBookTableView::CloseEditWindow(ABID inID, ABID inType) { + + LWindow *theWindow; + + if ( inType == ABTypeList ) { + theWindow = CAddressBookManager::FindListPropertiesWindow(inID); + } else { + Assert_(inType == ABTypePerson); + theWindow = CAddressBookManager::FindNamePropertiesWindow(inID); + } + + if ( theWindow != nil ) theWindow->DoClose(); +} + +//----------------------------------- +void CAddressBookTableView::SetColumnHeaders(DIR_Server* server) +//----------------------------------- +{ + MSG_SearchAttribute attr; + DIR_AttributeId id; + STableCell aCell; + const char* colName; + LTableViewHeader* tableHeader = GetTableHeader(); + PaneIDT headerPaneID; + + for (short col = 1; col <= tableHeader->CountColumns(); col ++) + { + headerPaneID = tableHeader->GetColumnPaneID(col); + switch (headerPaneID) + { + case CAddressBookPane::eColName: attr = attribCommonName; break; + case CAddressBookPane::eColEmail: attr = attrib822Address; break; + case CAddressBookPane::eColCompany: attr = attribOrganization; break; + case CAddressBookPane::eColLocality: attr = attribLocality; break; + case CAddressBookPane::eColWorkPhone: attr = attribPhoneNumber; break; + case CAddressBookPane::eColNickname: // no break; + default: attr = kNumAttributes; break; + } + + if (attr < kNumAttributes) + { + MSG_SearchAttribToDirAttrib(attr, &id); + colName = DIR_GetAttributeName(server, id); + if (colName && *colName) + { + LCaption* headerPane = dynamic_cast(FindPaneByID(headerPaneID)); + if (headerPane) + { + CStr255 cStr(colName); + headerPane->SetDescriptor((StringPtr)cStr); + } + } + } + } +} + +/*====================================================================================== + Force a sort to occur based upon the current sort column. +======================================================================================*/ + +void CAddressBookTableView::ForceCurrentSort() { + + UInt32 sortKey; + Boolean sortBackwards; + + GetSortParams(&sortKey, &sortBackwards); + +/* + mSearchManager->SortSearchResults(sortKey, sortBackwards); +*/ +} + + +/*====================================================================================== + Notification to sort the table. +======================================================================================*/ + +void CAddressBookTableView::ChangeSort(const LTableHeader::SortChange *inSortChange) { + + Assert_(GetMessagePane() != nil); + Assert_(sCurrentBook != nil); + + AB_CommandType sortCmd = SortCommandFromColumnType((EColType) inSortChange->sortColumnID); + + ::SetCursor(*::GetCursor(watchCursor)); + + // Call BE to sort the table + + CAddressBookManager::FailAddressError(AB_Command((ABPane *) GetMessagePane(), sortCmd, nil, 0)); +} + + +/*====================================================================================== + Update the currently displayed and selected row based upon the typed down text. +======================================================================================*/ + +void CAddressBookTableView::UpdateToTypedownText(Str255 inTypedownText) { + + MSG_ViewIndex index = eInvalidCachedRowIDType; + Int32 error = AB_GetIndexMatchingTypedown((ABPane *) GetMessagePane(), &index, P2CStr(inTypedownText), 0); + ASSERT_ADDRESS_ERROR(error); + + if ( index != eInvalidCachedRowIDType ) { + STableCell selectedCell = GetFirstSelectedCell(); + if ( IsValidCell(selectedCell) ) { + if ( (GetSelectedRowCount() > 1) || (selectedCell.row != (index + 1)) ) { + UnselectAllCells(); + } + } + STableCell cell(index + 1, 1); + SelectCell( cell ); + ScrollCellIntoFrame( cell ); + } +} + + +/*====================================================================================== + Return whether or not we are currently performing an LDAP search. +======================================================================================*/ + +Boolean CAddressBookTableView::IsLDAPSearching() { + + return ((CAddressBookWindow *) USearchHelper::GetPaneWindow(this))-> + GetAddressBookController()->IsLDAPSearching(); +} + + +/*====================================================================================== + Notification from the BE that something is finished changing in the display table. +======================================================================================*/ + +void CAddressBookTableView::ChangeFinished(MSG_Pane *inPane, MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, SInt32 inRowCount) { + + // If it's an initial insert call, and we're doing LDAP search, then simply pass + // this info on to the addressbook backend. We'll get a MSG_NotifyAll when + // the backend thinks we should redraw. + + if ( (mMysticPlane < (kMysticUpdateThreshHold + 2)) && + (inChangeCode == MSG_NotifyInsertOrDelete) && + IsLDAPSearching() ) { + + AB_LDAPSearchResults((ABPane *) GetMessagePane(), inStartRow - 1, inRowCount); + } + Inherited::ChangeFinished(inPane, inChangeCode, inStartRow, inRowCount); + // Because the search is asynchronous, we + // don't want to cause a redraw every time a row is inserted, because that causes + // drawing of rows in random, unsorted order. + + if (inChangeCode == MSG_NotifyInsertOrDelete && IsLDAPSearching()) + DontRefresh(); + else + Refresh(); + +} + +void CAddressBookTableView::PaneChanged(MSG_Pane *inPane, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value) +{ + switch (inNotifyCode) + { + case MSG_PaneDirectoriesChanged: + BroadcastMessage( MSG_PaneDirectoriesChanged, NULL ); + break; + default: + Inherited::PaneChanged(inPane, inNotifyCode, value); + break; + } +} + +//----------------------------------- +void CAddressBookTableView::ShowMyAddressCard () +//----------------------------------- +{ + // the StPersonEntry will take care of freeing up it's memory + PersonEntry person; + person.Initialize(); + + const char *userEmail = FE_UsersMailAddress(); + const char *userName = FE_UsersFullName(); + + if (userEmail) + person.pGivenName = XP_STRDUP(userName); + if (userName) + person.pEmailAddress = XP_STRDUP(userEmail); + + DIR_Server *pab = NULL; + ABID entryID; + ABook *abook = CAddressBookManager::GetAddressBook(); + + DIR_GetPersonalAddressBook(CAddressBookManager::GetDirServerList(), &pab); + XP_ASSERT(pab); + if (!pab) return; + + // this allocates pFamilyName + AB_BreakApartFirstName (abook, &person); + AB_GetEntryIDForPerson(pab, abook, &entryID, &person); + + if (entryID == MSG_MESSAGEKEYNONE) + NewCard(&person); + else + ShowEditWindowByEntryID(entryID, false); + + // pFamilyName was allocated in AB_BreakApartFirstName + XP_FREEIF(person.pGivenName); + XP_FREEIF(person.pFamilyName); + XP_FREEIF(person.pEmailAddress); +} + + +#pragma mark - + + + +/*====================================================================================== + Adjust the window to stored preferences. +======================================================================================*/ + +CAddressBookListTableView::~CAddressBookListTableView() { + + SetMessagePane( NULL ); +} + + +/*====================================================================================== + Dispose of the address list pane. +======================================================================================*/ +#if 0 +void CAddressBookListTableView::DisposeAddressListPane(Boolean inRefresh) { + + //if ( inRefresh && (mRows > 0) ) RemoveRows(mRows, 1, true); + + if ( GetMessagePane() != nil ) { + Int32 error = AB_CloseMailingListPane((MLPane **) &GetMessagePane()); + GetMessagePane() = nil; + ASSERT_ADDRESS_ERROR(error); + } +} +#endif + +/*====================================================================================== + Load and display the specified address book list. +======================================================================================*/ + +void CAddressBookListTableView::LoadAddressBookList(ABID &ioEntryListID) { + + Try_ { + if ( GetMessagePane() == nil ) + { + MWContext *context = CAddressBookWindow::GetMailContext(); + MSG_Pane * pane; + CAddressBookManager::FailAddressError( AB_CreateMailingListPane((MLPane **) &pane, + context, CMailNewsContext::GetMailMaster() ) ); + SetMessagePane( pane ); + MSG_SetFEData((MSG_Pane *) GetMessagePane(), CMailCallbackManager::Get()); + CMailCallbackListener::SetPane( (MSG_Pane*) GetMessagePane() ); + + CAddressBookManager::FailAddressError( + AB_InitializeMailingListPane((MLPane *) GetMessagePane(), &ioEntryListID, + sCurrentBook, CAddressBookManager::GetAddressBook() ) ); + } + } + Catch_(inErr) { + + SetMessagePane( NULL ); + + Throw_(inErr); + } + EndCatch_ +} + +/*====================================================================================== + Return whether a DropArea can accept the specified Drag. Override inherited method + to return true if ANY item is acceptable, instead of ALL items. +======================================================================================*/ + +Boolean CAddressBookListTableView::DragIsAcceptable(DragReference inDragRef) { + + Uint16 itemCount; + ::CountDragItems(inDragRef, &itemCount); + + for (Uint16 item = 1; item <= itemCount; item++) { + ItemReference itemRef; + ::GetDragItemReferenceNumber(inDragRef, item, &itemRef); + + if ( ItemIsAcceptable(inDragRef, itemRef) ) { + return true; // return on first successful item + } + } + + return false; +} + + +/*====================================================================================== + Return whether a Drag item is acceptable. +======================================================================================*/ + +Boolean CAddressBookListTableView::ItemIsAcceptable(DragReference inDragRef, + ItemReference inItemRef) { + + SAddressDragInfo info; + return CanInsertDragItem(inDragRef, inItemRef, &info); +} + + +/*====================================================================================== + Process an Item which has been dragged into a DropArea. +======================================================================================*/ + +void CAddressBookListTableView::ReceiveDragItem(DragReference inDragRef, DragAttributes inDragAttrs, + ItemReference inItemRef, Rect &inItemBounds) { + +#pragma unused (inDragAttrs) +#pragma unused (inItemBounds) + SAddressDragInfo info; + if (mDropRow == LArray::index_Last) + { + mDropRow = mRows; + } + Int32 insertRow = mDropRow; + if( mIsDropBetweenRows ) + insertRow --; + if( insertRow < 0 ) + insertRow = 0; + if ( insertRow > GetBENumRows() ) + insertRow = GetBENumRows(); + if ( CanInsertDragItem(inDragRef, inItemRef, &info) ) { + Try_ { + + CAddressBookManager::FailAddressError( + AB_AddIDToMailingListAt( (MLPane *) GetMessagePane(), info.id, + insertRow ) ); + + mDropRow++; + } + Catch_(inErr) { + + } + EndCatch_ + } +} + + +/*====================================================================================== + Initialize the table. +======================================================================================*/ + +void CAddressBookListTableView::FinishCreateSelf() +{ + + CAddressBookPane::FinishCreateSelf(); +} + + +/*====================================================================================== + Delete all selected items. +======================================================================================*/ + +void CAddressBookListTableView::DeleteSelection() { + + AssertFail_(GetMessagePane() != nil); + CMailSelection selection; + + if ( GetSelection( selection ) ) + { + TableIndexT count = selection.selectionSize; + TableIndexT *indices = (MSG_ViewIndex*)selection.GetSelectionList(); + int32 numRemoved = count; + while ( numRemoved-- >=0 ) + { + Try_ { + CAddressBookManager::FailAddressError(AB_RemoveIDFromMailingListAt((MLPane *) GetMessagePane(), + indices[numRemoved])); + } + Catch_(inErr) { + + Throw_(inErr); + } + EndCatch_ + } + } + +} + + +/*====================================================================================== + Open the selected message into its own window. +======================================================================================*/ + +void CAddressBookListTableView::OpenRow(TableIndexT inRow) +{ + const Boolean optionKeyDown = false; + + ShowEditWindowByEntryID(GetEntryID(inRow), optionKeyDown); +} + + +/*====================================================================================== + Return true if the specified drag item can be inserted into the list. +======================================================================================*/ + +Boolean CAddressBookListTableView::CanInsertDragItem(DragReference inDragRef, ItemReference inItemRef, + SAddressDragInfo *outInfo) { + + FlavorFlags flavorFlags; + + // Check that correct flavor is present, check to see if the entry is not in the list yet + if ( ::GetFlavorFlags(inDragRef, inItemRef, kMailAddresseeFlavor, &flavorFlags) == noErr ) { + + // Get the ABID of the item being dragged, either person or list + Size dataSize = sizeof(SAddressDragInfo); + + if ( ::GetFlavorData(inDragRef, inItemRef, kMailAddresseeFlavor, outInfo, &dataSize, 0) == noErr ) { + Assert_(dataSize == sizeof(SAddressDragInfo)); + + // Check with BE to see if the entry is already in the list + + if ( (((CAddressBookChildWindow *) USearchHelper::GetPaneWindow(this))->GetEntryID() != outInfo->id) && + (AB_GetIndexOfEntryID((AddressPane*)GetMessagePane(), outInfo->id) == MSG_VIEWINDEXNONE) ) { + return true; + } + } + } + return false; +} + + + +#pragma mark - +void CAddressBookChildWindow::FinishCreateSelf() +{ + LGAPushButton *control = dynamic_cast ( FindPaneByID('OkBt') ); + if( control ) + { + control->AddListener( this ); + control->SetDefaultButton( true ); + } + control = dynamic_cast ( FindPaneByID('CnBt') ); + if( control ) + control->AddListener( this ); +} + +/*====================================================================================== + Respond to broadcaster messages. +======================================================================================*/ + +void CAddressBookChildWindow::ListenToMessage(MessageT inMessage, void *ioParam) { + + switch ( inMessage ) { + case CSearchEditField::msg_UserChangedText: + if ( ioParam ) + { + switch( (*( (PaneIDT *) ioParam) ) ) + { + case CListPropertiesWindow::paneID_Name: + case CNamePropertiesWindow::paneID_FirstName: + case CNamePropertiesWindow::paneID_LastName: + UpdateTitle(); + break; + default: + break; + + } + } + break; + case msg_OK: + Try_{ + UpdateBackendToUI( + CAddressBookManager::GetPersonalBook(), CAddressBookManager::GetAddressBook() ); + DoClose(); + } Catch_ ( ioParam ) + { + ResIDT stringID = 0; + switch( ioParam ) + { + case XP_BKMKS_INVALID_NICKNAME: + stringID = 4; + break; + case MK_MSG_NEED_FULLNAME: + stringID = 3; + break; + case MK_ADDR_LIST_ALREADY_EXISTS: + stringID = 2; + break; + case MK_ADDR_ENTRY_ALREADY_EXISTS: + stringID = 1; + break; + default: + Throw_( ioParam ); + } + LStr255 errorString( kAddressbookErrorStrings, stringID ); + UStdDialogs::Alert( errorString, eAlertTypeCaution ); + } + break; + case msg_Cancel: + DoClose(); + break; + + default: + break; + } +} + +Boolean CAddressBookChildWindow::HandleKeyPress( + const EventRecord &inKeyEvent) +{ + Boolean keyHandled = false; + LControl *keyButton = nil; + + switch (inKeyEvent.message & charCodeMask) { + + case char_Enter: + case char_Return: + keyButton = dynamic_cast ( FindPaneByID('OkBt') ); + break; + + case char_Escape: + if ((inKeyEvent.message & keyCodeMask) == vkey_Escape) { + keyButton = dynamic_cast( FindPaneByID('CnBt') ); + } + break; + + default: + if (UKeyFilters::IsCmdPeriod(inKeyEvent)) { + keyButton = dynamic_cast( FindPaneByID('CnBt') ); + } else { + keyHandled = LWindow::HandleKeyPress(inKeyEvent); + } + break; + } + + if (keyButton != nil) { + keyButton->SimulateHotSpotClick(kControlButtonPart); + keyHandled = true; + } + + return keyHandled; +} + + +#pragma mark - + +/*====================================================================================== + Initialize the window. +======================================================================================*/ + +void CNamePropertiesWindow::FinishCreateSelf() { + + mFirstNameField = USearchHelper::FindViewEditField(this, paneID_FirstName); + mLastNameField = USearchHelper::FindViewEditField(this, paneID_LastName); + + Inherited::FinishCreateSelf(); // Call CMediatedWindow for now since we need to + // create a different context than CMailNewsWindow + CAddressBookChildWindow::FinishCreateSelf(); + // Call inherited method + FinishCreateWindow(); + + // Lastly, only link to broadcasters in our views + // need to listen to first/last name fields to update title + USearchHelper::LinkListenerToBroadcasters(USearchHelper::FindViewSubview(this, paneID_GeneralView), this); + + // Need Cooltalk since we listen to the popup + USearchHelper::LinkListenerToBroadcasters(USearchHelper::FindViewSubview(this, paneID_CooltalkView), this); +} + + +/*====================================================================================== + Respond to broadcaster messages. +======================================================================================*/ + +void CNamePropertiesWindow::ListenToMessage(MessageT inMessage, void *ioParam) +{ + switch ( inMessage ) + { + + case paneID_ConferencePopup: + SetConferenceText(); + break; + + default: + Inherited::ListenToMessage(inMessage, ioParam); + break; + } +} + + +/*====================================================================================== + Update the name properties to the current values in the dialog fields. +======================================================================================*/ + +void CNamePropertiesWindow::UpdateBackendToUI(DIR_Server *inDir, ABook *inABook) { + + LView *view; + StPersonEntry personEntry; + PersonEntry *entry = personEntry.GetEntry(); + + // General + view = USearchHelper::FindViewSubview(this, paneID_GeneralView); + + P2CStr(mFirstNameField->GetDescriptor((StringPtr) entry->pGivenName)); + P2CStr(mLastNameField->GetDescriptor((StringPtr) entry->pFamilyName)); + P2CStr(USearchHelper::FindViewEditField(view, paneID_EMail)->GetDescriptor((StringPtr) entry->pEmailAddress)); + P2CStr(USearchHelper::FindViewEditField(view, paneID_Nickname)->GetDescriptor((StringPtr) entry->pNickName)); + ((CSearchEditField *) USearchHelper::FindViewEditField(view, paneID_Notes))->GetText(entry->pInfo); + entry->HTMLmail = !(USearchHelper::FindViewControl(view, paneID_PrefersHTML)->GetValue() == 0); + P2CStr(USearchHelper::FindViewEditField(view, paneID_Company)->GetDescriptor((StringPtr) entry->pCompanyName)); + P2CStr(USearchHelper::FindViewEditField(view, paneID_Title)->GetDescriptor((StringPtr) entry->pTitle)); + // Contact + view = USearchHelper::FindViewSubview(this, paneID_ContactView); + + + P2CStr(USearchHelper::FindViewEditField(view, paneID_Address)->GetDescriptor((StringPtr) entry->pAddress)); + P2CStr(USearchHelper::FindViewEditField(view, paneID_City)->GetDescriptor((StringPtr) entry->pLocality)); + P2CStr(USearchHelper::FindViewEditField(view, paneID_State)->GetDescriptor((StringPtr) entry->pRegion)); + P2CStr(USearchHelper::FindViewEditField(view, paneID_ZIP)->GetDescriptor((StringPtr) entry->pZipCode)); + P2CStr(USearchHelper::FindViewEditField(view, paneID_Country)->GetDescriptor((StringPtr) entry->pCountry)); + P2CStr(USearchHelper::FindViewEditField(view, paneID_WorkPhone)->GetDescriptor((StringPtr) entry->pWorkPhone)); + P2CStr(USearchHelper::FindViewEditField(view, paneID_FaxPhone)->GetDescriptor((StringPtr) entry->pFaxPhone)); + P2CStr(USearchHelper::FindViewEditField(view, paneID_HomePhone)->GetDescriptor((StringPtr) entry->pHomePhone)); + + // Conference + view = USearchHelper::FindViewSubview( this, paneID_CooltalkView ); + P2CStr(USearchHelper::FindViewEditField( view, paneID_ConferenceAddress)->GetDescriptor((StringPtr) entry->pCoolAddress) ); + entry->UseServer =short( (USearchHelper::FindSearchPopup( view, paneID_ConferenceServer )->GetValue())-1);// entries are 0 based + // need to do CSID + MWContext* context= CAddressBookWindow::GetMailContext(); + INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(context); + entry->WinCSID = INTL_GetCSIWinCSID(c); + if ( mEntryID != CAddressBookPane::eNewEntryID ) // Existing entry + { + // Update BE + CAddressBookManager::FailAddressError( AB_ModifyUser(inDir, inABook, mEntryID, entry) ); + } + else // new entry + { + ABID entryID; + CAddressBookManager::FailAddressError( AB_AddUser( inDir, CAddressBookManager::GetAddressBook(), entry, &entryID) ); + } +} +// Update Caption text in Conference +void CNamePropertiesWindow::SetConferenceText( ) +{ + short serverType = USearchHelper::FindSearchPopup( this, paneID_ConferenceServer )->GetValue(); + LStr255 exampleString(kConferenceExampleStr, serverType ); + LCaption* text = dynamic_cast(this->FindPaneByID('CoES')); + Assert_(text); + if( text ) + text->SetDescriptor( exampleString); + LView *view = (LView*)USearchHelper::FindViewEditField( this, paneID_ConferenceAddress); + if( view ) + { + if( serverType == 1 ) + { + view->SetDescriptor("\p"); + view->Disable(); + } + else + view->Enable(); + } + +} + +/*====================================================================================== + Update the dialog fields to the current values of the name properties. +======================================================================================*/ + +void CNamePropertiesWindow::UpdateUIToBackend(DIR_Server *inDir, ABook *inABook, + ABID inEntryID) { + + if ( mEntryID == inEntryID ) return; + + + // Turn off listening + + StValueChanger change(mIsListening, false); + + LView *view = USearchHelper::FindViewSubview(this, paneID_GeneralView); + + char value[1024]; + + // General + + value[0] = 0; AB_GetGivenName(inDir, inABook, inEntryID, value); + mFirstNameField->SetDescriptor(C2PStr(value)); + + value[0] = 0; AB_GetFamilyName(inDir, inABook, inEntryID, value); + mLastNameField->SetDescriptor(C2PStr(value)); + + value[0] = 0; AB_GetEmailAddress(inDir, inABook, inEntryID, value); + USearchHelper::FindViewEditField(view, paneID_EMail)->SetDescriptor(C2PStr(value)); + + value[0] = 0; AB_GetNickname(inDir, inABook, inEntryID, value); + USearchHelper::FindViewEditField(view, paneID_Nickname)->SetDescriptor(C2PStr(value)); + + value[0] = 0; AB_GetInfo(inDir, inABook, inEntryID, value); + ((CSearchEditField *) USearchHelper::FindViewEditField(view, paneID_Notes))->SetText(value); + + XP_Bool prefersHTML; AB_GetHTMLMail(inDir, inABook, inEntryID, &prefersHTML); + USearchHelper::FindViewControl(view, paneID_PrefersHTML)->SetValue(prefersHTML ? 1 : 0); + + value[0] = 0; AB_GetCompanyName(inDir, inABook, inEntryID, value); + USearchHelper::FindViewEditField(view, paneID_Company)->SetDescriptor(C2PStr(value)); + + value[0] = 0; AB_GetTitle(inDir, inABook, inEntryID, value); + USearchHelper::FindViewEditField(view, paneID_Title)->SetDescriptor(C2PStr(value)); + + // Contact + view = USearchHelper::FindViewSubview(this, paneID_ContactView); + + + value[0] = 0; AB_GetStreetAddress(inDir, inABook, inEntryID, value); + USearchHelper::FindViewEditField(view, paneID_Address)->SetDescriptor(C2PStr(value)); + + value[0] = 0; AB_GetLocality(inDir, inABook, inEntryID, value); + USearchHelper::FindViewEditField(view, paneID_City)->SetDescriptor(C2PStr(value)); + + value[0] = 0; AB_GetRegion(inDir, inABook, inEntryID, value); + USearchHelper::FindViewEditField(view, paneID_State)->SetDescriptor(C2PStr(value)); + + value[0] = 0; AB_GetZipCode(inDir, inABook, inEntryID, value); + USearchHelper::FindViewEditField(view, paneID_ZIP)->SetDescriptor(C2PStr(value)); + + value[0] = 0; AB_GetCountry(inDir, inABook, inEntryID, value); + USearchHelper::FindViewEditField(view, paneID_Country)->SetDescriptor(C2PStr(value)); + + value[0] = 0; AB_GetWorkPhone(inDir, inABook, inEntryID, value); + USearchHelper::FindViewEditField(view, paneID_WorkPhone)->SetDescriptor(C2PStr(value)); + + value[0] = 0; AB_GetFaxPhone(inDir, inABook, inEntryID, value); + USearchHelper::FindViewEditField(view, paneID_FaxPhone)->SetDescriptor(C2PStr(value)); + + value[0] = 0; AB_GetHomePhone(inDir, inABook, inEntryID, value); + USearchHelper::FindViewEditField(view, paneID_HomePhone)->SetDescriptor(C2PStr(value)); + + // Conference + view = USearchHelper::FindViewSubview(this, paneID_CooltalkView); + value[0] = 0; AB_GetCoolAddress(inDir, inABook, inEntryID, value); + USearchHelper::FindViewEditField(view, paneID_ConferenceAddress)->SetDescriptor(C2PStr(value)); + + short serverType; + AB_GetUseServer( inDir,inABook, inEntryID, &serverType); + serverType++; // Popups and resources are 1 based + USearchHelper::FindSearchPopup( view, paneID_ConferenceServer )->SetValue( serverType ); + + SetConferenceText( ); + // + + mEntryID = inEntryID; + UpdateTitle(); +} + +void CNamePropertiesWindow::UpdateUIToPerson( PersonEntry* person, ABID inEntryID ) +{ + + LView *view = USearchHelper::FindViewSubview(this, paneID_GeneralView); + + // General + + mFirstNameField->SetDescriptor( CStr255( person->pGivenName )); + + mLastNameField->SetDescriptor( CStr255( person->pFamilyName )); + + USearchHelper::FindViewEditField(view, paneID_EMail)->SetDescriptor(CStr255( person->pEmailAddress )); + + mEntryID = inEntryID; +#if 0 // Currently this is only called from new card which leaves the rest of these fields nil pointers + USearchHelper::FindViewEditField(view, paneID_Nickname)->SetDescriptor(CStr255( person->pNickName )); + if( person->pInfo ) + ((CSearchEditField *) USearchHelper::FindViewEditField(view, paneID_Notes))->SetText( person->pInfo ); + + USearchHelper::FindViewControl(view, paneID_PrefersHTML)->SetValue( person->HTMLmail ? 1 : 0); + + // Contact + view = USearchHelper::FindViewSubview(this, paneID_ContactView); + + USearchHelper::FindViewEditField(view, paneID_Company)->SetDescriptor(CStr255( person->pCompanyName )); + + USearchHelper::FindViewEditField(view, paneID_Title)->SetDescriptor(CStr255( person->pTitle )); + + USearchHelper::FindViewEditField(view, paneID_Address)->SetDescriptor(CStr255( person->pAddress )); + + USearchHelper::FindViewEditField(view, paneID_City)->SetDescriptor(CStr255( person->pLocality )); + + USearchHelper::FindViewEditField(view, paneID_State)->SetDescriptor(CStr255( person->pRegion)); + + USearchHelper::FindViewEditField(view, paneID_ZIP)->SetDescriptor(CStr255( person->pZipCode )); + + USearchHelper::FindViewEditField(view, paneID_WorkPhone)->SetDescriptor(CStr255( person->pWorkPhone)); + + USearchHelper::FindViewEditField(view, paneID_FaxPhone)->SetDescriptor(CStr255( person->pFaxPhone )); + + USearchHelper::FindViewEditField(view, paneID_HomePhone)->SetDescriptor(CStr255( person->pHomePhone)); +#endif + // Conference + view = USearchHelper::FindViewSubview(this, paneID_CooltalkView); + USearchHelper::FindViewEditField(view, paneID_ConferenceAddress)->SetDescriptor(CStr255( person->pCoolAddress )); + + USearchHelper::FindSearchPopup( view, paneID_ConferenceServer )->SetValue( person->UseServer+1 ); + + SetConferenceText( ); + + UpdateTitle(); + +} + +/*====================================================================================== + Update the window title. +======================================================================================*/ + +void CNamePropertiesWindow::UpdateTitle() { + + CStr255 title; + { + CStr255 first, last; + mFirstNameField->GetDescriptor(first); + mLastNameField->GetDescriptor(last); + if ( last[0] ) first += " "; + first += last; + + ::GetIndString( title, 8903, 1); + ::StringParamText( title, first); + } + SetDescriptor( title ); +} + + +#pragma mark - + +/*====================================================================================== + Construct the list window. +======================================================================================*/ + +CListPropertiesWindow::CListPropertiesWindow(LStream *inStream) : + CAddressBookChildWindow(inStream, WindowType_AddressList), + mAddressBookListTable(nil), + mTitleField(nil) { + + SetPaneID(pane_ID); + +} + + +/*====================================================================================== + Dispose of the list window. +======================================================================================*/ + +CListPropertiesWindow::~CListPropertiesWindow() { +// Boolean canRotate = false; +// if ( IsVisible() ) { +// USearchHelper::FindWindowTabGroup(&mSubCommanders)->SetRotateWatchValue(&canRotate); +// } +// delete mAddressBookListTable; + mAddressBookListTable = nil; +} + +/*====================================================================================== + Load and display the specified address book list. +======================================================================================*/ + +void CListPropertiesWindow::UpdateUIToBackend(DIR_Server *inDir, ABook *inABook, ABID inEntryID) { + + AssertFail_(mAddressBookListTable != nil); + + // Update any currently pending data + + // Turn off listening + + StValueChanger change(mIsListening, false); + if( inEntryID != CAddressBookPane::eNewEntryID ) + mEntryID = inEntryID; // Needs to be set here + else + mEntryID = 0; + mAddressBookListTable->LoadAddressBookList( mEntryID ); + + if ( inEntryID != CAddressBookPane::eNewEntryID ) + { + char value[1024]; + + // Set edit fields + value[0] = 0; AB_GetFullName(inDir, inABook, inEntryID, value); + mTitleField->SetDescriptor(CStr255(value)); + + value[0] = 0; AB_GetNickname(inDir, inABook, inEntryID, value); + USearchHelper::FindViewEditField(this, paneID_Nickname)->SetDescriptor(CStr255(value)); + + value[0] = 0; AB_GetInfo(inDir, inABook, inEntryID, value); + USearchHelper::FindViewEditField(this, paneID_Description)->SetDescriptor(CStr255(value)); + + } + else + { + + CStr255 defaultName; + USearchHelper::AssignUString(uStr_ListDefaultName, defaultName); + mTitleField->SetDescriptor(defaultName); + } + + UpdateTitle(); +} + +/*====================================================================================== + Update the list properties to the current values in the dialog fields. +======================================================================================*/ + +void CListPropertiesWindow::UpdateBackendToUI(DIR_Server *inDir, ABook *inABook) { + +#pragma unused (inDir) +#pragma unused (inABook) + StMailingListEntry listEntry; + MailingListEntry *entry = listEntry.GetEntry(); + P2CStr(mTitleField->GetDescriptor((StringPtr) entry->pFullName)); + P2CStr(USearchHelper::FindViewEditField(this, paneID_Nickname)->GetDescriptor((StringPtr) entry->pNickName)); + P2CStr(USearchHelper::FindViewEditField(this, paneID_Description)->GetDescriptor((StringPtr) entry->pInfo)); + // Need to copy in the win CSID + MWContext* context= CAddressBookWindow::GetMailContext(); + INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(context); + entry->WinCSID = INTL_GetCSIWinCSID(c); + + MSG_ViewIndex index = 0; + CAddressBookManager::FailAddressError( AB_ModifyMailingListAndEntriesWithChecks( + (MLPane *)mAddressBookListTable->GetMessagePane(), entry, &index, 0 ) ); +} + + +/*====================================================================================== + Initialize the window. +======================================================================================*/ + +void CListPropertiesWindow::FinishCreateSelf() { + + mAddressBookListTable = + dynamic_cast(USearchHelper::FindViewSubview(this, paneID_AddressBookListTable)); + FailNILRes_(mAddressBookListTable); + mTitleField = USearchHelper::FindViewEditField(this, paneID_Name); + + Inherited::FinishCreateSelf(); // Call CMediatedWindow for now since we need to + // create a different context than CMailNewsWindow + CAddressBookChildWindow::FinishCreateSelf(); // Link OK Cancel Button + // Call inherited method + FinishCreateWindow(); + + // Lastly, link to our broadcasters, need to link to allow title update + USearchHelper::LinkListenerToBroadcasters(this, this); + +} + + + +/*====================================================================================== + Draw the window. +======================================================================================*/ + +void CListPropertiesWindow::DrawSelf() { + Rect frame; + + if ( mAddressBookListTable->CalcPortFrameRect(frame) && ::RectInRgn(&frame, mUpdateRgnH) ) { + + { + StExcludeVisibleRgn excludeRgn(mAddressBookListTable); + Inherited::DrawSelf(); + } + + StColorPenState::Normalize(); + ::EraseRect(&frame); + } else { + Inherited::DrawSelf(); + } + + USearchHelper::RemoveSizeBoxFromVisRgn(this); +} + + +/*====================================================================================== + Update the window title. +======================================================================================*/ + +void CListPropertiesWindow::UpdateTitle() { + + CStr255 title; + { + CStr255 name; + mTitleField->GetDescriptor(name); + ::GetIndString( title, 8903, 2); + ::StringParamText( title, name); + } + SetDescriptor( title ); +} + +//----------------------------------- +Boolean CListPropertiesWindow::ObeyCommand(CommandT inCommand, void *ioParam) +//----------------------------------- +{ + Boolean rtnVal = true; + + switch ( inCommand ) { + + case cmd_NewMailMessage: + mAddressBookListTable->ComposeMessage(); + break; + default: + rtnVal = Inherited::ObeyCommand(inCommand, ioParam); + break; + } + + return rtnVal; +} + +#pragma mark - + +/*====================================================================================== + Allocate and initialize the person entry data record. +======================================================================================*/ + +StPersonEntry::StPersonEntry() : + mPerson(nil) { + + UInt32 allocSize = sizeof(PersonEntry) + (kMaxNameLength + kMaxNameLength + kMaxNameLength + kMaxNameLength + + kMaxCompanyLength + kMaxLocalityLength + kMaxRegionLength + kMaxEmailAddressLength + + kMaxInfo + kMaxTitle + kMaxAddress + kMaxZipCode + kMaxPhone + kMaxPhone + kMaxPhone + + 1 + kMaxCoolAddress+ kMaxCountryLength); + + mPerson = (PersonEntry *) XP_CALLOC(1, allocSize); + FailNIL_(mPerson); + + char *currentString = ((char *) mPerson) + sizeof(PersonEntry); + + mPerson->pNickName = currentString; currentString += kMaxNameLength; + mPerson->pGivenName = currentString; currentString += kMaxNameLength; + mPerson->pMiddleName = currentString; currentString += kMaxNameLength; + mPerson->pFamilyName = currentString; currentString += kMaxNameLength; + mPerson->pCompanyName = currentString; currentString += kMaxCompanyLength; + mPerson->pLocality = currentString; currentString += kMaxLocalityLength; + mPerson->pRegion = currentString; currentString += kMaxRegionLength; + mPerson->pEmailAddress = currentString; currentString += kMaxEmailAddressLength; + mPerson->pInfo = currentString; currentString += kMaxInfo; + mPerson->pTitle = currentString; currentString += kMaxTitle; + mPerson->pAddress = currentString; currentString += kMaxAddress; + mPerson->pZipCode = currentString; currentString += kMaxZipCode; + mPerson->pCountry = currentString; currentString+= kMaxCountryLength; + mPerson->pWorkPhone = currentString; currentString += kMaxPhone; + mPerson->pFaxPhone = currentString; currentString += kMaxPhone; + mPerson->pHomePhone = currentString; currentString += kMaxPhone; + mPerson->pDistName = currentString; currentString += 1; //??? + mPerson->pCoolAddress = currentString; currentString += kMaxCoolAddress; + + AssertFail_(currentString == (((char *) mPerson) + allocSize)); + + mPerson->Security = 0; + mPerson->UseServer = 1; + mPerson->HTMLmail = 1; + mPerson->WinCSID = 0; + +} + + +#pragma mark - + +/*====================================================================================== + Allocate and initialize the person entry data record. +======================================================================================*/ + +StMailingListEntry::StMailingListEntry() : + mMailingList(nil) { + + UInt32 allocSize = sizeof(MailingListEntry) + (kMaxFullNameLength + kMaxNameLength + kMaxInfo + 1); + + mMailingList = (MailingListEntry *) XP_CALLOC(1, allocSize); + FailNIL_(mMailingList); + + char *currentString = ((char *) mMailingList) + sizeof(MailingListEntry); + + mMailingList->pFullName = currentString; currentString += kMaxFullNameLength; + mMailingList->pNickName = currentString; currentString += kMaxNameLength; + mMailingList->pInfo = currentString; currentString += kMaxInfo; + mMailingList->pDistName = currentString; currentString += 1; + + AssertFail_(currentString == (((char *) mMailingList) + allocSize)); + mMailingList->WinCSID = 0; + +} + +// CAddressBookController +void CAddressBookController::FinishCreateSelf() +{ + mAddressBookTable = dynamic_cast( FindPaneByID( paneID_AddressBookTable )); + mAddressBookTable->AddListener( this ); + FailNILRes_(mAddressBookTable); + + mDividedView = dynamic_cast(FindPaneByID( paneID_DividedView )); + FailNILRes_(mDividedView); + + LWindow* window = dynamic_cast( LWindow::FetchWindowObject(GetMacPort()) ); + mProgressCaption = dynamic_cast(window->FindPaneByID(kMailNewsStatusPaneID)); + FailNILRes_(mProgressCaption); + + mSearchButton = dynamic_cast(FindPaneByID( paneID_Search)); + FailNILRes_(mSearchButton); + + mStopButton = dynamic_cast(FindPaneByID( paneID_Stop)); + FailNILRes_(mStopButton); + + mTypedownName = dynamic_cast(FindPaneByID( paneID_TypedownName) ); + FailNILRes_(mTypedownName); + mTypedownName->AddListener(this); + SwitchTarget( mTypedownName ); + UReanimator::LinkListenerToControls(this, this, 8920); + PopulateDirectoryServers(); + + // Frame Highlighting + CTargetFramer* framer = new CTargetFramer(); + mAddressBookTable->AddAttachment(framer); + framer = new CTargetFramer(); + mTypedownName->AddAttachment(framer); +} + +void CAddressBookController::ListenToMessage(MessageT inMessage, void *ioParam) +{ + switch ( inMessage ) + { + case CSearchEditField::msg_UserChangedText: + // User changed the typedeown text + if ( mNextTypedownCheckTime == eDontCheckTypedown ) { + mNextTypedownCheckTime = LMGetTicks() + eCheckTypedownInterval; + } + break; + case CAddressBookTableView::cmd_NewAddressCard: + case CAddressBookTableView::cmd_NewAddressList: + case CAddressBookTableView::cmd_EditProperties: + case CAddressBookTableView::cmd_DeleteEntry: + case CAddressBookTableView::cmd_ComposeMailMessage: + case cmd_SearchAddresses: + mAddressBookTable->ButtonMessageToCommand(inMessage); + break; + + case paneID_DirServers: + SelectDirectoryServer(nil, GetServerIndexFromPopup()); + break; + + case CAddressBookTableView::cmd_ConferenceCall: + mAddressBookTable->ConferenceCall(); + break; + + // Status messages + case msg_NSCAllConnectionsComplete: + MessageWindStop(false); // line order.. + mProgressCaption->SetSeamless(); // ...does matter + Refresh(); // ? + break; + + case msg_NSCProgressEnd: + break; + + case msg_NSCProgressMessageChanged: + if ( ioParam != nil ) { + mProgressCaption->SetDescriptor(TString((const char*)ioParam)); + } else { + mProgressCaption->SetDescriptor("\p"); + } + break; + + case msg_NSCProgressPercentChanged: + mProgressCaption->SetValue(*(Int32*)ioParam); + break; + + case paneID_Search: + MessageWindSearch(); + break; + + case paneID_Stop: + MessageWindStop(true); + break; + + case MSG_PaneDirectoriesChanged: + UpdateToDirectoryServers(); + break; + + default: + // No superclass method + break; + } + +} + +Boolean CAddressBookController::ObeyCommand(CommandT inCommand, void *ioParam) +{ + Boolean cmdHandled = true; + switch ( inCommand ) + { + case paneID_Search: + MessageWindSearch(); + break; + + case paneID_Stop: + MessageWindStop(true); + break; + default: + cmdHandled = LCommander::ObeyCommand(inCommand, ioParam); + break; + } + return cmdHandled; +} + +void CAddressBookController::ReadStatus(LStream *inStatusData) +{ + mDividedView->RestorePlace(inStatusData); + mAddressBookTable->GetTableHeader()->ReadColumnState(inStatusData); +} + +void CAddressBookController::WriteStatus(LStream *outStatusData) +{ + + mDividedView->SavePlace(outStatusData); + mAddressBookTable->GetTableHeader()->WriteColumnState(outStatusData); +} + + +//----------------------------------- +void CAddressBookController::SpendTime(const EventRecord &inMacEvent) +// Check to see if the user has typed in text that needs to be searched. +//----------------------------------- +{ + if ( CAddressBookTableView::CurrentBookIsPersonalBook() ) { + if ( inMacEvent.when >= mNextTypedownCheckTime ) { + Assert_(mTypedownName); + Assert_(mAddressBookTable); + + Str255 typedownText; + mTypedownName->GetDescriptor(typedownText); + + mAddressBookTable->UpdateToTypedownText(typedownText); + mNextTypedownCheckTime = eDontCheckTypedown; + } + } else if ( !IsLDAPSearching() ) { + USearchHelper::EnableDisablePane(mSearchButton, !IsLDAPSearching()/*mTypedownName->GetTextLength() > 0*/, true); + } +} + + + +//----------------------------------- +void CAddressBookController::PopulateDirectoryServers() +// Populate the directory server popup menu with the current servers. +//----------------------------------- +{ + + CSearchPopupMenu *popup = USearchHelper::FindSearchPopup(this, paneID_DirServers); + popup->ClearMenu(); // Remove any current items + + XP_List *serverList = CAddressBookManager::GetDirServerList(); + if ( !serverList ) return; + for (Int32 i = 1; i <= XP_ListCount(serverList); i++) { + DIR_Server *server = (DIR_Server *) XP_ListGetObjectNum(serverList, i); + if ( (server->dirType == LDAPDirectory) || (server->dirType == PABDirectory) ) { + CStr255 string(server->description); + if ( string.Length() == 0 ) string += "????"; // Bug in string generation! + popup->AppendMenuItemCommand(i, string, true); + } + } + popup->SetValue(1); +} // CAddressBookWindow::PopulateDirectoryServers + +//----------------------------------- +void CAddressBookController::SelectDirectoryServer(DIR_Server *inServer, Int32 inServerIndex) +// Select the specified directory server into the address book table view. If inServer +// is not nil, select that server,; otherwise use the 1-based index given by +// inServerIndex to select the directory server at that index in the list returned +// by CAddressBookManager::GetDirServerList(). If the specified server cannot be +// found or is not a valid server that can be displayed, do nothing. +//----------------------------------- +{ + mProgressCaption->SetDescriptor("\p"); + AssertFail_(mAddressBookTable != nil); + SetCursor(*GetCursor(watchCursor)); + XP_List *serverList = CAddressBookManager::GetDirServerList(); + if ( !serverList ) return; + + if ( inServer == nil ) + inServer = (DIR_Server *) XP_ListGetObjectNum(serverList, inServerIndex); + else + { + DIR_Server *foundServer = nil; + for (Int32 i = 1; i <= (XP_ListCount(serverList) + 1); i++) + { + foundServer = (DIR_Server *) XP_ListGetObjectNum(serverList, i); + if ( foundServer == inServer ) break; + } + inServer = foundServer; + } + // Validate server + if ( (inServer == nil) || + !((inServer->dirType == LDAPDirectory) || (inServer->dirType == PABDirectory)) ) + return; // Can't find it + // Load server into address book table + + if ( !mAddressBookTable->LoadAddressBook(inServer) ) return; + + const Boolean isLDAPServer = (inServer->dirType == LDAPDirectory); + USearchHelper::ShowHidePane(mSearchButton, isLDAPServer); + USearchHelper::EnableDisablePane(mSearchButton, mTypedownName->GetTextLength() > 0, true); + USearchHelper::ShowHidePane(mStopButton, false); + USearchHelper::SelectEditField(mTypedownName); + mAddressBookTable->SetColumnHeaders(inServer); + + UpdatePort(); +} // CAddressBookWindow::SelectDirectoryServer + + + +//----------------------------------- +void CAddressBookController::MessageWindSearch( char* text) +// React to search message. +//----------------------------------- +{ + + if ( CAddressBookTableView::CurrentBookIsPersonalBook() || + IsLDAPSearching() ) return; + + + + + MessageT message = paneID_Search; + if( text == NULL ) + { + StDialogHandler handler(8980, nil); + // Select the "URL" edit field + CLDAPQueryDialog* dialog = dynamic_cast< CLDAPQueryDialog*>( handler.GetDialog() ); + Assert_( dialog ); + + dialog->Setup( mAddressBookTable->GetMessagePane(), mAddressBookTable->GetCurrentBook() ); + + + Boolean doSearch = false; + // Run the dialog + + + do { + message = handler.DoDialog(); + } while (message != paneID_Search && message != msg_Cancel); + } + if ( message == paneID_Search ) + { + CAddressBookManager::FailAddressError(AB_SearchDirectory((ABPane *) mAddressBookTable->GetMessagePane(), + text ) ); + + //USearchHelper::FindViewSubpane(this, paneID_SearchEnclosure)->Disable(); + USearchHelper::ShowHidePane(mSearchButton, false); + USearchHelper::ShowHidePane(mStopButton, true); + // UpdatePort(); + + } +} + +/*====================================================================================== + Update the window to the current directory servers. +======================================================================================*/ + +void CAddressBookController::UpdateToDirectoryServers() { + + CAddressBookWindow *addressWindow = dynamic_cast( + CWindowMediator::GetWindowMediator()->FetchTopWindow(WindowType_Address)); + + if ( addressWindow == nil ) return; + + PopulateDirectoryServers(); +} + +//----------------------------------- +Int32 CAddressBookController::GetServerIndexFromPopup() +//----------------------------------- +{ + CSearchPopupMenu *popup = USearchHelper::FindSearchPopup(this, paneID_DirServers); + if ( !popup ) return -1; + return popup->GetCurrentItemCommandNum(); +} + +//----------------------------------- +void CAddressBookController::SetPopupFromServerIndex(Int32 inServerIndex) +//----------------------------------- +{ + CSearchPopupMenu *popup = USearchHelper::FindSearchPopup(this, paneID_DirServers); + if ( !popup ) return; + popup->SetCurrentItemByCommand(inServerIndex); +} + +//----------------------------------- +void CAddressBookController::MessageWindStop(Boolean inUserAborted) +// React to stop message. +//----------------------------------- +{ + +#pragma unused (inUserAborted) + if ( CAddressBookTableView::CurrentBookIsPersonalBook() || + !IsLDAPSearching() ) return; + + AB_FinishSearch((ABPane *) mAddressBookTable->GetMessagePane(), CAddressBookWindow::GetMailContext()); + + //USearchHelper::FindViewSubpane(this, paneID_SearchEnclosure)->Enable(); + USearchHelper::ShowHidePane(mStopButton, false); + USearchHelper::ShowHidePane(mSearchButton, true); + USearchHelper::SelectEditField(mTypedownName); + UpdatePort(); +} + +//----------------------------------- +Boolean CAddressBookController::HandleKeyPress(const EventRecord &inKeyEvent) +//----------------------------------- +{ + Int16 theKey = inKeyEvent.message & charCodeMask; + + if (((theKey == char_Enter) || (theKey == char_Return)) && !IsLDAPSearching()) + { + CStr255 typedownText; + mTypedownName->GetDescriptor(typedownText); + if (typedownText.Length() > 0 ) + MessageWindSearch( typedownText ); + } + else if ( (((theKey == char_Escape) && ((inKeyEvent.message & keyCodeMask) == vkey_Escape)) || + UKeyFilters::IsCmdPeriod(inKeyEvent)) && IsLDAPSearching() ) + { + mStopButton->SimulateHotSpotClick(kControlButtonPart); + return true; + } + return false; +} + + +#if 1 +void CAddressBookManager::DoPickerDialog( CComposeAddressTableView* initialTable ) +{ + StDialogHandler handler ( 8990, nil ); + CAddressPickerWindow* dialog = dynamic_cast< CAddressPickerWindow* >( handler.GetDialog() ); + dialog->Setup( initialTable ); + dialog->Show(); + MessageT message; + do { + message = handler.DoDialog(); + } while ( message != CAddressPickerWindow::eOkayButton && + message != CAddressPickerWindow::eCancelButton ) ; +} + +// Mail News address picker +#pragma mark - + +void CAddressPickerWindow::FinishCreateSelf() +{ + mPickerAddresses = dynamic_cast< CComposeAddressTableView* >( FindPaneByID( 'Addr' ) ); + FailNILRes_( mPickerAddresses ); + + mAddressBookTable = dynamic_cast( FindPaneByID( paneID_AddressBookTable )); + FailNILRes_(mAddressBookTable); + mAddressBookTable->AddListener( this ); + UReanimator::LinkListenerToControls(this, this,CAddressPickerWindow:: res_ID); + // Adjust button state + ListenToMessage ( CStandardFlexTable::msg_SelectionChanged, nil ); + + CAddressBookWindow::FinishCreateSelf(); + +} + +void CAddressPickerWindow::Setup( CComposeAddressTableView* initialTable ) +{ + // Copy the old table to the new one + Assert_( initialTable ); + Assert_( mPickerAddresses ); + + TableIndexT numRows; + mInitialTable = initialTable; + initialTable->GetNumRows(numRows); + STableCell cell; + Uint32 size; + char* address = NULL; + for ( int32 i = 1; i <= numRows; i++ ) + { + EAddressType type = initialTable->GetRowAddressType( i ); + cell.row = i; + cell.col = 2; + size = sizeof(address); + initialTable->GetCellData(cell, &address, size); + mPickerAddresses->InsertNewRow( type, address, false ); + } +} + +void CAddressPickerWindow::ListenToMessage(MessageT inMessage, void *ioParam) +{ +#pragma unused (ioParam) + switch( inMessage ) + { + case eOkayButton: + CComposeAddressTableStorage* oldTableStorage =dynamic_cast< CComposeAddressTableStorage*> (mInitialTable->GetTableStorage() ); + mPickerAddresses->EndEditCell(); + mInitialTable->SetTableStorage( mPickerAddresses->GetTableStorage() ); + mPickerAddresses->SetTableStorage( oldTableStorage ); + break; + + case eHelp: + ShowHelp( HELP_SELECT_ADDRESSES ); + break; + + case eToButton: + AddSelection ( eToType ); + break; + + case eCcButton: + AddSelection ( eCcType ); + break; + + case eBccButton: + AddSelection( eBccType ); + break; + + case ePropertiesButton: + break; + case CStandardFlexTable::msg_SelectionChanged: + Boolean enable = mAddressBookTable->GetSelectedRowCount() >0; + + USearchHelper::EnableDisablePane( USearchHelper::FindViewControl( this ,eToButton ), enable, true ); + USearchHelper::EnableDisablePane( USearchHelper::FindViewControl( this ,eCcButton ), enable, true ); + USearchHelper::EnableDisablePane( USearchHelper::FindViewControl( this ,eBccButton ), enable, true ); + USearchHelper::EnableDisablePane( USearchHelper::FindViewControl( this ,ePropertiesButton ), enable, true ); + break; + + default: + break; + } + + +} + +void CAddressPickerWindow::AddSelection( EAddressType inAddressType ) +{ + + char* address = NULL; + //Uint32 size= sizeof(address); + TableIndexT row = 0; + + while ( mAddressBookTable->GetNextSelectedRow( row ) ) + { + mAddressBookTable->GetFullAddress( row, &address ); + mPickerAddresses->InsertNewRow( inAddressType, address, false ); + } +} + +#endif +#endif // MOZ_NEWADDR diff --git a/mozilla/cmd/macfe/MailNews/MailNewsAddressBook.h b/mozilla/cmd/macfe/MailNews/MailNewsAddressBook.h new file mode 100644 index 00000000000..285b2dad5ac --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/MailNewsAddressBook.h @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// MailNewsAddressBook.h + +#pragma once + +//#define MOZ_NEWADDR +#ifdef MOZ_NEWADDR + #include "CAddressBookWindows.h" +#else +class CComposeAddressTableView; + +/*====================================================================================*/ + #pragma mark INCLUDE FILES +/*====================================================================================*/ + + +#pragma mark - +/*====================================================================================*/ + #pragma mark TYPEDEFS +/*====================================================================================*/ + +typedef struct ABook ABook; +typedef struct DIR_Server DIR_Server; +typedef struct _XP_List XP_List; +typedef UInt32 ABID; +class CNamePropertiesWindow; +class CListPropertiesWindow; +#pragma mark - +/*====================================================================================*/ + #pragma mark CONSTANTS +/*====================================================================================*/ + + +#pragma mark - +/*====================================================================================*/ + #pragma mark CLASS DECLARATIONS +/*====================================================================================*/ + +class CAddressBookWindow; + +struct SAddressDragInfo +{ + ABID id; + DIR_Server* dir; +}; + +class CAddressBookManager +{ +public: + + // Should be called when the application starts up + static void OpenAddressBookManager(void); + // Should be called when the application closes + static void CloseAddressBookManager(void); + + static void ImportLDIF(const FSSpec& inSpec); + + static CAddressBookWindow* ShowAddressBookWindow(void); + + static XP_List *GetDirServerList(void); + static void SetDirServerList(XP_List *inList, Boolean inSavePrefs = true); + static DIR_Server *GetPersonalBook(void); + static ABook *GetAddressBook(void); + + static void FailAddressError(Int32 inError); + static CNamePropertiesWindow *FindNamePropertiesWindow(ABID inEntryID); + static CNamePropertiesWindow *GetNamePropertiesWindow(ABID inEntryID, Boolean inOptionKeyDown); + static CListPropertiesWindow *FindListPropertiesWindow(ABID inEntryID); + static CListPropertiesWindow *GetListPropertiesWindow(ABID inEntryID, Boolean inOptionKeyDown); + static void DoPickerDialog( CComposeAddressTableView* initialTable ); + +private: + + static void RegisterAddressBookClasses(void); + static int DirServerListChanged(const char*, void*) + { + sDirServerListChanged = true; + return 0; + } + + // Instance variables + + static XP_List *sDirServerList; + static Boolean sDirServerListChanged; + static ABook *sAddressBook; // Really, the global address book database + // used in conjunction with all address books + // (local and remote) associated with the + // application. This object is opened at application + // startup and not closed until the application closes. +}; +#endif // MOZ_NEWADDR diff --git a/mozilla/cmd/macfe/MailNews/MailNewsCallbacks.cp b/mozilla/cmd/macfe/MailNews/MailNewsCallbacks.cp new file mode 100644 index 00000000000..37671fa2c5d --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/MailNewsCallbacks.cp @@ -0,0 +1,333 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// MailNewsCallbacks.cp + +#include "MailNewsCallbacks.h" + +// XP headers +#include "fe_proto.h" +//#include "errcode.h" +//#include "msglpane.h" +#include "addrbook.h" + + +// FE +#include "CCheckMailContext.h" +#include "uprefd.h" +#include "macutil.h" + +#include "CMailFlexTable.h" +#include "CMessageWindow.h" +#include "CThreadWindow.h" + +#include "UMailFolderMenus.h" +#include "CMessageFolder.h" +#include "ufilemgr.h" + +//====================================== +// class CMailCallbackManager +//====================================== + +CMailCallbackManager* CMailCallbackManager::sMailCallbackManager = nil; + +//----------------------------------- +CMailCallbackManager::CMailCallbackManager() +//----------------------------------- +{ + sMailCallbackManager = this; +} + +//----------------------------------- +CMailCallbackManager::~CMailCallbackManager() +//----------------------------------- +{ + sMailCallbackManager = nil; +} + +//----------------------------------- +CMailCallbackManager* CMailCallbackManager::Get() +//----------------------------------- +{ + if (!sMailCallbackManager) + new CMailCallbackManager; + return sMailCallbackManager; +} + +//----------------------------------- +Boolean CMailCallbackManager::ValidData(MSG_Pane *inPane) +//----------------------------------- +{ + void* data = MSG_GetFEData(inPane); + //? We WERE getting callbacks before MSG_SetFEData was called. Assert_(data == this); + return (data == this); +} + +//----------------------------------- +void CMailCallbackManager::PaneChanged( + MSG_Pane *inPane, + XP_Bool asynchronous, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value) +//----------------------------------- +{ + if (inNotifyCode == MSG_PaneNotifyFolderDeleted) + { + // A folder can be deleted even with no active pane (eg, in IMAP synchronization). + // In this case, we get a call with inPane == NULL. + CMailFolderMixin::UpdateMailFolderMixins(); + } + if (ValidData(inPane)) + { + // the backend can give us a MSG_VIEWINDEXNONE (-1) in value, which we need to change to a 0 + if (value == MSG_VIEWINDEXNONE) + value = 0; + + // Because of the caching scheme for folders, we must update the caches first: + if (inNotifyCode == MSG_PaneNotifyFolderInfoChanged) + { + CCachedFolderLine::FolderInfoChanged(inPane, (MSG_FolderInfo*)value); + } + SPaneChangeInfo info(inPane, asynchronous, inNotifyCode, value); + BroadcastMessage(msg_PaneChanged, &info); + } +} // CMailCallbackManager::PaneChanged + +//----------------------------------- +void CMailCallbackManager::ChangeStarting( + MSG_Pane* inPane, + XP_Bool inAsync, + MSG_NOTIFY_CODE inNotifyCode, + MSG_ViewIndex inWhere, + int32 inCount) +//----------------------------------- +{ + if (ValidData(inPane)) + { + SLineChangeInfo info(inPane, inAsync, inNotifyCode, inWhere + 1, inCount); + BroadcastMessage(msg_ChangeStarting, &info); + } +} // CMailCallbackManager::ChangeStarting + +//----------------------------------- +void CMailCallbackManager::ChangeFinished( + MSG_Pane* inPane, + XP_Bool inAsync, + MSG_NOTIFY_CODE inNotifyCode, + MSG_ViewIndex inWhere, + int32 inCount) +//----------------------------------- +{ + if (ValidData(inPane)) + { + SLineChangeInfo info(inPane, inAsync, inNotifyCode, inWhere + 1, inCount); + BroadcastMessage(msg_ChangeFinished, &info); + } +} // CMailCallbackManager::ChangeFinished + +//====================================== +// class CMailCallbackListener +//====================================== + +//----------------------------------- +CMailCallbackListener::CMailCallbackListener() +//----------------------------------- +: LListener() +, mPane(nil) +{ + CMailCallbackManager::Get()->AddUser(this); + CMailCallbackManager::Get()->AddListener(this); +} + +//----------------------------------- +CMailCallbackListener::~CMailCallbackListener() +//----------------------------------- +{ + mPane = nil; // stop derived classes from listening to callbacks. + CMailCallbackManager::Get()->RemoveListener(this); + CMailCallbackManager::Get()->RemoveUser(this); +} + +//----------------------------------- +void CMailCallbackListener::ListenToMessage(MessageT inMessage, void* ioParam) +//----------------------------------- +{ + switch (inMessage) + { + case CMailCallbackManager::msg_PaneChanged: + if ( IsMyPane(ioParam) ) { + SPaneChangeInfo* info = reinterpret_cast(ioParam); + PaneChanged(info->pane, info->notifyCode, info->value); + } + break; + + case CMailCallbackManager::msg_ChangeStarting: + if ( IsMyPane(ioParam) ) { + SLineChangeInfo* info = reinterpret_cast(ioParam); + ChangeStarting(info->pane, info->changeCode, info->startRow, info->rowCount); + } + break; + + case CMailCallbackManager::msg_ChangeFinished: + if ( IsMyPane(ioParam) ) { + SLineChangeInfo* info = reinterpret_cast(ioParam); + ChangeFinished(info->pane, info->changeCode, info->startRow, info->rowCount); + } + break; + } +} // CMailCallbackListener::ListenToMessage + +//----------------------------------- +Boolean CMailCallbackListener::IsMyPane(void* info) const +//----------------------------------- +{ + return (mPane == reinterpret_cast(info)->pane); +} + +//----------------------------------- +void CMailCallbackListener::ChangeStarting( + MSG_Pane* /*inPane*/, + MSG_NOTIFY_CODE /*inChangeCode*/, + TableIndexT /*inStartRow*/, + SInt32 /*inRowCount*/) +//----------------------------------- +{ +} + +//----------------------------------- +void CMailCallbackListener::ChangeFinished( + MSG_Pane* /*inPane*/, + MSG_NOTIFY_CODE /*inChangeCode*/, + TableIndexT /*inStartRow*/, + SInt32 /*inRowCount*/) +//----------------------------------- +{ +} +//====================================== + + +//----------------------------------- +void FE_UpdateBiff(MSG_BIFF_STATE inState) +// Called when "check for mail" state changes. +//----------------------------------- +{ + CCheckMailContext::Get()->SetState(inState); +} + +// +// inDir argument is a Mac path. +uint32 FE_DiskSpaceAvailable( MWContext* inContext, const char* inDir) +//----------------------------------- +{ + Boolean isMailContext + = ( inContext->type == MWContextMailMsg + || inContext->type == MWContextMail + || inContext->type == MWContextMessageComposition + || inContext->type == MWContextMailNewsProgress); + + if (isMailContext) + { + FSSpec folder = CPrefs::GetFolderSpec( CPrefs::MailFolder ); + return GetFreeSpaceInBytes( folder.vRefNum ); + } + else + { + FSSpec folder; + Str255 fileName; + OSErr err; + if (inDir) + { + XP_MEMCPY(fileName, inDir, 255); + c2pstr((char*)fileName); + folder.vRefNum = folder.parID = 0; + err = FSMakeFSSpec(folder.vRefNum, folder.parID, fileName, &folder); + } + else + err = fnfErr; + if ( err != noErr) + { + XP_ASSERT(FALSE); + return -1; + } + else + return GetFreeSpaceInBytes( folder.vRefNum ); + } + +} + +//----------------------------------- +void FE_ListChangeStarting( + MSG_Pane* inPane, + XP_Bool inAsync, + MSG_NOTIFY_CODE inNotifyCode, + MSG_ViewIndex inWhere, + int32 inCount) +//----------------------------------- +{ + CMailCallbackManager::Get() + ->ChangeStarting(inPane, inAsync, inNotifyCode, inWhere, inCount); +} // FE_ListChangeStarting + +//----------------------------------- +void FE_ListChangeFinished( + MSG_Pane* inPane, + XP_Bool inAsync, + MSG_NOTIFY_CODE inNotifyCode, + MSG_ViewIndex inWhere, + int32 inCount) +//----------------------------------- +{ + CMailCallbackManager::Get() + ->ChangeFinished(inPane, inAsync, inNotifyCode, inWhere, inCount); +} // FE_ListChangeFinished + +//----------------------------------- +void FE_PaneChanged( + MSG_Pane *inPane, + XP_Bool asynchronous, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value) +//----------------------------------- +{ + CMailCallbackManager::Get()->PaneChanged(inPane, asynchronous, inNotifyCode, value); +} + +// ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ +// +// Awaiting a real composition implementation, +// at which point, these should move there. +// +// ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ + + +extern "C" void FE_InsertMessageCompositionText( MWContext* /*inContext*/, const char* /*text*/, XP_Bool /*leaveCursorAtBeginning*/); +void FE_InsertMessageCompositionText( MWContext* /*inContext*/, const char* /*text*/, XP_Bool /*leaveCursorAtBeginning*/) {} + +extern "C" void FE_MsgShowHeaders(MSG_Pane *pPane, MSG_HEADER_SET mhsHeaders); +void FE_MsgShowHeaders (MSG_Pane* /*comppane*/, MSG_HEADER_SET /*headers*/) {} + + +extern void FE_UpdateCompToolbar(MSG_Pane* /*comppane*/); // in CMailComposeWindow.cp + +MSG_Master* FE_GetMaster() +{ + // implement get master + return CMailNewsContext::GetMailMaster(); +} diff --git a/mozilla/cmd/macfe/MailNews/MailNewsCallbacks.h b/mozilla/cmd/macfe/MailNews/MailNewsCallbacks.h new file mode 100644 index 00000000000..7b5bef81f11 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/MailNewsCallbacks.h @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// MailNewsCallbacks.h + +#pragma once + +#include +#include + +#include "msgcom.h" + +//====================================== +class CMailCallbackManager : public LBroadcaster, public LSharable +//====================================== +{ + +public: + + enum + { + msg_PaneChanged = 'PnCh' + , msg_ChangeFinished = 'ChFn' + , msg_ChangeStarting = 'ChSt' + }; + CMailCallbackManager(); + virtual ~CMailCallbackManager(); + static CMailCallbackManager* Get(); + +public: + + void PaneChanged( + MSG_Pane *inPane, + XP_Bool asynchronous, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value); + void ChangeStarting( + MSG_Pane* inPane, + XP_Bool inAsync, + MSG_NOTIFY_CODE inNotifyCode, + MSG_ViewIndex inWhere, + int32 inCount); + void ChangeFinished( + MSG_Pane* inPane, + XP_Bool inAsync, + MSG_NOTIFY_CODE inNotifyCode, + MSG_ViewIndex inWhere, + int32 inCount); +private: + + Boolean ValidData(MSG_Pane *inPane); + +protected: + + static CMailCallbackManager* sMailCallbackManager; +}; + +//----------------------------------- +struct SMailCallbackInfo +//----------------------------------- +{ + MSG_Pane* pane; + XP_Bool async; + + SMailCallbackInfo(MSG_Pane* inPane, XP_Bool inAsync) : pane(inPane), async(inAsync){} +}; + +//----------------------------------- +struct SPaneChangeInfo : public SMailCallbackInfo +//----------------------------------- +{ + MSG_PANE_CHANGED_NOTIFY_CODE notifyCode; + int32 value; + + SPaneChangeInfo( + MSG_Pane* inPane, + XP_Bool inAsync, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 inValue) + : SMailCallbackInfo(inPane, inAsync) + , notifyCode(inNotifyCode) + , value(inValue) {} +}; + +typedef Uint32 TableIndexT; + +//----------------------------------- +struct SLineChangeInfo : public SMailCallbackInfo +//----------------------------------- +{ + MSG_NOTIFY_CODE changeCode; + MSG_ViewIndex startRow; + int32 rowCount; + + SLineChangeInfo( + MSG_Pane* inPane, + XP_Bool inAsync, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + int32 inCount) + : SMailCallbackInfo(inPane, inAsync) + , changeCode(inChangeCode) + , startRow(inStartRow) + , rowCount(inCount) {} +}; + +//====================================== +class CMailCallbackListener : public LListener +//====================================== +{ +protected: + CMailCallbackListener(); +public: + ~CMailCallbackListener(); + void SetPane(MSG_Pane* inPane) { mPane = inPane; } +protected: + virtual void ListenToMessage(MessageT message, void* ioParam); + virtual Boolean IsMyPane(void* info) const; // info from a ListenToMessage... +private: + void PaneChanged(void* ioParam); + void ChangeStarting(void* ioParam); + void ChangeFinished(void* ioParam); + + virtual void ChangeStarting( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount); + virtual void ChangeFinished( + MSG_Pane* inPane, + MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, + SInt32 inRowCount); + virtual void PaneChanged( + MSG_Pane* inPane, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value) = 0; // must always be supported. +private: + MSG_Pane* mPane; +}; diff --git a/mozilla/cmd/macfe/MailNews/MailNewsClasses.cp b/mozilla/cmd/macfe/MailNews/MailNewsClasses.cp new file mode 100644 index 00000000000..619428da02f --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/MailNewsClasses.cp @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// MailNewsClasses.cp + +#include "MailNewsClasses.h" + +//#define REGISTER_(letter,root) \ +// RegisterClass_(letter##root::class_ID, \ +// (ClassCreatorFunc)letter##root::Create##root##Stream); + +#define REGISTER_(letter,root) \ + RegisterClass_(letter##root); + +#define REGISTERC(root) REGISTER_(C,root) +#define REGISTERL(root) REGISTER_(L,root) + +// ### General Purpose UI Classes + #include "CSimpleTextView.h" + +// ### MailNews Specific UI Classes + #include "CThreadView.h" + #include "CSimpleFolderView.h" + #include "CMessageFolderView.h" + #include "CMessageWindow.h" + #include "CMailNewsWindow.h" + #include "CSubscribeView.h" + #include "COfflinePicker.h" + #include "CThreadWindow.h" + #include "CMessageWindow.h" + #include "CMessageView.h" + #include "CMailComposeWindow.h" + #include "CBiffButtonAttachment.h" + #include "CSubscribeWindow.h" + #include "CGAStatusBar.h" + #include "LGABox_fixes.h" + #include "SearchHelpers.h" + #include "CSizeBox.h" + + #include "CExpandoDivider.h" + + #include "MailNewsSearch.h" + #include "MailNewsFilters.h" + + #include "CMailFolderButtonPopup.h" + #include "StGetInfoHandler.h" + #include "CMailProgressWindow.h" + #include "CMessageAttachmentView.h" +//----------------------------------- +void RegisterAllMailNewsClasses(void) +//----------------------------------- +{ + // ### General Purpose UI Classes + REGISTERC(SimpleTextView) + + // stuff that was being registered twice (usually once in address book, the other time + // in a search window). Since the new PP complains when you do this, do it once and for + // all when we init mail/news + RegisterClass_(CGAStatusBar); + RegisterClass_(LGABox_fixes); + RegisterClass_(CSearchEditField); + RegisterClass_(CSizeBox); + RegisterClass_(CSearchCaption); + RegisterClass_(CSearchTabGroup); + + // ### MailNews Specific UI Classes + REGISTERC(MailNewsFolderWindow) + REGISTERC(ThreadWindow) + REGISTERC(ThreadView) + REGISTERC(SimpleFolderView) + REGISTERC(MessageFolderView) + REGISTERC(SubscribeView) + REGISTERC(OfflinePickerView) + REGISTERC(OfflinePickerWindow) + REGISTERC(ExpandoDivider) + REGISTERC(MessageWindow) + REGISTERC(MessageView) + REGISTERC(MailFolderButtonPopup) + REGISTERC(MailFolderPatternTextPopup) + REGISTERC(MailFolderGAPopup) + REGISTERC(AttachmentView) + REGISTERC(BiffButtonAttachment); + REGISTERC(MailProgressWindow) + REGISTERC(MessageAttachmentView) + + UComposeUtilities::RegisterComposeClasses(); + USubscribeUtilities::RegisterSubscribeClasses(); + CSearchWindowManager::RegisterSearchClasses(); + CFiltersWindowManager::RegisterFiltersClasses(); + UGetInfo::RegisterGetInfoClasses(); +} // RegisterAllMailNewsClasses diff --git a/mozilla/cmd/macfe/MailNews/MailNewsClasses.h b/mozilla/cmd/macfe/MailNews/MailNewsClasses.h new file mode 100644 index 00000000000..6ccf9741441 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/MailNewsClasses.h @@ -0,0 +1,25 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// MailNewsClasses.h + +#pragma once + +void RegisterAllMailNewsClasses(void); diff --git a/mozilla/cmd/macfe/MailNews/MailNewsFilters.cp b/mozilla/cmd/macfe/MailNews/MailNewsFilters.cp new file mode 100644 index 00000000000..a1cdaa19924 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/MailNewsFilters.cp @@ -0,0 +1,2556 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// MailNewsFilters.cp + +/*====================================================================================*/ + #pragma mark INCLUDE FILES +/*====================================================================================*/ + +#define DEBUGGER_ASSERTIONS + +#include "MailNewsFilters.h" +#include "CSearchManager.h" +#include "SearchHelpers.h" +#include "UMailFolderMenus.h" +#include "CCAption.h" + +#include "UStClasses.h" +#include "CMouseDragger.h" +#include "uprefd.h" +#include "macutil.h" +#include "MailNewsgroupWindow_Defines.h" +#include "uerrmgr.h" +#include "CMailNewsContext.h" +#include "Netscape_Constants.h" +#include "xp_str.h" +#include "UGraphicGizmos.h" +#include "LFlexTableGeometry.h" +#include "CTableKeyAttachment.h" +#include "URobustCreateWindow.h" +#include "CInlineEditField.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "UIHelper.h" +#include "CMessageFolder.h" +#include + +// BE Files + +#include "msg_filt.h" + +class CFiltersTableView; + +// Related files + +//#include "filters.cpp" +//#include "allxpstr.h" +//#include "msg.h" +//#include "msgcom.h" +//#include "msg_filt.h" +//#include "pmsgfilt.h" +//#include "pmsgsrch.h" +//#include "msgglue.cpp" +//#include "ptrarray.h" + +#define TABLE_TO_FILTER_INDEX(a) ((a) - 1) +#define ABS_VAL(v) (((v) < 0) ? -(v) : (v)) + +#define USE_NEW_FILTERS_SAVE + + +/*====================================================================================*/ + #pragma mark TYPEDEFS +/*====================================================================================*/ + + +/*====================================================================================*/ + #pragma mark CONSTANTS +/*====================================================================================*/ + +static const ResIDT cEnabledCheckCursorID = 29200; +static const ResIDT cHandOpenCursorID = 29201; +static const ResIDT cHandClosedCursorID = 29202; + +static const Int16 cColTextHMargin = 3; + +static const Int16 cDisclosureVMargin = 4; +static const Int16 cFiltersVMargin = 21; + +// Save window status version + +static const UInt16 cFiltersSaveWindowStatusVersion = 0x0008; + + +/*====================================================================================*/ + #pragma mark INTERNAL CLASS DECLARATIONS +/*====================================================================================*/ + +#pragma mark - + + + + +// CMailFolderPopup + +class CMailFolderPopup : public CGAIconPopup, + public CGAIconPopupFolderMixin { + +public: + + enum { class_ID = 'FDPU' }; + CMailFolderPopup(LStream *inStream) : + CGAIconPopup(inStream) { + } + virtual void GetCurrentItemTitle(Str255 outItemTitle) { + MGetCurrentMenuItemName(outItemTitle); + } + + virtual void FinishCreateSelf(void) { + CGAIconPopup::FinishCreateSelf(); + CMailFolderMixin::UpdateMailFolderMixinsNow(this); + } +protected: + +}; + + +class CMailFiltersWindow : public CMediatedWindow, + public CSaveWindowStatus, + public LListener { + +public: + + typedef CMediatedWindow Inherited; + + // Stream creator method + + enum { class_ID = 'FilT', pane_ID = class_ID, res_ID = 8700 }; + + // IDs for panes in associated view, also messages that are broadcast to this object + + enum { + paneID_New = 'NEWF' // New filter + , paneID_Duplicate = 'DUPL' // Duplicate filter + , paneID_Table = 'Tabl' // Filter table + , paneID_Description = 'DESC' // Filter description + , paneID_FiltersLog = 'FLOG' // Filter log checkbox + , paneID_DiscloseFilterAction = 'DFLT' // Disclosure triangle + , paneID_TopFrameEnclosure = 'TOPE' // Enclosure for top frame panes + , paneID_FilterActionView = 'FAVW' // Enclosure for filter action panes + , paneID_Actions = 'ACTN' // CSearchPopupMenu *, actions popup + , paneID_Folders = 'FLDR' // CSearchPopupMenu *, folders popup + , paneID_Priority = 'PRIO' // CSearchPopupMenu *, priority popup + , paneID_MatchAllRadio = 'MAND' // AND radio + , paneID_MatchAnyRadio = 'MOR ' // OR radio + , paneID_ServerSideFilterButton = 'SFBT' // Server side filters button + , paneID_FolderToFilterScopePopup = 'MfPM' // folder popup + + }; + + CMailFiltersWindow(LStream *inStream) : + CMediatedWindow(inStream, WindowType_MailFilters), + CSaveWindowStatus(this), + LListener(), + mFiltersTable(nil), + mFilterDescriptor(nil), + mDiscreteVSizing(1), + mDescriptionTextChangedIndex(0), + mFilterActionChangedIndex(0), + mFilterActionDisclosure(nil), + mFilterActionView(nil), + mMailFoldersPopup(nil), + mMailPriorityPopup(nil), + mSavedChanges(false), + mMailNewsContext(nil), + mRepopulateFlag( 0 ), mRuleType( filterInboxRule ) + { + SetPaneID(pane_ID); + SetRefreshAllWhenResized(false); + } + virtual ~CMailFiltersWindow(void); + + void RecalcMinMaxStdSizes(void); + + virtual const CSearchManager& GetSearchManager() const { return mSearchManager; } + virtual void Activate(); + virtual void Deactivate() + { + SaveCurrentFilterModifications(); + Inherited::Deactivate(); + } + void LoadFolderToFilter( ); +protected: + + // Overriden PowerPlant + + virtual void FinishCreateSelf(void); + virtual void DrawSelf(void); + virtual void ListenToMessage(MessageT inMessage, void *ioParam = nil); + virtual void AttemptClose(void) { + AttemptCloseWindow(); + CMediatedWindow::AttemptClose(); + } + virtual void DoClose(void) { + AttemptCloseWindow(); + CMediatedWindow::DoClose(); + } + virtual void DoSetBounds(const Rect &inBounds); + + virtual void ReadWindowStatus(LStream *inStatusData); + virtual void WriteWindowStatus(LStream *outStatusData); + virtual UInt16 GetValidStatusVersion(void) const { + return cFiltersSaveWindowStatusVersion; + } + + void UpdateToFilterLevels(Int16 inDeltaV); + + // Command and message methods + + void FilterSelectionChanged(void); + void ShowHideFilterAction(void); + Boolean FilterActionIsVisible(void) const { + return (mFilterActionDisclosure ? (mFilterActionDisclosure->GetValue() != 0) : false); + } + + void PopulateActionsMenu(void); + + void MessageActions(CSearchPopupMenu *inActionsMenu); + void SaveCurrentFilterModifications(void); + + void OpenFiltersList(void); + void UpdateActionToFilter(Boolean inEvenIfNotVisible = false); + void UpdateFilterToAction(Int32 inRow); + char * GetEscapedTempString(char * inStr); + + void EnableDisableFrameBindings(Boolean inEnable, SBooleanRect *ioTopFrameBinding, + SBooleanRect *ioActionBinding, SBooleanRect *ioDisclosureBinding); + + virtual ResIDT GetStatusResID(void) const { return res_ID; } + + + // Instance variables ========================================================== + UInt32 mRepopulateFlag; + CFiltersTableView *mFiltersTable; + LBroadcasterEditField *mFilterDescriptor; + Int32 mDescriptionTextChangedIndex; + Int32 mFilterActionChangedIndex; + Int16 mDiscreteVSizing; // Discrete vertical sizing for the window + Int16 mMinActionViewPortTop; // Minimum location for the filter action view top + Int16 mMinVSizeNoAction; // Minimum size for the window + + Boolean mSavedChanges; + + LControl *mDuplicateButton; + LControl *mFilterActionDisclosure; + LView *mFilterActionView; + + CMailFolderPopup *mMailFoldersPopup; + CSearchPopupMenu *mMailPriorityPopup; + CMailFolderPopup *mFolderToFilerPopup; + CSearchManager mSearchManager; + CMailNewsContext *mMailNewsContext; + LGARadioButton *mMatchAllRadio; + LGARadioButton *mMatchAnyRadio; + MSG_FilterType mRuleType; +}; + + +// CFiltersTableView + +class CFiltersTableView : public CStandardFlexTable, public LListener { +private: + typedef CStandardFlexTable Inherited; +public: + + // Broadcast messages + + enum { + msg_FilterTableSelectionChanged = 'chng' // nil + , msg_DoubleClickCell = 'doub' // nil + , msg_DeleteCell = 'dele' // nil + , msg_SaveModifications = 'save' // nil + }; + + // IDs for panes in associated view, also messages that are broadcast to this object + + enum { + paneID_OrderCaption = 'ORDR' + , paneID_NameCaption = 'NAME' + , paneID_EnabledCaption = 'ENBL' + , paneID_EditTitle = 'EDTL' // Edit field for title + }; + + enum { // Icon IDs + icm8_EnabledCheck = 29201 + , kEnabledCheckIconID = 15237 + , kDisabledCheckIconID = 15235 + , cSmallIconWidth = 16 + , cSmallIconHeight = 16 + }; + + enum { class_ID = 'FlTb' }; + CFiltersTableView(LStream *inStream) : + CStandardFlexTable(inStream), + mEditFilterName(nil), + mEditFilterNameRow(0), + mFilterList(nil), mRuleType( filterInboxRule ) { + SetRefreshAllWhenResized(false); + } + virtual ~CFiltersTableView(void); + + virtual void RemoveRows(Uint32 inHowMany, TableIndexT inFromRow, Boolean inRefresh); + void MoveRow(TableIndexT inCurrRow, TableIndexT inNewRow, + Boolean inRefresh); + + virtual void ApplyForeAndBackColors(void) const { // Make the thing public! + LTableView::ApplyForeAndBackColors(); + } + + void UpdateFilterNameChanged(void); + void SelectEditFilterName(TableIndexT inRow); + + + MSG_Filter *GetFilterAtRow(TableIndexT inRow) { + AssertFail_(mFilterList != nil); + MSG_Filter *filter = nil; + Int32 error = MSG_GetFilterAt(mFilterList, TABLE_TO_FILTER_INDEX(inRow), &filter); + AssertFail_(error == FilterError_Success); + AssertFail_(filter != nil); + return filter; + } + void GetFilterNameAtRow(TableIndexT inRow, CStr255 *outName) { + char *name = nil; + Int32 error = MSG_GetFilterName(GetFilterAtRow(inRow), &name); + Assert_(error == FilterError_Success); + Assert_(name != nil); + *outName = name; + } + Boolean GetFilterEnabledAtRow(TableIndexT inRow) { + XP_Bool enabled; + Int32 error = MSG_IsFilterEnabled(GetFilterAtRow(inRow), &enabled); + Assert_(error == FilterError_Success); + return enabled; + } + void GetFilterDescription(TableIndexT inRow, CStr255 *outDescription) { + char *description = nil; + Int32 error = MSG_GetFilterDesc(GetFilterAtRow(inRow), &description); + Assert_(error == FilterError_Success); + Assert_(description != nil); + *outDescription = description; + } + Boolean GetLogFiltersEnabled(void) { + Assert_(mFilterList != nil); + Boolean isEnabled = MSG_IsLoggingEnabled(mFilterList) ? true : false; + return isEnabled; + } + void GetFilterActionAtRow(TableIndexT inRow, StSearchDataBlock *outData, Int16 *outNumLevels, + MSG_RuleActionType *outAction, void **outValue); + void SetFilterActionAtRow(TableIndexT inRow, StSearchDataBlock *inData, + Int16 inNumLevels, MSG_RuleActionType inAction, + void *inValue); + + void SetFilterList(MSG_FilterList *inFilterList) { + mFilterList = inFilterList; + } + void SetFilterNameAtRow(TableIndexT inRow, const CStr255 *inName) { + FailFiltersError(MSG_SetFilterName(GetFilterAtRow(inRow), *inName)); + } + void SetFilterEnabledAtRow(TableIndexT inRow, Boolean inEnable) { + FailFiltersError(MSG_EnableFilter(GetFilterAtRow(inRow), inEnable)); + } + void SetFilterDescription(Int32 inRow, const CStr255 *inDescription) { + FailFiltersError(MSG_SetFilterDesc(GetFilterAtRow(inRow), *inDescription)); + } + + void NewFilter(void); + void DeleteFilter(void); + void DuplicateFilter(void); + void InsertFilterAt(TableIndexT inTableFilterRow, MSG_Filter *inFilter); + void RemoveFilterAt(TableIndexT inTableFilterRow, MSG_Filter **outFilter); + void SetLogFilters(Boolean inDoLog); + void SaveChangedFilterList(Int32 inRow = 0); + MSG_FilterList* GetFilterList() { return mFilterList; } + void LoadFiltersList(MSG_FolderInfo* folder, MSG_FilterType type); + void SetRuleType ( MSG_FilterType inType ) { mRuleType = inType; } +protected: + + // Unimplemented string IDs + + enum { + uStr_FilterFunctionNotImplemented = 29143 + , uStr_FilterFunctionParamError = 29144 + , uStr_FilterDefaultName = 8753 + , uStr_FilterDefaultDescription = 8754 + }; + + virtual void FinishCreateSelf(void); + virtual void ListenToMessage(MessageT inMessage, void *ioParam = nil); + virtual void ReconcileFrameAndImage(Boolean inRefresh); + virtual Boolean ObeyCommand(CommandT inCommand, void *ioParam); + + virtual void AdjustCursorSelf(Point inPortPt, const EventRecord &/*inMacEvent*/); + virtual Boolean ClickSelect(const STableCell &inCell, const SMouseDownEvent &inMouseDown); + virtual void TrackSelection( const SMouseDownEvent & /* inMouseDown */ ) { + // do nothing + }; + virtual void ClickCell(const STableCell &inCell, const SMouseDownEvent &inMouseDown); + virtual void ScrollBits(Int32 inLeftDelta, Int32 inTopDelta) { + USearchHelper::ScrollViewBits(this, inLeftDelta, inTopDelta); + } + virtual void SelectionChanged(void) { + BroadcastMessage(msg_FilterTableSelectionChanged); + } + enum EMouseTableLocation { eInNone = 0, eInEnabledCheck = 1, eInTitle = 2, eInCellContent = 3 }; + EMouseTableLocation GetMouseTableLocation(Point inPortPt, Rect *outLocationRect = nil); + + void GetFilterTitleRect(TableIndexT inRow, Rect *outTitleRect, + CStr255 *outNameString = nil); + void SwitchEditFilterName(TableIndexT inRow, const Rect *inNameLocalFrame, + CStr255 *inNameString, SMouseDownEvent *ioMouseDown); + void GetDisplayData(const STableCell &inCell, CStr255 *outDisplayText, + Int16 *outHorizJustType, ResIDT *outIconID); + void ToggleFilterEnabled(TableIndexT inRow, Rect *inLocalIconRect); + + virtual void DrawCellContents(const STableCell &inCell, const Rect &inLocalRect); + virtual void SetUpTableHelpers(void); + virtual void DeleteSelection(void); + + virtual Int32 GetBENumRows(void) { + Assert_(mFilterList != nil); + Int32 numFilters; + FailFiltersError(MSG_GetFilterCount(mFilterList, &numFilters)); + return numFilters; + } + + + + static void FailFiltersError(MSG_FilterError inError); + static void BEDeleteFilter(Int32 inFilterIndex, MSG_FilterList *ioFilterList); + static void BEMoveFilter(Int32 inFromIndex, Int32 inToIndex, MSG_FilterList *ioFilterList); + + // Instance variables ========================================================== + + CInlineEditField *mEditFilterName; + Int32 mEditFilterNameRow; + MSG_FilterList *mFilterList; // BE filter list + MSG_FilterType mRuleType; +}; + + +// CDeleteFilterAction + +class CDeleteFilterAction : public LAction { + +public: + + CDeleteFilterAction(CFiltersTableView *inTable, TableIndexT inTableFilterRow) : + LAction(CMailFiltersWindow::res_ID, 1) { + mTable = inTable; + mTableFilterRow = inTableFilterRow; + mFilter = nil; + } + + virtual void Finalize(void) { + if ( mFilter != nil ) { + MSG_FilterError error = MSG_DestroyFilter(mFilter); + mFilter = nil; + Assert_(error == FilterError_Success); + } + } + + virtual void RedoSelf(void) { + if ( mFilter == nil ) { + mTable->RemoveFilterAt(mTableFilterRow, &mFilter); + } + } + + virtual void UndoSelf(void) { + if ( mFilter != nil ) { + mTable->InsertFilterAt(mTableFilterRow, mFilter); + mFilter = nil; + } + } + +protected: + + // Instance variables ========================================================== + + CFiltersTableView *mTable; + TableIndexT mTableFilterRow; // The row that the filter belonged to + MSG_Filter *mFilter; // The filter itself +}; + + +// CMoveFilterAction + +class CMoveFilterAction : public LAction { + +public: + + CMoveFilterAction(CFiltersTableView *inTable, TableIndexT inStartFilterRow, + TableIndexT inEndFilterRow) : + LAction(CMailFiltersWindow::res_ID, 2) { + mTable = inTable; + mStartFilterRow = inStartFilterRow; + mEndFilterRow = inEndFilterRow; + } + + virtual void RedoSelf(void) { + mTable->MoveRow(mStartFilterRow, mEndFilterRow, true); + mTable->SelectScrollCell(STableCell(mEndFilterRow, 1)); + } + + virtual void UndoSelf(void) { + mTable->MoveRow(mEndFilterRow, mStartFilterRow, true); + mTable->SelectScrollCell(STableCell(mStartFilterRow, 1)); + } + +protected: + + // Instance variables ========================================================== + + CFiltersTableView *mTable; + TableIndexT mStartFilterRow; // Starting row, before redo + TableIndexT mEndFilterRow; // Ending row, after redo +}; + +#if 0 +// CFilterTableSelector + +class CFilterTableSelector : public CRowTableSelector { + +public: + + CFilterTableSelector(CFiltersTableView *inTableView) : + CRowTableSelector(inTableView, false) { + + } + +protected: + + virtual UInt32 GetRowUniqueID(const TableIndexT inRow) const { + return inRow; + } + virtual TableIndexT GetUniqueIDRow(const UInt32 inID) const { + return inID; + } +}; +#endif + +// CFilterTableKeyAttachment + +class CFilterTableKeyAttachment : public CTableKeyAttachment { + +public: + + CFilterTableKeyAttachment(CFiltersTableView *inTableView) : + CTableKeyAttachment(inTableView) { + + } + +protected: + + virtual void SelectCell(const STableCell &inCell, Boolean /*multiple*/ = false) { + AssertFail_(mTableView != nil); + (dynamic_cast(mTableView))->SelectScrollCell(inCell); + } +}; + + +#pragma mark - + +/*====================================================================================*/ + #pragma mark INTERNAL FUNCTION PROTOTYPES +/*====================================================================================*/ + + +#pragma mark - + +/*====================================================================================*/ + #pragma mark CLASS IMPLEMENTATIONS +/*====================================================================================*/ + + +/*====================================================================================== + Register all classes associated with the filters window. +======================================================================================*/ + +void CFiltersWindowManager::RegisterFiltersClasses(void) +{ + RegisterClass_(CMailFiltersWindow); + RegisterClass_(CFiltersTableView); + RegisterClass_(CInlineEditField); + RegisterClass_(CMailFolderPopup); +} + + +/*====================================================================================== + Show the filters dialog by bringing it to the front if it is not already. Create it + if needed. +======================================================================================*/ + +void CFiltersWindowManager::ShowFiltersWindow(void) { + + // Find out if the window is already around + CMailFiltersWindow *filtersDialog = dynamic_cast( + CWindowMediator::GetWindowMediator()->FetchTopWindow(WindowType_MailFilters)); + + if ( filtersDialog == nil ) { + // Search dialog has not yet been created, so create it here and display it. + filtersDialog = dynamic_cast( + URobustCreateWindow::CreateWindow(CMailFiltersWindow::res_ID, + LCommander::GetTopCommander())); + } + + // Select the window + + filtersDialog->Select(); +} + + +#pragma mark - + +/*====================================================================================== + Update any current status info. +======================================================================================*/ + +CMailFiltersWindow::~CMailFiltersWindow(void) { + + RemoveAllAttachments(); + // Kludgy, but prevents crash in LUndoer caused by view being destroyed before + // attachments. + + Boolean canRotate = false; + if ( IsVisible() ) { + SaveCurrentFilterModifications(); + USearchHelper::FindWindowTabGroup(&mSubCommanders)->SetRotateWatchValue(&canRotate); + } + delete mFiltersTable; + mFiltersTable = nil; + if ( mMailNewsContext ) { + mMailNewsContext->RemoveUser(this); + } +} + +/*====================================================================================== + Activate window +======================================================================================*/ + +void +CMailFiltersWindow::Activate() +{ + mSearchManager.SetSearchScope( scopeMailFolder, nil); + Inherited::Activate(); +} + +/*====================================================================================== + Finish creating the filters dialog. +======================================================================================*/ + +void CMailFiltersWindow::FinishCreateSelf(void) { + + CMediatedWindow::FinishCreateSelf(); + + // This is lame. The only reason we are creating a context is so that + // we can have ownership of the master without it being deleted! We'll need + // to change this later! + mMailNewsContext = new CMailNewsContext(MWContextMailFilters); + FailNIL_(mMailNewsContext); + mMailNewsContext->AddUser(this); + + AddAttachment(new LUndoer); + + mFiltersTable = dynamic_cast( + USearchHelper::FindViewSubpane(this, paneID_Table)); + + mFilterDescriptor = USearchHelper::FindViewEditField(this, paneID_Description); + AssertFail_(mFiltersTable != nil); + + FindUIItemPtr(this, paneID_Duplicate, mDuplicateButton); + FindUIItemPtr(this, paneID_DiscloseFilterAction, mFilterActionDisclosure); +// mDuplicateButton = USearchHelper::FindViewControl(this, paneID_Duplicate); +// mFilterActionDisclosure = USearchHelper::FindViewControl(this, paneID_DiscloseFilterAction); + mFilterActionView = USearchHelper::FindViewSubview(this, paneID_FilterActionView); + mMailFoldersPopup = dynamic_cast(USearchHelper::FindViewSubpane(this, paneID_Folders)); + AssertFail_(mMailFoldersPopup != nil); + mMailPriorityPopup = USearchHelper::FindSearchPopup(this, paneID_Priority); + + mFilterActionView->SetRefreshAllWhenResized(false); + USearchHelper::FindViewSubview(this, paneID_TopFrameEnclosure)->SetRefreshAllWhenResized(false); + + { // Get the discrete sizing for the window and resize it if necessary to make + // the table size integral with the table's row height + Rect frame; + mFiltersTable->CalcPortFrameRect(frame); + Int16 rowHeight = mFiltersTable->GetRowHeight(1); + Int16 tableHeight = frame.bottom - frame.top; + Int16 vResize = tableHeight % rowHeight; // Make integral of the table row height + if ( vResize != 0 ) { + CSaveWindowStatus::GetPaneGlobalBounds(this, &frame); + Int16 deltaV = rowHeight - vResize; + frame.bottom += deltaV; + mMinMaxSize.top += deltaV; + + CMediatedWindow::DoSetBounds(frame); // Don't call our DoSetBounds()! + } + + mFilterActionView->CalcPortFrameRect(frame); + mMinActionViewPortTop = frame.top; + mMinVSizeNoAction = mMinMaxSize.top; + mDiscreteVSizing = rowHeight; + } + + // initialize the foldertofilter popup control + FindUIItemPtr( this, paneID_FolderToFilterScopePopup, mFolderToFilerPopup ); + CMailFolderMixin::FolderChoices filePopupChoices + = static_cast(CMailFolderMixin::eWantNews + CMailFolderMixin::eWantDividers + + CMailFolderMixin::eWantInbox + CMailFolderMixin::eWantPublicFolder); + mFolderToFilerPopup->MSetFolderChoices( filePopupChoices ); + CMailFolderMixin::UpdateMailFolderMixinsNow( mFolderToFilerPopup ); + + // Load selected folder + LoadFolderToFilter( ); + + // Initialize the search manager + mSearchManager.AddListener(this); + mSearchManager.InitSearchManager(this, USearchHelper::FindWindowTabGroup(&mSubCommanders), + scopeMailFolder); + + + // Link to listeners + + mFiltersTable->AddListener(this); + mFilterDescriptor->AddListener(this); + USearchHelper::FindSearchPopup(this, paneID_Actions)->AddListener(this); + FindUIItemPtr(this, paneID_MatchAllRadio, mMatchAllRadio ); + FindUIItemPtr(this, paneID_MatchAnyRadio, mMatchAnyRadio ); + + mMailFoldersPopup->AddListener(this); + mMailPriorityPopup->AddListener(this); + + UReanimator::LinkListenerToControls(this, this, GetStatusResID()); + + // Call inherited method + FinishCreateWindow(); + +} + + +/*====================================================================================== + Draw the window. +======================================================================================*/ + +void CMailFiltersWindow::DrawSelf(void) { + + Rect frame; + + if ( mFiltersTable->CalcPortFrameRect(frame) && ::RectInRgn(&frame, mUpdateRgnH) ) { + { + StExcludeVisibleRgn excludeRgn(mFiltersTable); + CMediatedWindow::DrawSelf(); + } + + StColorPenState::Normalize(); + ::EraseRect(&frame); + } else { + CMediatedWindow::DrawSelf(); + } + + USearchHelper::RemoveSizeBoxFromVisRgn(this); +} + + +/*====================================================================================== + React to message broadcast by the controls. +======================================================================================*/ + +void CMailFiltersWindow::ListenToMessage(MessageT inMessage, void *ioParam) { + + switch ( inMessage ) { + case paneID_ServerSideFilterButton: + { + Assert_( mFolderToFilerPopup ); + CMessageFolder folder = mFolderToFilerPopup->MGetSelectedFolder(); + MSG_FolderInfo* folderInfo = (MSG_FolderInfo*)folder.GetFolderInfo(); + (void)MSG_GetAdminUrlForFolder(*mMailNewsContext, folderInfo, MSG_AdminServerSideFilters); + break; + } + + case paneID_FolderToFilterScopePopup: + LoadFolderToFilter(); + break; + + case CFiltersTableView::msg_FilterTableSelectionChanged: + FilterSelectionChanged(); + break; + + case paneID_New: + mFiltersTable->NewFilter(); + Boolean actionVisible = (mFilterActionDisclosure->GetValue() != 0); + if (!actionVisible) + { + mFilterActionDisclosure->SetValue(1); + ShowHideFilterAction(); + } + break; + + case paneID_Duplicate: + mFiltersTable->DuplicateFilter(); + break; + + case paneID_FiltersLog: + mFiltersTable->SetLogFilters((*((Int32 *) ioParam) != Button_Off)); + break; + + case paneID_DiscloseFilterAction: + ShowHideFilterAction(); + break; + + case CFiltersTableView::msg_DeleteCell: + mFiltersTable->DeleteFilter(); + break; + + case CFiltersTableView::msg_SaveModifications: + SaveCurrentFilterModifications(); + break; + + case CSearchEditField::msg_UserChangedText: + if ( (*((PaneIDT *) ioParam) == paneID_Description) && (mDescriptionTextChangedIndex < 0) ) { + // Positive value indicates user changed + mDescriptionTextChangedIndex = -mDescriptionTextChangedIndex; + } + break; + + case CSearchManager::msg_UserChangedSearchParameters: + if ( mFilterActionChangedIndex < 0 ) { + // Positive value indicates user changed + mFilterActionChangedIndex = -mFilterActionChangedIndex; + } + break; + + case paneID_Actions: + MessageActions((CSearchPopupMenu *) ioParam); + if ( mFilterActionChangedIndex < 0 ) { + // Positive value indicates user changed + mFilterActionChangedIndex = -mFilterActionChangedIndex; + } + break; + + case paneID_Folders: + case paneID_Priority: + if ( mFilterActionChangedIndex < 0 ) { + // Positive value indicates user changed + mFilterActionChangedIndex = -mFilterActionChangedIndex; + } + break; + + case CSearchManager::msg_SearchParametersResized: + UpdateToFilterLevels(*((Int16 *) ioParam)); + break; + + default: + // No inherited method + break; + } +} + + +/*====================================================================================== + Adjust the window to stored preferences. +======================================================================================*/ + +void CMailFiltersWindow::ReadWindowStatus(LStream *inStatusData) { + + if ( inStatusData != nil ) { + + Rect bounds; + *inStatusData >> bounds; + + Boolean actionVisible; + *inStatusData >> actionVisible; + + mFilterActionDisclosure->SetValue(actionVisible ? 1 : 0); + + CSaveWindowStatus::MoveWindowTo(this, topLeft(bounds)); + + TableIndexT lastSelectedRow; + CStr255 lastSelectedName; + + *inStatusData >> lastSelectedRow; + *inStatusData >> (StringPtr) lastSelectedName; +// Int32 booleanPopupValue; + + Boolean didSelectCell = false; + + // Try to select the last selected filter + if ( (lastSelectedRow != 0) && mFiltersTable->IsValidRow(lastSelectedRow) ) { + CStr255 name; + mFiltersTable->GetFilterNameAtRow(lastSelectedRow, &name); + if ( ::EqualString(name, lastSelectedName, true, true) ) { + mFiltersTable->SelectScrollCell(STableCell(lastSelectedRow, 1)); + didSelectCell = true; + } + } + + // Finally, try to display the last number of visible cells + + Int16 numVisibleCells; + *inStatusData >> numVisibleCells; + + SDimension16 frameSize; + mFiltersTable->GetFrameSize(frameSize); + + Int16 shouldBeHeight = mFiltersTable->GetRowHeight(1) * numVisibleCells; + + if (shouldBeHeight != frameSize.height) { + CSaveWindowStatus::GetPaneGlobalBounds(this, &bounds); + bounds.bottom += (shouldBeHeight - frameSize.height); + CSaveWindowStatus::VerifyWindowBounds(this, &bounds); + DoSetBounds(bounds); + } + + mFiltersTable->ReadSavedTableStatus(inStatusData); + + if ( didSelectCell ) return; + } else { + mFilterActionDisclosure->SetValue(0); // Hide action by default + CSaveWindowStatus::MoveWindowToAlertPosition(this); + } + + mSearchManager.SetNumVisibleLevels(1); + RecalcMinMaxStdSizes(); + FilterSelectionChanged(); +} + + +/*====================================================================================== + Get window stored preferences. +======================================================================================*/ + +void CMailFiltersWindow::WriteWindowStatus(LStream *outStatusData) { + + CSaveWindowStatus::WriteWindowStatus(outStatusData); + + TableIndexT lastSelectedRow = 0; + CStr255 lastSelectedName = CStr255::sEmptyString; + + // Try to store the last selected filter + STableCell selectedCell = mFiltersTable->GetFirstSelectedCell(); + if ( mFiltersTable->IsValidCell(selectedCell) ) { + lastSelectedRow = selectedCell.row; + mFiltersTable->GetFilterNameAtRow(lastSelectedRow, &lastSelectedName); + } + + *outStatusData << (Boolean) FilterActionIsVisible(); + *outStatusData << lastSelectedRow; + *outStatusData << (StringPtr) lastSelectedName; + + // Save number of visible rows in the table + + SDimension16 frameSize; + mFiltersTable->GetFrameSize(frameSize); + + Int16 numVisibleCells = frameSize.height / mFiltersTable->GetRowHeight(1); + *outStatusData << numVisibleCells; + + mFiltersTable->WriteSavedTableStatus(outStatusData); +} + + +/*====================================================================================== + Make resizing of the window integral to the row size of the filter table. +======================================================================================*/ + +void CMailFiltersWindow::DoSetBounds(const Rect &inBounds) { + + Rect newBounds = inBounds; + + USearchHelper::CalcDiscreteWindowResizing(this, &newBounds, 1, mDiscreteVSizing); + + CMediatedWindow::DoSetBounds(newBounds); +} + + +/*====================================================================================== + Recalculate the window's std sizes based on the current number of filters. +======================================================================================*/ + +void CMailFiltersWindow::RecalcMinMaxStdSizes(void) { + + Rect windBounds, viewBounds; + CalcPortFrameRect(windBounds); + mStandardSize.width = windBounds.right - windBounds.left; + + if ( FilterActionIsVisible() ) { + mFilterActionView->CalcPortFrameRect(viewBounds); + Assert_(viewBounds.top >= mMinActionViewPortTop); + mMinMaxSize.top = windBounds.bottom - (viewBounds.top - mMinActionViewPortTop); + } else { + mMinMaxSize.top = mMinVSizeNoAction; + } + + mFiltersTable->CalcPortFrameRect(viewBounds); + + TableIndexT rows, cols; + mFiltersTable->GetTableSize(rows, cols); + + Int32 deltaVSize = (rows * (Int32) mFiltersTable->GetRowHeight(1)) - + (Int32) (viewBounds.bottom - viewBounds.top); + + Int32 newHeight = (Int32) (windBounds.bottom - windBounds.top) + deltaVSize; + if ( newHeight > mMinMaxSize.bottom ) { + newHeight = mMinMaxSize.bottom; + } else if ( newHeight < mMinMaxSize.top ) { + newHeight = mMinMaxSize.top; + } + mStandardSize.height = newHeight; +} + + +/*====================================================================================== + The selection of a filter changed in the table. +======================================================================================*/ + +void CMailFiltersWindow::FilterSelectionChanged(void) { + + SaveCurrentFilterModifications(); + + STableCell selectedCell = mFiltersTable->GetFirstSelectedCell(); + Boolean filtersAreSelected = mFiltersTable->IsValidCell(selectedCell); + + // We don't really have a way to display JavaScript filters right now + MSG_FilterType filterType; + + Boolean enableDuplicateButton = filtersAreSelected; + Boolean enableFilterDescription = filtersAreSelected; + Boolean enableFilterActionView = filtersAreSelected; + + if (filtersAreSelected) { + MSG_Filter *selectedFilter; + selectedFilter = mFiltersTable->GetFilterAtRow(selectedCell.row); + MSG_GetFilterType(selectedFilter, &filterType); + if (filterType == filterInboxJavaScript || filterType == filterNewsJavaScript) { + enableDuplicateButton = false; + enableFilterActionView = false; + } + } + + USearchHelper::EnableDisablePane(mDuplicateButton, enableDuplicateButton); + USearchHelper::EnableDisablePane(mFilterDescriptor->GetSuperView(), enableFilterDescription ); + USearchHelper::EnableDisablePane(mFilterActionView, enableFilterActionView); + + Boolean enableAndOrRadios = enableFilterActionView && (mSearchManager.GetNumVisibleLevels() > 1); + USearchHelper::EnableDisablePane(mMatchAllRadio, enableAndOrRadios); + USearchHelper::EnableDisablePane(mMatchAnyRadio, enableAndOrRadios); + // If the filter text changed, update the BE + + CStr255 filterDescriptor; + + // Update the displayed text + if ( filtersAreSelected ) { + mFiltersTable->GetFilterDescription(selectedCell.row, &filterDescriptor); + mDescriptionTextChangedIndex = -selectedCell.row; + } else { + filterDescriptor[0] = 0; + } + + mFilterDescriptor->SetDescriptor(filterDescriptor); + + UpdateActionToFilter(); + + if ( IsVisible() ) UpdatePort(); // For immediate action +} + + +/*====================================================================================== + Setup the dialog so that the specified number of search levels are visible. +======================================================================================*/ + +void CMailFiltersWindow::UpdateToFilterLevels(Int16 inDeltaV) { + + if ( (inDeltaV == 0) ) return; + + // Disable bottom binding of necessary panes + + SBooleanRect topFrameBinding, actionBinding, disclosureBinding; + EnableDisableFrameBindings(false, &topFrameBinding, &actionBinding, &disclosureBinding); + + // Resize window + + mFilterActionView->ResizeFrameBy(0, inDeltaV, true); + + if ( FilterActionIsVisible() ) { + Rect bounds; + CSaveWindowStatus::GetPaneGlobalBounds(this, &bounds); + bounds.bottom += inDeltaV; + CMediatedWindow::DoSetBounds(bounds); // Update window size + RecalcMinMaxStdSizes(); + } + + // Reenable bottom binding of top panes + + EnableDisableFrameBindings(true, &topFrameBinding, &actionBinding, &disclosureBinding); +} + + +/*====================================================================================== + Show or hide the filter action depending upon its current state. +======================================================================================*/ + +void CMailFiltersWindow::ShowHideFilterAction(void) { + + Boolean actionVisible = (mFilterActionDisclosure->GetValue() != 0); + LView *paramEnclosure = USearchHelper::FindViewSubview(mFilterActionView, CSearchManager::paneID_ParamEncl); + + if ( actionVisible ) { + StValueChanger change(*((Int32 *) &mFilterActionDisclosure), nil); + UpdateActionToFilter(true); + } + + Rect windowBounds, disclosureRect; + CSaveWindowStatus::GetPaneGlobalBounds(this, &windowBounds); + CSaveWindowStatus::GetPaneGlobalBounds(mFilterActionDisclosure, &disclosureRect); + + // Disable bottom binding of necessary panes + + SBooleanRect topFrameBinding, actionBinding, disclosureBinding; + EnableDisableFrameBindings(false, &topFrameBinding, &actionBinding, &disclosureBinding); + + // Resize window + + if ( actionVisible ) { + Rect enclosureBounds; + CSaveWindowStatus::GetPaneGlobalBounds(mFilterActionView, &enclosureBounds); + Int16 newBottom = enclosureBounds.bottom /*+ cFiltersVMargin*/; + windowBounds.bottom = newBottom; + CMediatedWindow::DoSetBounds(windowBounds); + paramEnclosure->Show(); + ((LGABox *) mFilterActionView)->SetBorderStyle(borderStyleGA_BezelBorder); + } else { + Int16 newBottom = disclosureRect.bottom + cDisclosureVMargin; + windowBounds.bottom = newBottom; + CMediatedWindow::DoSetBounds(windowBounds); + paramEnclosure->Hide(); + ((LGABox *) mFilterActionView)->SetBorderStyle(borderStyleGA_NoBorder); + } + + // Reenable bottom binding of top panes + + EnableDisableFrameBindings(true, &topFrameBinding, &actionBinding, &disclosureBinding); + + RecalcMinMaxStdSizes(); +} + +//----------------------------------- +void CMailFiltersWindow::UpdateActionToFilter(Boolean inEvenIfNotVisible) +//----------------------------------- +{ + + mFilterActionChangedIndex = 0; + + if ( !FilterActionIsVisible() && !inEvenIfNotVisible ) return; + + STableCell selectedCell = mFiltersTable->GetFirstSelectedCell(); + if ( !mFiltersTable->IsValidCell(selectedCell) ) return; + + // don't try and do anything with JavaScript filters because we can't + MSG_Filter *selectedFilter; + selectedFilter = mFiltersTable->GetFilterAtRow(selectedCell.row); + MSG_FilterType filterType; + MSG_GetFilterType(selectedFilter, &filterType); + if (filterType == filterInboxJavaScript || filterType == filterNewsJavaScript) { + mSearchManager.SetNumVisibleLevels(1); + LEditField *txtField = dynamic_cast(FindPaneByID(CSearchManager::paneID_TextValue)); + if (txtField != NULL) + txtField->SetDescriptor("\p"); + + return; + } + + Try_ { + + StSearchDataBlock paramData; + Int16 numLevels; + MSG_RuleActionType action; + void *value; + + mFiltersTable->GetFilterActionAtRow(selectedCell.row, ¶mData, &numLevels, &action, &value); + + // Update to filter levels + mSearchManager.SetSearchParameters(numLevels, paramData.GetData()); + + // Update to filter action + USearchHelper::FindSearchPopup(this, paneID_Actions)->SetCurrentItemByCommand(action); + + SearchLevelParamT* currentData = paramData.GetData(); + (currentData->boolOp ? mMatchAllRadio : mMatchAnyRadio)->SetValue(Button_On); + + mDescriptionTextChangedIndex = selectedCell.row; + + switch ( action ) + { + case acMoveToFolder: + // Set the folder name + value = GetEscapedTempString((char*)value); + if ( !mMailFoldersPopup->MSetSelectedFolderName((const char*) value) ) + mFilterActionChangedIndex = selectedCell.row; + break; + case acChangePriority: + // Get the priority + AssertFail_(mMailPriorityPopup->IsLatentVisible()); + mMailPriorityPopup->SetCurrentItemByCommand(*((MSG_PRIORITY *) &value)); + break; + case acDelete: + case acMarkRead: + case acKillThread: + case acWatchThread: + break; + default: + AssertFail_(false); // Should never get here! + break; + } + } + Catch_(inErr) + { + USearchHelper::GenericExceptionAlert(inErr); + // Don't throw here! Should really never get an error! + } + EndCatch_ + + if ( mFilterActionChangedIndex != 0 || mDescriptionTextChangedIndex != 0) SaveCurrentFilterModifications(); + mFilterActionChangedIndex = -selectedCell.row; + mDescriptionTextChangedIndex = -selectedCell.row; +} + +//----------------------------------- +void CMailFiltersWindow::UpdateFilterToAction(Int32 inRow) +//----------------------------------- +{ + // don't try and do anything with JavaScript filters because we can't + MSG_Filter *selectedFilter; + selectedFilter = mFiltersTable->GetFilterAtRow(inRow); + MSG_FilterType filterType; + MSG_GetFilterType(selectedFilter, &filterType); + if (filterType == filterInboxJavaScript || filterType == filterNewsJavaScript) + return; + + Try_ { + // Get rules + + Int16 numVisLevels = mSearchManager.GetNumVisibleLevels(); + AssertFail_(numVisLevels > 0); // Make sure we get something back! + + StSearchDataBlock ruleData(numVisLevels, StSearchDataBlock::eAllocateStrings); + mSearchManager.GetSearchParameters(ruleData.GetData()); + + MSG_RuleActionType action = (MSG_RuleActionType) + USearchHelper::FindSearchPopup(this, paneID_Actions)->GetCurrentItemCommandNum(); + + void *value = nil; + MSG_PRIORITY priority; + switch ( action ) + { + case acMoveToFolder: + // Get the folder name + value = (void *) mMailFoldersPopup->MGetSelectedFolderName(); + AssertFail_(value != nil); + value = GetEscapedTempString((char*)value); + break; + + case acChangePriority: + // Get the priority + Assert_(mMailPriorityPopup->IsLatentVisible()); + priority = (MSG_PRIORITY) mMailPriorityPopup->GetCurrentItemCommandNum(); + value = (void *) priority; + break; + + case acDelete: + case acMarkRead: + case acKillThread: + case acWatchThread: + break; + + default: + AssertFail_(false); // Should never get here! + break; + } + + mFiltersTable->SetFilterActionAtRow(inRow, &ruleData, numVisLevels, action, value); + + } + Catch_(inErr) + { + USearchHelper::GenericExceptionAlert(inErr); + // Don't throw here! Should really never get an error! + } + EndCatch_ +} + +//----------------------------------- +char * CMailFiltersWindow::GetEscapedTempString(char * inStr) +//----------------------------------- +{ + // Make sure that the name is fully escaped + static char escapedName[256]; + XP_STRCPY(escapedName, inStr); + NET_UnEscape(escapedName); + char * temp = NET_Escape(escapedName, URL_PATH); + if (temp) + { + XP_STRCPY(escapedName, temp); + XP_FREE(temp); + } + return escapedName; +} + + +/*====================================================================================== + The specified filter has changed. Save the filter list to disk. If inRow is 0, + the entire list has changed, so save it all. +======================================================================================*/ + +void CMailFiltersWindow::SaveCurrentFilterModifications(void) { + + if ( ((mDescriptionTextChangedIndex > 0) || (mFilterActionChangedIndex > 0)) ) { + + Try_ { + Int16 saveChangeIndex = 0; + + if ( (mDescriptionTextChangedIndex > 0) ) { + CStr255 filterDescriptor; + mFilterDescriptor->GetDescriptor(filterDescriptor); + mFiltersTable->SetFilterDescription(mDescriptionTextChangedIndex, &filterDescriptor); + saveChangeIndex = mDescriptionTextChangedIndex; + } + + if ( (mFilterActionChangedIndex > 0) ) { + UpdateFilterToAction(mFilterActionChangedIndex); + Assert_(saveChangeIndex ? (saveChangeIndex == mFilterActionChangedIndex) : true); + saveChangeIndex = mFilterActionChangedIndex; + } + + mFiltersTable->SaveChangedFilterList(saveChangeIndex); + } + Catch_(inErr) { + // Do nothing, we should never really get an error here + USearchHelper::GenericExceptionAlert(inErr); + } + EndCatch_ + + mDescriptionTextChangedIndex = 0; + mFilterActionChangedIndex = 0; + } +} + + +/*====================================================================================== + Enable or disable frame bindings in preparation for changing the window size. +======================================================================================*/ + +void CMailFiltersWindow::EnableDisableFrameBindings(Boolean inEnable, SBooleanRect *ioTopFrameBinding, + SBooleanRect *ioActionBinding, SBooleanRect *ioDisclosureBinding) { + + LView *topFrameEnclosure = USearchHelper::FindViewSubview(this, paneID_TopFrameEnclosure); + LView *disclosureSuper = mFilterActionDisclosure->GetSuperView(); + + if ( !inEnable ) { + topFrameEnclosure->GetFrameBinding(*ioTopFrameBinding); + mFilterActionView->GetFrameBinding(*ioActionBinding); + disclosureSuper->GetFrameBinding(*ioDisclosureBinding); + + ioTopFrameBinding->bottom = ioActionBinding->bottom = ioDisclosureBinding->bottom = false; + } else { + ioTopFrameBinding->bottom = ioActionBinding->bottom = ioDisclosureBinding->bottom = true; + } + + topFrameEnclosure->SetFrameBinding(*ioTopFrameBinding); + mFilterActionView->SetFrameBinding(*ioActionBinding); + disclosureSuper->SetFrameBinding(*ioDisclosureBinding); +} + + +/*====================================================================================== + Populate the actions menu. +======================================================================================*/ + +static const Int16 cMaxNumActionItems = 8; + +void CMailFiltersWindow::PopulateActionsMenu(void) { + + CSearchPopupMenu *theMenu = USearchHelper::FindSearchPopup(this, paneID_Actions); + + Assert_(theMenu->GetNumCommands() == 6); + theMenu->SetIndexCommand(1, acMoveToFolder); + theMenu->SetIndexCommand(2, acChangePriority); + theMenu->SetIndexCommand(3, acDelete); + theMenu->SetIndexCommand(4, acMarkRead); + theMenu->SetIndexCommand(5, acKillThread); + theMenu->SetIndexCommand(6, acWatchThread); + +#if 0 // BE strings are lame! + theMenu->ClearMenu(); + + StPointerBlock menuData(sizeof(MSG_RuleMenuItem) * cMaxNumActionItems); + + MSG_RuleMenuItem *menuItems = (MSG_RuleMenuItem *) menuData.mPtr; + UInt16 numMenuItems = cMaxNumActionItems; + + FailFiltersError(MSG_GetRuleActionMenuItems(mRuleType, menuItems, + &numMenuItems)); + AssertFail_(numMenuItems > 0); + + MSG_RuleMenuItem *endItem = menuItems + numMenuItems; + + do { + theMenu->AppendMenuItemCommand(menuItems->attrib, C2PStr(menuItems->name), true); + } while ( ++menuItems < endItem ); +#endif // 0 +} + + +/*====================================================================================== + React to actions message. +======================================================================================*/ + +void CMailFiltersWindow::MessageActions(CSearchPopupMenu *inActionsMenu) { + + MSG_RuleActionType action = (MSG_RuleActionType) inActionsMenu->GetCurrentItemCommandNum(); + + Boolean showPriorities = false, showFolders = false; + + if ( action == acMoveToFolder ) { + showFolders = true; + } else if ( action == acChangePriority ) { + showPriorities = true; + } + + USearchHelper::ShowHidePane(mMailPriorityPopup, showPriorities); + USearchHelper::ShowHidePane(mMailFoldersPopup, showFolders); +} + +void CMailFiltersWindow::LoadFolderToFilter( ) +{ + Assert_( mFolderToFilerPopup ); + CMessageFolder folder = mFolderToFilerPopup->MGetSelectedFolder(); + MSG_FolderInfo* folderInfo = (MSG_FolderInfo*)folder.GetFolderInfo(); + MSG_FilterType type = filterInbox; + mRuleType = filterInboxRule; + if ( folder.IsNewsgroup() ) + { + type = filterNews; + mRuleType = filterNewsRule; + } + + mFiltersTable->SetRuleType( mRuleType ); + PopulateActionsMenu(); + mSearchManager.PopulatePriorityMenu(mMailPriorityPopup); + + mFiltersTable->LoadFiltersList( folderInfo, type ); + + LGACheckbox *logBox; + FindUIItemPtr(this, paneID_FiltersLog, logBox); +// USearchHelper::FindViewControl(this, paneID_FiltersLog) + logBox->SetValue( mFiltersTable->GetLogFiltersEnabled() ? 1 : 0); + // Does the server support server side filters? + XP_Bool enable = false; + + enable = MSG_HaveAdminUrlForFolder( folderInfo, MSG_AdminServerSideFilters ); + LControl* serverFilterButton = dynamic_cast(FindPaneByID( CMailFiltersWindow::paneID_ServerSideFilterButton )); + if( serverFilterButton) + USearchHelper::EnableDisablePane( serverFilterButton ,enable, true ); +} + + +#pragma mark - +/*====================================================================================== + Update any current status info. +======================================================================================*/ + +CFiltersTableView::~CFiltersTableView(void) { + + if ( IsVisible() ) { + UpdateFilterNameChanged(); + } + if ( mFilterList != nil ) { + MSG_CancelFilterList(mFilterList); + mFilterList = nil; + } +} + + +/*====================================================================================== + Finish initializing the object. +======================================================================================*/ + +void CFiltersTableView::FinishCreateSelf(void) { + +// long isDoubleByte; + + CStandardFlexTable::FinishCreateSelf(); + + mEditFilterName = (CInlineEditField *) USearchHelper::FindViewEditField(this, paneID_EditTitle); +#if 0 + // See CInlineEditField.cp for explanation of double byte wierdness + isDoubleByte = ::GetScriptManagerVariable(smDoubleByte); + mEditFilterName->SetGrowableBorder(!isDoubleByte); +#endif + mEditFilterName->AddListener(this); + +} + + +/*====================================================================================== + Obey commands. +======================================================================================*/ + +Boolean CFiltersTableView::ObeyCommand(CommandT inCommand, void *ioParam) { + + Boolean cmdHandled = true; + + switch (inCommand) { + + case msg_TabSelect: + break; + + default: + cmdHandled = CStandardFlexTable::ObeyCommand(inCommand, ioParam); + break; + } + + return cmdHandled; +} + + +/*====================================================================================== + React to message broadcast by the controls. +======================================================================================*/ + +void CFiltersTableView::ListenToMessage(MessageT inMessage, void *ioParam) { + + switch ( inMessage ) { + + case CInlineEditField::msg_InlineEditFieldChanged: + if ( (*((PaneIDT *) ioParam) == paneID_EditTitle) && (mEditFilterNameRow < 0) ) { + mEditFilterNameRow = -mEditFilterNameRow; // Text has changed + } + break; + + case CInlineEditField::msg_HidingInlineEditField: + if ( (*((PaneIDT *) ioParam) == paneID_EditTitle) ) { + UpdateFilterNameChanged(); + mEditFilterNameRow = 0; + } + break; + + default: + //Inherited::ListenToMessage(inMessage, ioParam); + break; + } +} + + +/*====================================================================================== + Load the filters list and initialize the table accordingly. +======================================================================================*/ + +void CFiltersTableView::LoadFiltersList(MSG_FolderInfo* folder, MSG_FilterType type ) +{ + Assert_( folder ); + if( folder == NULL ) + return; + // If there is a filterlist save it before loading in the new list + if ( mFilterList ) + SaveChangedFilterList(); + // Open the filters list + //FailFiltersError(MSG_OpenFilterList(CMailNewsContext::GetMailMaster(), filterInbox, + // &mFilterList)); + + + FailFiltersError( MSG_OpenFolderFilterListFromMaster(CMailNewsContext::GetMailMaster(), folder, type, &mFilterList) ); + + // Initialize the filters table with the new data + Int32 numFilters = GetBENumRows(); + RemoveAllRows(true); + Assert_(mRows == 0); + InsertRows(numFilters, 0, nil, 0, true); + +} + + +/*====================================================================================== + The specified filter has changed. Save the filter list to disk. If inRow is 0, + the entire list has changed, so save it all. +======================================================================================*/ + +void CFiltersTableView::SaveChangedFilterList(Int32 /*inRow*/) { + + // This is a temp fix waiting for a solution! + + Try_ { + AssertFail_(mFilterList != nil); + +#ifdef USE_NEW_FILTERS_SAVE + FailFiltersError(MSG_SaveFilterList(mFilterList)); +#else // USE_NEW_FILTERS_SAVE + // Close filter list to save it + FailFiltersError(MSG_CloseFilterList(mFilterList)); + mFilterList = nil; + // Open the filters list again + FailFiltersError(MSG_OpenFilterList(CMailNewsContext::GetMailMaster(), filterInbox, + &mFilterList)); +#endif // USE_NEW_FILTERS_SAVE + + } + Catch_(inErr) { + // This is obviously a major error! Just close and delete the window in this case + USearchHelper::GenericExceptionAlert(inErr); + } + EndCatch_ + + if ( mFilterList == nil ) { + USearchHelper::GetPaneWindow(this)->DoClose(); // Bad, bad, bad boy! Will change later + } +} + + +/*====================================================================================== + Get the filter action data for the specified row. Caller must delete memory using + ::DisposePtr(). +======================================================================================*/ + +void CFiltersTableView::GetFilterActionAtRow( + TableIndexT inRow, StSearchDataBlock *outData, Int16 *outNumLevels, + MSG_RuleActionType *outAction, void **outValue) +{ + MSG_Filter *filter = GetFilterAtRow(inRow); + + MSG_Rule *rule = nil; + FailFiltersError(MSG_GetFilterRule(filter, &rule)); + AssertFail_(rule != nil); + + { + Int32 tempNumLevels; + FailFiltersError(MSG_RuleGetNumTerms(rule, &tempNumLevels)); + *outNumLevels = tempNumLevels; + } + AssertFail_(*outNumLevels > 0); + + outData->Allocate(*outNumLevels, StSearchDataBlock::eDontAllocateStrings); + SearchLevelParamT *curLevel = outData->GetData(); + Int16 index = 0; + + do { + FailFiltersError(MSG_RuleGetTerm(rule, index, &curLevel->val.attribute, + &curLevel->op, &curLevel->val, &curLevel->boolOp, nil )); + ++curLevel; + } while ( ++index < *outNumLevels ); + + FailFiltersError(MSG_RuleGetAction(rule, outAction, outValue)); +} + + +/*====================================================================================== + Update the filter to the user specified action. +======================================================================================*/ + +void CFiltersTableView::SetFilterActionAtRow(TableIndexT inRow, StSearchDataBlock *inData, + Int16 inNumLevels, MSG_RuleActionType inAction, + void *inValue) { + + MSG_Filter *newFilter = nil; + + Try_ { + + // Contruct a new filter (this is the only way! :-( ) + + MSG_Filter *filter = GetFilterAtRow(inRow); + + char *text; + FailFiltersError(MSG_GetFilterName(filter, &text)); + + FailFiltersError(MSG_CreateFilter(mRuleType, text, &newFilter)); + + FailFiltersError(MSG_GetFilterDesc(filter, &text)); + FailFiltersError(MSG_SetFilterDesc(newFilter, text)); + + XP_Bool isEnabled; + FailFiltersError(MSG_IsFilterEnabled(filter, &isEnabled)); + FailFiltersError(MSG_EnableFilter(newFilter, isEnabled)); + + MSG_Rule *newRule; + FailFiltersError(MSG_GetFilterRule(newFilter, &newRule)); + + // Add rules to filter + + SearchLevelParamT *curLevel = inData->GetData(), *endLevel = curLevel + inNumLevels; + + do { + char *customHeader = nil; + + if( curLevel->val.attribute == attribOtherHeader ) + { + CMailFiltersWindow *filterWindow = dynamic_cast( USearchHelper::GetPaneWindow(this) ); + customHeader = ( filterWindow->GetSearchManager() ).GetSelectedCustomHeader( curLevel ); + } + + FailFiltersError(MSG_RuleAddTerm(newRule, curLevel->val.attribute, + curLevel->op, &curLevel->val, curLevel->boolOp, customHeader )); + } while ( ++curLevel < endLevel ); + + // Add filter action + + FailFiltersError(MSG_RuleSetAction(newRule, inAction, inValue)); + + Int32 filterIndex = TABLE_TO_FILTER_INDEX(inRow); + + Int32 numFilters = GetBENumRows(); + + // Add the new filter to the end of the list (API doesn't let us do it any other way!) + FailFiltersError(MSG_SetFilterAt(mFilterList, numFilters, newFilter)); + newFilter = nil; + + // Remove and delete the old edit filter from the list + BEDeleteFilter(filterIndex, mFilterList); + --numFilters; + + // Move new filter into the correct position + if ( filterIndex != numFilters ) { + BEMoveFilter(numFilters, filterIndex, mFilterList); + } + } + Catch_(inErr) { + if ( newFilter != nil ) { + MSG_FilterError error = MSG_DestroyFilter(newFilter); + newFilter = nil; + Assert_(error == FilterError_Success); + } + + Throw_(inErr); + } + EndCatch_ +} + + +/*====================================================================================== + Create a new filter by bringing up a dialog for user interaction. +======================================================================================*/ + +void CFiltersTableView::NewFilter(void) { + + STableCell selectedCell = GetFirstSelectedCell(); + if ( !IsValidCell(selectedCell) ) { + // Add new filter to end of table if no cells selected + GetTableSize(selectedCell.row, selectedCell.col); + } + ++selectedCell.row; + + // Create a new default filter + + MSG_Filter *newFilter = nil; + PostAction(nil); + + Try_ { + AssertFail_(mFilterList != nil); + + Int32 numFilters = GetBENumRows(); + + // Contruct a new filter + + CStr255 macString; + USearchHelper::AssignUString(uStr_FilterDefaultName, macString); + FailFiltersError(MSG_CreateFilter( mRuleType, macString, &newFilter)); + + USearchHelper::AssignUString(uStr_FilterDefaultDescription, macString); + FailFiltersError(MSG_SetFilterDesc(newFilter, macString)); + + XP_Bool isEnabled = 1; + FailFiltersError(MSG_EnableFilter(newFilter, isEnabled)); + + MSG_Rule *rule = nil; + FailFiltersError(MSG_GetFilterRule(newFilter, &rule)); + AssertFail_(rule != nil); + + USearchHelper::AssignUString(uStr_FilterDefaultName, macString); + MSG_SearchValue value; + value.attribute = attribSender; + value.u.string = ""; + XP_Bool boolOp = true; + FailFiltersError(MSG_RuleAddTerm(rule, attribSender, opContains, &value, boolOp, nil )); + + // Add filter action + + macString[0] = 0; + FailFiltersError(MSG_RuleSetAction(rule, acMoveToFolder, (char*)macString)); + + // Add the new filter to the end of the list (API doesn't let us do it any other way!) + FailFiltersError(MSG_SetFilterAt(mFilterList, numFilters, newFilter)); + newFilter = nil; + + // Move new filter into the correct position + if ( TABLE_TO_FILTER_INDEX(selectedCell.row) != numFilters ) { + BEMoveFilter(numFilters, TABLE_TO_FILTER_INDEX(selectedCell.row), mFilterList); + } + + SaveChangedFilterList(selectedCell.row); + + InsertRows(1, selectedCell.row - 1, nil, 0, true); + ScrollCellIntoFrame(selectedCell); + SelectEditFilterName(selectedCell.row); + ((CMailFiltersWindow *) USearchHelper::GetPaneWindow(this))->RecalcMinMaxStdSizes(); + } + Catch_(inErr) { + if ( newFilter != nil ) { + MSG_FilterError error = MSG_DestroyFilter(newFilter); + newFilter = nil; + Assert_(error == FilterError_Success); + } + Refresh(); + Throw_(inErr); + } + EndCatch_ +} + + +/*====================================================================================== + Duplicate selected filters. +======================================================================================*/ + +void CFiltersTableView::DuplicateFilter(void) { + SaveChangedFilterList(); + STableCell selectedCell = GetFirstSelectedCell(); + AssertFail_(IsValidCell(selectedCell)); + + AssertFail_(mFilterList != nil); + + MSG_Filter *newFilter = nil; + PostAction(nil); + + Try_ { + + Int32 newIndex = TABLE_TO_FILTER_INDEX(selectedCell.row); + + MSG_Filter *selectedFilter; + FailFiltersError(MSG_GetFilterAt(mFilterList, newIndex, &selectedFilter)); + AssertFail_(selectedFilter != nil); + + char *text; + FailFiltersError(MSG_GetFilterName(selectedFilter, &text)); + MSG_FilterType filterType; + FailFiltersError(MSG_GetFilterType(selectedFilter, &filterType)); + + // Contruct a new filter + FailFiltersError(MSG_CreateFilter(filterType, text, &newFilter)); + + FailFiltersError(MSG_GetFilterDesc(selectedFilter, &text)); + FailFiltersError(MSG_SetFilterDesc(newFilter, text)); + + XP_Bool isEnabled; + FailFiltersError(MSG_IsFilterEnabled(selectedFilter, &isEnabled)); + FailFiltersError(MSG_EnableFilter(newFilter, isEnabled)); + + MSG_Rule *selectedRule, *newRule; + FailFiltersError(MSG_GetFilterRule(selectedFilter, &selectedRule)); + FailFiltersError(MSG_GetFilterRule(newFilter, &newRule)); + + Int32 numTerms; + FailFiltersError(MSG_RuleGetNumTerms(selectedRule, &numTerms)); + + { + MSG_SearchAttribute attrib; + MSG_SearchOperator op; + MSG_SearchValue value; + XP_Bool boolOp; + SearchTextValueT searchText; + value.u.string = searchText.text; + + for (Int32 curTerm = 0; curTerm < numTerms; ++curTerm) { + + FailFiltersError(MSG_RuleGetTerm(selectedRule, curTerm, &attrib, + &op, &value, &boolOp, nil )); + FailFiltersError(MSG_RuleAddTerm(newRule, attrib, op, &value, boolOp, nil )); + } + } + + MSG_RuleActionType actionType; + void *actionValue; + FailFiltersError(MSG_RuleGetAction(selectedRule, &actionType, &actionValue)); + FailFiltersError(MSG_RuleSetAction(newRule, actionType, actionValue)); + + Int32 numFilters = GetBENumRows(); + + // Add the new filter to the end of the list (API doesn't let us do it any other way!) + FailFiltersError(MSG_SetFilterAt(mFilterList, numFilters, newFilter)); + newFilter = nil; + + // Move new filter into the correct position + ++newIndex; + if ( newIndex != numFilters ) BEMoveFilter(numFilters, newIndex, mFilterList); + + SaveChangedFilterList(++selectedCell.row); + InsertRows(1, selectedCell.row - 1, nil, 0, true); + ScrollCellIntoFrame(selectedCell); + SelectEditFilterName(selectedCell.row); + ((CMailFiltersWindow *) USearchHelper::GetPaneWindow(this))->RecalcMinMaxStdSizes(); + } + Catch_(inErr) { + if ( newFilter != nil ) { + MSG_FilterError error = MSG_DestroyFilter(newFilter); + newFilter = nil; + Assert_(error == FilterError_Success); + } + Refresh(); + Throw_(inErr); + } + EndCatch_ +} + + +/*====================================================================================== + Delete selected filters. +======================================================================================*/ + +void CFiltersTableView::DeleteFilter(void) { + + STableCell selectedCell = GetFirstSelectedCell(); +// AssertFail_(IsValidCell(selectedCell)); // don't assert: list can be empty + if (IsValidCell(selectedCell)) + { + CDeleteFilterAction *action = new CDeleteFilterAction(this, selectedCell.row); + FailNIL_(action); + + // Update our table + if ( (selectedCell.row == mRows) && (selectedCell.row > 1) ) { + --selectedCell.row; // Move up one cell + } + Boolean validNewCell = selectedCell.row != 0; + { + // Turn off listening if we have a valid cell so that we are not + // updated during an empty table selection + StValueChanger change(mIsListening, !validNewCell); + PostAction(action); + } + if ( validNewCell ) SelectScrollCell(selectedCell); + ((CMailFiltersWindow *) USearchHelper::GetPaneWindow(this))->RecalcMinMaxStdSizes(); + } +} + + +/*====================================================================================== + Insert the specified filter into the table. +======================================================================================*/ + +void CFiltersTableView::InsertFilterAt(TableIndexT inTableFilterRow, MSG_Filter *inFilter) { + + Int32 numFilters = GetBENumRows(); + + // Add the new filter to the end of the list (API doesn't let us do it any other way!) + FailFiltersError(MSG_SetFilterAt(mFilterList, numFilters, inFilter)); + + // Move new filter into the correct position + if ( TABLE_TO_FILTER_INDEX(inTableFilterRow) != numFilters ) { + BEMoveFilter(numFilters, TABLE_TO_FILTER_INDEX(inTableFilterRow), mFilterList); + } + SaveChangedFilterList(inTableFilterRow); + + InsertRows(1, inTableFilterRow - 1, nil, 0, true); + SelectScrollCell(STableCell(inTableFilterRow, 1)); +} + + +/*====================================================================================== + Insert the specified filter into the table. +======================================================================================*/ + +void CFiltersTableView::RemoveFilterAt(TableIndexT inTableFilterRow, MSG_Filter **outFilter) { + + MSG_Filter *filter = GetFilterAtRow(inTableFilterRow); + FailFiltersError(MSG_RemoveFilterAt(mFilterList, TABLE_TO_FILTER_INDEX(inTableFilterRow))); + SaveChangedFilterList(inTableFilterRow); + + RemoveRows(1, inTableFilterRow, true); + *outFilter = filter; +} + + +/*====================================================================================== + Move the row given by inCurrRow to the row given by inNewRow. If the row is selected, + it remains selected. +======================================================================================*/ + +void CFiltersTableView::RemoveRows(Uint32 inHowMany, TableIndexT inFromRow, Boolean inRefresh) { + + STableCell selectedCell = GetFirstSelectedCell(); + Boolean cellWasSelected = IsValidCell(selectedCell); + + LTableView::RemoveRows(inHowMany, inFromRow, inRefresh); + + if ( cellWasSelected ) { + selectedCell = GetFirstSelectedCell(); + if ( !IsValidCell(selectedCell) ) { + // Selected cell was removed! + if ( mEditFilterName->IsVisible() || (mEditFilterNameRow > 0) ) { + mEditFilterName->Hide(); + mEditFilterNameRow = 0; + } + SelectionChanged(); + } + } +} + + +/*====================================================================================== + Move the row given by inCurrRow to the row given by inNewRow. If the row is selected, + it remains selected. +======================================================================================*/ + +void CFiltersTableView::MoveRow(TableIndexT inCurrRow, TableIndexT inNewRow, Boolean inRefresh) { + + Assert_(IsValidRow(inCurrRow) && IsValidRow(inNewRow)); + if ( inCurrRow == inNewRow ) return; + + STableCell currCell(inCurrRow, 1); + Boolean isSelected = CellIsSelected(currCell); + + // Save any current filter modifications, since they will become invalid after the filter is moved + + BroadcastMessage(msg_SaveModifications); + + // Call BE to move the filter + + AssertFail_(mFilterList != nil); + BEMoveFilter(TABLE_TO_FILTER_INDEX(inCurrRow), TABLE_TO_FILTER_INDEX(inNewRow), mFilterList); + SaveChangedFilterList(); + + // Update our table + if ( isSelected ) { + // Hide selections from displaying + StVisRgn emptyVisRgn(GetMacPort()); + StopBroadcasting(); // Don't broadcast that the selection has changed, because it really hasn't + SelectCell(STableCell(inNewRow, 1)); + StartBroadcasting(); + } + + if ( inRefresh ) { + RefreshRowRange(inCurrRow, inNewRow); + } +} + + +/*====================================================================================== + Adjusts the Image so that it fits within the Frame. Get rid of the flicker! +======================================================================================*/ + +void CFiltersTableView::ReconcileFrameAndImage(Boolean /*inRefresh*/) { + + SPoint32 oldScrollPos; + GetScrollPosition(oldScrollPos); + + LTableView::ReconcileFrameAndImage(false); + + SPoint32 newScrollPos; + GetScrollPosition(newScrollPos); + + if ( ((newScrollPos.v != oldScrollPos.v) || (newScrollPos.h != oldScrollPos.h)) ) { + Refresh(); + } +} + + +/*====================================================================================== + Setup the helpers for the table. +======================================================================================*/ + +void CFiltersTableView::SetUpTableHelpers(void) { + + // Add helpers + + LFlexTableGeometry *geometry = new LFlexTableGeometry(this, mTableHeader); + FailNIL_(geometry); + SetTableGeometry(geometry); + + //CFilterTableSelector *selector = new LTableRowSelector(this); + // FailNIL_(selector); + SetTableSelector(new LTableRowSelector(this, false)); + + CFilterTableKeyAttachment *ketAttachment = new CFilterTableKeyAttachment(this); + FailNIL_(ketAttachment); + AddAttachment(ketAttachment); +} + + +/*====================================================================================== + Draw the cell. +======================================================================================*/ + +void CFiltersTableView::DrawCellContents(const STableCell &inCell, const Rect &inLocalRect) +{ + + CStr255 displayText; + ResIDT iconID; + Int16 horizJustType; + GetDisplayData(inCell, &displayText, &horizJustType, &iconID); + + Rect cellDrawFrame; + + if ( iconID != 0 ) + { + ::SetRect(&cellDrawFrame, 0, 0, cSmallIconWidth, cSmallIconHeight); + UGraphicGizmos::CenterRectOnRect(cellDrawFrame, inLocalRect); + ::PlotIconID(&cellDrawFrame, ttNone, ttNone, iconID); + } else + { + cellDrawFrame = inLocalRect; + ::InsetRect(&cellDrawFrame, cColTextHMargin, 0); // For a nicer look between cells + if ( displayText.Length() > 0 ) + { + UGraphicGizmos::PlaceTextInRect((char *) &displayText[1], displayText[0], + cellDrawFrame, horizJustType, teCenter, + &mTextFontInfo, true, truncMiddle); + } + } +} + + +/*====================================================================================== + Adjust a cell's cursor. +======================================================================================*/ + +void CFiltersTableView::AdjustCursorSelf(Point inPortPt, const EventRecord &inMacEvent) { + + EMouseTableLocation location = GetMouseTableLocation(inPortPt); + + switch ( location ) { +// case eInEnabledCheck: +// TrySetCursor(cEnabledCheckCursorID); +// break; + case eInTitle: + TrySetCursor(iBeamCursor); + break; + case eInCellContent: + TrySetCursor(cHandOpenCursorID); + break; + default: + LTableView::AdjustCursorSelf(inPortPt, inMacEvent); + break; + } +} + + +/*====================================================================================== + Adjust selection in response to a click in the specified cell. +======================================================================================*/ + +Boolean CFiltersTableView::ClickSelect(const STableCell &inCell, const SMouseDownEvent &inMouseDown) { + + Rect localClickRect; + + EMouseTableLocation location = GetMouseTableLocation(inMouseDown.wherePort, &localClickRect); + + switch ( location ) { + case eInEnabledCheck: + ToggleFilterEnabled(inCell.row, &localClickRect); + return false; + case eInTitle: { + SMouseDownEvent mouseEvent = inMouseDown; + SwitchEditFilterName(inCell.row, &localClickRect, nil, &mouseEvent); + } + return false; + default: + LCommander::SwitchTarget(this); + break; + } + + return LTableView::ClickSelect(inCell, inMouseDown); +} + + +/*====================================================================================== + Click the cell. +======================================================================================*/ + +void CFiltersTableView::ClickCell(const STableCell &inCell, const SMouseDownEvent &inMouseDown) { + + if ( LPane::GetClickCount() == 2 ) { + + BroadcastMessage(msg_DoubleClickCell); + + } else if ( mRows > 1 ) { + + CTableRowDragger dragger(inCell.row); + + SPoint32 startMouseImagePt; + LocalToImagePoint(inMouseDown.whereLocal, startMouseImagePt); + + // Restrict dragging to a thin vertical column the height of the image + + LImageRect dragPinRect; + { + Int32 left, top, right, bottom; + GetImageCellBounds(inCell, left, top, right, bottom); + dragPinRect.Set(startMouseImagePt.h, startMouseImagePt.v - top, startMouseImagePt.h, + mImageSize.height - (bottom - startMouseImagePt.v) + 1); + } + + TrySetCursor(cHandClosedCursorID); + dragger.DoTrackMouse(this, &startMouseImagePt, &dragPinRect, 2, 2); + + TableIndexT newRow = dragger.GetDraggedRow(); + + if ( newRow ) { + CMoveFilterAction *action = new CMoveFilterAction(this, inCell.row, newRow); + FailNIL_(action); + PostAction(action); + } + } +} + + +/*====================================================================================== + Determine if the mouse location is in the enabled check box. +======================================================================================*/ + +CFiltersTableView::EMouseTableLocation CFiltersTableView::GetMouseTableLocation(Point inPortPt, + Rect *outLocationRect) { + + PortToLocalPoint(inPortPt); + SPoint32 imagePoint; + LocalToImagePoint(inPortPt, imagePoint); + STableCell hitCell; +// Rect localCellRect; + + EMouseTableLocation rtnVal = eInNone; + + if ( GetCellHitBy(imagePoint, hitCell) ) { + + const PaneIDT colType = GetCellDataType(hitCell); + + switch ( colType ) { + + case paneID_OrderCaption: + rtnVal = eInCellContent; + break; + + case paneID_NameCaption: { + Rect checkRect; + GetFilterTitleRect(hitCell.row, &checkRect); + if ( ::PtInRect(inPortPt, &checkRect) ) { + if ( outLocationRect != nil ) *outLocationRect = checkRect; + rtnVal = eInTitle; + } else { + rtnVal = eInCellContent; + } + } + break; + + case paneID_EnabledCaption: + if ( outLocationRect ) { + Rect localCellRect; + GetLocalCellRect(hitCell, localCellRect); + ::SetRect(outLocationRect, 0, 0, cSmallIconWidth, cSmallIconHeight); + UGraphicGizmos::CenterRectOnRect(*outLocationRect, localCellRect); + } + rtnVal = eInEnabledCheck; + break; + + default: + Assert_(false); // Should never get here! + break; + } + } + + return rtnVal; +} + + +/*====================================================================================== + Get the rect bounding the specified filter's title. +======================================================================================*/ + +void CFiltersTableView::GetFilterTitleRect(TableIndexT inRow, Rect *outTitleRect, + CStr255* /*outNameString*/) { + + STableCell filterCell(inRow, mTableHeader->ColumnFromID(paneID_NameCaption)); + + Assert_(IsValidCell(filterCell)); + + GetLocalCellRect(filterCell, *outTitleRect); + ::InsetRect(outTitleRect, cColTextHMargin, 0); + + GetSuperView()->EstablishPort(); + UTextTraits::SetPortTextTraits(mTextTraitsID); + + ::InsetRect(outTitleRect, -CInlineEditField::cEditBoxMargin, 0); +} + + +/*====================================================================================== + Switch the current filter name being edited. +======================================================================================*/ + +void CFiltersTableView::SwitchEditFilterName(TableIndexT inRow, const Rect *inNameLocalFrame, + CStr255 *inNameString, SMouseDownEvent *ioMouseDown) { + + UpdateFilterNameChanged(); + + if ( inRow != 0 ) { + mEditFilterNameRow = -inRow; + + SPoint32 imagePoint; + LocalToImagePoint(topLeft(*inNameLocalFrame), imagePoint); + imagePoint.h -= 1; imagePoint.v += 1; + + LCommander::SwitchTarget(mEditFilterName); + + StopBroadcasting(); // Don't broadcast that the selection has changed until we update the edit field + SelectCell(STableCell(inRow, 1)); + StartBroadcasting(); + + CStr255 nameString; + if ( inNameString == nil ) { + inNameString = &nameString; + GetFilterNameAtRow(inRow, inNameString); + } + + mEditFilterName->UpdateEdit(*inNameString, &imagePoint, ioMouseDown); + + SelectionChanged(); + } +} + + +/*====================================================================================== + If the filter name has changed, update the filter name in the filter list. +======================================================================================*/ + +void CFiltersTableView::UpdateFilterNameChanged(void) { + + if ( mEditFilterName->IsVisible() || mEditFilterNameRow > 0 ) { + // Filter is currently being edited + TableIndexT row = mEditFilterNameRow < 0 ? -mEditFilterNameRow : mEditFilterNameRow; + CStr255 name; + mEditFilterName->GetDescriptor(name); + SetFilterNameAtRow(row, &name); + SaveChangedFilterList(row); + } +} + + +/*====================================================================================== + Select the filter's name for editing. +======================================================================================*/ + +void CFiltersTableView::SelectEditFilterName(TableIndexT inRow) { + + Rect titleRect; + CStr255 nameString; + GetFilterTitleRect(inRow, &titleRect, &nameString); + SwitchEditFilterName(inRow, &titleRect, &nameString, nil); +} + + +/*====================================================================================== + Delete all selected items. +======================================================================================*/ + +void CFiltersTableView::DeleteSelection(void) { + + BroadcastMessage(msg_DeleteCell); +} + + +/*====================================================================================== + Get the display data for the specified cell. +======================================================================================*/ + +void CFiltersTableView::GetDisplayData(const STableCell &inCell, CStr255 *outDisplayText, + Int16 *outHorizJustType, ResIDT *outIconID) { + + const PaneIDT colType = GetCellDataType(inCell); + + *outDisplayText = CStr255::sEmptyString; + *outIconID = 0; + + switch ( colType ) { + + case paneID_OrderCaption: { + Str15 numString; + ::NumToString(inCell.row, numString); + *outDisplayText = numString; + *outHorizJustType = /*teFlushRight*/teCenter; + } + break; + + case paneID_NameCaption: { + if ( inCell.row != ABS_VAL(mEditFilterNameRow) ) { + GetFilterNameAtRow(inCell.row, outDisplayText); + *outHorizJustType = teFlushLeft; + } + } + break; + + case paneID_EnabledCaption: { + if ( GetFilterEnabledAtRow(inCell.row) ) { + *outIconID = kEnabledCheckIconID; + } else { + *outIconID = kDisabledCheckIconID; + } + } + break; + + default: + Assert_(false); // Should never get here! + break; + } +} + + +/*====================================================================================== + Toggle the enabled setting of the filter at the specified row. +======================================================================================*/ + +void CFiltersTableView::ToggleFilterEnabled(TableIndexT inRow, Rect *inLocalIconRect) { + + Boolean doEnable = !GetFilterEnabledAtRow(inRow); + SetFilterEnabledAtRow(inRow, doEnable); + + if ( doEnable ) { + if ( FocusExposed() ) { + ::PlotIconID(inLocalIconRect, ttNone, ttNone, kEnabledCheckIconID); + } + } else { + LocalToPortPoint(topLeft(*inLocalIconRect)); + LocalToPortPoint(botRight(*inLocalIconRect)); + InvalPortRect(inLocalIconRect); + UpdatePort(); + } + SaveChangedFilterList(inRow); +} + + +/*====================================================================================== + Set the enabled setting of the filter log. +======================================================================================*/ + +void CFiltersTableView::SetLogFilters(Boolean inDoLog) { + + Assert_(mFilterList != nil); + Boolean isEnabled = MSG_IsLoggingEnabled(mFilterList) ? true : false; + if ( isEnabled != inDoLog ) { + FailFiltersError(MSG_EnableLogging(mFilterList, inDoLog)); + SaveChangedFilterList(); + } +} + + +/*====================================================================================== + Remove the specified filter from the list and delete it. +======================================================================================*/ + +void CFiltersTableView::BEDeleteFilter(Int32 inFilterIndex, MSG_FilterList *ioFilterList) { + + // Remove and delete the old edit filter + MSG_Filter *filter = nil; + FailFiltersError(MSG_GetFilterAt(ioFilterList, inFilterIndex, &filter)); + AssertFail_(filter != nil); + FailFiltersError(MSG_RemoveFilterAt(ioFilterList, inFilterIndex)); + FailFiltersError(MSG_DestroyFilter(filter)); +} + + +/*====================================================================================== + Remove the specified filter from the list and delete it. +======================================================================================*/ + +void CFiltersTableView::BEMoveFilter(Int32 inFromIndex, Int32 inToIndex, MSG_FilterList *ioFilterList) { + + AssertFail_(inFromIndex != inToIndex); + + Int32 increment = (inFromIndex > inToIndex) ? -1 : 1; + MSG_FilterMotion motion = (increment < 0) ? filterUp : filterDown; + + for (Int32 curRow = inFromIndex; curRow != inToIndex; curRow += increment) { + FailFiltersError(MSG_MoveFilterAt(ioFilterList, curRow, motion)); + } +} + + +/*====================================================================================== + Raise a relevant exception if MSG_SearchError is not SearchError_Success. +======================================================================================*/ + +void CFiltersTableView::FailFiltersError(MSG_FilterError inError) { + + if ( inError == FilterError_Success ) return; + + switch ( inError ) { + + case FilterError_OutOfMemory: + Throw_(memFullErr); + break; + + case FilterError_NotImplemented: { +#ifdef Debug_Signal + CStr255 errorString; + USearchHelper::AssignUString(uStr_FilterFunctionNotImplemented, errorString); + ErrorManager::PlainAlert((StringPtr) errorString); +#endif // Debug_Signal + Throw_(unimpErr); + } + break; + + case FilterError_InvalidVersion: + case FilterError_InvalidIndex: + case FilterError_InvalidMotion: + case FilterError_InvalidFilterType: + case FilterError_NullPointer: + case FilterError_NotRule: + case FilterError_NotScript: + case FilterError_SearchError: { +#ifdef Debug_Signal + CStr255 errorString; + USearchHelper::AssignUString(uStr_FilterFunctionParamError, errorString); + ErrorManager::PlainAlert((StringPtr) errorString); +#endif // Debug_Signal + Throw_(paramErr); + } + break; + + case FilterError_FileError: + Throw_(ioErr); + break; + + default: + AssertFail_(false); + Throw_(32000); // Who knows? + break; + + } +} diff --git a/mozilla/cmd/macfe/MailNews/MailNewsFilters.h b/mozilla/cmd/macfe/MailNews/MailNewsFilters.h new file mode 100644 index 00000000000..568f955d856 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/MailNewsFilters.h @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// MailNewsFilters.h + +#pragma once + +/*====================================================================================== + DESCRIPTION: Defines classes related to the Mail & News search dialog. The search + dialog is really implemented as a normal window. +======================================================================================*/ + + +/*====================================================================================*/ + #pragma mark INCLUDE FILES +/*====================================================================================*/ + + +#pragma mark - +/*====================================================================================*/ + #pragma mark TYPEDEFS +/*====================================================================================*/ + + +#pragma mark - +/*====================================================================================*/ + #pragma mark CONSTANTS +/*====================================================================================*/ + + +#pragma mark - +/*====================================================================================*/ + #pragma mark CLASS DECLARATIONS +/*====================================================================================*/ + +class CFiltersWindowManager { + +public: + + static void RegisterFiltersClasses(void); + static void ShowFiltersWindow(void); + +private: + +}; + diff --git a/mozilla/cmd/macfe/MailNews/MailNewsSearch.cp b/mozilla/cmd/macfe/MailNews/MailNewsSearch.cp new file mode 100644 index 00000000000..af0603a53cb --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/MailNewsSearch.cp @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// MailNewsSearch.cp + +/*====================================================================================*/ + #pragma mark INCLUDE FILES +/*====================================================================================*/ + +#define DEBUGGER_ASSERTIONS + +#include "MailNewsSearch.h" +#include "SearchHelpers.h" +#include "CDateView.h" +#include "CMessageSearchWindow.h" +#include "CSearchTableView.h" +#include "CCaption.h" +#include "CSingleTextColumn.h" +#include "CTSMEditField.h" +#include "URobustCreateWindow.h" +#include "LCommander.h" + +#pragma mark - +/*====================================================================================*/ + #pragma mark CLASS IMPLEMENTATIONS +/*====================================================================================*/ + +//----------------------------------- +void CSearchWindowManager::RegisterSearchClasses() +// Register all classes associated with the search window. +//----------------------------------- +{ + + USearchHelper::RegisterClasses(); + CDateView::RegisterDateClasses(); + + RegisterClass_(CMessageSearchWindow); + + RegisterClass_(CSearchTableView); + RegisterClass_(CListenerCaption); + + RegisterClass_(LTableView); + RegisterClass_(CSingleTextColumn); + + +} // CSearchWindowManager::RegisterSearchClasses + +//----------------------------------- +void CSearchWindowManager::ShowSearchWindow() +// Show the search dialog by bringing it to the front if it is not already. Create it +// if needed. +//----------------------------------- +{ + + // Find out if the window is already around + CMessageSearchWindow *searchWindow = dynamic_cast( + CWindowMediator::GetWindowMediator()->FetchTopWindow(WindowType_SearchMailNews)); + + if ( searchWindow == nil ) + { + // Search dialog has not yet been created, so create it here and display it. + searchWindow = dynamic_cast( + URobustCreateWindow::CreateWindow(CMessageSearchWindow::res_ID, + LCommander::GetTopCommander())); + } + + // Select the window + if (searchWindow) + { + // We set the scope of the search window only when the user issues the + // Search command. Previously, it was done on Activate and ReadWindowStatus. + searchWindow->SetUpBeforeSelecting(); + searchWindow->Select(); + } +} // CSearchWindowManager::ShowSearchWindow + + + + diff --git a/mozilla/cmd/macfe/MailNews/MailNewsSearch.h b/mozilla/cmd/macfe/MailNews/MailNewsSearch.h new file mode 100644 index 00000000000..adb7bf8a5ab --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/MailNewsSearch.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// MailNewsSearch.h + +#pragma once + +#define NOLDAPWINDOW +/*====================================================================================*/ + #pragma mark CLASS DECLARATIONS +/*====================================================================================*/ + +class CSearchWindowManager { + +public: + + static void RegisterSearchClasses(void); + static void ShowSearchWindow(void); + +private: + +}; diff --git a/mozilla/cmd/macfe/MailNews/MailNewsgroupWindow_Defines.h b/mozilla/cmd/macfe/MailNews/MailNewsgroupWindow_Defines.h new file mode 100644 index 00000000000..5ea41c0205e --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/MailNewsgroupWindow_Defines.h @@ -0,0 +1,275 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// MailNewsgroupWindow_Defines.h + +#pragma once + +// ID space +// +// All resources and cmd#s should fall within this range +// +// Mail/Newsgroups 10500-10999 + + +// Menu ResIDs +enum +{ + kMailNewsViewMenuID = 10500 +, kMailNewsMessageMenuID = 10501 +, kMailNewsFolderMenuID = 10502 +, kMailMenuID = 10503 +}; + + +//----------------------------------- +// COMMAND #s +// There are more command numbers in MailNewsGUI.h +//----------------------------------- +enum +{ + // these require messages to be selected +// cmd_OpenMailMessage = cmd_Open + cmd_OpenMailMessage = 10500 +//, cmd_DeleteMessage = 10500 // NO. Use cmd_Clear +, cmd_ReplyToSender = 10510 +, cmd_ReplyToAll = 10511 +, cmd_ForwardMessage = 10512 +, cmd_ForwardMessageQuoted = 10513 +, cmd_AddSenderToAddressBook = 10514 +, cmd_AddAllToAddressBook = 10515 +, cmd_MarkRead = 10516 +, cmd_MarkUnread = 10517 +, cmd_ForwardMessageAttachment = 10518 +, cmd_ForwardMessageInline = 10519 + +// these require folders to be selected +//, cmd_DeleteFolder = 10501 // No. Use cmd_Clear +, cmd_RenameFolder = 10520 + + + // these do not require a selection +//, cmd_NewMailMessage = cmd_New +, cmd_NewMailMessage = 10530 + +, cmd_EmptyTrash = 10531 +, cmd_MailFilters = 10532 +, cmd_NextMessage = 10534 +, cmd_NextUnreadMessage = 10535 +, cmd_PreviousMessage = 10536 + + +, cmd_FirstSortCommand = 10537 +, cmd_SortByDate = 10537 +, cmd_SortBySubject = 10538 +, cmd_SortBySender = 10539 +, cmd_SortByThread = 10540 +, cmd_SortByPriority = 10541 +, cmd_SortBySize = 10542 +, cmd_SortByStatus = 10543 +, cmd_SortByLocation = 10544 +, cmd_SortByFlagged = 10545 +, cmd_SortByReadness = 10546 +, cmd_SortByOrderReceived = 10547 // note new item here, and changed IDs below +, cmd_SortAscending = 10548 +, cmd_SortDescending = 10549 +, cmd_LastSortCommand = cmd_SortByReadness + +, cmd_MarkReadByDate = 10550 +, cmd_MarkReadForLater = 10551 + +, cmd_NewsGroups = 10701 // Show folder window with news selected +, cmd_ToolbarMode = 0 // ¥¥¥ FIX ME: common!! advanced <-> novice +, cmd_SendOutboxMail = 10702 // ¥¥¥ Message Menu + +, cmd_CloseAll = 10703 + +, cmd_NextUnreadGroup = 10704 +, cmd_NextFolder = 10705 +, cmd_FirstFlagged = 10706 +, cmd_PreviousFlagged = 10707 +, cmd_NextFlagged = 10708 +, cmd_NextUnreadThread = 10709 + +, cmd_SendMessage = 10610 +, cmd_QuoteMessage = 10611 +, cmd_SendMessageLater = 10612 +, cmd_SaveDraft = 10613 + +, cmd_CopyMailMessages = 10614 //ioParam = (char *), BE name of filing folder +, cmd_MoveMailMessages = 10615 //ioParam = (char *), BE name of filing folder + +, cmd_ViewAllThreads = 10616 +, cmd_ViewKilledThreads = 10617 +, cmd_ViewThreadsWithNew = 10618 +, cmd_ViewWatchedThreadsWithNew= 10619 +, cmd_ViewNewOnly = 10620 + +, cmd_ToggleThreadWatched = 10621 +, cmd_ToggleThreadKilled = 10622 + +, cmd_AttachmentsInline = 10623 +, cmd_AttachmentsAsLinks = 10624 + +, cmd_SearchAddresses = 10627 // LDAP address search + +, cmd_ToggleOffline = 10628 // Go Offline/Go Online +, cmd_SelectForOffline = 10629 +, cmd_FlaggedForOffline = 10630 +, cmd_SynchronizeForOffline = 10631 + +, cmd_ToggleFolderPane = 10635 +, cmd_SaveTemplate = 10636 + +, cmd_RelocateViewToFolder= 'MfPM' // same as the class ID of the button that does it. + +, cmd_NewsFirst = 14000 // Subscribe window +, cmd_NewsToggleSubscribe = cmd_NewsFirst+0 +, cmd_NewsExpandGroup = cmd_NewsFirst+1 +, cmd_NewsExpandAll = cmd_NewsFirst+2 +, cmd_NewsCollapseGroup = cmd_NewsFirst+3 +, cmd_NewsCollapseAll = cmd_NewsFirst+4 +, cmd_NewsGetGroups = cmd_NewsFirst+5 +, cmd_NewsSearch = cmd_NewsFirst+6 +, cmd_NewsGetNew = cmd_NewsFirst+7 +, cmd_NewsClearNew = cmd_NewsFirst+8 +, cmd_NewsHostChanged = cmd_NewsFirst+9 +, cmd_NewsSetSubscribe = cmd_NewsFirst+10 +, cmd_NewsClearSubscribe = cmd_NewsFirst+11 +}; + +// String stuff for menus that change +enum +{ + kMailNewsMenuStrings = 10500 +, kOpenFolderStrID = 1 +, kOpenDiscussionStrID +, kOpenMessageStrID +, kOpenMailServerStrID +, kOpenNewsServerStrID +, kNextChunkMessagesStrID +}; + + + + +// +// Window PPob IDs +// +enum +{ + kMailNewsWindowPPob = 10500 +, kMailMessageWindowPPob = 10506 +, kNewsgroupWindowPPob = 10507 + +, kRenameFolderDialogPPob = 10510 +, kNewFolderDialogPPob = 10511 + +, kMailComposeWindowPPob = 10610 +}; + + +// MailNews window pane ID's +enum +{ + kMailNewsTabSwitcherPaneID = 'TbSw' +, kMailNewsTabContainerPaneID = 'Cont' +, kMailNewsStatusPaneID = 'Stat' +, kOfflineButtonPaneID = 'SBof' +}; + +// +// Tab IDs +// +// The message ID's associated with each tab. +// +// Independent of tab-order. +// +// These also match the ppob id's of each view +// + +enum +{ + kInboxTabID = 10501 +, kDraftsTabID = 10502 +, kOutboxTabID = 10503 +, kFiledMailTabID = 10504 +, kNewsgroupsTabID = 10505 +}; + +// +// Table Column IDs +// +// The column header panes must have these id's +// +enum +{ + /* CThreadView */ + kMarkedReadMessageColumn = 'Read' +, kThreadMessageColumn = 'Thrd' // also used for Icon column in dir. search results +, kSubjectMessageColumn = 'Subj' +, kSenderMessageColumn = 'Sndr' +, kAddresseeMessageColumn = 'SdTo' +, kDateMessageColumn = 'Date' +, kPriorityMessageColumn = 'Prio' +, kSizeMessageColumn = 'Size' +, kUnreadMessageColumn = 'Unrd' +, kStatusMessageColumn = 'Stus' +, kTotalMessageColumn = 'Totl' +, kFlagMessageColumn = 'Flag' +, kHiddenOrderReceivedColumn = 'Rcvd' + + /* CMessageFolderView */ +, kFolderNameColumn = 'FNam' +, kFolderNumUnreadColumn = 'NumU' +, kFolderNumTotalColumn = 'NumT' + + /* COfflinePickerView */ +, kSelectFolderColumn = 'SelF' + + /* CSubscribeView */ +, kNewsgroupNameColumn = 'NuNm' +, kNewsgroupSubscribedColumn = 'NuSb' +, kNewsgroupPostingsColumn = 'NuPo' + + + /* CSearchTableView */ +, kStatusMessageLocation = 'Loca' +, kNameEntryColumn = 'Name' +, kEmailEntryColumn = 'Emal' +, kCompanyEntryColumn = 'Comp' +, kCityEntryColumn = 'City' +, kPhoneEntryColumn = 'Phon' +}; + +// +// ID#s of 'Cols' resources, for saving the column +// positions of the various table views... +// +enum +{ + kSavedInboxColumnStateID = 10501, + kSavedDraftsColumnStateID = 10502, + kSavedOutboxColumnStateID = 10503, + kSavedFiledMailColumnStateID = 10504, + kSavedFiledMailFolderColumnStateID = 10506, + + kSavedNewsgroupColumnStateID = 10505 +}; diff --git a/mozilla/cmd/macfe/MailNews/SearchHelpers.cp b/mozilla/cmd/macfe/MailNews/SearchHelpers.cp new file mode 100644 index 00000000000..593009d48d2 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/SearchHelpers.cp @@ -0,0 +1,2020 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// SearchHelpers.cp + +/*====================================================================================*/ + #pragma mark INCLUDE FILES +/*====================================================================================*/ + +#define DEBUGGER_ASSERTIONS + +#include "SearchHelpers.h" +#include "CMailFolderButtonPopup.h" +#include "uprefd.h" +#include "uerrmgr.h" +#include "CDeviceLoop.h" +#include "LTableViewHeader.h" +#include "UGraphicGizmos.h" +#include "PascalString.h" + +#include +#include +#include +#include +#include "UStClasses.h" +#include "CSearchManager.h" + +// BE stuff + +#define B3_SEARCH_API +#include "msg_srch.h" + + +#pragma mark - +/*====================================================================================*/ + #pragma mark INTERNAL FUNCTION PROTOTYPES +/*====================================================================================*/ + +static pascal void DoNothingStdRect(GrafVerb /*theVerb*/, Rect */*theRect*/) { } +static pascal void DoNothingStdRgn(GrafVerb /*theVerb*/, RgnHandle /*theRgn*/) { } +static pascal void ForgetRoutineDesc(UniversalProcPtr *routineDesc) { +#ifdef powerc // Only do for power pc + if ( *routineDesc ) + { + DisposeRoutineDescriptor(*routineDesc); + *routineDesc = nil; + } +#else // powerc // Only do for power pc + #pragma unused(routineDesc) +#endif // powerc +} + +static const Int16 cWindowDesktopMargin = 4; + + +#pragma mark - +/*====================================================================================*/ + #pragma mark CLASS IMPLEMENTATIONS +/*====================================================================================*/ + +CQDProcs USearchHelper::sStdProcs = { (QDTextUPP) -1L }; + +/*====================================================================================== + Register classes in this file. +======================================================================================*/ + +void USearchHelper::RegisterClasses() +{ + RegisterClass_(CFolderScopeGAPopup); + RegisterClass_(CFolderMoveGAPopup); + RegisterClass_(CSearchDateField); +} + +/*====================================================================================== + Find the specified dialog control. +======================================================================================*/ + +LControl *USearchHelper::FindViewControl(LView *inView, PaneIDT inID) +{ + LControl *theControl = dynamic_cast(inView->FindPaneByID(inID)); + AssertFail_(theControl != nil); + return theControl; +} + +/*====================================================================================== + Find the specified view subpane. +======================================================================================*/ + +LPane *USearchHelper::FindViewSubpane(LView *inView, PaneIDT inID) +{ + LPane *thePane = inView->FindPaneByID(inID); + AssertFail_(thePane != nil); + + return thePane; +} + +/*====================================================================================== + Find the specified view subpane. +======================================================================================*/ + +LView *USearchHelper::FindViewSubview(LView *inView, PaneIDT inID) +{ + LView *theView = dynamic_cast(inView->FindPaneByID(inID)); + AssertFail_(theView != nil); + + return theView; +} + + +/*====================================================================================== + Find the specified view edit field. +======================================================================================*/ + +LBroadcasterEditField *USearchHelper::FindViewEditField(LView *inView, PaneIDT inID) +{ + LBroadcasterEditField *theEditField = dynamic_cast(inView->FindPaneByID(inID)); + AssertFail_(theEditField != nil); + + return theEditField; +} + +/*====================================================================================== + Find the specified view popup menu. +======================================================================================*/ + +CFolderScopeGAPopup *USearchHelper::FindFolderScopePopup(LView *inView, PaneIDT inID) +{ + CFolderScopeGAPopup *thePopup = dynamic_cast(inView->FindPaneByID(inID)); + AssertFail_(thePopup != nil); + + return thePopup; +} + +CSearchPopupMenu *USearchHelper::FindSearchPopup(LView *inView, PaneIDT inID) +{ + CSearchPopupMenu *thePopup = dynamic_cast(inView->FindPaneByID(inID)); + AssertFail_(thePopup != nil); + + return thePopup; +} + +/*====================================================================================== + Find the specified date view. +======================================================================================*/ + +CSearchDateField *USearchHelper::FindViewDateField(LView *inView, PaneIDT inID) +{ + CSearchDateField *theDateField = dynamic_cast(inView->FindPaneByID(inID)); + AssertFail_(theDateField != nil); + + return theDateField; +} + +/*====================================================================================== + Get the window associated with the specified pane. +======================================================================================*/ + +LWindow *USearchHelper::GetPaneWindow(LPane *inPane) +{ + LWindow *theWindow = LWindow::FetchWindowObject(inPane->GetMacPort()); + AssertFail_(theWindow != nil); + + return theWindow; +} + +/*====================================================================================== + Find the first view subpane with the specified id if it is visible. +======================================================================================*/ + +LPane *USearchHelper::FindViewVisibleSubpane(LView *inView, PaneIDT inID) +{ + static LPane *sPane = nil; // Reduce recursive requirements + static LPane *sSub; + + if ( (inID == inView->GetPaneID()) && inView->IsVisible() ) + { + sPane = inView; + } else + { + // Search all subpanes + LArrayIterator iterator(inView->GetSubPanes(), LArrayIterator::from_Start); + while ( iterator.Next(&sSub) ) + { + LView *theView = dynamic_cast(sSub); + if ( theView != nil ) + { + sPane = FindViewVisibleSubpane(theView, inID); + } else if ( (inID == sSub->GetPaneID()) && sSub->IsVisible() ) + { + sPane = sSub; + break; + } + } + } + + return sPane; +} + + + + + +/*====================================================================================== + Find the tab group for the view. +======================================================================================*/ + +CSearchTabGroup *USearchHelper::FindWindowTabGroup(LArray *inSubcommanders) +{ + LArrayIterator iterator(*inSubcommanders); + LCommander *theSub; + CSearchTabGroup *theTabGroup = nil; + + while ( (theTabGroup == nil) && iterator.Next(&theSub) ) + { + theTabGroup = dynamic_cast(theSub); + } + + AssertFail_(theTabGroup != nil); + return theTabGroup; +} + + + + +/*====================================================================================== + Remove the size box area of the window from the visible region. This method should + be called at the end of the window's DrawSelf() method. +======================================================================================*/ + +void USearchHelper::RemoveSizeBoxFromVisRgn(LWindow *inWindow) +{ + Rect sizeBox; + RgnHandle visRgn = inWindow->GetMacPort()->visRgn; + + inWindow->CalcPortFrameRect(sizeBox); + sizeBox.left = sizeBox.right - 15; + sizeBox.top = sizeBox.bottom - 15; + + if ( ::RectInRgn(&sizeBox, visRgn) ) + { + StRegion diffRgn(sizeBox); + ::DiffRgn(visRgn, diffRgn, visRgn); + } +} + + +/*====================================================================================== + Refresh the specified port rect for the given pane. +======================================================================================*/ + +void USearchHelper::RefreshPortFrameRect(LPane *inPane, Rect *inPortRect) +{ + if ( !inPane->IsVisible() || (inPane->GetSuperView() == nil) ) return; + + Rect superRevealed; + inPane->GetSuperView()->GetRevealedRect(superRevealed); + if ( ::SectRect(inPortRect, &superRevealed, inPortRect) ) + { + inPane->InvalPortRect(inPortRect); + } +} + + +/*====================================================================================== + Refresh the specified local rect for the given pane. +======================================================================================*/ + +void USearchHelper::RefreshLocalFrameRect(LPane *inPane, Rect *inLocalRect) +{ + inPane->LocalToPortPoint(topLeft(*inLocalRect)); + inPane->LocalToPortPoint(botRight(*inLocalRect)); + + RefreshPortFrameRect(inPane, inLocalRect); +} + + +/*====================================================================================== + Link the specified listener to the any and all pane broadcasters within the view + specified by inContainer. +======================================================================================*/ + +void USearchHelper::LinkListenerToBroadcasters(LView *inContainer, LListener *inListener) +{ + // First, check out the container itself + + if ( (dynamic_cast(inContainer)) != inListener ) + { + LBroadcaster *broadcaster = dynamic_cast(inContainer); + if ( broadcaster != nil ) broadcaster->AddListener(inListener); + } + + // Iterate through all subviews + + LArrayIterator iterator(inContainer->GetSubPanes()); + + LPane *pane; + + while ( iterator.Next(&pane) ) + { + LView *view = dynamic_cast(pane); + if ( view != nil ) + { + LinkListenerToBroadcasters(view, inListener); + } else + { + LBroadcaster *broadcaster = dynamic_cast(pane); + if ( broadcaster != nil ) broadcaster->AddListener(inListener); + } + } +} + + +/*====================================================================================== + Get the length of the specified edit field text. +======================================================================================*/ + +Int16 USearchHelper::GetEditFieldLength(LEditField *inEditField) +{ + + return (**inEditField->GetMacTEH()).teLength; +} + + +/*====================================================================================== + Return a display name for the specified folder name. +======================================================================================*/ + +void USearchHelper::AssignUString(Int16 inStringIndex, CString& outString) +{ + ::GetIndString(outString, 8600, inStringIndex); +} + + +/*====================================================================================== + Enable or disable the specified button without redrawing it! +======================================================================================*/ + +void USearchHelper::EnableDisablePane(LPane *inPane, Boolean inEnable, Boolean inRefresh) +{ + Boolean isEnabled = inPane->IsEnabled(); + Boolean isVisible = inPane->IsVisible(); + + if ( isVisible && ((isEnabled && inEnable) || (!isEnabled && !inEnable)) ) return; + + if ( isVisible ) inPane->Hide(); + if ( inEnable ) + { + inPane->Enable(); + } else + { + inPane->Disable(); + } + if ( isVisible ) inPane->Show(); + if ( !inRefresh ) + { + inPane->DontRefresh(); + } +} + + +/*====================================================================================== + Show or hide the specified pane. +======================================================================================*/ + +void USearchHelper::ShowHidePane(LPane *inPane, Boolean inDoShow) +{ + if ( inDoShow ) + { + inPane->Show(); + } else + { + inPane->Hide(); + } +} + + +/*====================================================================================== + Handle a dialog Enter, Return, and Cancel keys. Return true if the key was + handled, false otherwise. +======================================================================================*/ + +Boolean USearchHelper::HandleDialogKey(LView *inView, const EventRecord &inKeyEvent) +{ + Int16 theKey = inKeyEvent.message & charCodeMask; + + if ( ((theKey == char_Enter) || (theKey == char_Return)) ) + { + + USearchHelper::FindViewControl(inView, paneID_Okay)->SimulateHotSpotClick(kControlButtonPart); + return true; + } else if ( (((theKey == char_Escape) && ((inKeyEvent.message & keyCodeMask) == vkey_Escape)) || + UKeyFilters::IsCmdPeriod(inKeyEvent)) ) +{ + + USearchHelper::FindViewControl(inView, paneID_Cancel)->SimulateHotSpotClick(kControlButtonPart); + return true; + } + + return false; +} + + +/*====================================================================================== + Set the descriptor for the pane if it is different from the current descriptor. +======================================================================================*/ + +void USearchHelper::SetPaneDescriptor(LPane *inPane, const CStr255& inDescriptor) +{ + CStr255 currentDesc; + inPane->GetDescriptor(currentDesc); + + if ( currentDesc != inDescriptor ) + { + inPane->SetDescriptor(inDescriptor); + } +} + + +/*====================================================================================== + Set the default status of the specified button. +======================================================================================*/ + +void USearchHelper::SetDefaultButton(LView *inView, PaneIDT inID, Boolean inIsDefault) +{ + LControl *theControl = USearchHelper::FindViewControl(inView, inID); + LGAPushButton *pushButton = dynamic_cast(theControl); + Assert_(pushButton != nil); + + if ( pushButton != nil ) pushButton->SetDefaultButton(inIsDefault, true); +} + + +/*====================================================================================== + If the specified edit field is not already the target, select its text and make it + the current target. +======================================================================================*/ + +void USearchHelper::SelectEditField(LEditField *inEditField) +{ + if ( !inEditField->IsTarget() && inEditField->IsVisible() && + LCommander::SwitchTarget(inEditField) ) { // Become the current target + { + StExcludeVisibleRgn emptyRgn(inEditField); + inEditField->SelectAll(); // Select all text + } + inEditField->Refresh(); + } +} + + +/*====================================================================================== + If the specified date view is not already the target, select its text and make it + the current target. +======================================================================================*/ + +void USearchHelper::SelectDateView(CDateView *inDateView) +{ + if ( !inDateView->ContainsTarget() && inDateView->IsVisible() ) { // Become the current target + { + StExcludeVisibleRgn emptyRgn(inDateView); + inDateView->Select(); + } + inDateView->Refresh(); + } +} + + +/*====================================================================================== + Determine if the last clicked pane interacted with the user in some way (return true) + or if the last click was just a dead click. +======================================================================================*/ + +Boolean USearchHelper::LastPaneWasActiveClickPane() +{ + LPane *lastPane = LPane::GetLastPaneClicked(); + + if ( lastPane == nil ) return false; + + // Check for any control + void *result = dynamic_cast(lastPane); + + if ( result == nil ) + { + // Check for commander + result = dynamic_cast(lastPane); + } + + return (result != nil); +} + + +/*====================================================================================== + Recalculate the bounds for the window based upon increment amounts for changing the + window's size. Make sure that the new window size will not extend beyond the + desktop bounds. Note that this method does NOT check against the min/max size for + the window, just as the LWindow::DoSetBounds() doesn't. The inSizeBoxLeft and + inSizeBoxTop parameters represent the distance between the bottom, right of the + window and the top, left corner of the size box (always >= 0). +======================================================================================*/ + +void USearchHelper::CalcDiscreteWindowResizing(LWindow *inWindow, Rect *ioNewBounds, + const Int16 inHIncrement, const Int16 inVIncrement, + Boolean inCheckDesktop, Int16 inSizeBoxLeft, + Int16 inSizeBoxTop) +{ + Rect currentBounds, minMaxSizes; + + CSaveWindowStatus::GetPaneGlobalBounds(inWindow, ¤tBounds); + inWindow->GetMinMaxSize(minMaxSizes); + + Assert_((inSizeBoxLeft >= 0) && (inSizeBoxTop >= 0)); + const Int16 curHeight = currentBounds.bottom - currentBounds.top; + const Int16 curWidth = currentBounds.right - currentBounds.left; + const Int16 vResizeAmount = ioNewBounds->bottom - ioNewBounds->top - curHeight; + const Int16 hResizeAmount = ioNewBounds->right - ioNewBounds->left - curWidth; + + if ( (vResizeAmount != 0) || (hResizeAmount != 0) ) + { + Int16 vDelta = vResizeAmount % inVIncrement; + Int16 hDelta = hResizeAmount % inHIncrement; + Int16 vResize = vResizeAmount, hResize = hResizeAmount; + + if ( vDelta != 0 ) + { + if ( vDelta < 0 ) vDelta = -vDelta; + vDelta = ((vDelta > (inVIncrement>>1)) ? inVIncrement - vDelta : -vDelta); + vResize += (vResizeAmount < 0) ? -vDelta : vDelta; + } + if ( hDelta != 0 ) + { + if ( hDelta < 0 ) hDelta = -vDelta; + hDelta = ((hDelta > (inVIncrement>>1)) ? inHIncrement - hDelta : -hDelta); + hResize += (hResizeAmount < 0) ? -hDelta : hDelta; + } + + AssertFail_(!(vResize % inVIncrement)); // Should be integral + AssertFail_(!(hResize % inHIncrement)); // Should be integral + + if ( inCheckDesktop ) + { + // Make sure we don't extend beyond the bounds of the desktop + Point desktopBotRight = botRight((**(::GetGrayRgn())).rgnBBox); + if ( (vResize > 0) && + (currentBounds.bottom + vResize + cWindowDesktopMargin - inSizeBoxTop) > desktopBotRight.v ) + { + vResize -= inVIncrement; + } + if ( (hResize > 0) && + (currentBounds.right + hResize + cWindowDesktopMargin - inSizeBoxLeft) > desktopBotRight.h ) + { + hResize -= inHIncrement; + } + } + + ioNewBounds->bottom = ioNewBounds->top + curHeight + vResize; + ioNewBounds->right = ioNewBounds->left + curWidth + hResize; + + // Check against min/max sizes + + Int16 newHeight = ioNewBounds->bottom - ioNewBounds->top; + Int16 newWidth = ioNewBounds->right - ioNewBounds->left; + + if ( newHeight < minMaxSizes.top ) + { + ioNewBounds->bottom += inVIncrement; + } else if ( newHeight > minMaxSizes.bottom ) + { + ioNewBounds->bottom -= inVIncrement; + } + if ( newWidth < minMaxSizes.left ) + { + ioNewBounds->right += inHIncrement; + } else if ( newWidth > minMaxSizes.right ) + { + ioNewBounds->right -= inHIncrement; + } + } +} + +/*====================================================================================== + Scroll the views bit without redrawing the update region with the window's background + color. +======================================================================================*/ + +void USearchHelper::ScrollViewBits(LView *inView, Int32 inLeftDelta, Int32 inTopDelta) +{ + if ( !inView->FocusDraw() ) return; + + if ( sStdProcs.textProc == ((QDTextUPP) -1L) ) + { + SetStdCProcs(&sStdProcs); + sStdProcs.rectProc = NewQDRectProc(DoNothingStdRect); + sStdProcs.rgnProc = NewQDRgnProc(DoNothingStdRgn); + if ( !sStdProcs.rectProc || !sStdProcs.rgnProc ) + { + ForgetRoutineDesc((UniversalProcPtr *) &sStdProcs.rectProc); + ForgetRoutineDesc((UniversalProcPtr *) &sStdProcs.rgnProc); + sStdProcs.textProc = (QDTextUPP) -1L; + FailOSErr_(memFullErr); + } + } + + Rect frame; + Point offset; + + // Get Frame in Port coords + inView->GetRevealedRect(frame); + offset = topLeft(frame); + inView->PortToLocalPoint(topLeft(frame)); + inView->PortToLocalPoint(botRight(frame)); + offset.h = offset.h - frame.left; + offset.v = offset.v - frame.top; + + // Scroll Frame, clipping to the update region + RgnHandle updateRgnH = NewRgn(); + + GrafPtr currentPort = UQDGlobals::GetCurrentPort(); + + if ( currentPort->grafProcs ) + { + QDRectUPP saveStdRect = currentPort->grafProcs->rectProc; + QDRgnUPP saveStdRgn = currentPort->grafProcs->rgnProc; + currentPort->grafProcs->rectProc = sStdProcs.rectProc; + currentPort->grafProcs->rgnProc = sStdProcs.rgnProc; + ::ScrollRect(&frame, -inLeftDelta, -inTopDelta, updateRgnH); + currentPort->grafProcs->rectProc = saveStdRect; + currentPort->grafProcs->rgnProc = saveStdRgn; + } else + { + QDProcsPtr saveProcs = currentPort->grafProcs; + currentPort->grafProcs = (QDProcsPtr) &sStdProcs; + ::ScrollRect(&frame, -inLeftDelta, -inTopDelta, updateRgnH); + currentPort->grafProcs = saveProcs; + } + + // Force redraw of update region + ::OffsetRgn(updateRgnH, offset.h, offset.v); + inView->InvalPortRgn(updateRgnH); + ::DisposeRgn(updateRgnH); +} + + +/*====================================================================================== + KeyIsDown + + Determine whether or not the specified key is being pressed. Keys are specified by + hardware-specific key scan code (NOT the character). See PP_KeyCodes.h. +======================================================================================*/ + +Boolean USearchHelper::KeyIsDown(Int16 inKeyCode) +{ + KeyMap theKeys; + + ::GetKeys(theKeys); + + return (::BitTst(((char *) &theKeys) + inKeyCode / 8, (long) 7 - (inKeyCode % 8))); +} + + +/*====================================================================================== + Show a generic exception alert. +======================================================================================*/ + +void USearchHelper::GenericExceptionAlert(OSErr inErr) +{ + CStr255 errorString; + CStr31 numString; + ::NumToString(inErr, numString); + USearchHelper::AssignUString(uStr_ExceptionErrorOccurred, errorString); + ::StringParamText(errorString, numString); + ::SetCursor(&qd.arrow); + ErrorManager::PlainAlert((StringPtr) errorString); + ::HiliteMenu(0); +} + + +#ifdef NOT_YET +/*====================================================================================== + Return a display name for the specified folder name. +======================================================================================*/ + +LStr255 *USearchHelper::GetFolderDisplayName(const char *inFolderName, + LStr255 *outFolderName) +{ + outFolderName->Assign(inFolderName); + P2CStr(((StringPtr) *outFolderName)); + NET_UnEscape((char *) ((StringPtr) *outFolderName)); + C2PStr((char *) ((StringPtr) *outFolderName)); + return outFolderName; +} + + +#endif // NOT_YET + + +#pragma mark - + +//----------------------------------- +CSearchPopupMenu::CSearchPopupMenu(LStream *inStream) : + CGAIconPopup(inStream), mCommands(sizeof(CommandT)), + mLastPopulateID(0), mDefaultItem(1), mOldValue( 1 ) +//----------------------------------- +{ +} + +//----------------------------------- +CommandT +CSearchPopupMenu::GetOldItemCommand() const +//----------------------------------- +{ + return mOldValue; +} + +//----------------------------------- +void CSearchPopupMenu::PopulateMenu(MSG_SearchMenuItem *inMenuItems, const Int16 inNumItems, + CommandT inNewCommandIfInvalid, UInt32 inPopulateID) +// Populate the menu with the specified items. The strings in inMenuItems have been +// converted to pascal strings. The MSG_SearchMenuItem.attrib field is used for the +// menu command. inNewCommandIfInvalid specifies the new menu item to make current +// if the currently selected menu item becomes invalid. inPopulateID is a unique +// non-zero identifier used for determining whether or not we actually need to repopulate +// the menu. If inPopulateID is 0, the menu is always repopulated. +//----------------------------------- +{ + // We need to remember here, that since the menu is cached, it might not jive with + // the command array (i.e. menu items might have been set in another instance of + // this class). The mCommands array store data related to our state. + + MenuHandle menuH = GetMacMenuH(); + Int16 startNumMenuItems = ::CountMItems(menuH); + Int16 startNumCommands = mCommands.GetCount(); + +// if ( (inPopulateID != 0) && (inPopulateID == mLastPopulateID) ) +// { +// AssertFail_(startNumMenuItems == inNumItems); +// AssertFail_(startNumCommands == inNumItems); +// return; // Already populated! +// } + + const Int16 curItem = GetValue(); + Boolean curItemChanged = (startNumCommands > 0) ? false : true; + Boolean curItemTextChanged = (startNumCommands == 0) && (inNumItems > 0); + mDefaultItem = 1; + + // Do we need to adjust the menu? + if ( inNumItems < startNumMenuItems ) + { + do + { + ::DeleteMenuItem(menuH, startNumMenuItems--); + } while ( startNumMenuItems != inNumItems ); + } + // Do we need to adjust the command array? + if ( inNumItems < startNumCommands ) + { + Int16 deleteStartIndex = inNumItems + 1; + if ( IsBetween(curItem, deleteStartIndex, startNumCommands) ) + { + curItemChanged = true; + } + mCommands.RemoveItemsAt(LArray::index_Last, deleteStartIndex); + startNumCommands = inNumItems; + } else if ( inNumItems > startNumCommands ) + { + mCommands.AdjustAllocation(inNumItems - startNumCommands); + } + + // Add new items + MSG_SearchMenuItem *curMenuItem = inMenuItems; + + for (Int32 i = 1; i <= inNumItems; ++i, ++curMenuItem) + { + CommandT command = curMenuItem->attrib; + if ( inNewCommandIfInvalid == command && curMenuItem->isEnabled) + { + mDefaultItem = i; + } + // Do we need to adjust the menu? + if ( i <= startNumMenuItems ) + { + // There is already an item in the menu + CStr255 curMenuString; // Strings are not necessary 31 character file names + ::GetMenuItemText(menuH, i, curMenuString); + if ( !::EqualString(curMenuString, (UInt8 *) curMenuItem->name, true, true) ) + { + ::SetMenuItemText(menuH, i, (UInt8 *) curMenuItem->name); + if ( i == curItem ) + { + curItemTextChanged = true; + } +// the isEnabled flag is obsolete and basically random. +// if ( curMenuItem->isEnabled ) +// { +// ::EnableItem(menuH, i); +// } else +// { +// ::DisableItem(menuH, i); +// } + } + } else + { + ::AppendMenu(menuH, (UInt8 *) curMenuItem->name); + // curMenuItem->name can have meta character data in it + // So this really is necessary + ::SetMenuItemText( menuH, i, (UInt8 *) curMenuItem->name); +// the isEnabled flag is obsolete and basically random. +// if ( !curMenuItem->isEnabled ) +// { +// ::DisableItem(menuH, i); +// } + } + // Do we need to adjust the commands? + if ( i <= startNumCommands ) + { + // There is already an item in the menu + mCommands.AssignItemsAt(1, i, &command); + if ( !curMenuItem->isEnabled && (i == curItem) ) + { + curItemChanged = true; + } + } else + { + mCommands.InsertItemsAt(1, i, &command); + } + } + + if ( inNumItems != startNumCommands ) + { + SetPopupMinMaxValues(); + } + + if ( curItemChanged ) + { + mValue = mDefaultItem; + RefreshMenu(); + } else if ( curItemTextChanged ) + { + RefreshMenu(); + } + + mLastPopulateID = inPopulateID; +} + + +/*====================================================================================== + Get the command for the specified menu item. Pass 0 for inMenuItem to get the + currently selected menu item. If the index is invalid, return cmd_InvalidMenuItem. +======================================================================================*/ + +CommandT CSearchPopupMenu::GetCurrentItemCommandNum(Int16 inMenuItem) +{ + ArrayIndexT index = (inMenuItem > 0) ? inMenuItem : mValue; + CommandT command = cmd_InvalidMenuItem; + + // check if valid index + if( mCommands.ValidIndex(index) ) + command = *((CommandT *) mCommands.GetItemPtr(index)); + else + return mOldValue; + + if( command == cmd_InvalidMenuItem ) + return mOldValue; + else + return command; + +// return cmd_InvalidMenuItem; +} + + +/*====================================================================================== + Select the item with the specified command. +======================================================================================*/ + +void CSearchPopupMenu::SetCurrentItemByCommand(CommandT inMenuCommand) +{ + Int16 index = GetCommandItemIndex(inMenuCommand); + if ( index && (GetValue() != index) ) + { + SetValue(index); + } +} + + + +/*====================================================================================== + Refresh instead of drawing immediately. Also fixes bug in PowerPlant if an exception + is raised when broadcasting the value message. +======================================================================================*/ + +void CSearchPopupMenu::SetValue(Int32 inValue) +{ + CommandT menuCommand; + bool success = mCommands.FetchItemAt( inValue, &menuCommand ); +// *** const Int16 oldValue = GetValue(); + mOldValue = GetValue(); + +// if ( oldValue != inValue ) + if ( mOldValue != inValue ) + { + RefreshMenu(); + MenuHandle menuH = GetMacMenuH(); + if( menuCommand == CSearchManager::eCustomizeSearchItem && success ) + { + mValue = inValue; + BroadcastValueMessage(); +// inValue = oldValue; + inValue = mOldValue; + FocusDraw(); + } + if ( menuH ) SetupCurrentMenuItem(menuH, inValue); + Try_ + { + LControl::SetValue(inValue); + } + Catch_(inErr) + { + // Reset the old value, new value is unacceptable +// if ( menuH ) SetupCurrentMenuItem(menuH, oldValue); + if ( menuH ) SetupCurrentMenuItem(menuH, mOldValue); +// mValue = oldValue; + mValue = mOldValue; + SetHiliteState(false); + Throw_(inErr); + } + EndCatch_ + } +} + + +/*====================================================================================== + Get the index of the item with the specified command. +======================================================================================*/ + +Int16 CSearchPopupMenu::GetCommandItemIndex(CommandT inMenuCommand) +{ + ArrayIndexT index = mCommands.FetchIndexOf(&inMenuCommand); + + return ((index != LArray::index_Bad) ? index : 0); +} + + +/*====================================================================================== + Clear all items in the menu. +======================================================================================*/ + +void CSearchPopupMenu::ClearMenu(Int16 inStartItem) +{ + if ( GetNumCommands() < inStartItem ) return; + + mCommands.RemoveItemsAt(LArray::index_Last, inStartItem); + MenuHandle menuH = GetMacMenuH(); + Int16 numMenuItems = ::CountMItems(menuH); + while ( numMenuItems >= inStartItem ) + { + ::DeleteMenuItem(menuH, numMenuItems--); + } + SetPopupMinMaxValues(); +} + + +/*====================================================================================== + Append the specified menu item to the menu. +======================================================================================*/ + +void CSearchPopupMenu::AppendMenuItemCommand(CommandT inCommand, ConstStr255Param inName, + Boolean inIsEnabled) +{ + MenuHandle menuH = GetMacMenuH(); + Int16 numMenuItems = ::CountMItems(menuH); + + AssertFail_(numMenuItems == mCommands.GetCount()); + + mCommands.InsertItemsAt(1, LArray::index_Last, &inCommand); + ::AppendMenu(menuH, inName); + // I assume that we don't want Metacharacter data + ::SetMenuItemText( menuH, numMenuItems+1, inName ); + if ( !inIsEnabled ) + { + ::DisableItem(menuH, numMenuItems + 1); + } + SetPopupMinMaxValues(); +} + + +/*====================================================================================== + Set the specified menu item in the menu. +======================================================================================*/ + +void CSearchPopupMenu::SetMenuItemCommand(CommandT inOldCommand, CommandT inNewCommand, + ConstStr255Param inName, Boolean inIsEnabled) +{ + Int16 index = GetCommandItemIndex(inOldCommand); + if ( !index ) return; // Not found! + + Boolean doRefresh = (GetValue() == index); + + MenuHandle menuH = GetMacMenuH(); + Int16 numMenuItems = ::CountMItems(menuH); + + AssertFail_(numMenuItems == mCommands.GetCount()); + + mCommands.AssignItemsAt(1, index, &inNewCommand); + + if ( doRefresh ) + { + Str255 tempString; + ::GetMenuItemText(menuH, index, tempString); + doRefresh = !::EqualString(tempString, inName, true, true); + } + ::SetMenuItemText(menuH, index, inName); + + if ( inIsEnabled ) + { + ::EnableItem(menuH, numMenuItems + 1); + } else + { + ::DisableItem(menuH, numMenuItems + 1); + } + if ( doRefresh ) + { + RefreshMenu(); + } +} + + +/*====================================================================================== + Remove the specified menu item from the menu. +======================================================================================*/ + +void CSearchPopupMenu::RemoveMenuItemCommand(CommandT inRemoveCommand, + CommandT inNewCommandIfInvalid) +{ + Int16 index = GetCommandItemIndex(inRemoveCommand); + if ( !index ) return; // Not found! + + Boolean doRefresh = (GetValue() == index); + + MenuHandle menuH = GetMacMenuH(); + Int16 numMenuItems = ::CountMItems(menuH); + + AssertFail_(numMenuItems == mCommands.GetCount()); + + mCommands.RemoveItemsAt(1, index); + ::DeleteMenuItem(menuH, index); + + SetPopupMinMaxValues(); + + if ( doRefresh ) + { + index = GetCommandItemIndex(inNewCommandIfInvalid); + if ( !index ) index = 1; + mValue = index; + RefreshMenu(); + } +} + + +/*====================================================================================== + Finish creating the search popup menu. +======================================================================================*/ + +void CSearchPopupMenu::FinishCreateSelf() +{ + Inherited::FinishCreateSelf(); + + MenuHandle menuH = GetMacMenuH(); + Int16 numMenuItems = ::CountMItems(menuH); + + if ( numMenuItems > 0 ) + { + AssertFail_(mCommands.GetCount() == 0); + CommandT command = 0; + mCommands.InsertItemsAt(numMenuItems, LArray::index_Last, &command); + } +} + + +/*====================================================================================== + Send a reference to ourselves as the param. +======================================================================================*/ + +void CSearchPopupMenu::BroadcastValueMessage() +{ + BroadcastMessage(mValueMessage, this); +} + + +/*====================================================================================== + Don't do a double draw of the popup. +======================================================================================*/ + +Boolean CSearchPopupMenu::TrackHotSpot(Int16 inHotSpot, Point inPoint, Int16 inModifiers) +{ + Boolean rtnVal = Inherited::TrackHotSpot(inHotSpot, inPoint, inModifiers); + if ( rtnVal ) DontRefresh(); + + return rtnVal; +} + + +#pragma mark - +/*====================================================================================== + Just update the text area. Don't allow more than mMaxChars to be set. +======================================================================================*/ + +void CSearchEditField::SetDescriptor(ConstStr255Param inDescriptor) +{ + { + Str255 curDesc; + GetDescriptor(curDesc); + if ( ::EqualString(inDescriptor, curDesc, true, true) ) return; + } + + Int16 length = -1; + if ( inDescriptor[0] > mMaxChars ) + { + length = inDescriptor[0]; + ((UInt8 *) inDescriptor)[0] = mMaxChars; + } + + ::TESetText(inDescriptor + 1, inDescriptor[0], mTextEditH); + + if ( length >= 0 ) ((UInt8 *) inDescriptor)[0] = length; + + Draw(nil); + + BroadcastValueMessage(); +} + + +/*====================================================================================== + Set the text for the field. This can be used for text that is greater than 255 in + length. +======================================================================================*/ + +void CSearchEditField::SetText(const char *inText) +{ + if( inText != NULL ) + { + Int16 length = LString::CStringLength(inText); + if ( length > mMaxChars ) length = mMaxChars; + + ::TESetText(inText, length, mTextEditH); + } + Draw(nil); + + BroadcastValueMessage(); +} + + +/*====================================================================================== + Get the text for the field. This can be used for text that is greater than 255 in + length. The returned string is null-terminated. +======================================================================================*/ + +char *CSearchEditField::GetText(char *outText) +{ + CharsHandle theText = ::TEGetText(mTextEditH); + Int16 length = ::GetHandleSize(theText); + Assert_(length <= mMaxChars); + + ::BlockMoveData(*theText, outText, length); + + outText[length] = 0; + + return outText; +} + + +/*====================================================================================== + Just invalidate the right side of the pane. +======================================================================================*/ + +static const Int16 cRefreshEditMargin = 4; +void CSearchEditField::ResizeFrameBy(Int16 inWidthDelta, Int16 inHeightDelta, + Boolean inRefresh) +{ + Assert_(!inHeightDelta); + if ( !inWidthDelta ) return; + + Inherited::ResizeFrameBy(inWidthDelta, inHeightDelta, inRefresh); + + if ( inRefresh && IsVisible() ) + { + Rect invalRect; + CalcPortFrameRect(invalRect); + ::InsetRect(&invalRect, -1, -1); // Make bigger for shadows + if ( inWidthDelta < 0 ) + { + invalRect.left = invalRect.right - cRefreshEditMargin; + invalRect.right -= inWidthDelta; + } else + { + invalRect.left = invalRect.right - inWidthDelta - cRefreshEditMargin; + } + InvalPortRect(&invalRect); + } +} + + +/*====================================================================================== + Don't call LBroadcasterEditField::HandleKeyPress() since it intecepts Enter and + Return keys. +======================================================================================*/ + +Boolean CSearchEditField::HandleKeyPress(const EventRecord &inKeyEvent) +{ + return LBroadcasterEditField::LEditField_HandleKeyPress(inKeyEvent); +} + + +#pragma mark - +/*====================================================================================== + Only rotate the target if mCanRotate is true. Also, fix a bug in PowerPlant. +======================================================================================*/ + +void CSearchTabGroup::RotateTarget(Boolean inBackward) +{ + if ( (mCanRotate != nil) && !(*mCanRotate) ) return; + + LCommander *onDutySub = GetTarget(); //GetOnDutySub(); + Int32 pos = (onDutySub != nil) ? mSubCommanders.FetchIndexOf(onDutySub) : + mSubCommanders.GetCount() + 1; + + if ( (pos > 1) || (onDutySub != nil) ) { + Boolean switched = false; + Int32 startingPos = pos; + if ( startingPos > mSubCommanders.GetCount() ) + { + startingPos = mSubCommanders.GetCount(); + } else if ( startingPos < 0 ) + { + startingPos = 0; + } + LCommander *newTarget; + do + { + if (inBackward) + { + if (--pos <= 0) + { + // Wrap around to last cmdr + pos = mSubCommanders.GetCount(); + } + + } else + { + if (++pos > mSubCommanders.GetCount()) + { + pos = 1; // Wrap around to first cmdr + } + } + // Find SubCommander by position + // and see if it wants to become + // the new target + mSubCommanders.FetchItemAt(pos, newTarget); + switched = newTarget->ProcessCommand(msg_TabSelect); + + } while (!switched && (pos != startingPos)); + + if (switched) { // We found a willing subcommander + SwitchTarget(newTarget); + } + } +} + + +#pragma mark - +/*====================================================================================== + Draw when disabled as grayish text. +======================================================================================*/ + +void CSearchCaption::DrawSelf() +{ + Rect frame; + CalcLocalFrameRect(frame); + + Int16 just = UTextTraits::SetPortTextTraits(mTxtrID); + + RGBColor textColor; + ::GetForeColor(&textColor); + + ApplyForeAndBackColors(); + + if ( IsEnabled() ) + { + ::RGBForeColor(&textColor); + UTextDrawing::DrawWithJustification((Ptr) &mText[1], mText[0], frame, just); + } else + { + StDeviceLoop theLoop(frame); + Int16 depth; + while ( theLoop.NextDepth(depth) ) + { + if ( depth < 4 ) + { + ::TextMode(grayishTextOr); + } else + { + textColor = UGAColorRamp::GetColor(7); + ::RGBForeColor(&textColor); + } + UTextDrawing::DrawWithJustification((Ptr) &mText[1], mText[0], frame, just); + } + ::TextMode(srcOr); + ::ForeColor(blackColor); + } +} + + + +#pragma mark - + +RgnHandle StSectClipRgnState::sClipRgn = nil; +Boolean StSectClipRgnState::sSetClip = false; + +/*====================================================================================== + Clip to the intersection of the BOUNDS of the current clipping region. Don't create + more than one of these objects at a time! If inDisplayRect is non-nil, the routine + first checks to see if inDisplayRect is completely within inLocalRect AND the + current clipping region, and if so, does nothing. +======================================================================================*/ + +StSectClipRgnState::StSectClipRgnState(const Rect *inLocalRect, const Rect *inDisplayRect) +{ + Assert_(!sSetClip); + + if ( inDisplayRect != nil ) + { + if ( ((**UQDGlobals::GetCurrentPort()->clipRgn).rgnSize == sizeof(Region)) && + RectEnclosesRect(&(**UQDGlobals::GetCurrentPort()->clipRgn).rgnBBox, inDisplayRect) && + RectEnclosesRect(inLocalRect, inDisplayRect) ) + { + + return; // All's well + } + } + if ( sClipRgn == nil ) sClipRgn = ::NewRgn(); + if ( sClipRgn != nil ) + { + ::GetClip(sClipRgn); + Rect newClip; + ::SectRect(inLocalRect, &(**sClipRgn).rgnBBox, &newClip); + ::ClipRect(&newClip); + sSetClip = true; + } +} + + +/*====================================================================================== + Restore original clip region. +======================================================================================*/ + +StSectClipRgnState::~StSectClipRgnState() +{ + + if ( sSetClip ) + { + Assert_(sClipRgn != nil); + ::SetClip(sClipRgn); + if ( (**sClipRgn).rgnSize > 128 ) + { + ::EmptyRgn(sClipRgn); + } + sSetClip = false; + } +} + + +/*====================================================================================== + Return true if inInsideRect is completely enclosed by inEnclosingRect. +======================================================================================*/ + +Boolean StSectClipRgnState::RectEnclosesRect(const Rect *inEnclosingRect, + const Rect *inInsideRect) +{ + + return ((inEnclosingRect->left <= inInsideRect->left) && + (inEnclosingRect->top <= inInsideRect->top) && + (inEnclosingRect->right >= inInsideRect->right) && + (inEnclosingRect->bottom >= inInsideRect->bottom)); +} + + +#pragma mark - + +#if 0 +/*====================================================================================== + Determine if the specified cell is selected. +======================================================================================*/ + +Boolean CRowTableSelector::CellIsSelected(const STableCell &inCell) const +{ + if ( GetSelectedRowCount() > 0 ) +{ + + AssertFail_(GetCount() == GetSelectedRowCount()); + UInt32 id = GetRowUniqueID(inCell.row); + + if ( id != eInvalidID ) + { + return (FetchIndexOf(&id) != LArray::index_Bad); + } + } + + return false; +} + + +/*====================================================================================== + Select the specified cell. +======================================================================================*/ + +void CRowTableSelector::DoSelect(const TableIndexT inRow, Boolean inSelect, + Boolean inHilite, Boolean inNotify) +{ + UInt32 id = GetRowUniqueID(inRow); + if ( id == eInvalidID ) return; + + STableCell theCell(inRow, 1); + const ArrayIndexT index = FetchIndexOf(&id); + + if ( (index != LArray::index_Bad) == inSelect ) return; + + if ( inSelect && !mAllowMultiple ) + { + if ( mSelectedRowCount > 0 ) + { + DoSelectAll(false, false); + mSelectedRowCount = 0; // to be safe + } + } + + // Maintain the count + if ( inSelect ) + { + InsertItemsAt(1, LArray::index_First, &id); + ++mSelectedRowCount; + } + + if ( inHilite ) + { + mTableView->HiliteCell(theCell, inSelect); + + /* + // hilite (or unhilite) the entire row + UInt32 nCols, nRows; + + mTableView->GetTableSize(nRows, nCols); + + theCell.row = inRow; + for (theCell.col = 1; theCell.col <= nCols; theCell.col++) + { + mTableView->HiliteCell(theCell, inSelect); + } + */ + } + + if ( !inSelect ) + { + RemoveItemsAt(1, index); + --mSelectedRowCount; + } + + if ( inNotify ) mTableView->SelectionChanged(); +} + + +/*====================================================================================== + Remove the specified cells from the table. +======================================================================================*/ + +void CRowTableSelector::RemoveRows(Uint32 inHowMany, TableIndexT inFromRow) +{ + Boolean notify = false; + Assert_(mSelectedRowCount == mItemCount); + // mSelectedRowCount is inherited from LTableRowSelector. + // mItemCount is inherited from LArray. + // They should always agree! + if (mRowCount== 0) + { + AssertFail_(mRowCount >= mSelectedRowCount); // # selected <= # total rows + TableIndexT cols; + mTableView->GetTableSize(mRowCount, cols); + return; // nothing to do. + } + if ( inHowMany >= mRowCount ) + { + // Removing all rows from the table + AssertFail_(mRowCount >= mSelectedRowCount); // # selected <= # total rows + mRowCount = 0; // maintain the table count + if (mItemCount > 0) // clear the array + RemoveItemsAt(LArray::index_Last, LArray::index_First); + notify = (mSelectedRowCount > 0); + mSelectedRowCount = 0; + } + else + { + UInt32 curRow, nRows = mRowCount; // (table row count) + if ( nRows > (inFromRow + inHowMany - 1) ) + nRows = (inFromRow + inHowMany - 1); + for (curRow = inFromRow; curRow <= nRows; ++curRow) + { + UInt32 id = GetRowUniqueID(curRow); + ArrayIndexT index = FetchIndexOf(&id); + if ( index != LArray::index_Bad ) + { + RemoveItemsAt(1, index); + --mSelectedRowCount; + notify = true; + } + } + AssertFail_(mRowCount >= inHowMany); + mRowCount -= inHowMany; + } + + if ( notify ) mTableView->SelectionChanged(); +} + + +/*====================================================================================== + Return the first selected cell's row, defined as the min row & col (closest to + top-left corner). Return 0 if no row is selected. +======================================================================================*/ + +TableIndexT CRowTableSelector::GetFirstSelectedRow() const +{ + Int32 count = GetCount(); + TableIndexT row = 0x7FFFFFFF; + + if ( count > 0 ) +{ + + Int32 curItem = 1; + + do + { + TableIndexT curRow = GetUniqueIDRow(*((UInt32 *) GetItemPtr(curItem))); + if ( curRow < row ) row = curRow; + } while ( ++curItem <= count ); + } + + return ((row == 0x7FFFFFFF) ? 0 : row); +} + + +/*====================================================================================== + Return an array of selected row indices with the base given by inBase. The caller + must dispose of the returned memory using ::DisposePtr(). The function returns + the number of elements in outIndices, and 0 if no elements are selected and + nothing is allocated. +======================================================================================*/ + +TableIndexT CRowTableSelector::GetSelectedIndices(TableIndexT **outIndices, Int16 inBase) const +{ + TableIndexT count = GetCount(); + *outIndices = nil; + + if ( count > 0 ) +{ + + *outIndices = (TableIndexT *) XP_ALLOC(count * sizeof(TableIndexT)); + FailNIL_(*outIndices); + TableIndexT curItem = 1; + + do + { + (*outIndices)[curItem - 1] = + GetUniqueIDRow(*((UInt32 *) GetItemPtr(curItem))) - 1 + inBase; + } while ( ++curItem <= count ); + } + + return count; +} + #endif + +#pragma mark - +#if 0 +/*====================================================================================== + Adjust the window to stored preferences. +======================================================================================*/ + +void CExTable::ReadSavedTableStatus(LStream *inStatusData) +{ + if ( inStatusData != nil ) + { + + AssertFail_(mTableHeader != nil); + + mTableHeader->ReadColumnState(inStatusData); + } +} + + +/*====================================================================================== + Get window stored preferences. +======================================================================================*/ + +void CExTable::WriteSavedTableStatus(LStream *outStatusData) +{ + AssertFail_(mTableHeader != nil); + + mTableHeader->WriteColumnState(outStatusData); +} + + +/*====================================================================================== + Find status. Override it because of the overload in the inherited class. +======================================================================================*/ + +void CExTable::FindCommandStatus(CommandT inCommand, Boolean &outEnabled, Boolean &outUsesMark, + Char16 &outMark, Str255 outName) +{ + CStandardFlexTable::FindCommandStatus(inCommand, outEnabled, outUsesMark, + outMark, outName); +} + + +/*====================================================================================== + Obey commands. +======================================================================================*/ + +Boolean CExTable::ObeyCommand(CommandT inCommand, void *ioParam) +{ + Boolean cmdHandled = true; + + switch (inCommand) + { + + case msg_TabSelect: + break; + + default: + cmdHandled = CStandardFlexTable::ObeyCommand(inCommand, ioParam); + break; + } + + return cmdHandled; +} + + +/*====================================================================================== + Draw when disabled as grayish text. +======================================================================================*/ + +void CExTable::ScrollBits(Int32 inLeftDelta, Int32 inTopDelta) +{ + USearchHelper::ScrollViewBits(this, inLeftDelta, inTopDelta); +} + + +/*====================================================================================== + Draw the table. +======================================================================================*/ + +void CExTable::DrawSelf() +{ + UTextTraits::SetPortTextTraits(mTextTraitsID); + + RgnHandle localUpdateRgnH = GetLocalUpdateRgn(); + Rect updateRect = (**localUpdateRgnH).rgnBBox; + ::DisposeRgn(localUpdateRgnH); + + STableCell topLeftCell, botRightCell; + FetchIntersectingCells(updateRect, topLeftCell, botRightCell); + + Boolean isActive = IsActive() && IsTarget(); + + STableCell cell(1, 1); + for (cell.row = topLeftCell.row; cell.row <= botRightCell.row; cell.row++) + { + DrawCellRow(cell, false, isActive); + } + +} + + +/*====================================================================================== + Activate the table. +======================================================================================*/ + +void CExTable::ActivateSelf() +{ + FocusDrawSelectedCells(true); + // Do nothing +} + + +/*====================================================================================== + Deactivate the table. +======================================================================================*/ + +void CExTable::DeactivateSelf() +{ + // Do nothing +} + + +/*====================================================================================== + Become the current target. +======================================================================================*/ + +void CExTable::BeTarget() +{ + FocusDrawSelectedCells(true); +} + + +/*====================================================================================== + Don't be the current target. +======================================================================================*/ + +void CExTable::DontBeTarget() +{ + StValueChanger change(mFlags, mFlags | flag_IsLoosingTarget); + FocusDrawSelectedCells(true); +} + + +/*====================================================================================== + Hilite the current selection. +======================================================================================*/ + + +void CExTable::HiliteSelection(Boolean /* inActively */, Boolean inHilite ) +{ + if ( mFlags & flag_IsDrawingSelf ) return; + FocusDrawSelectedCells(inHilite); +} + + +/*====================================================================================== + Hilite the current selection. +======================================================================================*/ + +void CExTable::HiliteCellInactively(const STableCell &inCell, Boolean inHilite) +{ + FocusDrawSelectedCells(inHilite, &inCell); +} + + +/*====================================================================================== + Hilite the current selection. +======================================================================================*/ + +void CExTable::HiliteCellActively(const STableCell &inCell, Boolean inHilite) +{ + FocusDrawSelectedCells(inHilite, &inCell); +} + + +/*====================================================================================== + Focus the table and draw the specified cell. +======================================================================================*/ + +void CExTable::FocusDrawSelectedCells(Boolean inHilite, const STableCell *inCell) +{ + TableIndexT startRow, endRow; + + if ( FocusExposed() && GetVisibleRowRange(&startRow, &endRow) ) + { + STableCell selectedCell; + if ( inCell == nil ) + { + selectedCell.row = startRow; + selectedCell.col = 1; + } else + { + selectedCell = *inCell; + } + if ( IsValidCell(selectedCell) && (selectedCell.row >= startRow) && + (selectedCell.row <= endRow) ) + { + Boolean isActive = (DrawFullHiliting() && IsActive() && IsTarget()); + + StValueChanger change(mFlags, inHilite ? mFlags : + mFlags | flag_DontDrawHiliting); + UTextTraits::SetPortTextTraits(mTextTraitsID); + StColorPenState::Normalize(); + do + { + if ( CellIsSelected(selectedCell) ) + { + DrawCellRow(selectedCell, true, isActive); + } + } while ( (inCell == nil) && (++selectedCell.row <= endRow) ); + } + } +} + + +/*====================================================================================== + Draw the table. +======================================================================================*/ + +void CExTable::DrawCellRow(STableCell &inCell, Boolean inErase, Boolean inActive) +{ + TableIndexT numRows, numCols; + GetTableSize(numRows, numCols); + + Rect hiliteRect, cellRect; + + // Draw the hiliting + + Boolean drawHilited = CellIsSelected(inCell) && DrawHiliting(); + + if ( drawHilited || inErase ) + { + GetLocalCellRect(STableCell(inCell.row, 1), hiliteRect); + GetLocalCellRect(STableCell(inCell.row, numCols), cellRect); + + hiliteRect.right = cellRect.right; + } + + if ( drawHilited ) + { + RGBColor hiliteColor; + LMGetHiliteRGB(&hiliteColor); + + Int16 depth; + CDeviceLoop deviceLoop(hiliteRect, depth); + + if ( depth ) do + { + mBlackAndWhiteHiliting = inActive && ((depth < 4) || ((hiliteColor.red == 0) && + (hiliteColor.green == 0) && + (hiliteColor.blue == 0))); + if ( mBlackAndWhiteHiliting ) + { + ::ForeColor(blackColor); + } else + { + ::RGBForeColor(&hiliteColor); + } + if ( inActive ) + { + ::PaintRect(&hiliteRect); + } else + { + if ( inErase ) + { + ::InsetRect(&hiliteRect, 1, 1); + ::EraseRect(&hiliteRect); + ::InsetRect(&hiliteRect, -1, -1); + } + ::FrameRect(&hiliteRect); + } + + ::ForeColor(blackColor); + + for (inCell.col = 1; inCell.col <= numCols; inCell.col++) + { + GetLocalCellRect(inCell, cellRect); + DrawCell(inCell, cellRect); + } + } while ( deviceLoop.NextDepth(depth) ); + + } else +{ + + mBlackAndWhiteHiliting = false; // Doesn't matter here, we aren't drawing hilited + + if ( inErase ) ::EraseRect(&hiliteRect); + + // Draw the cell data + + for (inCell.col = 1; inCell.col <= numCols; inCell.col++) + { + GetLocalCellRect(inCell, cellRect); + DrawCell(inCell, cellRect); + } + } + + inCell.col = numCols; +} + + +/*====================================================================================== + Draw hilited text in the table. +======================================================================================*/ + +void CExTable::PlaceHilitedTextInRect(const char *inText, Uint32 inTextLength, const Rect &inRect, + Int16 inHorizJustType, Int16 inVertJustType, + const FontInfo *inFontInfo, Boolean inDoTruncate, + TruncCode inTruncWhere) +{ + if ( inTextLength > 0 ) + { + RGBColor foreColor; + ::GetForeColor(&foreColor); + Boolean drawBW = GetBlackAndWhiteHiliting(); + ::ForeColor(drawBW ? whiteColor : blackColor); + UGraphicGizmos::PlaceTextInRect(inText, inTextLength, inRect, inHorizJustType, + inVertJustType, inFontInfo, inDoTruncate, + inTruncWhere); + ::RGBForeColor(&foreColor); + } +} + + +/*====================================================================================== + Get the range of visible rows in the table's frame. +======================================================================================*/ + +Boolean CExTable::GetVisibleRowRange(TableIndexT *inStartRow, TableIndexT *inEndRow) +{ + Rect visRect; + GetRevealedRect(visRect); // Check if Table is revealed + + if ( !::EmptyRect(&visRect) ) + { + PortToLocalPoint(topLeft(visRect)); + PortToLocalPoint(botRight(visRect)); + STableCell topLeftCell, botRightCell; + FetchIntersectingCells(visRect, topLeftCell, botRightCell); + if ( IsValidCell(topLeftCell) && IsValidCell(botRightCell) ) + { + *inStartRow = topLeftCell.row; + *inEndRow = botRightCell.row; + return true; + } + } + + return false; +} + +void CExTable::ChangeStarting(MSG_Pane *inPane, MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, SInt32 inRowCount) +{ + switch ( inChangeCode ) + { + // Do this before the change occurs, because the code for removing + // the rows queries the backend for the message key. By the time the change + // has finished, this call won't work. + case MSG_NotifyInsertOrDelete: + if ( inRowCount < 0 ) + RemoveRows(-inRowCount, inStartRow, true); + break; + default: + Inherited::ChangeStarting(inPane, inChangeCode, inStartRow, inRowCount); + break; + } +} + +/*====================================================================================== + Notification from the BE that something is finished changing in the display table. +======================================================================================*/ + +void CExTable::ChangeFinished(MSG_Pane */*inPane*/, MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, SInt32 inRowCount) +{ + if ( (--mMysticPlane) > (kMysticUpdateThreshHold + 1) ) return; + + MsgPaneChanged(); + + switch ( inChangeCode ) + { + case MSG_NotifyInsertOrDelete: + Assert_(inRowCount != 0); + if ( inRowCount > 0 ) + { + InsertRows(inRowCount, inStartRow - 1, nil, 0, true); + } else + { + //RemoveRows(-inRowCount, inStartRow, true); See ChangeStarting(); + } + break; + + case MSG_NotifyChanged: + { + STableCell topLeft, botRight; + TableIndexT rows; + + topLeft.col = 1; + topLeft.row = inStartRow; + + GetTableSize(rows, botRight.col); + botRight.row = inStartRow + inRowCount - 1; + RefreshCellRange(topLeft, botRight); + } + break; + + case MSG_NotifyScramble: + case MSG_NotifyAll: + if ( mMysticPlane < kMysticUpdateThreshHold ) + { + SInt32 diff = GetBENumRows() - mRows; + if ( diff > 0 ) + { + InsertRows(diff, 1, nil, 0, false); + } else if ( diff < 0 ) + { + RemoveRows(-diff, 1, false); + } + Refresh(); + } + break; + + default: + break; + } +} +#endif diff --git a/mozilla/cmd/macfe/MailNews/SearchHelpers.h b/mozilla/cmd/macfe/MailNews/SearchHelpers.h new file mode 100644 index 00000000000..7fe2a284c45 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/SearchHelpers.h @@ -0,0 +1,561 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// SearchHelpers.h + +#pragma once + +/*====================================================================================== + DESCRIPTION: Defines classes related to the Mail & News search dialog. +======================================================================================*/ + + +/*====================================================================================*/ + #pragma mark INCLUDE FILES +/*====================================================================================*/ + +#include "CDateView.h" +#include "CGAIconPopup.h" +#include "CSaveWindowStatus.h" +#include "LTableRowSelector.h" +#include "CMailFlexTable.h" + +#include +#include + +// FE Stuff + +#include "macutil.h" + +class CFolderScopeGAPopup; +class CSearchPopupMenu; +class LBroadcasterEditField; +class CSearchDateField; +class CSearchTabGroup; +class LTableView; +class CString; + +#pragma mark - +/*====================================================================================*/ + #pragma mark TYPEDEFS +/*====================================================================================*/ + +typedef struct MSG_SearchMenuItem MSG_SearchMenuItem; + + +#pragma mark - +/*====================================================================================*/ + #pragma mark CONSTANTS +/*====================================================================================*/ + + +#pragma mark - +/*====================================================================================*/ + #pragma mark EXTERNAL FUNCTION PROTOTYPES +/*====================================================================================*/ + +inline Boolean IsBetween(Int32 inVal, Int32 inMin, Int32 inMax) { + return ((inVal >= inMin) && (inVal <= inMax)); +} + +#ifdef DEBUGGER_ASSERTIONS + #undef AssertFail_ + #undef Assert_ + #ifdef Debug_Signal + #define AssertFail_(test) ThrowIfNot_(test) + #define Assert_(test) do { \ + if ( !(test) ) { \ + Try_ { \ + Throw_(noErr); \ + } \ + Catch_(inErr) { \ + } \ + EndCatch_ \ + } \ + } while ( 0 ) + #else // Debug_Signal + #define AssertFail_(test) ((void) 0) + #define Assert_(test) ((void) 0) + #endif // Debug_Signal +#endif // DEBUGGER_ASSERTIONS + +#undef FailNILRes_ +#define FailNILRes_(res) ThrowIfResFail_(res) +#undef FailResErr_ +#define FailResErr_() ThrowIfResError_() + + +#pragma mark - +/*====================================================================================*/ + #pragma mark CLASS DECLARATIONS +/*====================================================================================*/ + +// USearchHelper + +class USearchHelper { + +public: + + // Indexes into STR#8600 + + enum { + uStr_AllMailFolders = 1 + , uStr_AllNewsgroups + , uStr_AllMailFoldersNewsgroups + , uStr_OneMessageFoundSelected + , uStr_NoMessagesFound + , uStr_MultipleMessagesFound + , uStr_OneMessageFound + , uStr_MultipleMessagesSelected + , uStr_SelectedMailFolders + , uStr_SelectedNewsgroups + , uStr_SelectedMailFoldersNewsgroups + , uStr_InFrontFolder + , uStr_SearchForMessages + , uStr_SearchForAddresses + , uStr_FilterDefaultName + , uStr_FilterDefaultDescription + , uStr_SearchFunctionNotImplemented + , uStr_SearchFunctionParamError + , uStr_ExceptionErrorOccurred + , uStr_FilterFunctionNotImplemented + , uStr_FilterFunctionParamError + , uStr_SearchFunctionNothingToSearch + }; + + static void RegisterClasses(); + + static LControl *FindViewControl(LView *inView, PaneIDT inID); + static LPane *FindViewSubpane(LView *inView, PaneIDT inID); + static LPane *FindViewVisibleSubpane(LView *inView, PaneIDT inID); + static LView *FindViewSubview(LView *inView, PaneIDT inID); + static LBroadcasterEditField *FindViewEditField(LView *inView, PaneIDT inID); + static CSearchPopupMenu *FindSearchPopup(LView *inView, PaneIDT inID); + static CFolderScopeGAPopup *FindFolderScopePopup(LView *inView, PaneIDT inID); + static CSearchDateField *FindViewDateField(LView *inView, PaneIDT inID); + static CSearchTabGroup *FindWindowTabGroup(LArray *inSubcommanders); + + static void RemoveSizeBoxFromVisRgn(LWindow *inWindow); + static void RefreshPortFrameRect(LPane *inPane, Rect *inPortRect); + static void RefreshLocalFrameRect(LPane *inPane, Rect *inLocalRect); + + static LWindow *GetPaneWindow(LPane *inPane); + static void LinkListenerToBroadcasters(LView *inContainer, LListener *inListener); + static Int16 GetEditFieldLength(LEditField *inEditField); + static void AssignUString(Int16 inStringIndex, CString& outString); + static void EnableDisablePane(LPane *inPane, Boolean inEnable, Boolean inRefresh = true); + static void ShowHidePane(LPane *inPane, Boolean inDoShow); + static void SetPaneDescriptor(LPane *inPane, const CStr255& inDescriptor); + static void SetDefaultButton(LView *inView, PaneIDT inID, Boolean inIsDefault = true); + static void SelectEditField(LEditField *inEditField); + static void SelectDateView(CDateView *inDateView); + static Boolean LastPaneWasActiveClickPane(); + static void CalcDiscreteWindowResizing(LWindow *inWindow, Rect *ioNewBounds, + const Int16 inHIncrement, const Int16 inVIncrement, + Boolean inCheckDesktop = true, Int16 inSizeBoxLeft = 15, + Int16 inSizeBoxTop = 15); + static void ScrollViewBits(LView *inView, Int32 inLeftDelta, Int32 inTopDelta); + + enum { + char_OptionKey = 0x003A + , char_ControlKey = 0x003B + , char_CommandKey = 0x0037 + , char_ShiftKey = 0x0038 + }; + static Boolean KeyIsDown(Int16 inKeyCode); + static void GenericExceptionAlert(OSErr inErr); + + + enum { + paneID_Okay = 'OKAY' // OK button + , paneID_Cancel = 'CNCL' // Cancel button + }; + static Boolean HandleDialogKey(LView *inView, const EventRecord &inKeyEvent); + + static Boolean MenuItemIsEnabled(MenuHandle inMenuH, Int16 inItem) { + return ((**inMenuH).enableFlags & (1L<<(inItem - 0))); + } + +#ifdef NOT_YET + static void GetFolderDisplayName(const char *inFolderName, + CStr255& outFolderName); +#endif // NOT_YET + +private: + + // Instance variables ========================================================== + + static CQDProcs sStdProcs; +}; + +//////////////////////////////////////////////////////////////////////////////////////////// +// CSearchPopupMenu + +class CSearchPopupMenu : public CGAIconPopup { + + + typedef CGAIconPopup Inherited; + + +public: + + enum { class_ID = 'SPUP' }; + + CSearchPopupMenu(LStream *inStream); + + Boolean IsLatentVisible() { + return ((mVisible == triState_Latent) || (mVisible == triState_On)); + } + + enum { cmd_InvalidMenuItem = -1, cmd_DividerMenuItem = -2 }; + + virtual CommandT GetCurrentItemCommandNum(Int16 inMenuItem = 0); + Int16 GetCommandItemIndex(CommandT inMenuCommand); + void SetCurrentItemByCommand(CommandT inMenuCommand); + Int16 GetNumCommands() { + return mCommands.GetCount(); + } + CommandT GetOldItemCommand() const; + + virtual void SetValue(Int32 inValue); + void SetToDefault() { + SetValue(mDefaultItem); + } + + void PopulateMenu(MSG_SearchMenuItem *inMenuItems, const Int16 inNumItems, + CommandT inNewCommandIfInvalid, UInt32 inPopulateID = 0); + void ClearMenu(Int16 inStartItem = 1); + void AppendMenuItemCommand(CommandT inCommand, ConstStr255Param inName, Boolean inIsEnabled); + void SetMenuItemCommand(CommandT inOldCommand, CommandT inNewCommand, + ConstStr255Param inName, Boolean inIsEnabled); + void SetIndexCommand(Int16 inIndex, CommandT inCommand) { + mCommands.AssignItemsAt(1, inIndex, &inCommand); + } + void RemoveMenuItemCommand(CommandT inRemoveCommand, + CommandT inNewCommandIfInvalid); +// unused code - 11/21/97 +// +// Boolean UpdateMenuItemCommand(Int16 inMenuItem, CommandT inNewCommand, +// ConstStr255Param inName, Boolean inIsEnabled); +// void SetCurrentItemByName(ConstStr255Param inName); + +protected: + + virtual void FinishCreateSelf(); + virtual void BroadcastValueMessage(); + virtual Boolean TrackHotSpot(Int16 inHotSpot, Point inPoint, Int16 inModifiers); + + // Instance variables ========================================================== + + LArray mCommands; + UInt32 mLastPopulateID; + Int16 mDefaultItem; + Int16 mOldValue; + +}; // CSearchPopupMenu + +// CSearchEditField + +class CSearchEditField : public LGAEditField { + + + typedef LGAEditField Inherited; + + +public: + + enum { class_ID = 'SEFD' }; + + // Broadcast messages + + enum { + msg_UserChangedText = 'cTxt' // PaneIDT * + }; + + CSearchEditField(LStream *inStream) : + LGAEditField(inStream) { + SetRefreshAllWhenResized(false); + } + + Int16 GetTextLength() { + return (**mTextEditH).teLength; + } + Boolean IsLatentVisible() { + return ((mVisible == triState_Latent) || (mVisible == triState_On)); + } + virtual void UserChangedText() { + PaneIDT paneID = mPaneID; + BroadcastMessage(msg_UserChangedText, &paneID); + } + virtual void SetDescriptor(ConstStr255Param inDescriptor); + + void SetText(const char *inText); + char *GetText(char *outText); + virtual void ResizeFrameBy(Int16 inWidthDelta, Int16 inHeightDelta, + Boolean inRefresh); + virtual Boolean HandleKeyPress(const EventRecord &inKeyEvent); + +protected: + +}; + + +// CSearchDateField + +class CSearchDateField : public CDateView { + +public: + + enum { class_ID = 'SDFD' }; + + CSearchDateField(LStream *inStream) : + CDateView(inStream) { + } + + Boolean IsLatentVisible() { + return ((mVisible == triState_Latent) || (mVisible == triState_On)); + } + +protected: + +}; + + +// CSearchTabGroup + +class CSearchTabGroup : public LTabGroup { + +public: + + // Stream creator method + + enum { class_ID = 'SrTg' }; + + CSearchTabGroup(LStream* /*inStream*/) : + LTabGroup(), + mCanRotate(nil) { + } + CSearchTabGroup() : + LTabGroup(), + mCanRotate(nil) { + } + + void SetRotateWatchValue(Boolean *inWatchValue) { + mCanRotate = inWatchValue; + } + virtual void RotateTarget(Boolean inBackward); + +protected: + + // Instance variables ========================================================== + + Boolean *mCanRotate; +}; + + +// CSearchCaption + +class CSearchCaption : public LCaption { + +public: + + // Stream creator method + + enum { class_ID = 'SrCp' }; + + CSearchCaption(LStream *inStream) : + LCaption(inStream) { + } + +protected: + + virtual void DrawSelf(); + virtual void EnableSelf() { + Draw(nil); + } + virtual void DisableSelf() { + Draw(nil); + } +}; + +// StTableClipRgnState + +class StSectClipRgnState { + +public: + + StSectClipRgnState(const Rect *inLocalRect, const Rect *inDisplayRect = nil); + ~StSectClipRgnState(); + + static Boolean RectEnclosesRect(const Rect *inEnclosingRect, const Rect *inInsideRect); + +protected: + + // Instance variables ========================================================== + + static RgnHandle sClipRgn; + static Boolean sSetClip; +#ifdef Debug_Signal + static Boolean sTableClipRgnAround; +#endif // Debug_Signal +}; + +#if 0 +// CRowTableSelector + +class CRowTableSelector : public LTableRowSelector { + +public: + + CRowTableSelector(LTableView *inTableView, Boolean inAllowMultiple = true) : + LTableRowSelector(inTableView, inAllowMultiple), + mRowCount(0) { + + mItemSize = sizeof(UInt32); + mComparator = LLongComparator::GetComparator(); + mKeepSorted = true; + } + + virtual Boolean CellIsSelected(const STableCell &inCell) const; + virtual TableIndexT GetSelectedIndices(TableIndexT **outIndices, Int16 inBase) const; + virtual TableIndexT GetFirstSelectedRow() const; + UInt32 GetRowCount() const { return mRowCount; } + +protected: + + enum { eInvalidID = 0xFFFFFFFF }; + virtual UInt32 GetRowUniqueID(const TableIndexT inRow) const = 0L; + virtual TableIndexT GetUniqueIDRow(const UInt32 inID) const = 0L; + + virtual void DoSelect(const TableIndexT inRow, Boolean inSelect, + Boolean inHilite, Boolean inNotify); + virtual void InsertRows(UInt32 inHowMany, TableIndexT /*inAfterRow*/) { + mRowCount += inHowMany; + } + virtual void RemoveRows(Uint32 inHowMany, TableIndexT inFromRow); + + // Instance variables ========================================================== + + UInt32 mRowCount; // Count of the number of rows in the table. Need this + // since RemoveRows() is called after rows have been removed! +}; +#endif //0 +#if 0 //0 +// CExTable + +class CExTable : public CMailFlexTable { + +private: + typedef CMailFlexTable Inherited; + +public: + + CExTable(LStream *inStream) : + CMailFlexTable(inStream), + mFlags(0), + mBlackAndWhiteHiliting(false) { + + } + + Boolean CellsAreSelected() { + return IsValidCell(GetFirstSelectedCell()); + } + TableIndexT GetNumRows() { + return mRows; + } + void SelectScrollCell(const STableCell &inCell) { + SelectCell(inCell); + ScrollCellIntoFrame(inCell); + } + + void RefreshRowRange(TableIndexT inStartRow, TableIndexT inEndRow) { + TableIndexT topRow, botRow; + if ( inStartRow < inEndRow ) { + topRow = inStartRow; botRow = inEndRow; + } else { + topRow = inEndRow; botRow = inStartRow; + } + RefreshCellRange(STableCell(topRow, 1), STableCell(botRow, mCols)); + } + virtual void ReadSavedTableStatus(LStream *inStatusData); + virtual void WriteSavedTableStatus(LStream *outStatusData); + virtual Boolean CellHasDropFlag(const STableCell &/*inCell*/, Boolean &/*outIsExpanded*/) const { return false; } + +protected: + + enum { // Flags + flag_DontDrawHiliting = 0x01 + , flag_IsDrawingSelf = 0x02 + , flag_IsLoosingTarget = 0x04 + }; + + virtual MSG_Pane *GetBEPane() const = 0L; + virtual Boolean IsMyPane(void* info) const { + return (GetBEPane() == reinterpret_cast(info)->pane); + } + + virtual void FindCommandStatus(CommandT inCommand, Boolean &outEnabled, Boolean &outUsesMark, + Char16 &outMark, Str255 outName); + virtual Boolean ObeyCommand(CommandT inCommand, void *ioParam = nil); + + virtual void ScrollBits(Int32 inLeftDelta, Int32 inTopDelta); + virtual void DrawSelf(); + virtual void ActivateSelf(); + virtual void DeactivateSelf(); + virtual void BeTarget(); + virtual void DontBeTarget(); + virtual void HiliteSelection(Boolean inActively, Boolean inHilite); + virtual void HiliteCellInactively(const STableCell &inCell, Boolean inHilite); + virtual void HiliteCellActively(const STableCell &inCell, Boolean inHilite); + + Boolean GetBlackAndWhiteHiliting() { + return mBlackAndWhiteHiliting; + } + Boolean DrawHiliting() { + return !(mFlags & flag_DontDrawHiliting); + } + Boolean DrawFullHiliting() { + return !(mFlags & flag_IsLoosingTarget); + } + + void FocusDrawSelectedCells(Boolean inHilite, const STableCell *inCell = nil); + void DrawCellRow(STableCell &inCell, Boolean inErase, Boolean inActive); + void PlaceHilitedTextInRect(const char *inText, Uint32 inTextLength, const Rect &inRect, + Int16 inHorizJustType, Int16 inVertJustType, + const FontInfo *inFontInfo, Boolean inDoTruncate, + TruncCode inTruncWhere); + Boolean GetVisibleRowRange(TableIndexT *inStartRow, TableIndexT *inEndRow); + + // Backend methods +#if 0 + virtual void MsgPaneChanged() = 0L; + virtual Int32 GetBENumRows() = 0L; + virtual void ChangeStarting(MSG_Pane *inPane, MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, SInt32 inRowCount); + virtual void ChangeFinished(MSG_Pane *inPane, MSG_NOTIFY_CODE inChangeCode, + TableIndexT inStartRow, SInt32 inRowCount); +#endif + // Instance variables ========================================================== + + UInt8 mFlags; + Boolean mBlackAndWhiteHiliting; +}; + +#endif diff --git a/mozilla/cmd/macfe/MailNews/StGetInfoHandler.cp b/mozilla/cmd/macfe/MailNews/StGetInfoHandler.cp new file mode 100644 index 00000000000..c0d8182fe27 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/StGetInfoHandler.cp @@ -0,0 +1,1229 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// StGetInfoHandler.cp + +#include "StGetInfoHandler.h" + +#include "XP_Core.h" // typedefs +#include "msgcom.h" + +#include "CMessageFolder.h" +#include "CMailNewsContext.h" +#include "MPreference.h" +#include "CPasteSnooper.h" +#include "UMailFilters.h" +//#include "MailNewsMediators.h" + +#include "ufilemgr.h" +#include "prefapi.h" + +#include + + +//====================================== +#pragma mark +class StFolderGetInfoHandler : public StGetInfoHandler +//====================================== +{ + public: + StFolderGetInfoHandler( + ResIDT resID, + CMessageFolder& folder, + LCommander *inSuperCommander); +//---- +// Data +//---- + CMessageFolder mFolder; + Int32 mFolderPrefFlags; +}; // class StFolderGetInfoHandler + +//====================================== +#pragma mark +class StMailFolderGetInfoHandler : public StFolderGetInfoHandler +//====================================== +{ + public: + StMailFolderGetInfoHandler( + CMessageFolder& folder, + LCommander *inSuperCommander) + : StFolderGetInfoHandler(12500, folder, inSuperCommander) {} + ~StMailFolderGetInfoHandler(); + virtual void ReadDataFields(); + virtual void WriteDataFields(); + virtual Boolean UserDismissed(); +}; // class StMailFolderGetInfoHandler + +//====================================== +#pragma mark +class StNewsGroupGetInfoHandler : public StFolderGetInfoHandler +//====================================== +{ + public: + StNewsGroupGetInfoHandler( + CMessageFolder& folder, + LCommander *inSuperCommander) + : StFolderGetInfoHandler(12501, folder, inSuperCommander) {} + ~StNewsGroupGetInfoHandler(); + virtual void ReadDataFields(); + virtual void WriteDataFields(); + virtual Boolean UserDismissed(); +//---- +// Data +//---- + XP_Bool mUseRetrievalDefaults, mUseBodyPurgeDefaults, mUseHeaderPurgeDefaults; + XP_Bool mByReadness; + XP_Bool mUnreadOnly; + XP_Bool mByDate; + XP_Bool mHTML; + int32 mDaysOld; + int32 mHeaderDaysKeep; + int32 mBodyDaysKeep; + XP_Bool mKeepUnreadOnly; + MSG_PurgeByPreferences mBodyPurgeBy; + MSG_PurgeByPreferences mHeaderPurgeBy; + int32 mNumHeadersToKeep; +}; // class StNewsGroupGetInfoHandler + +//====================================== +class StNewsHostGetInfoHandler : public StGetInfoHandler +//====================================== +{ + public: + StNewsHostGetInfoHandler( + MSG_Host* inHost, + LCommander *inSuperCommander); + ~StNewsHostGetInfoHandler(); + virtual void ReadDataFields(); + virtual void WriteDataFields(); +//---- +// Data +//---- + MSG_Host* mHost; + XP_Bool mPromptPassword; +}; // class StNewsHostGetInfoHandler + + +//====================================== +#pragma mark +class StMailServerInfoHandler : public StGetInfoHandler +//====================================== +{ + private: + typedef StGetInfoHandler Inherited; + + public: + enum { + kMailServerNameCaption = 'SNAM', + kMailServerNameEditText = 'SNME', + kMailServerTypeCaption = 'TYPE', + kMailServerTypePopup = 'TYPP', + kIMAPServerUserNameEditText = 'IUSR', + kPOPServerUserNameEditText = 'PUSR', + kIMAPServerSavePasswordCheckbox = 'IRMB', + kPOPServerSavePasswordCheckbox = 'PRMB', + kIMAPServerCheckMailCheckbox = 'ICHK', + kPOPServerCheckMailCheckbox = 'PCHK', + kIMAPServerCheckTimeEditText = 'IMIN', + kPOPServerCheckTimeEditText = 'PMIN' + }; + + StMailServerInfoHandler( + CStr255& ioName, + Boolean& ioPOP, + Boolean inIsNewServer, + Boolean inAllowServerNameEdit, + Boolean inAllowServerTypeEdit, + ServerNameValidationFunc inValidationFunc, + CServerListMediator* inValidationServerList, + LCommander* inSuperCommander); + + ~StMailServerInfoHandler(); + virtual void ReadDataFields(); + virtual void WriteDataFields(); + virtual Boolean UserDismissed(); + virtual Boolean GetPop() { return mPOP; } + virtual void GetServerName(CStr255 &outName); + + protected: + void SetPOP(Boolean inDialogInit); + void SetIMAP(Boolean inDialogInit); + void SetDefaultUserName(); +//---- +// Data +//---- + protected: + + + Boolean mPOP; + Boolean mOriginallyPOP; + Boolean mIsNewServer; + Boolean mAllowServerNameEdit; + Boolean mAllowServerTypeEdit; + CStr255 mName; + ServerNameValidationFunc mServerNameValidationFunc; + CServerListMediator* mValidationServerList; + + enum { kNumPolyprefControls = 4 }; + enum PopupValues { kPOP = 1, kIMAP = 2 }; + // Popup menu values are 1-based. So + // these are the actual IntPref values PLUS ONE +}; // class StMailServerInfoHandler + +#pragma mark - + +//---------------------------------------------------------------------------------------- +StGetInfoHandler::~StGetInfoHandler() +//---------------------------------------------------------------------------------------- +{ + Assert_( mMessage == msg_Cancel || mDataWritten); +} + +//---------------------------------------------------------------------------------------- +MessageT StGetInfoHandler::DoDialog() +//---------------------------------------------------------------------------------------- +{ + ReadDataFields(); + do { + mMessage = StStdDialogHandler::DoDialog(); + } while (!UserDismissed()); + + return mMessage; +} + +//---------------------------------------------------------------------------------------- +Boolean StGetInfoHandler::UserDismissed() +//---------------------------------------------------------------------------------------- +{ + return (mMessage == msg_OK) || (mMessage == msg_Cancel); +} + + +//---------------------------------------------------------------------------------------- +void StGetInfoHandler::HideAndCripplePane(PaneIDT paneID) +//---------------------------------------------------------------------------------------- +{ + HidePane(paneID); + MPreferenceBase::SetPaneWritePref(GetDialog(), paneID, false); +} + + +//---------------------------------------------------------------------------------------- +void StGetInfoHandler::ShowAndUncripplePane(PaneIDT paneID) +//---------------------------------------------------------------------------------------- +{ + ShowPane(paneID); + MPreferenceBase::SetPaneWritePref(GetDialog(), paneID, true); +} + + +#pragma mark - + +//---------------------------------------------------------------------------------------- +StFolderGetInfoHandler::StFolderGetInfoHandler( + ResIDT inResID, + CMessageFolder& inFolder, + LCommander *inSuperCommander) +//---------------------------------------------------------------------------------------- +: StGetInfoHandler(inResID, inSuperCommander) +, mFolder(inFolder) +, mFolderPrefFlags((inFolder.GetFolderPrefFlags())) +{ + mFolder.FolderInfoChanged(); // make sure it's the latest. +} + +#pragma mark - + +//---------------------------------------------------------------------------------------- +StMailFolderGetInfoHandler::~StMailFolderGetInfoHandler() +//---------------------------------------------------------------------------------------- +{ + if (mMessage != msg_Cancel) + WriteDataFields(); +} + +//---------------------------------------------------------------------------------------- +void StMailFolderGetInfoHandler::ReadDataFields() +//---------------------------------------------------------------------------------------- +{ + { // <- scope for buffer string (allow compiler to recycle) + char buffer[256]; + strcpy(buffer, mFolder.GetName()); + NET_UnEscape(buffer); + SetText('FNAM', CStr255(buffer)); + } // <- scope for buffer string (allow compiler to recycle) + + if (mFolder.IsLocalMailFolder()) + { + HidePane('SHAR'); // Hide the tab button for "sharing" + HidePane('CNTC'); // Hide the tab button for "download" + + const char* unixPath = MSG_GetFolderNameFromID(mFolder.GetFolderInfo()); + // note: this is a shared reference, should not be freed. + if (unixPath) + { + XP_StatStruct stats; + if (XP_Stat(unixPath, &stats, xpMailFolder) == 0) + { + SetNumberText('USED', stats.st_size); + } + char* macPath = CFileMgr::MacPathFromUnixPath(unixPath); + if (macPath) + SetText('LNAM', CStr255(macPath)); + XP_FREEIF((char*)macPath); + } + int32 deletedBytes = mFolder.GetDeletedBytes(); + if (deletedBytes >= 0) + SetNumberText('WAST', deletedBytes); + } + else // remote folder + { + HidePane('WPan'); // Hide all the disk/wasted stats and the cleanup button. + + + MSG_FolderInfo* theFolderInfo = mFolder.GetFolderInfo(); + + Assert_(theFolderInfo); + + // fill in the folder type in caption 'FTYP' + char *folderType = ::MSG_GetFolderTypeName(theFolderInfo); + + if (folderType) + SetText('FTYP', CStr255(folderType)); + XP_FREEIF((char*)folderType); + + + // fill in the folder description in caption 'FDSC' + char *folderDesc = ::MSG_GetFolderTypeDescription(theFolderInfo); + + if (folderDesc) + SetText('FDSC', CStr255(folderDesc)); + XP_FREEIF((char*)folderDesc); + + // fill in folder permissions in 'PERT' + + char *folderRightsString = ::MSG_GetACLRightsStringForFolder(theFolderInfo); + if (folderRightsString) + SetText('PERT', CStr255(folderRightsString)); + XP_FREEIF((char*)folderRightsString); + + if ( ::MSG_HaveAdminUrlForFolder( theFolderInfo, MSG_AdminFolder) ) + { + // show ACL panel 'SPRP' + LPane *aclPanelView = GetPane('SPRP'); + ThrowIfNil_(aclPanelView); + aclPanelView->Show(); + + // hide message 'NSHF' + LPane *noSharingMessage = GetPane('NSHF'); + ThrowIfNil_(noSharingMessage); + noSharingMessage->Hide(); + } + else + { + LPane *aclPanelView = GetPane('SPRP'); + ThrowIfNil_(aclPanelView); + aclPanelView->Hide(); + + // hide message 'NSHF' + LPane *noSharingMessage = GetPane('NSHF'); + ThrowIfNil_(noSharingMessage); + noSharingMessage->Show(); + } + + } + + SInt32 count = mFolder.CountUnseen(); + if (count >= 0) + SetNumberText('UNRD', count); + count = mFolder.CountMessages(); + if (count >= 0) + SetNumberText('TOTL', count); + SetBoolean('DnMe', (mFolderPrefFlags & MSG_FOLDER_PREF_OFFLINE) != 0); +} + +//---------------------------------------------------------------------------------------- +void StMailFolderGetInfoHandler::WriteDataFields() +//---------------------------------------------------------------------------------------- +{ + CStr255 newName; + // we do this to get around the fact that CString (from which CStr255 + // is derived) implememts operator const char*() by using a circular buffer + // of 4 static strings. If more than four clients use this "cast", the + // first client's string will be overwritten. Here, MSG_RenameMailFolder + // ends up presenting a prompt dialog, and enough calls to + // CString::operator const char*() are made that this happens! + GetText('FNAM', newName); + if (newName != CStr255(mFolder.GetName())) + { + char* temp = ::NET_Escape((const char *)newName, URL_PATH); + if (temp) + ::MSG_RenameMailFolder(mFolder.GetFolderPane(), mFolder.GetFolderInfo(), temp); + XP_FREEIF(temp); + } + if (GetBoolean('DnMe')) + mFolderPrefFlags |= MSG_FOLDER_PREF_OFFLINE; + else + mFolderPrefFlags &= ~MSG_FOLDER_PREF_OFFLINE; + MSG_SetFolderPrefFlags(mFolder.GetFolderInfo(), mFolderPrefFlags); + mDataWritten = true; +} + +//---------------------------------------------------------------------------------------- +Boolean StMailFolderGetInfoHandler::UserDismissed() +//---------------------------------------------------------------------------------------- +{ + if (mMessage == 'CMPR') + { + MSG_ViewIndex index = mFolder.GetIndex(); + ::MSG_Command( + mFolder.GetFolderPane(), + MSG_CompressFolder, + &index, + 1); + return true; + } + + // user clicked privileges button. Do ACL URL + if (mMessage == 'PRIV') + { + // Get ourselves a context for the URL dispatch. + // If it's not a browser context, the right thing happens and a browser context is made + MSG_Pane *folderPane = mFolder.GetFolderPane(); + + Assert_(folderPane); // since this properties dialog was brough up from the folder pane, + // one should exist + + ::MSG_GetAdminUrlForFolder(::MSG_GetContext(folderPane), mFolder.GetFolderInfo(), MSG_AdminFolder); + } + + return StFolderGetInfoHandler::UserDismissed(); +} + +#pragma mark - + +//---------------------------------------------------------------------------------------- +StNewsGroupGetInfoHandler::~StNewsGroupGetInfoHandler() +//---------------------------------------------------------------------------------------- +{ + if (mMessage != msg_Cancel) + WriteDataFields(); +} + +//---------------------------------------------------------------------------------------- +void StNewsGroupGetInfoHandler::ReadDataFields() +//---------------------------------------------------------------------------------------- +{ + CStr255 prettyName(mFolder.GetPrettyName()); + if (prettyName.Length() > 0) + { + SetText('FNAM', prettyName); + CStr255 geekNameFormat; + GetText('GNAM', geekNameFormat); + char buf[255]; + XP_SPRINTF(buf, geekNameFormat, mFolder.GetName()); + SetText('GNAM', CStr255(buf)); + } + else + { + SetText('FNAM', CStr255(mFolder.GetName())); + SetText('GNAM', "\p"); + } + const char* unixPath = MSG_GetFolderNameFromID(mFolder.GetFolderInfo()); + if (unixPath) + { + XP_StatStruct stats; + if (XP_Stat(unixPath, &stats, xpMailFolder) == 0) + { + SetNumberText('USED', stats.st_size); + } + char* macPath = CFileMgr::MacPathFromUnixPath(unixPath); + XP_FREEIF((char*)unixPath); + } + + SInt32 count = mFolder.CountUnseen(); + if (count >= 0) + SetNumberText('UNRD', count); + count = mFolder.CountMessages(); + if (count >= 0) + SetNumberText('TOTL', count); + SetBoolean('DnMe', (mFolderPrefFlags & MSG_FOLDER_PREF_OFFLINE) != 0); + + MSG_GetOfflineRetrievalInfo( + (MSG_FolderInfo*)mFolder.GetFolderInfo(), + &mUseRetrievalDefaults, + &mByReadness, + &mUnreadOnly, + &mByDate, + &mDaysOld); + SetBoolean('UDFL', mUseRetrievalDefaults); + if (mUseRetrievalDefaults) + { + LControl *theControl; + + XP_Bool unreadOnly = true; + PREF_GetBoolPref("offline.news.download.unread_only", &unreadOnly); + theControl = (LControl *)mDialog->FindPaneByID('OUNR'); + XP_ASSERT(theControl); + theControl->SetValue(unreadOnly); + theControl->Disable(); + + XP_Bool byDate = true; + PREF_GetBoolPref("offline.news.download.by_date", &byDate); + theControl = (LControl *)mDialog->FindPaneByID('BYDT'); + XP_ASSERT(theControl); + theControl->SetValue(byDate); + theControl->Disable(); + + int32 daysOld = 30; + PREF_GetIntPref("offline.news.download.days", &daysOld); + theControl = (LControl *)mDialog->FindPaneByID('DAYS'); + XP_ASSERT(theControl); + theControl->SetValue(daysOld); + theControl->Disable(); + } + else + { + SetBoolean('OUNR', mUnreadOnly); + SetBoolean('BYDT', mByDate); + SetNumberText('DAYS', mDaysOld); + } + + SetBoolean( + 'HTML', + ::MSG_IsHTMLOK(CMailNewsContext::GetMailMaster(), mFolder.GetFolderInfo()) + ); + //MSG_PurgeByPreferences bodyPurgeBy; + MSG_GetArticlePurgingInfo( + (MSG_FolderInfo*)mFolder.GetFolderInfo(), + &mUseBodyPurgeDefaults, + &mBodyPurgeBy, + &mBodyDaysKeep); + Assert_(mBodyPurgeBy != MSG_PurgeByNumHeaders); + SetBoolean('RmBd', mBodyPurgeBy == MSG_PurgeByAge); + SetNumberText('BdDy', mBodyDaysKeep); + + MSG_GetHeaderPurgingInfo( + (MSG_FolderInfo*)mFolder.GetFolderInfo(), + &mUseHeaderPurgeDefaults, + &mHeaderPurgeBy, + &mKeepUnreadOnly, + &mHeaderDaysKeep, + &mNumHeadersToKeep); + switch (mHeaderPurgeBy) + { + case MSG_PurgeNone: + SetBoolean('KpAl', true); + break; + case MSG_PurgeByAge: + SetBoolean('KpDa', true); + break; + case MSG_PurgeByNumHeaders: + SetBoolean('KpNw', true); + break; + } + SetBoolean('KpUn', mKeepUnreadOnly); + SetNumberText('KDYS', mHeaderDaysKeep); + SetNumberText('KpCt', mNumHeadersToKeep); + + // if we are offline the download now button needs to be disabled + LPane *downloadButton = mDialog->FindPaneByID('DOWN'); + XP_ASSERT(downloadButton != nil); + XP_Bool online = true; + PREF_GetBoolPref("network.online", &online); + if (!online) + downloadButton->Disable(); + +} // StNewsGroupGetInfoHandler::ReadDataFields + +//---------------------------------------------------------------------------------------- +void StNewsGroupGetInfoHandler::WriteDataFields() +//---------------------------------------------------------------------------------------- +{ + if (GetBoolean('DnMe')) + mFolderPrefFlags |= MSG_FOLDER_PREF_OFFLINE; + else + mFolderPrefFlags &= ~MSG_FOLDER_PREF_OFFLINE; + MSG_SetFolderPrefFlags((MSG_FolderInfo*)mFolder.GetFolderInfo(), mFolderPrefFlags); + + XP_Bool useDefaults = GetBoolean('UDFL'); + XP_Bool unreadOnly; + XP_Bool byReadness; + XP_Bool byDate; + int32 daysOld; + int32 headerDaysKeep = GetNumberText('KDYS'); + if (useDefaults) + { + // if we are using pref defaults then just restore what we read in + byReadness = mByReadness; + unreadOnly = mUnreadOnly; + byDate = mByDate; + daysOld = mDaysOld; + } + else + { + byReadness = unreadOnly = GetBoolean('OUNR'); + byDate = GetBoolean('BYDT'); + daysOld = GetNumberText('DAYS'); + } + MSG_SetOfflineRetrievalInfo( + (MSG_FolderInfo*)mFolder.GetFolderInfo(), + useDefaults, + byReadness, + unreadOnly, + byDate, + daysOld); + + + int32 bodyDaysKeep = GetNumberText('BdDy'); + MSG_PurgeByPreferences bodyPurgeBy = MSG_PurgeNone; + if (GetBoolean('RmBd')) + bodyPurgeBy = MSG_PurgeByAge; + useDefaults = (mUseBodyPurgeDefaults + && bodyPurgeBy == mBodyPurgeBy + && bodyDaysKeep == mBodyDaysKeep); + MSG_SetArticlePurgingInfo( + (MSG_FolderInfo*)mFolder.GetFolderInfo(), + useDefaults, + bodyPurgeBy, + bodyDaysKeep); + + MSG_PurgeByPreferences headerPurgeBy = MSG_PurgeNone; + if (GetBoolean('KpDa')) + headerPurgeBy = MSG_PurgeByAge; + else if (GetBoolean('KpNw')) + headerPurgeBy = MSG_PurgeByNumHeaders; + unreadOnly = GetBoolean('KpUn'); + int32 numHeadersToKeep = GetNumberText('KpCt'); + useDefaults = (mUseHeaderPurgeDefaults + && headerPurgeBy == mHeaderPurgeBy + && unreadOnly == mUnreadOnly + && headerDaysKeep == mHeaderDaysKeep + && numHeadersToKeep == mNumHeadersToKeep + && unreadOnly == mKeepUnreadOnly); + MSG_SetHeaderPurgingInfo( + (MSG_FolderInfo*)mFolder.GetFolderInfo(), + useDefaults, + headerPurgeBy, + unreadOnly, + headerDaysKeep, + numHeadersToKeep + ); + ::MSG_SetIsHTMLOK( + CMailNewsContext::GetMailMaster(), + mFolder.GetFolderInfo(), + nil, + GetBoolean('HTML')); + mDataWritten = true; +} // StNewsGroupGetInfoHandler::WriteDataFields + +//---------------------------------------------------------------------------------------- +Boolean StNewsGroupGetInfoHandler::UserDismissed() +//---------------------------------------------------------------------------------------- +{ + if (mMessage == 'SYNC') + return true; + // 'DOWN' is the pane ID of the download now button + if (mMessage == 'DOWN') + { + // only allowed to download now once + LPane *downloadButton = mDialog->FindPaneByID('DOWN'); + XP_ASSERT(downloadButton != nil); + downloadButton->Disable(); + // make call to backend + ::MSG_DownloadFolderForOffline( + CMailNewsContext::GetMailMaster(), + mFolder.GetFolderPane(), + (MSG_FolderInfo*)mFolder.GetFolderInfo()); + return false; + } + if ('UDFL' == mMessage) + { + LControl *theControl; + + if (GetBoolean('UDFL')) + { + XP_Bool unreadOnly = true; + PREF_GetBoolPref("offline.news.download.unread_only", &unreadOnly); + theControl = (LControl *)mDialog->FindPaneByID('OUNR'); + XP_ASSERT(theControl); + theControl->SetValue(unreadOnly); + theControl->Disable(); + + XP_Bool byDate = true; + PREF_GetBoolPref("offline.news.download.by_date", &byDate); + theControl = (LControl *)mDialog->FindPaneByID('BYDT'); + XP_ASSERT(theControl); + theControl->SetValue(byDate); + theControl->Disable(); + + int32 daysOld = 30; + PREF_GetIntPref("offline.news.download.days", &daysOld); + theControl = (LControl *)mDialog->FindPaneByID('DAYS'); + XP_ASSERT(theControl); + theControl->SetValue(daysOld); + theControl->Disable(); + } + else + { + theControl = (LControl *)mDialog->FindPaneByID('OUNR'); + XP_ASSERT(theControl); + theControl->SetValue(mUnreadOnly); + theControl->Enable(); + + theControl = (LControl *)mDialog->FindPaneByID('BYDT'); + XP_ASSERT(theControl); + theControl->SetValue(mByDate); + theControl->Enable(); + + theControl = (LControl *)mDialog->FindPaneByID('DAYS'); + XP_ASSERT(theControl); + theControl->SetValue(mDaysOld); + theControl->Enable(); + } + return false; + } + return StGetInfoHandler::UserDismissed(); +} + +#pragma mark - + +//---------------------------------------------------------------------------------------- +StNewsHostGetInfoHandler::StNewsHostGetInfoHandler( + MSG_Host* inHost, + LCommander *inSuperCommander) +//---------------------------------------------------------------------------------------- +: StGetInfoHandler(12502, inSuperCommander) +, mHost(inHost) +{ +} + +//---------------------------------------------------------------------------------------- +StNewsHostGetInfoHandler::~StNewsHostGetInfoHandler() +//---------------------------------------------------------------------------------------- +{ + if (mMessage != msg_Cancel) + WriteDataFields(); +} + +//---------------------------------------------------------------------------------------- +void StNewsHostGetInfoHandler::ReadDataFields() +//---------------------------------------------------------------------------------------- +{ + SetText('FNAM', CStr255(::MSG_GetHostName(mHost))); + SetNumberText('PORT', ::MSG_GetHostPort(mHost)); + CStr255 securityString; + ::GetIndString(securityString, 7099, 13 + ::MSG_IsHostSecure(mHost)); + SetText('SECU', securityString); + mPromptPassword = ::MSG_GetNewsHostPushAuth(MSG_GetNewsHostFromMSGHost(mHost)); + if (mPromptPassword) + SetBoolean('AskP', true); + else + SetBoolean('Anon', true); +} // StNewsHostGetInfoHandler::ReadDataFields + +//---------------------------------------------------------------------------------------- +void StNewsHostGetInfoHandler::WriteDataFields() +//---------------------------------------------------------------------------------------- +{ + XP_Bool promptPassword = GetBoolean('AskP'); + if (promptPassword != mPromptPassword) + ::MSG_SetNewsHostPushAuth(MSG_GetNewsHostFromMSGHost(mHost), promptPassword); + mDataWritten = true; +} // StNewsHostGetInfoHandler::WriteDataFields + +#pragma mark - + + + +//---------------------------------------------------------------------------------------- +StMailServerInfoHandler::StMailServerInfoHandler( + CStr255& ioName, + Boolean& ioPOP, + Boolean inIsNewServer, + Boolean inAllowServerNameEdit, + Boolean inAllowServerTypeEdit, + ServerNameValidationFunc inValidationFunc, + CServerListMediator* inValidationServerList, + LCommander* inSuperCommander) +//---------------------------------------------------------------------------------------- +: StGetInfoHandler(12503, inSuperCommander) +, mPOP(ioPOP) +, mOriginallyPOP(ioPOP) +, mIsNewServer(inIsNewServer) +, mAllowServerNameEdit(inAllowServerNameEdit) +, mAllowServerTypeEdit(inAllowServerTypeEdit) +, mName(ioName) +, mServerNameValidationFunc(inValidationFunc) +, mValidationServerList(inValidationServerList) +{ +} + +//---------------------------------------------------------------------------------------- +StMailServerInfoHandler::~StMailServerInfoHandler() +//---------------------------------------------------------------------------------------- +{ + if (mMessage == msg_OK) + { + MPreferenceBase::SetWriteOnDestroy(true); + WriteDataFields(); + } +} + +//---------------------------------------------------------------------------------------- +Boolean StMailServerInfoHandler::UserDismissed() +//---------------------------------------------------------------------------------------- +{ + LWindow* window = GetDialog(); + + if (mMessage == kMailServerTypePopup) // type popup menu (IMAP/POP) + { + Boolean wantPOP = (GetValue(kMailServerTypePopup) == kPOP); + if (wantPOP != mPOP) + { + if (wantPOP) + SetPOP(false); + else + SetIMAP(false); + } + } + + // Disable OK if server name is a duplicate of an existing server + LEditField *nameEdit = dynamic_cast(window->FindPaneByID(kMailServerNameEditText)); + Assert_(nameEdit); + + CStr255 serverName; + nameEdit->GetDescriptor(serverName); + + if (mServerNameValidationFunc != nil) + { + if ( (*mServerNameValidationFunc)(serverName, mIsNewServer, mValidationServerList) ) + EnablePane('OKOK'); + else + DisablePane('OKOK'); + } + + // should also disable OK if the user name field is empty + + + MessageT result = Inherited::UserDismissed(); + + if (mMessage == msg_OK) + { + mName = serverName; + } + + return result; +} + +//---------------------------------------------------------------------------------------- +void StMailServerInfoHandler::SetPOP(Boolean inDialogInit) +//---------------------------------------------------------------------------------------- +{ + HidePane('IMPB'); // hide the imap tab + HidePane('ADVB'); // hide the advanced tab + ShowPane('POPB'); // show the POP tab + + SetText(kMailServerTypeCaption, "\pPOP"); // I don't think this has to be localized + SetValue(kMailServerTypePopup, kPOP); // set the POP popup to true + + // Copy the user name from the + HideAndCripplePane(kIMAPServerUserNameEditText); + HideAndCripplePane(kIMAPServerSavePasswordCheckbox); + HideAndCripplePane(kIMAPServerCheckMailCheckbox); + HideAndCripplePane(kIMAPServerCheckTimeEditText); + + ShowAndUncripplePane(kPOPServerUserNameEditText); + ShowAndUncripplePane(kPOPServerSavePasswordCheckbox); + ShowAndUncripplePane(kPOPServerCheckMailCheckbox); + ShowAndUncripplePane(kPOPServerCheckTimeEditText); + + if (! inDialogInit) // copy the user name over + { + CopyTextValue(kIMAPServerUserNameEditText, kPOPServerUserNameEditText); + CopyTextValue(kIMAPServerCheckTimeEditText, kPOPServerCheckTimeEditText); + + CopyNumberValue(kIMAPServerSavePasswordCheckbox, kPOPServerSavePasswordCheckbox); + CopyNumberValue(kIMAPServerCheckMailCheckbox, kPOPServerCheckMailCheckbox); + } + + mPOP = true; +} // StMailServerInfoHandler::SetPOP + +//---------------------------------------------------------------------------------------- +void StMailServerInfoHandler::SetIMAP(Boolean inDialogInit) +//---------------------------------------------------------------------------------------- +{ + HidePane('POPB'); // hide the POP tab + ShowPane('IMPB'); // show the imap tab + ShowPane('ADVB'); // show the advanced tab + + SetText(kMailServerTypeCaption, "\pIMAP"); // I don't think this has to be localized + SetValue(kMailServerTypePopup, kIMAP); // set the POP popup to IMAP + + HideAndCripplePane(kPOPServerUserNameEditText); + HideAndCripplePane(kPOPServerSavePasswordCheckbox); + HideAndCripplePane(kPOPServerCheckMailCheckbox); + HideAndCripplePane(kPOPServerCheckTimeEditText); + + ShowAndUncripplePane(kIMAPServerUserNameEditText); + ShowAndUncripplePane(kIMAPServerSavePasswordCheckbox); + ShowAndUncripplePane(kIMAPServerCheckMailCheckbox); + ShowAndUncripplePane(kIMAPServerCheckTimeEditText); + + if (! inDialogInit) // copy the user name over + { + CopyTextValue(kPOPServerUserNameEditText, kIMAPServerUserNameEditText); + CopyTextValue(kPOPServerCheckTimeEditText, kIMAPServerCheckTimeEditText); + + CopyNumberValue(kPOPServerSavePasswordCheckbox, kIMAPServerSavePasswordCheckbox); + CopyNumberValue(kPOPServerCheckMailCheckbox, kIMAPServerCheckMailCheckbox); + } + + mPOP = false; +} // StMailServerInfoHandler::SetIMAP + +//---------------------------------------------------------------------------------------- +void StMailServerInfoHandler::SetDefaultUserName() +// Set username to identity.username if it is not set. This +// gives a sensible default for the username field. +//---------------------------------------------------------------------------------------- +{ + CStr255 uname; + + if (mPOP) + GetText(kPOPServerUserNameEditText, uname); + else + GetText(kIMAPServerUserNameEditText, uname); + + if (uname.IsEmpty()) + { + UGetInfo::GetDefaultUserName(uname); + + if (mPOP) + SetText(kPOPServerUserNameEditText, uname); + else + SetText(kIMAPServerUserNameEditText, uname); + } +} + + +//---------------------------------------------------------------------------------------- +void StMailServerInfoHandler::GetServerName(CStr255 &outName) +//---------------------------------------------------------------------------------------- +{ + outName = mName; +} + + +//---------------------------------------------------------------------------------------- +void StMailServerInfoHandler::ReadDataFields() +//---------------------------------------------------------------------------------------- +{ + LWindow* window = GetDialog(); + // It's only legal to switch from POP to IMAP or back if there is exactly one server + // in question. + // In this case we also need to be careful to copy the text between the POP user name + // and IMAP user name fields for consistency when flipping between them + + if (mAllowServerTypeEdit) + { + HidePane(kMailServerTypeCaption); // the static text + ShowPane(kMailServerTypePopup); // the popup menu + SetValue(kMailServerTypePopup, kIMAP); // set the popup to IMAP as default + } + else + { + HidePane(kMailServerTypePopup); // the popup menu. + ShowPane(kMailServerTypeCaption); // the static text + } + + if (mAllowServerNameEdit) + { + HidePane(kMailServerNameCaption); + ShowPane(kMailServerNameEditText); // edittext + SetText(kMailServerNameEditText, mName); + + // A paste snooper to filter out bad characters + LBroadcasterEditField *serverNameEditField = dynamic_cast(window->FindPaneByID(kMailServerNameEditText)); + ThrowIfNil_(serverNameEditField); + serverNameEditField->AddAttachment(new CPasteSnooper("\r\n\t :;,/", "\b\b\b\b____")); + + // Also set a key filter. Are we paranoid or what? + serverNameEditField->SetKeyFilter(UMailFilters::ServerNameKeyFilter); + + } + else + { + ShowPane(kMailServerNameCaption); + HidePane(kMailServerNameEditText); // edittext + SetText(kMailServerNameCaption, mName); + SetText(kMailServerNameEditText, mName); // used to write prefs on destruction + } + + // show and hide the right controls + if (mPOP) + SetPOP(true); + else + SetIMAP(true); + + SetDefaultUserName(); + window->Show(); + // No need to do anything about the other prefs, + // the prefs controls have already read and initialize themselves. +} // StMailServerInfoHandler::ReadDataFields + +//---------------------------------------------------------------------------------------- +void StMailServerInfoHandler::WriteDataFields() +//---------------------------------------------------------------------------------------- +{ + mDataWritten = true; // this is just for the assert in the base class destructor + + if (! MPreferenceBase::GetWriteOnDestroy()) + return; + +#ifdef BEFORE_INVISIBLE_POPSERVER_NAME_EDITFIELD_TRICK_WAS_THOUGHT_OF + // Popup is now a pref control. Server name is now an invisible edit field in the prefs + // pane (and that's the only case where it is changeable). + PREF_SetIntPref("mail.server_type", (int)!mPOP); // 0=POP, 1=IMAP + if (mPOP) + PREF_SetCharPref("network.hosts.pop_server", mName); +#endif + +#if 0 + if (mCreate || (mOriginallyPOP && !mPOP)) + { + // If we changed from POP to IMAP, we have to create the IMAP server doohicky here + + if (!mPOP) + { + // Create the host date in msglib first, using default values. Then, write out + // the preferences (automatic when the dialog is destroyed) so that they get + // suitably modified. + CStr255 uname; + GetText(kIMAPServerUserNameEditText, uname); + const char* userNameString = nil; + const char* serverNameString = nil; + try + { + userNameString = XP_STRDUP(uname); + ThrowIfNil_(userNameString); + serverNameString = XP_STRDUP(mName); + ThrowIfNil_(userNameString); + ::MSG_CreateIMAPHost( + CMailNewsContext::GetMailMaster(), + serverNameString, + false, // XP_Bool isSecure, + userNameString, // const char *userName, + false, // XP_Bool checkNewMail, + 0, // int biffInterval, + false, // XP_Bool rememberPassword, + true, // XP_Bool usingSubscription, + false, // XP_Bool overrideNamespaces, + nil, // const char *personalOnlineDir, + nil, // const char *publicOnlineDir, + nil // const char *otherUsersOnlineDir + ); + } + catch(...) + { + } + XP_FREEIF(const_cast(userNameString)); + XP_FREEIF(const_cast(serverNameString)); + } + } +#endif + + // No need to do anything else, the prefs controls will write themselves if WriteOnDestroy is true +} // StMailServerInfoHandler::WriteDataFields + +#pragma mark - + +//---------------------------------------------------------------------------------------- +UInt32 UGetInfo::CountIMAPServers() +//---------------------------------------------------------------------------------------- +{ + char *serverList = nil; + PREF_CopyCharPref("network.hosts.imap_servers", &serverList); + char* residue = serverList; + UInt32 result = 0; + do + { + if (residue) + { + residue++; // skip the comma (or, on the first pass, the initial char). + result++; + residue = XP_STRCHR(residue, ','); + } + } while (residue); + XP_FREEIF(serverList); + return result; +} + +//---------------------------------------------------------------------------------------- +void UGetInfo::ConductFolderInfoDialog(CMessageFolder& inFolder, LCommander* inSuper) +//---------------------------------------------------------------------------------------- +{ + if (inFolder.IsMailServer()) + { + try + { + CStr255 pname(inFolder.GetName()); + if (inFolder.IsLocalMailFolder()) + { + // More special POP dreck + char* name = nil; + ::PREF_CopyCharPref("network.hosts.pop_server", &name); + pname = name; + XP_FREEIF(name); + } + Boolean usePOP = !inFolder.IsIMAPMailFolder(); + + if (ConductMailServerInfoDialog(pname, usePOP, false, false, false, + kStandAloneDialog, nil, nil, inSuper)) + { + PREF_SavePrefFile(); + } + } + catch(...) + { + } + } + else if (inFolder.IsMailFolder()) + { + StMailFolderGetInfoHandler handler(inFolder, inSuper); + handler.DoDialog(); + } + else if (inFolder.IsNewsHost()) + { + StNewsHostGetInfoHandler handler(::MSG_GetHostForFolder(inFolder), inSuper); + handler.DoDialog(); + } + else if (inFolder.IsNewsgroup()) + { + StNewsGroupGetInfoHandler handler(inFolder, inSuper); + handler.DoDialog(); + } +} // UGetInfo::ConductGetInfoDialog + +//---------------------------------------------------------------------------------------- +Boolean UGetInfo::ConductMailServerInfoDialog( + CStr255& ioName, + Boolean& ioPOP, + Boolean inIsNewServer, + Boolean inAllowServerNameEdit, + Boolean inAllowTypeChange, + Boolean inUsePrefsCache, + ServerNameValidationFunc inValidationFunc, + CServerListMediator* inValidationServerMediator, + LCommander* inSuper) +// +// The mail server properties dialog is brought up from two places; the folder +// pane, as a Get Info dialog, and in the preferences, as the mail server edit +// dialog. When instantiated from the prefs dialog, we only want to save out +// the prefs if the user OKs the entire prefs dialog. To achieve this we +// have the controls write out their values into a temp prefs tree, e.g. +// "temp_prefs_mac.network.mail.check_time". When the user OKs the prefs dialog +// all these prefs are copied to the main preferences, in CPrefsDialog::Finished(). +// +// This prefs cacheing functionality is perfomed by MPreferenceBase. +// +// When used as a properties dialog, the controls write their values directly +// into the main prefs. +// +//---------------------------------------------------------------------------------------- +{ + MessageT result; + + // Use the nifty "mail.imap.server.." preference convention. Our + // constructor resources have "mail.imap.server.^0.", and the MPreferenceBase + // class knows how to replace ^0 by the replacement string. + + { + MPreferenceBase::StReplacementString stringSetter(ioName); // used for replacing ^0 in the controls prefs + // strings with the server name + MPreferenceBase::StWriteOnDestroy writeSetter(false); + MPreferenceBase::StUseTempPrefCache tempSetter(inUsePrefsCache); // used to force the controls to read (if pos) + // and write to a temp prefs tree + + { // <-- StMailServerInfoHandler scope + StMailServerInfoHandler theHandler(ioName, ioPOP, inIsNewServer, inAllowServerNameEdit, inAllowTypeChange, + inValidationFunc, inValidationServerMediator, inSuper); + + result = theHandler.DoDialog(); + + // The user may have modified the server name. Update the replacement string before handler destruction + CStr255 newServerName; + theHandler.GetServerName(newServerName); + + ioName = newServerName; + ioPOP = theHandler.GetPop(); + + MPreferenceBase::SetReplacementString(newServerName); + + // Handler is destroyed, and prefs saved. Write on destroy is set in the destructor + } // <-- StMailServerInfoHandler scope + } + + return (result == msg_OK); +} // UGetInfo::ConductMailServerInfoDialog + +//---------------------------------------------------------------------------------------- +Boolean UGetInfo::ConductNewsServerInfoDialog( + MSG_Host* inHost, + LCommander* inSuper) +//---------------------------------------------------------------------------------------- +{ + StNewsHostGetInfoHandler handler(inHost, inSuper); + MessageT result = handler.DoDialog(); + return (result == msg_OK); +} // UGetInfo::ConductNewsServerInfoDialog + +//---------------------------------------------------------------------------------------- +/* static */ void UGetInfo::GetDefaultUserName(CStr255& outName) +// Set username to identity.username if it is not set. This +// gives a sensible default for the username field. +//---------------------------------------------------------------------------------------- +{ + char cname[256]; + int cnamelength = sizeof(cname); + // Many people set up their profiles as "user@bar.com". Check for + // that format (no space, and an '@') + PREF_GetCharPref("mail.identity.username", cname, &cnamelength); + char* atPos = XP_STRCHR(cname, '@'); + char* spacePos = XP_STRCHR(cname, ' '); + if (spacePos || !atPos) + { + // Oh well, try using the email address up to the '@' + PREF_GetCharPref("mail.identity.useremail", cname, &cnamelength); + // strip off the username from the email address. + atPos = XP_STRCHR(cname, '@'); + } + if (atPos) + *atPos = 0; + outName = cname; +} + +//---------------------------------------------------------------------------------------- +void UGetInfo::RegisterGetInfoClasses() +//---------------------------------------------------------------------------------------- +{ +} + diff --git a/mozilla/cmd/macfe/MailNews/StGetInfoHandler.h b/mozilla/cmd/macfe/MailNews/StGetInfoHandler.h new file mode 100644 index 00000000000..ec39eea4eb3 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/StGetInfoHandler.h @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// StGetInfoHandler.h + +#pragma once +#include "UStdDialogs.h" + +class LCommander; +class CMessageFolder; +class CServerListMediator; +class MSG_Host; + + +//====================================== +class StGetInfoHandler : public StStdDialogHandler +//====================================== +{ + public: + StGetInfoHandler(ResIDT inDialogResID, + LCommander *inSuper) + : StStdDialogHandler(inDialogResID, inSuper) + , mDataWritten(false) + {} + virtual ~StGetInfoHandler(); + virtual void ReadDataFields() = 0; + virtual void WriteDataFields() = 0; + virtual Boolean UserDismissed(); + virtual MessageT DoDialog(); + + virtual void HideAndCripplePane(PaneIDT paneID); // crippled panes don't write their prefs out + virtual void ShowAndUncripplePane(PaneIDT paneID); + + protected: + Boolean mDataWritten; + +}; // class StGetInfoHandler + + +typedef Boolean (*ServerNameValidationFunc)(const CStr255& inServerName, Boolean inNewServer, const CServerListMediator* inServerList); + + +//====================================== +class UGetInfo +//====================================== +{ + public: + static UInt32 CountIMAPServers(); + static void ConductFolderInfoDialog(CMessageFolder&, LCommander* inSuper); + enum { kStandAloneDialog = false, kSubDialogOfPrefs = true }; + static Boolean ConductMailServerInfoDialog( + CStr255& ioName, + Boolean& ioPOP, + Boolean inIsNewServer, + Boolean inAllowServerNameEdit = false, + Boolean inAllowTypeChange = false, // whether to allow the user to change POP to IMAP or v.v. + Boolean inFromPrefsDialog = false, + ServerNameValidationFunc inValidationFunc = nil, + CServerListMediator* inValidationServerMediator = nil, + LCommander* inSuper = nil); + static Boolean ConductNewsServerInfoDialog( + MSG_Host* inHost, + LCommander* inSuper = nil); + static void RegisterGetInfoClasses(); + static void GetDefaultUserName(CStr255&); +}; + diff --git a/mozilla/cmd/macfe/MailNews/UIHelper.h b/mozilla/cmd/macfe/MailNews/UIHelper.h new file mode 100644 index 00000000000..f3cbb8d02cf --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/UIHelper.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// UIHelper.h + +#pragma once + +/////////////////////////////////////////////////////////////////////////////////// +// +// This file contains some helper functions for UI manipulation. +// +/////////////////////////////////////////////////////////////////////////////////// +// +// FindUIItemPtr - returns a pointer to the proper item +// Usage: +// +// LGAPushButton* button; +// LView view; +// PaneIDT paneID; +// +// FindUIItem( view, paneID, button ); +// +// It will throw an exception if the element is missing or the passed +// pointer is the wrong type. +// +/////////////////////////////////////////////////////////////////////////////////// + +#include "PP_Types.h" +#include "LView.h" +#include "nc_exception.h" + +/////////////////////////////////////////////////////////////////////////////////// +// FindUIItemPtr +template +inline +void +FindUIItemPtr( LView *inView, long inID, T*& outItemPtr ) +{ + outItemPtr = dynamic_cast( inView->FindPaneByID( inID ) ); + if( !outItemPtr ) + throw nc_exception( eUIResourceError ); +} diff --git a/mozilla/cmd/macfe/MailNews/UIMAPSubscriptionUpgrade.cp b/mozilla/cmd/macfe/MailNews/UIMAPSubscriptionUpgrade.cp new file mode 100644 index 00000000000..202f4d94cb9 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/UIMAPSubscriptionUpgrade.cp @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +// This file implements a callback declared in msgcom.h, for presenting the IMAP +// subscription upgrade dialog. + +#include "msgcom.h" // For the prototype, and the enum for the return value. + +#include "PascalString.h" +#include "macutil.h" +#include "UStdDialogs.h" +#include "resgui.h" // for msg_Help +#include "xp_help.h" +#include +#include // for SysBeep() + +//---------------------------------------------------------------------------------------- +MSG_IMAPUpgradeType FE_PromptIMAPSubscriptionUpgrade(MWContext*, const char* hostName) +//---------------------------------------------------------------------------------------- +{ + StDialogHandler handler(14007, NULL); + LWindow* dialog = handler.GetDialog(); // initially invisible. + + // Find the radio button for the "Automatic" choice. + LGARadioButton* autoButton = (LGARadioButton*)dialog->FindPaneByID('AUTO'); + SignalIf_(!autoButton); + if (!autoButton) + return MSG_IMAPUpgradeDont; + + // Replace ^0 in the radio button string with the host name that was given to us + CStr255 workString; + autoButton->GetDescriptor(workString); + StringParamText(workString, hostName); + autoButton->SetDescriptor(workString); + + // Run the dialog + MSG_IMAPUpgradeType result = MSG_IMAPUpgradeAutomatic; + MessageT message = msg_Nothing; + if (UStdDialogs::TryToInteract()) + { + dialog->Show(); + do { + message = handler.DoDialog(); + switch (message) + { + case msg_Help: + ShowHelp( HELP_IMAP_UPGRADE ); + break; +#if 0 + // This doesn't work, and here's why. When one of the radio buttons is + // clicked, first the value message gets broadcast (good), and then + // the radio group broadcasts "msg_ControlClicked". Unfortunately, + // StDialogHandler merely stores the last message it receives! + case 'AUTO': + result = MSG_IMAPUpgradeAutomatic; + break; + case 'PICK': + result = MSG_IMAPUpgradeCustom; + break; +#endif + } + } while (message != msg_OK && message != msg_Cancel); + } + // Use the OK/Cancel message together with the value of the the radio button for + // the automatic choice to determine the result. + if (message != msg_OK) + result = MSG_IMAPUpgradeDont; + else if (autoButton->GetValue() == Button_On) + result = MSG_IMAPUpgradeAutomatic; + else + result = MSG_IMAPUpgradeCustom; + return result; +} // FE_PromptIMAPSubscriptionUpgrade diff --git a/mozilla/cmd/macfe/MailNews/UMailFilters.cp b/mozilla/cmd/macfe/MailNews/UMailFilters.cp new file mode 100644 index 00000000000..f8e42f6a3d2 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/UMailFilters.cp @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// UMailFilters.cp + +//////////////////////////////////////////////////////////////////////////////////////// +// +// File containing key filters for Mail/News +// +// Usage +// ----- +// +// LEditField* edit = dynamic_cast( theWindow->FindPaneByID('XyWz') ); +// edit->SetKeyFilter( UMailFilters::HeaderField ); +// +// +//////////////////////////////////////////////////////////////////////////////////////// + +#include "UKeyFilters.h" +#include "UMailFilters.h" + + +EKeyStatus +UMailFilters::CustomHeaderFilter(TEHandle /*inMacTEH*/, Char16 inKeyCode, Char16 &ioCharCode, SInt16 /*inModifiers*/) +{ + if( IsTEDeleteKey( inKeyCode ) ) // need to trap this before the printing characters + return keyStatus_Input; + + if( IsActionKey( inKeyCode ) ) // need to trap this before the printing characters + return keyStatus_PassUp; + + if ( !IsPrintingChar( ioCharCode ) ) // ASCII 23 to 126 + return keyStatus_Reject; + + if ( ioCharCode == ':' || ioCharCode == ' ' ) // ':' and SPACE + { + ::SysBeep(32); + return keyStatus_Reject; + } + + return keyStatus_Input; +} + + + +// --------------------------------------------------------------------------- +// * ServerNameKeyFilter [static] +// --------------------------------------------------------------------------- + +EKeyStatus +UMailFilters::ServerNameKeyFilter(TEHandle /*inMacTEH*/, Char16 inKeyCode, Char16 &ioCharCode, SInt16 /*inModifiers*/) +{ + EKeyStatus theKeyStatus = keyStatus_PassUp; + + if (IsTEDeleteKey(inKeyCode)) + theKeyStatus = keyStatus_TEDelete; + else if (IsTECursorKey(inKeyCode)) + theKeyStatus = keyStatus_TECursor; + else if (IsExtraEditKey(inKeyCode)) + theKeyStatus = keyStatus_ExtraEdit; + else if (ioCharCode == ',' || ioCharCode == ' ' || ioCharCode == '/' || ioCharCode == ':' || ioCharCode == ';') + theKeyStatus = keyStatus_Reject; + else if (IsPrintingChar(ioCharCode)) + theKeyStatus = keyStatus_Input; + + return theKeyStatus; +} diff --git a/mozilla/cmd/macfe/MailNews/UMailFilters.h b/mozilla/cmd/macfe/MailNews/UMailFilters.h new file mode 100644 index 00000000000..614a73f032f --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/UMailFilters.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// UMailFilters.h + +#pragma once + +//////////////////////////////////////////////////////////////////////////////////////// +// +// File containing key filters for Mail/News +// +// Usage +// ----- +// +// LEditField* edit = dynamic_cast( theWindow->FindPaneByID('XyWz') ); +// edit->SetKeyFilter( UMailFilters::HeaderField ); +// +//////////////////////////////////////////////////////////////////////////////////////// + + +class UMailFilters : public UKeyFilters +{ + public: + + static EKeyStatus CustomHeaderFilter(TEHandle inMacTEH, Char16 inKeyCode, Char16 &ioCharCode, SInt16 inModifiers); + + static EKeyStatus ServerNameKeyFilter(TEHandle inMacTEH, Char16 inKeyCode, Char16 &ioCharCode, SInt16 inModifiers); + + protected: + + private: +}; + diff --git a/mozilla/cmd/macfe/MailNews/UMailSelection.cp b/mozilla/cmd/macfe/MailNews/UMailSelection.cp new file mode 100644 index 00000000000..1360c960941 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/UMailSelection.cp @@ -0,0 +1,142 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +#include "UMailSelection.h" + +#include "msgcom.h" + +//---------------------------------------------------------------------------------------- +CMailSelection::CMailSelection() +//---------------------------------------------------------------------------------------- +: selectionList(NULL) +, xpPane(NULL) +, selectionSize(0) +, singleIndex(MSG_VIEWINDEXNONE) +{ +} + +//---------------------------------------------------------------------------------------- +void /*sic*/ CMailSelection::operator=(const CMailSelection& original) +//---------------------------------------------------------------------------------------- +{ + xpPane = original.xpPane; + singleIndex = original.singleIndex; + if (singleIndex != MSG_VIEWINDEXNONE) + SetSingleSelection(singleIndex); + else + { + selectionSize = original.selectionSize; + selectionList = const_cast(original.GetSelectionList()); + } +} + +//---------------------------------------------------------------------------------------- +const MSG_ViewIndex* CMailSelection::GetSelectionList() const +//---------------------------------------------------------------------------------------- +{ + return selectionList; +} // CMailSelection::GetSelectionList + +//---------------------------------------------------------------------------------------- +void CMailSelection::Normalize() +//---------------------------------------------------------------------------------------- +{ + // If singleIndex is a real index, then fix selectionList to + // point to it (eg, after assignment or fetching the bits out of + // a drag object) + if (singleIndex != MSG_VIEWINDEXNONE) + SetSingleSelection(singleIndex); +} + +//---------------------------------------------------------------------------------------- +CPersistentMailSelection::~CPersistentMailSelection() +// Can't be implemented inline, because it's a virtual function in a shared header file. +//---------------------------------------------------------------------------------------- +{ + DestroyData(); +} // CPersistentMailSelection::~CPersistentMailSelection + +//---------------------------------------------------------------------------------------- +const MSG_ViewIndex* CPersistentMailSelection::GetSelectionList() const +//---------------------------------------------------------------------------------------- +{ + CPersistentMailSelection* s = const_cast(this); + Assert_(s); + s->MakeAllVolatile(); + return selectionList; +} // CPersistentMailSelection::GetSelectionList + +//---------------------------------------------------------------------------------------- +void CPersistentMailSelection::CloneData() +//---------------------------------------------------------------------------------------- +{ + if (selectionSize == 0 || singleIndex != MSG_VIEWINDEXNONE) + return; // nothing to do + MSG_ViewIndex* src = selectionList; // owned by somebody else + selectionList = new MSG_ViewIndex[selectionSize]; + MSG_ViewIndex* dst = selectionList; + for (int i = 0; i < selectionSize; i++,dst++) + *dst = *src++; +} + +//---------------------------------------------------------------------------------------- +void CPersistentMailSelection::DestroyData() +//---------------------------------------------------------------------------------------- +{ + // You must call this if you called CloneData. + if (selectionSize != 0 && singleIndex == MSG_VIEWINDEXNONE) + delete [] selectionList; +} +//---------------------------------------------------------------------------------------- +void CPersistentFolderSelection::MakePersistent(MSG_ViewIndex& ioKey) const +//---------------------------------------------------------------------------------------- +{ + ioKey = (MSG_ViewIndex)::MSG_GetFolderInfo( + xpPane, + ioKey); +} + +//---------------------------------------------------------------------------------------- +void CPersistentFolderSelection::MakeVolatile(MSG_ViewIndex& ioKey) const +//---------------------------------------------------------------------------------------- +{ + ioKey = ::MSG_GetFolderIndexForInfo( + xpPane, + (MSG_FolderInfo *)ioKey, + true); +} + +//---------------------------------------------------------------------------------------- +void CPersistentMessageSelection::MakePersistent(MSG_ViewIndex& ioKey) const +//---------------------------------------------------------------------------------------- +{ + ioKey = (MSG_ViewIndex)::MSG_GetMessageKey( + xpPane, + ioKey); +} + +//---------------------------------------------------------------------------------------- +void CPersistentMessageSelection::MakeVolatile(MSG_ViewIndex& ioKey) const +//---------------------------------------------------------------------------------------- +{ + ioKey = ::MSG_GetMessageIndexForKey( + xpPane, + (MessageKey)ioKey, + true); +} diff --git a/mozilla/cmd/macfe/MailNews/UMailSelection.h b/mozilla/cmd/macfe/MailNews/UMailSelection.h new file mode 100644 index 00000000000..e1585dbdf43 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/UMailSelection.h @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1996 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +#pragma once + +typedef unsigned long MSG_ViewIndex; +typedef struct MSG_Pane MSG_Pane; + +//======================================================================================== +class CMailSelection +// Represents a selection in a CMailFlexTable (which can be folders or messages). Used +// for drag and drop, and all msglib command execution. +//======================================================================================== +{ +public: + CMailSelection(); + CMailSelection(const CMailSelection& original) + { + *this = original; // the assignment op. doesn't need to destroy anything... + } + void /*sic*/ operator=(const CMailSelection& original); + + virtual const MSG_ViewIndex* GetSelectionList() const; + + void SetSingleSelection(MSG_ViewIndex inSingleIndex) + { selectionSize = 1; + singleIndex = inSingleIndex; + selectionList = &singleIndex; + } + void Normalize(); + +public: + MSG_Pane* xpPane; // (threadpane or folder pane) + MSG_ViewIndex selectionSize; // # of selected indices (1-based) + MSG_ViewIndex singleIndex; // Something for selectionList to point to, without + // the need for an actual list to be present. Useful for + // the proxy drag case, e.g. +protected: + friend class CMailFlexTable; + MSG_ViewIndex* selectionList; // List of selected indices (zero-based), + // a reference to the mSelectionList in the table, + // so none of this data needs to be deleted by this + // object. +}; // class CMailSelection + +//======================================================================================== +class CPersistentMailSelection +// "Owns" its data. Clones data when duplicated, destroys data when deleted. +// The list of selected items can either be a list of indices (volatile) or a list of +// persistent keys (persistent). This state is indicated by the mDataIsVolatile member. +// Constructors and assignment operators always convert any non-trivial data to persistent +// form. +// GetSelectionList() still returns a list of indices (ie, the volatile form of the data), +// so it converts to volatile form before returning the result. +// This is an abstract class, because the form of the persistent key is particular to the +// view. See also the derived classes, CPersistentFolderSelection, and +// CPersistentMessageSelection. These classes override the MakePersistent() and +// MakeVolatile() methods. +//======================================================================================== +: public CMailSelection +{ + typedef CMailSelection Inherited; + +public: + // No copy constructor in the base class.! + // Since MakeAllPersistent() needs virtual functions, and we + // need the inherited behavior, we can't implement a copy constructor (indeed, + // it will crash if we do. Therefore we provide only a void constructor, + // forcing clients to use the assignment operator, which is safe. + CPersistentMailSelection() + : mDataIsVolatile (true) + { + } + virtual ~CPersistentMailSelection(); + + void /*sic*/ operator=(const CMailSelection& original) + { + DestroyData(); // like all good little assignment operators + CMailSelection::operator=(original); + mDataIsVolatile = true; + CloneData(); + MakeAllPersistent(); + } + virtual const MSG_ViewIndex* GetSelectionList() const; + +protected: + // We need versions of these for folders (MSG_FolderInfo*) and messages (MessageKey) + // See CPersistentFolderSelection and CPersistentMessageSelection. + virtual void MakePersistent(MSG_ViewIndex& ioKey) const = 0; + virtual void MakeVolatile(MSG_ViewIndex& ioKey) const = 0; + + void MakeAllVolatile() + { + if (mDataIsVolatile) + return; + MSG_ViewIndex* cur = selectionList; + for (int i = 0; i < selectionSize; i++,cur++) + MakeVolatile(*cur); + mDataIsVolatile = true; + } + void MakeAllPersistent() + { + if (!mDataIsVolatile) + return; + MSG_ViewIndex* cur = selectionList; + for (int i = 0; i < selectionSize; i++,cur++) + MakePersistent(*cur); + mDataIsVolatile = false; + } + void CloneData(); + + void DestroyData(); + + // data + Boolean mDataIsVolatile; +}; // class CPersistentMailSelection + +//======================================================================================== +class CPersistentFolderSelection +//======================================================================================== +: public CPersistentMailSelection +{ + public: + CPersistentFolderSelection(const CMailSelection& orig) + : CPersistentMailSelection() + { + // Void constructor of base class has now been + // called. Call the assignment operator to copy + // and convert. + CPersistentMailSelection::operator=(orig); + } + virtual void MakePersistent(MSG_ViewIndex& ioKey) const; + virtual void MakeVolatile(MSG_ViewIndex& ioKey) const; +}; // class CPersistentFolderSelection + +//======================================================================================== +class CPersistentMessageSelection +//======================================================================================== +: public CPersistentMailSelection +{ + public: + CPersistentMessageSelection(const CMailSelection& original) + : CPersistentMailSelection() + { + // Void constructor of base class has now been called. + // Call the assignment operator to copy and convert. + CPersistentMailSelection::operator=(original); + } + virtual void MakePersistent(MSG_ViewIndex& ioKey) const; + virtual void MakeVolatile(MSG_ViewIndex& ioKey) const; +}; // class CPersistentMessageSelection diff --git a/mozilla/cmd/macfe/MailNews/UMessageLibrary.cp b/mozilla/cmd/macfe/MailNews/UMessageLibrary.cp new file mode 100644 index 00000000000..59eac59948f --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/UMessageLibrary.cp @@ -0,0 +1,251 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// UMessageLibrary.cp + +#include "UMessageLibrary.h" + +// Want a command number? They hide in several places... +#include "Netscape_Constants.h" +#include "resgui.h" +#include "MailNewsgroupWindow_Defines.h" +#include "CApplicationEventAttachment.h" + +// necessary for hack that makes sure we can only have one +// open MSG_GetNewMail command at a time +#include "CMailProgressWindow.h" + +#include "macutil.h" +#include "PascalString.h" +#include "UOffline.h" + +//====================================== +// class UMessageLibrary +// Utility calls +//====================================== + +//----------------------------------- +/*static*/ Boolean UMessageLibrary::FindMessageLibraryCommandStatus( + MSG_Pane* inPane, + MSG_ViewIndex* inIndices, + int32 inNumIndices, + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName) +// returns false if not a msglib command. +// Commands enabled/disabled here are obeyed in CMailFlexTable::ObeyMessageLibraryCommand +//----------------------------------- +{ + MSG_CommandType cmd; + switch (inCommand) + { + case cmd_ToggleOffline: + case cmd_SynchronizeForOffline: + UOffline::FindOfflineCommandStatus(inCommand, outEnabled, outName); + return true; + case cmd_ToggleThreadKilled: + // cmd_ToggleThreadKilled: ugh! it's a navigate command, but navigatestatus + // doesn't support it! So when testing for enabling, we have to call commandstatus, + // but in executing, we have to call navigate. UMessageLibrary now treats this + // as a navigate command, hence this hack here to override this behavior. + cmd = MSG_ToggleThreadKilled; + break; + case cmd_CompressAllFolders: + cmd = MSG_CompressFolder; // see hack below. + break; + default: + cmd = UMessageLibrary::GetMSGCommand(inCommand); + break; + } + + + if (!UMessageLibrary::IsValidCommand(cmd)) + return false; + if (inPane == nil) + return false; + + XP_Bool selectable = false; + MSG_COMMAND_CHECK_STATE checkedState; + const char* display_string = nil; + XP_Bool plural; + if (MSG_CommandStatus( + inPane, + cmd, + inIndices, + inNumIndices, + &selectable, + &checkedState, + &display_string, + &plural) + >= 0) + { + // The logic is: if the selection permits MSG_CompressFolder, then do that. + // Otherwise, try for MSG_CompressAllFolders. + if (cmd == MSG_CompressFolder && !selectable) + { + const char* redo_string = nil; + if (MSG_CommandStatus( + inPane, + MSG_CompressAllFolders, + inIndices, + inNumIndices, + &selectable, + &checkedState, + &redo_string, + &plural) + >= 0 && selectable) + { + display_string = redo_string; + } + } + outEnabled = (Boolean)selectable; + outUsesMark = true; + if (checkedState == MSG_Checked) + outMark = checkMark; + else + outMark = 0; + switch (cmd) + { + // commands where we don't like the back end's command strings: + case MSG_AddSender: + case MSG_AddAll: + case MSG_ReplyToSender: + case MSG_ReplyToAll: + case MSG_PostReply: + case MSG_PostAndMailReply: + case MSG_DoRenameFolder: + case MSG_DeleteMessage: + case MSG_DeleteMessageNoTrash: + break; + // All other commands, use the back-end string, if any. + default: + if (display_string && *display_string) + *(CStr255*)outName = display_string; + } + } + + if ((cmd == MSG_GetNewMail) && selectable) + if (CMailProgressWindow::GettingMail()) + outEnabled = false; + + return true; +} + +//----------------------------------- +MSG_CommandType UMessageLibrary::GetMSGCommand(CommandT inCommand) +//----------------------------------- +{ + switch (inCommand) + { + case cmd_Undo: return MSG_Undo; + case cmd_Redo: return MSG_Redo; + case cmd_Clear: + return ( + CApplicationEventAttachment::CurrentEventHasModifiers(optionKey) + ? MSG_DeleteNoTrash + : MSG_Delete + ); + case cmd_NewFolder: return MSG_NewFolder; + case cmd_RenameFolder: return MSG_DoRenameFolder; + case cmd_EmptyTrash: return MSG_EmptyTrash; + case cmd_CompressFolder: return MSG_CompressFolder; + case cmd_CompressAllFolders: return MSG_CompressAllFolders; + case cmd_ManageMailAccount: return MSG_ManageMailAccount; + case cmd_NewNewsgroup: return MSG_NewNewsgroup; + case cmd_ModerateNewsgroup: return MSG_ModerateNewsgroup; + + case cmd_NewMailMessage: return MSG_MailNew; + case cmd_ReplyToSender: return MSG_ReplyToSender; + case cmd_PostReply: return MSG_PostReply; + case cmd_PostAndMailReply: return MSG_PostAndMailReply; + case cmd_PostNew: return MSG_PostNew; + case cmd_ReplyToAll: return MSG_ReplyToAll; + case cmd_ForwardMessage: return MSG_ForwardMessage; + case cmd_ForwardMessageQuoted: return MSG_ForwardMessageQuoted; + case cmd_ForwardMessageAttachment: return MSG_ForwardMessageAttachment; + case cmd_ForwardMessageInline: return MSG_ForwardMessageInline; + case cmd_MarkMessage: return MSG_MarkMessages; // same cmd + case cmd_MarkSelectedMessages: return MSG_MarkMessages; // '' '' + case cmd_MarkUnread: return MSG_MarkMessagesUnread; + case cmd_MarkRead: return MSG_MarkMessagesRead; + case cmd_UnmarkSelectedMessages: return MSG_UnmarkMessages; + case cmd_MarkThreadRead: return MSG_MarkThreadRead; + case cmd_MarkAllRead: return MSG_MarkAllRead; + + case cmd_GetNewMail: return MSG_GetNewMail; + case cmd_SelectForOffline: return MSG_RetrieveSelectedMessages; + case cmd_FlaggedForOffline: return MSG_RetrieveMarkedMessages; + + case cmd_GetMoreMessages: return MSG_GetNextChunkMessages; + case cmd_ShowOnlyUnreadMessages: return MSG_ViewNewOnly; + case cmd_ShowMicroMessageHeaders: return MSG_ShowMicroHeaders; + case cmd_ShowSomeMessageHeaders: return MSG_ShowSomeHeaders; + case cmd_ShowAllMessageHeaders: return MSG_ShowAllHeaders; + case cmd_DeliverQueuedMessages: return MSG_DeliverQueuedMessages; + case cmd_UpdateMessageCount: return MSG_UpdateMessageCount; + case cmd_ShowAllMessages: return MSG_ViewAllThreads; + + case cmd_AttachmentsInline: return MSG_AttachmentsInline; + case cmd_AttachmentsAsLinks: return MSG_AttachmentsAsLinks; + case cmd_AddSenderToAddressBook: return MSG_AddSender; + case cmd_AddAllToAddressBook: return MSG_AddAll; + //case cmd_ToggleThreadKilled: return MSG_ToggleThreadKilled; // actually a motiontype! + case cmd_ToggleThreadWatched: return MSG_ToggleThreadWatched; + case cmd_ViewAllThreads: return MSG_ViewAllThreads; + case cmd_ViewKilledThreads: return MSG_ViewKilledThreads; + case cmd_ViewThreadsWithNew: return MSG_ViewThreadsWithNew; + case cmd_ViewWatchedThreadsWithNew: return MSG_ViewWatchedThreadsWithNew; + case cmd_ViewNewOnly: return MSG_ViewNewOnly; + case cmd_EditMessage: return MSG_OpenMessageAsDraft; + case cmd_WrapLongLines: return MSG_WrapLongLines; + +//#define MAP_CMD_TO_MSG(verb) case cmd_##verb: return MSG_##verb; + + } + return (MSG_CommandType)invalid_command; +} + +//----------------------------------- +MSG_MotionType UMessageLibrary::GetMotionType(CommandT inCommand) +//----------------------------------- +{ + switch (inCommand) + { + case cmd_Back: return MSG_Back; + case cmd_GoForward: return MSG_Forward; + case cmd_NextMessage: return MSG_NextMessage; + case cmd_NextUnreadMessage: return MSG_NextUnreadMessage; + case cmd_PreviousUnreadMessage: return MSG_PreviousUnreadMessage; + case cmd_PreviousMessage: return MSG_PreviousMessage; + case cmd_NextUnreadGroup: return MSG_NextUnreadGroup; + case cmd_NextUnreadThread: return MSG_NextUnreadThread; + case cmd_MarkReadForLater: return MSG_LaterMessage; + case cmd_NextFolder: return MSG_NextFolder; + case cmd_FirstFlagged: return MSG_FirstFlagged; + case cmd_PreviousFlagged: return MSG_PreviousFlagged; + case cmd_NextFlagged: return MSG_NextFlagged; + case cmd_ToggleThreadKilled: return (MSG_MotionType)MSG_ToggleThreadKilled; + + } + return (MSG_MotionType)invalid_command; +} + diff --git a/mozilla/cmd/macfe/MailNews/UMessageLibrary.h b/mozilla/cmd/macfe/MailNews/UMessageLibrary.h new file mode 100644 index 00000000000..7ac87258bed --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/UMessageLibrary.h @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// UMessageLibrary.h + +#pragma once +#include "msgcom.h" + +//====================================== +class UMessageLibrary +// Utility calls +//====================================== +{ +public: + enum { invalid_command = 0xFFFFFFFF }; + + static MSG_MotionType GetMotionType(CommandT inCommand); + // Converts a FE command number to a msgcom.h motion command ID. + // Test result with IsValidMotion() + static Boolean IsValidMotion(MSG_MotionType cmd) { return cmd != invalid_command; } + + static MSG_CommandType GetMSGCommand(CommandT inCommand); + // Converts a FE command number to a msgcom.h command ID. + // Test result with IsValidCommand + static Boolean IsValidCommand(MSG_CommandType cmd) { return cmd != invalid_command; } + static Boolean FindMessageLibraryCommandStatus( + MSG_Pane* inPane, + MSG_ViewIndex* inIndices, + int32 inNumIndices, + CommandT inCommand, + Boolean &outEnabled, + Boolean &outUsesMark, + Char16 &outMark, + Str255 outName); +}; // class UMessageLibrary diff --git a/mozilla/cmd/macfe/MailNews/UNewFolderDialog.cp b/mozilla/cmd/macfe/MailNews/UNewFolderDialog.cp new file mode 100644 index 00000000000..f3f72d7fe68 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/UNewFolderDialog.cp @@ -0,0 +1,566 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// UNewFolderDialog.cp + +#ifndef MOZ_LITE + +#include "UNewFolderDialog.h" + +#include "CMessageFolder.h" +#include "PascalString.h" +#include "UMailFolderMenus.h" +#include "CMailFolderButtonPopup.h" +#include "CMailNewsContext.h" +#include "CMailProgressWindow.h" +#include "MailNewsCallbacks.h" + +#include "prefapi.h" +#define WANT_ENUM_STRING_IDS +#include "allxpstr.h" +#undef WANT_ENUM_STRING_IDS + +#include "macutil.h" +#include "uerrmgr.h" +#include "ufilemgr.h" +#include "uprefd.h" + +#include +#include +#include + +//---------------------------------------------------------------------------------------- +Boolean UFolderDialogs::ConductNewFolderDialog( + const CMessageFolder& inParentFolder, + CMailCallbackListener* inListener) +//---------------------------------------------------------------------------------------- +{ + // Put up dialog + StDialogHandler handler(14010, NULL); + + // Select the "Host" edit field + LWindow* dialog = handler.GetDialog(); + LGAEditField *namefield = (LGAEditField*)dialog->FindPaneByID('Name'); + SignalIf_(!namefield); + CMailFolderGAPopup* folderPopup = (CMailFolderGAPopup*)dialog->FindPaneByID('MfPM'); + SignalIf_(!folderPopup); + if (!folderPopup || ! namefield) + return false; + dialog->SetLatentSub(namefield); + CMailFolderMixin::FolderChoices c + = (CMailFolderMixin::FolderChoices)( + (int)CMailFolderMixin::eWantPOP + | (int)CMailFolderMixin::eWantIMAP + | (int)CMailFolderMixin::eWantHosts); + folderPopup->MSetFolderChoices(c); + CMailFolderMixin::UpdateMailFolderMixinsNow(folderPopup); + MSG_Master* master = CMailNewsContext::GetMailMaster(); + MSG_FolderInfo* suggestedParent + = ::MSG_SuggestNewFolderParent(inParentFolder, master); + folderPopup->MSetSelectedFolder(suggestedParent, false); + + // Run the dialog + MessageT message = msg_Nothing; + Boolean userChangedPort = false; + do { + message = handler.DoDialog(); + } while (message != msg_OK && message != msg_Cancel); + + // Use the result. + Boolean folderCreated = false; + if (message == msg_OK) + { + CStr255 nametext; + namefield->GetDescriptor(nametext); + CStr255 commandName; + dialog->GetDescriptor(commandName); + + try + { + dialog->Hide(); + MSG_Pane* pane = CMailProgressWindow::JustGiveMeAPane(commandName); + // this throws if appropriate + if (inListener) + inListener->SetPane(pane); + folderCreated = ::MSG_CreateMailFolderWithPane( + pane, + master, + (MSG_FolderInfo*)folderPopup->MGetSelectedFolder(), + nametext) == 0; + if( folderCreated == false ) + { + // Send a message out to close the progress window + FE_PaneChanged( pane, false , MSG_PaneProgressDone, 0); + } + } + catch (...) + { + throw; + } + } + return folderCreated; +} // ConductNewFolderDialog + +//---------------------------------------------------------------------------------------- +Boolean UFolderDialogs::GetDefaultFolderName( + FolderKind inKind, + CStr255& outFolderName) +//---------------------------------------------------------------------------------------- +{ + switch (inKind) + { + case mail_fcc: + case news_fcc: + outFolderName = XP_GetString(MK_MSG_SENT_L10N_NAME); + return true; + case drafts: + outFolderName = XP_GetString(MK_MSG_DRAFTS_L10N_NAME); + return true; + case templates: + outFolderName = XP_GetString(MK_MSG_TEMPLATES_L10N_NAME); + return true; + } + return false; +} // GetDefaultFolderName + +//---------------------------------------------------------------------------------------- +Boolean UFolderDialogs::FindLocalMailHost(CMessageFolder& outFolder) +//---------------------------------------------------------------------------------------- +{ + MSG_Master* master = CMailNewsContext::GetMailMaster(); + // See also UMailFolderMenus::UpdateMailFolderMixinsNow + Int32 numFoldersMail = ::MSG_GetFoldersWithFlag(master, MSG_FOLDER_FLAG_MAIL, nil, 0); + Assert_(numFoldersMail > 0); // Should have at least some permanent mail folders! + StPointerBlock folderInfoData(sizeof(MSG_FolderInfo *) * numFoldersMail); + MSG_FolderInfo **folderInfo = (MSG_FolderInfo **) folderInfoData.mPtr; + Int32 numFolders2 = ::MSG_GetFoldersWithFlag( + master, MSG_FOLDER_FLAG_MAIL, folderInfo, numFoldersMail); + Assert_(numFolders2 > 0); // Should have at least some permanent mail folders! + Assert_(numFolders2 == numFoldersMail); + for (int i = 0; i < numFoldersMail; i++, folderInfo++) + { + CMessageFolder f(*folderInfo); + if (!f.IsIMAPMailFolder() && f.GetLevel() == kRootLevel) + { + outFolder = f; + return true; + } + } + return false; +} // UFolderDialogs::FindLocalMailHost + +//---------------------------------------------------------------------------------------- +Boolean UFolderDialogs::GetFolderAndServerNames( + const CMessageFolder& inFolder, + FolderKind inKind, + CStr255& outFolderName, + CMessageFolder& outServer, + Boolean* outMatchesDefault) +//---------------------------------------------------------------------------------------- +{ + CStr255 defaultFolderName; + if (!GetDefaultFolderName(inKind, defaultFolderName)) + return false; + if (inFolder.GetFolderInfo() == nil) + { + if (!FindLocalMailHost(outServer)) + return false; + outFolderName = defaultFolderName; + if (outMatchesDefault) + *outMatchesDefault = true; + return true; + } + else if (inFolder.IsMailServer()) + { + outServer = inFolder; + outFolderName = defaultFolderName; + if (outMatchesDefault) + *outMatchesDefault = true; + return true; + } + else if (inFolder.IsLocalMailFolder()) + { + // GetHostFolderinfo doesn't work for local folders... + if (!FindLocalMailHost(outServer)) + return false; + } + else + outServer.SetFolderInfo(GetHostFolderInfo(inFolder)); + outFolderName = inFolder.GetName(); + if (outMatchesDefault) + *outMatchesDefault = (defaultFolderName == outFolderName + && inFolder.GetLevel() == kSpecialFolderLevel); + return true; +} // UFolderDialogs::GetFolderAndServerNames + +//---------------------------------------------------------------------------------------- +Boolean UFolderDialogs::GetFolderAndServerNames( + const char* inPrefName, + CMessageFolder& outFolder, + CStr255& outFolderName, + CMessageFolder& outServer, + Boolean* outMatchesDefault) +//---------------------------------------------------------------------------------------- +{ + FolderKind kind; + if (XP_STRSTR(inPrefName, ".default_fcc")) + { + if (XP_STRSTR(inPrefName, "news") == inPrefName) + kind = news_fcc; + else + kind = mail_fcc; + // The Dogbert (v4.0) pref was binary (a mac alias); If we're upgrading from Dogbert, + // Even in 4.5, local folders still use this + // scheme. + AliasHandle aliasH = NULL; + int size; + void* alias; + if (PREF_CopyBinaryPref(inPrefName, &alias, &size ) == 0) + { + PtrToHand(alias, &(Handle)aliasH, size); + XP_FREE(alias); + + FSSpec target; + Boolean wasChanged; // ignored + OSErr err = ResolveAlias(nil, aliasH, &target, &wasChanged); + DisposeHandle((Handle)aliasH); + + if (err == noErr) + { + // We have to work out whether this is the default folder. There are two + // possibilities that should cause a match. + CStr255 defaultFolderName; + GetDefaultFolderName(kind, defaultFolderName); // eg "Sent", "Templates"... + + // Case (1) + // The spec is the spec of the + // root of the local mail folder tree (usually "Mail", but user-settable). + // This, by convention, signifies the folder with the default name (eg "Sent") + // for this type of special folder. + FSSpec mailTop = CPrefs::GetFolderSpec(CPrefs::MailFolder); + Boolean matchesDefault = mailTop. vRefNum == target.vRefNum + && mailTop.parID == target.parID + && *(CStr63*)mailTop.name == *(CStr63*)target.name; + + if (matchesDefault) + { + // Change target spec to be the spec of the actual folder + CStr255 relativePath; + relativePath += ':'; + relativePath += *(CStr63*)mailTop.name; + relativePath += ':'; + relativePath += defaultFolderName; + err = FSMakeFSSpec( + mailTop.vRefNum, + mailTop.parID, + relativePath, + &target); + if (err != fnfErr) + ThrowIfOSErr_(err); // This would be bad? + } + // Case (2) + // The spec is explicitly the spec of the default folder. + else + { + // Get a prototype for a file inside the top mail folder + // Try again with that plus the default name. + mailTop = CPrefs::GetFilePrototype(CPrefs::MailFolder); + matchesDefault = mailTop. vRefNum == target.vRefNum + && mailTop.parID == target.parID + && defaultFolderName == *(CStr63*)target.name; + } + if (outMatchesDefault) + *outMatchesDefault = matchesDefault; + outFolderName = target.name; + // Check whether the directory exists. + FInfo info; + err = FSpGetFInfo(&target, &info); + if (err == fnfErr) + { + outFolder.SetFolderInfo(nil); + } + else + { + char* appPath = CFileMgr::EncodedPathNameFromFSSpec(target, true); + if (!appPath) + return false; + char* url = XP_STRDUP("mailbox:"); + StrAllocCat(url, appPath); + XP_FREE(appPath); + if (!url) + return false; + MSG_FolderInfo* info = ::MSG_GetFolderInfoFromURL( + CMailNewsContext::GetMailMaster(), + url, false); + XP_FREE(url); + outFolder.SetFolderInfo(info); + } + FindLocalMailHost(outServer); + return true; + } + } + } + // Remaining cases are URL paths. + else if (XP_STRCMP(inPrefName, "news.imap_sentmail_path") == 0) + kind = news_fcc; + else if (XP_STRCMP(inPrefName, "mail.imap_sentmail_path") == 0) + kind = mail_fcc; + else if (XP_STRSTR(inPrefName, "drafts")) + kind = drafts; + else if (XP_STRSTR(inPrefName, "templates")) + kind = templates; + // Here's the new way. It's always a char pref, and a URL. + char* url; + int prefResult = PREF_CopyCharPref(inPrefName, &url); + if (prefResult == PREF_NOERROR) + { + MSG_FolderInfo* info = ::MSG_GetFolderInfoFromURL( + CMailNewsContext::GetMailMaster(), + url, false); + outFolder.SetFolderInfo(info); + } + return GetFolderAndServerNames( + outFolder, + kind, + outFolderName, + outServer, + outMatchesDefault); +} // UFolderDialogs::GetFolderAndServerNames + +//---------------------------------------------------------------------------------------- +static Boolean SavePrefAsAlias(UFolderDialogs::FolderKind kind) +// More mess here, because the mail_fcc and news_fcc prefs are either: +// Binary prefs (aliases) if they're local, or +// String prefs (URLs) if they're IMAP. +//---------------------------------------------------------------------------------------- +{ + XP_Bool useIMAP; + if (kind == UFolderDialogs::mail_fcc) + { + if (PREF_NOERROR != PREF_GetBoolPref("mail.use_imap_sentmail", &useIMAP) || !useIMAP) + return true; + } + else if (kind == UFolderDialogs::news_fcc) + { + if (PREF_NOERROR != PREF_GetBoolPref("news.use_imap_sentmail", &useIMAP) || !useIMAP) + return true; + } + return false; +} // SavePrefAsAlias + +//---------------------------------------------------------------------------------------- +void UFolderDialogs::BuildPrefName(FolderKind kind, CStr255& outPrefName, Boolean& outSaveAsAlias) // e.g. Sent +//---------------------------------------------------------------------------------------- +{ + outPrefName = "\p^0.default_^1"; + outSaveAsAlias = false; + const char* r1 = (kind == news_fcc) ? "news" : "mail"; + const char* r2; + switch (kind) + { + case drafts: + r2 = "drafts"; + break; + case templates: + r2 = "templates"; + break; + case news_fcc: + case mail_fcc: + outSaveAsAlias = SavePrefAsAlias(kind); + if (!outSaveAsAlias) + { + // IMAP case : completely different prefname + outPrefName = "^0.imap_sentmail_path"; + } + r2 = "fcc"; + break; + } + StringParamText(outPrefName, r1, r2); +} // UFolderDialogs::BuildPrefName + +//====================================== +class CCreateFolderListener +// This is so we can get notified when the new folder is created, and select it in +// the popup menu. +//====================================== +: public CMailCallbackListener +{ +public: + CCreateFolderListener(CMailFolderGAPopup* inPopup) + : CMailCallbackListener() + , mPopup(inPopup) {} + virtual void PaneChanged( + MSG_Pane*, + MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode, + int32 value) + { + MSG_FolderInfo* info = nil; + // not notified for local folders. GRR!!! + if (inNotifyCode == MSG_PaneNotifySelectNewFolder) + { + MSG_ViewIndex index = (MSG_ViewIndex)value; + if (index == MSG_VIEWINDEXNONE) + return; // This is what we used to get + MSG_Pane* folderPane = ::MSG_FindPaneOfType( + CMailNewsContext::GetMailMaster(), + nil, + MSG_FOLDERPANE); + if (!folderPane) + return; + info = ::MSG_GetFolderInfo(folderPane, index); + } + else if (inNotifyCode != MSG_PaneProgressDone) + return; + if (mPopup) + CMailFolderMixin::UpdateMailFolderMixinsNow(mPopup); + if (info) + mPopup->MSetSelectedFolder(info, false); + } + CMailFolderGAPopup* mPopup; +}; // class CCreateFolderListener + +//---------------------------------------------------------------------------------------- +CMessageFolder UFolderDialogs::ConductSpecialFolderDialog( + FolderKind inKind, // e.g. Sent + const CMessageFolder& inCurFolder // URL of currentURL if pref is not saved yet + ) +// Returns nil if user cancels, URL otherwise. +// Sorry about the spaghetti. It's all single-purpose code. Ugh. +//---------------------------------------------------------------------------------------- +{ + // Get the strings we'll need + CStr255 defaultFolderName; + GetDefaultFolderName(inKind, defaultFolderName); + + // Construct the prefname from the kind + CStr255 prefName; + Boolean saveAsAlias; + BuildPrefName(inKind, prefName, saveAsAlias); + + // Put up dialog (which is initially invisible, because fiddling with the title + // is unsightly after it is visible). + StDialogHandler handler(14011, NULL); + + LWindow* dialog = handler.GetDialog(); + CStr255 workString; + dialog->GetDescriptor(workString); + StringParamText(workString, defaultFolderName); + dialog->SetDescriptor(workString); + + LCaption* blurb = (LCaption*)dialog->FindPaneByID('Blrb'); + SignalIf_(!blurb); + if (!blurb) + return nil; + blurb->GetDescriptor(workString); + StringParamText(workString, defaultFolderName); + blurb->SetDescriptor(workString); + + LGARadioButton* simpleCaseRadio = (LGARadioButton*)dialog->FindPaneByID('Fldr'); + SignalIf_(!simpleCaseRadio); + if (!simpleCaseRadio) + return nil; + simpleCaseRadio->GetDescriptor(workString); + StringParamText(workString, defaultFolderName); + simpleCaseRadio->SetDescriptor(workString); + + CMailFolderGAPopup* serverPopup = (CMailFolderGAPopup*)dialog->FindPaneByID('Srvr'); + SignalIf_(!serverPopup); + if (!serverPopup) + return nil; + + LGARadioButton* customCaseRadio = (LGARadioButton*)dialog->FindPaneByID('Othr'); + SignalIf_(!customCaseRadio); + if (!customCaseRadio) + return nil; + + CMailFolderGAPopup* folderPopup = (CMailFolderGAPopup*)dialog->FindPaneByID('MfPM'); + SignalIf_(!folderPopup); + if (!folderPopup) + return nil; + + // Set up the popup folder menus, and select the current preference + CStr255 curFolderName; + CMessageFolder curFolder = inCurFolder, curServer; + Boolean matchesDefault; + // If we were passed a current default from an unsaved pref, use that. + if (inCurFolder.GetFolderInfo() != nil) + GetFolderAndServerNames( + inCurFolder, + inKind, + curFolderName, + curServer, + &matchesDefault); + else if (!GetFolderAndServerNames( + prefName, + curFolder, + curFolderName, + curServer, + &matchesDefault)) + Assert_(false); + + if (matchesDefault) + simpleCaseRadio->SetValue(1); + else + customCaseRadio->SetValue(1); + + CMailFolderMixin::FolderChoices c + = (CMailFolderMixin::FolderChoices)((int)CMailFolderMixin::eWantPOP + (int)CMailFolderMixin::eWantIMAP); + folderPopup->MSetFolderChoices(c); + CMailFolderMixin::UpdateMailFolderMixinsNow(folderPopup); + folderPopup->MSetSelectedFolder(curFolder, false); + CCreateFolderListener creationListener(folderPopup); + // create a listener to reset the menu when a folder's created. + + c = (CMailFolderMixin::FolderChoices)((int)CMailFolderMixin::eWantHosts); + serverPopup->MSetFolderChoices(c); + CMailFolderMixin::UpdateMailFolderMixinsNow(serverPopup); + serverPopup->MSetSelectedFolder(curServer, false); + + // Run the dialog + MessageT message = msg_Nothing; + dialog->Show(); + do { + message = handler.DoDialog(); + switch (message) + { + case 'NewÉ': // the "new folder" button + { + CMessageFolder parent(nil); + ConductNewFolderDialog(parent, &creationListener); + break; + } + case 'HELP': + SysBeep(1); + break; + } + } while (message != msg_OK && message != msg_Cancel); + + // Use the result. + if (message == msg_OK) + { + if (simpleCaseRadio->GetValue() == 1) + return serverPopup->MGetSelectedFolder(); + else + return folderPopup->MGetSelectedFolder(); + } + return nil; +} // ConductSpecialFolderDialog + +#endif // MOZ_LITE diff --git a/mozilla/cmd/macfe/MailNews/UNewFolderDialog.h b/mozilla/cmd/macfe/MailNews/UNewFolderDialog.h new file mode 100644 index 00000000000..a4af9520644 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/UNewFolderDialog.h @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// UNewFolderDialog.h + +#pragma once + +class CMessageFolder; +class CMailCallbackListener; +class CStr255; +struct MSG_Pane; +struct MSG_FolderInfo; + +//====================================== +class UFolderDialogs +//====================================== +{ +public: + static Boolean ConductNewFolderDialog( + const CMessageFolder& inParentFolder, + CMailCallbackListener* inListener = nil); + // Can listen for MSG_PaneNotifySelectNewFolder + enum FolderKind { drafts, templates, mail_fcc, news_fcc, num_kinds }; + static CMessageFolder + ConductSpecialFolderDialog( + FolderKind inKind, // e.g. Sent + const CMessageFolder& inCurFolder // if pref is not saved yet + ); + // Returns nil if user cancels, chosen folder. + + static void BuildPrefName( + FolderKind inKind, + CStr255& outPrefName, + Boolean& outSaveAsAlias); + static Boolean FindLocalMailHost( + CMessageFolder& outFolder); + static Boolean GetDefaultFolderName( + FolderKind inKind, + CStr255& outFolderName); + static Boolean GetFolderAndServerNames( + const char* inPrefName, + CMessageFolder& outFolder, + CStr255& outFolderName, + CMessageFolder& outServer, + Boolean* outMatchesDefault = nil); + static Boolean GetFolderAndServerNames( + const CMessageFolder& inFolder, + FolderKind inKind, + CStr255& outFolderName, + CMessageFolder& outServer, + Boolean* outMatchesDefault = nil); +}; // class UFolderDialogs diff --git a/mozilla/cmd/macfe/MailNews/UOffline.cp b/mozilla/cmd/macfe/MailNews/UOffline.cp new file mode 100644 index 00000000000..c1f9d50f8e7 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/UOffline.cp @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// UOffline.cp + +#include "UOffline.h" + +#include "MailNewsgroupWindow_Defines.h" +#include "CMailNewsContext.h" +#include "CMailProgressWindow.h" +#include "COfflinePicker.h" +#include "UModalDialogs.h" +#include "MPreference.h" +#include "macutil.h" +#include "uapp.h" + +#include "prefapi.h" +#include "msgcom.h" +#include "PascalString.h" +#include "xp_help.h" + +//------------------------------------------------------------------------------ +// ¥ ObeySynchronizeCommand +//------------------------------------------------------------------------------ +// Run the "Download Offline Items" dialog where the user can check what kind +// of items should be downloaded: Mail Folders, Discussion Groups or Directories. +// Since we are using the magic CPrefCheckbox(es) in that dialog, we have to +// delete the dialog and its handler for the check-boxes to actually write their +// new state on disk and update the Prefs file. +// +Boolean UOffline::ObeySynchronizeCommand(Boolean stayModal) +{ + + Str255 windowTitle; // Get it from the offline dialog, to use in the progress dialog. + + { // <-- start of scope for StDialogHandler + + // Save and restore the prefs write state + MPreferenceBase::StWriteOnDestroy stateSetter(true); + + // Put up dialog. + StDialogHandler aHandler(20002, NULL); + + LWindow* dialog = aHandler.GetDialog(); + dialog->GetDescriptor(windowTitle); + + // Run the dialog + MessageT message = msg_OK; // first pass message + do + { + switch (message) + { + case 'Sele': // Select button + COfflinePickerWindow::DisplayDialog(); + break; + + case 'Help': + ShowHelp(HELP_MAILNEWS_SYNCHRONIZE); + break; + + case 'GetM': // check-boxes + case 'GetN': + case 'GetD': + case 'SndM': + case msg_OK: // first pass + LControl *getmailBox = (LControl*)dialog->FindPaneByID('GetM'); + LControl *getnewsBox = (LControl*)dialog->FindPaneByID('GetN'); + LControl *getdirBox = (LControl*)dialog->FindPaneByID('GetD'); + LControl *sendBox = (LControl*)dialog->FindPaneByID('SndM'); + LControl *flaggedBox = (LControl*)dialog->FindPaneByID('GetF'); + LControl *syncButton = (LControl*)dialog->FindPaneByID('Sync'); + SignalIf_(!((getmailBox && getnewsBox && getdirBox && sendBox && flaggedBox && syncButton))); + + if ((getmailBox->GetValue() == 0) + && (getnewsBox->GetValue() == 0)) + { + flaggedBox->Disable(); + } + else + { + flaggedBox->Enable(); + } + + if ((getmailBox->GetValue() == 0) + && (getnewsBox->GetValue() == 0) + && (getdirBox->GetValue() == 0) + && (sendBox->GetValue() == 0)) + { + syncButton->Disable(); + } + else + { + syncButton->Enable(); + } + break; + } + + message = aHandler.DoDialog(); + + } while (message != msg_OK && message != msg_Cancel); + + // Use the result. + if (message != msg_OK) + return false; + } // <-- End of scope for StDialogHandler (need to save prefs before calling SynchronizeForOffline) + + // Start the download, using the prefs + ResIDT resID = (stayModal ? CMailProgressWindow::res_ID_modal : CMailProgressWindow::res_ID_modeless); + CMailProgressWindow* progressWindow = CMailProgressWindow::SynchronizeForOffline(windowTitle, resID); + if (progressWindow && stayModal) + { + CNSContext* cnsContext = progressWindow->GetWindowContext(); + if (cnsContext) + { + do + { + CFrontApp::GetApplication()->ProcessNextEvent(); + } while (XP_IsContextBusy(*cnsContext)); + } + } + + return true; +} + +//------------------------------------------------------------------------------ +// ¥ ObeyToggleOfflineCommand +//------------------------------------------------------------------------------ +// +Boolean UOffline::ObeyToggleOfflineCommand() +{ + CMailProgressWindow::ToggleOffline(); + return true; +} + + +//------------------------------------------------------------------------------ +// ¥ FindOfflineCommandStatus +//------------------------------------------------------------------------------ +// +void UOffline::FindOfflineCommandStatus(CommandT inCommand, Boolean& enabled, Str255 name) +{ + enabled = true; + switch (inCommand) + { + case cmd_ToggleOffline: + ::GetIndString(name, 1037, (UOffline::AreCurrentlyOnline() ? 4 : 3)); + break; + + case cmd_SynchronizeForOffline: + enabled = UOffline::AreCurrentlyOnline(); + break; + } +} + + +//------------------------------------------------------------------------------ +// ¥ AreCurrentlyOnline +//------------------------------------------------------------------------------ +// +Boolean UOffline::AreCurrentlyOnline() +{ + return !NET_IsOffline(); +} diff --git a/mozilla/cmd/macfe/MailNews/UOffline.h b/mozilla/cmd/macfe/MailNews/UOffline.h new file mode 100644 index 00000000000..eda32da5015 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/UOffline.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1997 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// UOffline.h + +#pragma once +struct MSG_Pane; + +//----------------------------------- +class UOffline +//----------------------------------- +{ +public: + enum { syncModal = true, syncModeless = false }; + + static Boolean AreCurrentlyOnline(); + + static Boolean ObeyToggleOfflineCommand(); + static Boolean ObeySynchronizeCommand(Boolean stayModal = syncModal); + static void FindOfflineCommandStatus(CommandT inCommand, Boolean& enabled, Str255 name); + // Handles the Go Online/Go offline command +}; diff --git a/mozilla/cmd/macfe/MailNews/build/MailNews.mcp b/mozilla/cmd/macfe/MailNews/build/MailNews.mcp new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mozilla/cmd/macfe/MailNews/build/MailNews_DebugHeaders.pch b/mozilla/cmd/macfe/MailNews/build/MailNews_DebugHeaders.pch new file mode 100644 index 00000000000..54e0ef3f2d6 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/build/MailNews_DebugHeaders.pch @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ +// MailNews_Headers.pch +// +// NOTE: +// +// You will typically not need to edit this file. If you want to add +// a file to the C/C++ precompiled header, do it in Comm_Headers.c +// or Borwser_Headers.cp respectively. +// +// If you're doing a non-debug build, use the non-debug project which +// will has pch files that generate non-debug dumps. +// +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ + +#include "MacPrefix_debug.h" + +#ifdef powerc + #pragma precompile_target "MailNews_HeadersPPC" +#else + #pragma precompile_target "MailNews_Headers68K" +#endif + +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ +// ¥ Include the list of headers. +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ + +#include "Comm_Headers.c" + diff --git a/mozilla/cmd/macfe/MailNews/build/MailNews_DebugHeaders.pch++ b/mozilla/cmd/macfe/MailNews/build/MailNews_DebugHeaders.pch++ new file mode 100644 index 00000000000..a868884702d --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/build/MailNews_DebugHeaders.pch++ @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +// + +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ +// MailNews_Headers.pch++ +// +// NOTE: +// +// You will typically not need to edit this file. If you want to add +// a file to the C/C++ precompiled header, do it in Comm_Headers.c +// or Borwser_Headers.cp respectively. +// +// If you're doing a non-debug build, use the non-debug project which +// will has pch files that generate non-debug dumps. +// +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ + +#include "MacPrefix_debug.h" + +#ifdef powerc + #pragma precompile_target "MailNews_HeadersPPC++" +#else + #pragma precompile_target "MailNews_Headers68K++" +#endif + +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ +// ¥ Include the headers. +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ + +extern "C" { + #include "Comm_Headers.c" +} + +#include "Comm_Headers.cp" + + diff --git a/mozilla/cmd/macfe/MailNews/build/MailNews_DebugPrefix.h b/mozilla/cmd/macfe/MailNews/build/MailNews_DebugPrefix.h new file mode 100644 index 00000000000..b18dd9376fa --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/build/MailNews_DebugPrefix.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ +// MailNews_DebugPrefix.h +// +// NOTE: +// You typically won't need to change anything in this file. +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ + +#include "IDE_Options.h" + + +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ +// ¥ When we split out the procompiled headers seperately, we will not +// be including them here. We will instead define things like +// PowerPlant_PCH and include them at the top of the applicable source +// modules +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ + +#ifdef __powerc + #ifdef __cplusplus + #include "MailNews_HeadersPPC++" + #else + #include "MailNews_HeadersPPC" + #endif +#else + #ifdef __cplusplus + #include "MailNews_Headers68K++" + #else + #include "MailNews_Headers68K" + #endif +#endif + + diff --git a/mozilla/cmd/macfe/MailNews/build/MailNews_Headers.pch b/mozilla/cmd/macfe/MailNews/build/MailNews_Headers.pch new file mode 100644 index 00000000000..4dce1a9906e --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/build/MailNews_Headers.pch @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ +// MailNews_Headers.pch +// +// NOTE: +// +// You will typically not need to edit this file. If you want to add +// a file to the C/C++ precompiled header, do it in Comm_Headers.c +// or Borwser_Headers.cp respectively. +// +// If you're doing a non-debug build, use the non-debug project which +// will has pch files that generate non-debug dumps. +// +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ + +#include "MacPrefix.h" + +#ifdef powerc + #pragma precompile_target "MailNews_HeadersPPC" +#else + #pragma precompile_target "MailNews_Headers68K" +#endif + +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ +// ¥ Include the list of headers. +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ + +#include "Comm_Headers.c" + diff --git a/mozilla/cmd/macfe/MailNews/build/MailNews_Headers.pch++ b/mozilla/cmd/macfe/MailNews/build/MailNews_Headers.pch++ new file mode 100644 index 00000000000..9cc6fe16310 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/build/MailNews_Headers.pch++ @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +// + +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ +// MailNews_Headers.pch++ +// +// NOTE: +// +// You will typically not need to edit this file. If you want to add +// a file to the C/C++ precompiled header, do it in Comm_Headers.c +// or Borwser_Headers.cp respectively. +// +// If you're doing a non-debug build, use the non-debug project which +// will has pch files that generate non-debug dumps. +// +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ + +#include "MacPrefix.h" + +#ifdef powerc + #pragma precompile_target "MailNews_HeadersPPC++" +#else + #pragma precompile_target "MailNews_Headers68K++" +#endif + +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ +// ¥ Include the headers. +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ + +extern "C" { + #include "Comm_Headers.c" +} + +#include "Comm_Headers.cp" + + diff --git a/mozilla/cmd/macfe/MailNews/build/MailNews_Prefix.h b/mozilla/cmd/macfe/MailNews/build/MailNews_Prefix.h new file mode 100644 index 00000000000..de808c6e020 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/build/MailNews_Prefix.h @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ +// MailNews_Prefix.h +// +// NOTE: +// You typically won't need to change anything in this file. +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ + +#include "IDE_Options.h" + + +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ +// ¥ When we split out the procompiled headers seperately, we will not +// be including them here. We will instead define things like +// PowerPlant_PCH and include them at the top of the applicable source +// modules +// ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ + +#ifdef __powerc + #ifdef __cplusplus + #include "MailNews_HeadersPPC++" + #else + #include "MailNews_HeadersPPC" + #endif +#else + #ifdef __cplusplus + #include "MailNews_Headers68K++" + #else + #include "MailNews_Headers68K" + #endif +#endif + + diff --git a/mozilla/cmd/macfe/MailNews/nc_exception.h b/mozilla/cmd/macfe/MailNews/nc_exception.h new file mode 100644 index 00000000000..d64010515d7 --- /dev/null +++ b/mozilla/cmd/macfe/MailNews/nc_exception.h @@ -0,0 +1,291 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + + +// nc_exception.h + +#pragma once + +////////////////////////////////////////////////////////////////////////// +// +// This file contains several classes for exceptions support in Communicator. +// The classes can be used as they are or they can be further subclassed. +// +// When using the supplied classes as they are, the user should check if +// the current codes and strings, stored in "exception_codes.h" and +// "exception_str.r", suffice his/her needs. If they don't he/she should +// add the proper codes and strings to the two aforementioned files. +// +// When the user needs to subclass the supplied classes, usually to supply +// additional granularity, he/she should provide a constructor, a specific getter +// method for the error code and override the GetStringResID, method in order +// to return the proper resource ID for the exception strings. He/she should +// also add a new enum and string resource. +// +// Only code declared in the proper enums in the file "exception_codes.h" should +// be used for exceptions. This will ensure compatibility and proper documentation. +// +// By appropiatelly subclassing and keeping the policy to declare and maintain +// suclass enum as error code we can be assured of consistent errors and +// proper documentation. +// +////////////////////////////////////////////////////////////////////////// +// +// USAGE: +// +// try{ +// foo(); +// } +// catch( nc_exception_subclass& error ) +// { +// // do something +// } +// catch( nc_exception& error ) +// { +// // do something +// } +// catch( ... ) +// { +// // do something +// } +// ... +// +// foo() { +// throw nc_exception_subclass( eSampleException ); +// } +// +////////////////////////////////////////////////////////////////////////// +// +// When What +// ----- ----- +// +// 12/3/97 Genesis +// 1/6/97 Changed to reflect the usage of string resources for internationalization. +// +////////////////////////////////////////////////////////////////////////// + +#pragma exceptions on // make sure that exceptions are on + +#include "exception_codes.h" // file containing the exception codes +#include "reserr.h" + +#pragma mark class nc_exception +////////////////////////////////////////////////////////////////////////// +// +// nc_exception +// +// Base class for all the exceptions. +// +// Methods: +// +// nc_exception( const int errorCode ) - constructor used to set the proper +// exception code +// +// GetErrorCode() - getter that returns the error code +// +// DisplaySimpleAlert() - methods that displays the default alert using the +// string from the default resource. +// +////////////////////////////////////////////////////////////////////////// + +class nc_exception +{ + public: + nc_exception( const GenericCode errorCode ); + virtual ~nc_exception(); + + virtual void DisplaySimpleAlert() const; + + protected: + + const int GetErrorCode() const; + void SetErrorCode( const GenericCode errorCode ); + void SetErrorCode( const int errorCode ); + + virtual short GetStringResID() const; + + private: + nc_exception(); // private - empty constructor is not allowed + + int mErrorCode; + +}; + +////////////////////////////////////////////////////////////////////////// +// nc_exception inlines + +inline +nc_exception::nc_exception() +{ +} + +inline +nc_exception::~nc_exception() +{ +} + +inline +nc_exception::nc_exception( const GenericCode errorCode ) : + mErrorCode( (int) errorCode ) +{ +} + +inline +short +nc_exception::GetStringResID() const +{ + return genericExStringsID; +} + +inline +void +nc_exception::DisplaySimpleAlert() const +{ + Str255 errorString, codeString; + short stringResID = GetStringResID(); + + ::GetIndString( errorString, stringResID, mErrorCode ); + ::NumToString( mErrorCode, codeString ); + ::ParamText( errorString, codeString, nil, nil ); + ::CautionAlert( ALRT_ErrorOccurred, nil ); +} + +inline +void +nc_exception::SetErrorCode( const int errorCode ) +{ + mErrorCode = errorCode; +} + +inline +const int +nc_exception::GetErrorCode() const +{ + return mErrorCode; +} + +#pragma mark - +#pragma mark class mail_exception +////////////////////////////////////////////////////////////////////////// +// mail_exception +// +// subclass for the mail/news errors. +// +////////////////////////////////////////////////////////////////////////// + +class mail_exception : public nc_exception +{ + +public: + + mail_exception( const MailCode errorCode ); + const MailCode GetMailErrorCode() const; + +protected: + + virtual short GetStringResID() const; +private: + + mail_exception(); // private - empty constructor is not allowed + +}; + +////////////////////////////////////////////////////////////////////////// +// mail_exception inlines + +inline +mail_exception::mail_exception() +{ +} + +inline +mail_exception::mail_exception( const MailCode errorCode ) +{ + SetErrorCode( (int) errorCode ); +} + +inline +const MailCode +mail_exception::GetMailErrorCode() const +{ + return ( ( MailCode ) GetErrorCode() ); +} + +inline +short +mail_exception::GetStringResID() const +{ + return mailExStringsID; +} + + +#pragma mark - +#pragma mark class browser_exception +////////////////////////////////////////////////////////////////////////// +// browser_exception +// +// subclass for the browser errors. +// +////////////////////////////////////////////////////////////////////////// + +class browser_exception : public nc_exception +{ + +public: + + browser_exception( const BrowserCode errorCode ); + const BrowserCode GetBrowserErrorCode() const; + +protected: + + virtual short GetStringResID() const; +private: + + browser_exception(); // private - empty constructor is not allowed + +}; + +////////////////////////////////////////////////////////////////////////// +// browser_exception inlines + +inline +browser_exception::browser_exception() +{ +} + +inline +browser_exception::browser_exception( const BrowserCode errorCode ) +{ + SetErrorCode( (int) errorCode ); +} + +inline +const BrowserCode +browser_exception::GetBrowserErrorCode() const +{ + return ( ( BrowserCode ) GetErrorCode() ); +} + +inline +short +browser_exception::GetStringResID() const +{ + return browserExStringsID; +} + diff --git a/mozilla/cmd/winfe/abapi.h b/mozilla/cmd/winfe/abapi.h new file mode 100644 index 00000000000..60ff8941524 --- /dev/null +++ b/mozilla/cmd/winfe/abapi.h @@ -0,0 +1,297 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +// +// This is a header file for the Address Book API support within +// Communicator. +// +// Written by: Rich Pizzarro (rhp@netscape.com) +// +#ifndef __ABAPI +#define __ABAPI + +#include "nabapi.h" // for all address book defines + +// These are very important for compatibility going forward... +#define NAB_VERSION_MAJOR 1 /* Major version number; changes with major code release number */ +#define NAB_VERSION_MINOR 0 /* Minor version number; changes with point release number. */ + +#ifdef WIN16 +typedef unsigned char UCHAR; +#endif + +#define MAX_NAME_LEN 256 +#define MAX_ADDRBOOK_NAME_LEN 512 +#define MAX_CON 4 // Maximum NABAPI sessions supported +#define MAX_POINTERS 32 +#define MAX_LDIF_SIZE 16000 // Max size for address book LDIF allocation stuff +#define MAX_LDIFSEARCH_SIZE 4096 // Max size for address book LDIF allocation stuff + +#ifndef NABAPIDLL + +#include "abcom.h" // For MSG_ & AB_ stuff... +#include "msgcom.h" + +// +// The Address Book class that will act as the internal mechanism for +// Communicator to control multiple Address Book sessions. +// +class CNABConnection +{ +protected: + LONG m_ID; + BOOL m_defaultConnection; + LONG m_sessionCount; + LONG m_nameIndex; + LONG m_nameTotalCount; + + // Methods +public: + MSG_Pane *m_ContainerPane; // Container Pane... + AB_ContainerInfo **m_ContainerArray; // The array of container info pointers... + LONG m_ContainerArrayCount; // The count of the number of + // m_ContainerArray entries + MSG_Pane *m_AddressBookPane; // Container info for a particular addr book + + CNABConnection ( LONG ); + ~CNABConnection ( ); + + // ID related methods + LONG GetID( ) { return m_ID; } ; + + // Dealing with the default session... + BOOL IsDefault( ) { return m_defaultConnection; } ; + void SetDefault( BOOL flag ) { m_defaultConnection = flag; } ; + + // For handling multiple sessions on a profile name... + LONG GetSessionCount( ) { return m_sessionCount; } ; + void IncrementSessionCount() { ++m_sessionCount; } ; + void DecrementSessionCount() { --m_sessionCount; } ; + + // For enumerating names... + void SetNameIndex( LONG mIndex ) { m_nameIndex = mIndex; } ; + void IncrementNameIndex( ) { ++m_nameIndex; } ; + LONG GetNameIndex( ) { return m_nameIndex; }; + + void SetNameTotalCount( LONG mTotal ) { m_nameTotalCount = mTotal; } ; + LONG GetNameTotalCount( ) { return m_nameTotalCount; }; + + // For AB Calls... + BOOL RefreshABContainerList ( void ); + AB_ContainerInfo *FindContainerInfo( LPSTR, LPSTR ); + + +}; + +#endif // NABAPIDLL + +// +// Defines needed for requests being made with the WM_COPYDATA call... +// +typedef enum { + NSCP_NABStartRequestID = 200, + NSCP_NAB_Open, + NSCP_NAB_Close, + NSCP_NAB_GetAddressBookList, + NSCP_NAB_GetDefaultAddressBook, + NSCP_NAB_CreatePersonalAddressBook, + NSCP_NAB_FreeMemory, + NSCP_NAB_SaveAddressBookToHTML, + NSCP_NAB_ImportLDIFFile, + NSCP_NAB_ExportLDIFFile, + NSCP_NAB_GetFirstAddressBookEntry, + NSCP_NAB_GetNextAddressBookEntry, + NSCP_NAB_FindAddressBookEntry, + NSCP_NAB_InsertAddressBookEntry, + NSCP_NAB_UpdateAddressBookEntry, + NSCP_NAB_DeleteAddressBookEntry, + NSCP_NABEndRequestID // Note: this is a marker for NAB IPC requests +} NSCP_NAB_IPC_REQUEST; + +// +// This is to keep track of the pointers allocated in the MAPI DLL +// and deal with them correctly. +// +#define NAB_FLAT_MEMORY 0 +#define NAB_ADDRBOOK_STRUCT 1 + +typedef struct { + LPVOID lpMem; + char memType; +} nabMemTrackerType; + +// +// This is the generic message that WM_COPYDATA will send to the +// Communicator product to allow it to attach to shared memory. +// NOTE: On Win16, this will simply reference a pointer. +// +typedef struct { + char smemName[64]; // Name of shared memory + DWORD smemSize; // Size of shared memory + LPVOID lpsmem; // Will be really used in Win16 only +} NABIPCType; + +// +// These are message specific structures that will be used for +// the various Address Book operations. +// +typedef struct { + DWORD ipcWorked; // Necessary for IPC check with Communicator + NABConnectionID abConnection; + UINT_16 majorVerNumber; + UINT_16 minorVerNumber; +} NAB_OpenType; + +typedef struct { + DWORD ipcWorked; // Necessary for IPC check with Communicator + NABConnectionID abConnection; +} NAB_CloseType; + +typedef struct { + DWORD ipcWorked; // Necessary for IPC check with Communicator + NABConnectionID abConnection; + UINT_32 abookCount; + char blobFileName[_MAX_PATH]; // file name N abooks on disk +} NAB_GetAddressBookListType; + +typedef struct { + DWORD ipcWorked; // Necessary for IPC check with Communicator + NABConnectionID abConnection; + BOOL defaultFound; + // NABAddrBookDescType flattened + char reserved[16]; // future use + char abookDesc[MAX_ADDRBOOK_NAME_LEN]; // description of address book + char abookFileName[_MAX_PATH]; // file name of abooks on disk +} NAB_GetDefaultAddressBookType; + +typedef struct { + DWORD ipcWorked; // Necessary for IPC check with Communicator + NABConnectionID abConnection; + // NABAddrBookDescType flattened + char reserved[16]; // future use + char abookName[MAX_ADDRBOOK_NAME_LEN]; // description of address book + char abookFileName[_MAX_PATH]; // file name of abooks on disk +} NAB_CreatePersonalAddressBookType; + +typedef struct { + DWORD ipcWorked; // Necessary for IPC check with Communicator + NABConnectionID abConnection; + // NABAddrBookDescType flattened + char reserved[16]; // future use + char abookName[MAX_ADDRBOOK_NAME_LEN]; // description of address book + char abookFileName[_MAX_PATH]; // file name of abooks on disk + // Return stuff + char HTMLFileName[_MAX_PATH]; // saved HTML file name +} NAB_SaveAddressBookToHTMLType; + +typedef struct { + DWORD ipcWorked; // Necessary for IPC check with Communicator + NABConnectionID abConnection; + BOOL deleteImportFile; + char LDIFFileName[_MAX_PATH]; // LDIF file to import + // NABAddrBookDescType flattened + char reserved[16]; // future use + char abookName[MAX_ADDRBOOK_NAME_LEN]; // description of address book + char abookFileName[_MAX_PATH]; // file name of abooks on disk +} NAB_ImportLDIFFileType; + +typedef struct { + DWORD ipcWorked; // Necessary for IPC check with Communicator + NABConnectionID abConnection; + char LDIFFileName[_MAX_PATH]; // LDIF file to export + // NABAddrBookDescType flattened + char reserved[16]; // future use + char abookName[MAX_ADDRBOOK_NAME_LEN]; // description of address book + char abookFileName[_MAX_PATH]; // file name of abooks on disk +} NAB_ExportLDIFFileType; + +typedef struct { + DWORD ipcWorked; // Necessary for IPC check with Communicator + NABConnectionID abConnection; + // NABAddrBookDescType flattened + char reserved[16]; // future use + char abookName[MAX_ADDRBOOK_NAME_LEN]; // description of address book + char abookFileName[_MAX_PATH]; // file name of abooks on disk + // Returned Values + NABUserID userID; + NABUpdateTime updateTime; + char userInfo[MAX_LDIF_SIZE]; +} NAB_GetFirstAddressBookEntryType; + +typedef struct { + DWORD ipcWorked; // Necessary for IPC check with Communicator + NABConnectionID abConnection; + // NABAddrBookDescType flattened + char reserved[16]; // future use + // Returned Values + NABUserID userID; + NABUpdateTime updateTime; + char userInfo[MAX_LDIF_SIZE]; +} NAB_GetNextAddressBookEntryType; + +typedef struct { + DWORD ipcWorked; // Necessary for IPC check with Communicator + NABConnectionID abConnection; + // NABAddrBookDescType flattened + char reserved[16]; // future use + char abookName[MAX_ADDRBOOK_NAME_LEN]; // description of address book + char abookFileName[_MAX_PATH]; // file name of abooks on disk + // Input Values + char ldifSearchAttribute[MAX_LDIFSEARCH_SIZE]; + // Output values + NABUserID userID; + NABUpdateTime updateTime; + char userInfo[MAX_LDIF_SIZE]; +} NAB_FindAddressBookEntryType; + +typedef struct { + DWORD ipcWorked; // Necessary for IPC check with Communicator + NABConnectionID abConnection; + // NABAddrBookDescType flattened + char reserved[16]; // future use + char abookName[MAX_ADDRBOOK_NAME_LEN]; // description of address book + char abookFileName[_MAX_PATH]; // file name of abooks on disk + // Insert Value + char userInfo[MAX_LDIF_SIZE]; + // Update Value + NABUserID userID; +} NAB_InsertAddressBookEntryType; + +typedef struct { + DWORD ipcWorked; // Necessary for IPC check with Communicator + NABConnectionID abConnection; + // NABAddrBookDescType flattened + char reserved[16]; // future use + char abookName[MAX_ADDRBOOK_NAME_LEN]; // description of address book + char abookFileName[_MAX_PATH]; // file name of abooks on disk + // Update Value + NABUserID userID; + char userInfo[MAX_LDIF_SIZE]; +} NAB_UpdateAddressBookEntryType; + +typedef struct { + DWORD ipcWorked; // Necessary for IPC check with Communicator + NABConnectionID abConnection; + // NABAddrBookDescType flattened + char reserved[16]; // future use + char abookName[MAX_ADDRBOOK_NAME_LEN]; // description of address book + char abookFileName[_MAX_PATH]; // file name of abooks on disk + // Delete user Value + NABUserID userID; +} NAB_DeleteAddressBookEntryType; + +#endif // __ABAPI diff --git a/mozilla/cmd/winfe/abhook.cpp b/mozilla/cmd/winfe/abhook.cpp new file mode 100644 index 00000000000..b9f20a741c7 --- /dev/null +++ b/mozilla/cmd/winfe/abhook.cpp @@ -0,0 +1,1874 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +// +// Address Book API Hooks for Communicator +// Note: THIS LIVES IN COMMUNICATOR THOUGH IT IS IN THE ABAPI FOR +// BUILD REASONS +// Written by: Rich Pizzarro (rhp@netscape.com) +// March 1998 + +#ifdef NSCPNABAPIDLL // Is this part of the DLL or Communicator? +#include +#include +#else +#include "stdafx.h" +#endif + +#include +#include +#include +#include + +#include "wfemsg.h" // for WFE_MSGGetMaster() +#include "nsstrseq.h" +#include "abapi.h" +#include "abhook.h" +#include "nabapi.h" +#include "mapismem.h" +#include "hiddenfr.h" +//#include "msgcom.h" +#include "abcom.h" +#include "abutils.h" + +extern CNetscapeApp theApp; + +// +// Static defines for the Address Book support... +// +static CNABConnection *nabConnections[MAX_CON] = {NULL, NULL, NULL, NULL}; + +// +// Forward declarations... +// +LONG ProcessNAB_Open(NAB_OpenType *openPtr); +LONG ProcessNAB_Close(NAB_CloseType *closePtr); +LONG ProcessNAB_GetAddressBookList(NAB_GetAddressBookListType *getPtr); +LONG ProcessNAB_GetDefaultAddressBook(NAB_GetDefaultAddressBookType *getPtr); +LONG ProcessNAB_CreatePersonalAddressBook(NAB_CreatePersonalAddressBookType *createPtr); +LONG ProcessNAB_SaveAddressBookToHTML(NAB_SaveAddressBookToHTMLType *savePtr); +LONG ProcessNAB_ImportLDIFFile(NAB_ImportLDIFFileType *importPtr); +LONG ProcessNAB_ExportLDIFFile(NAB_ExportLDIFFileType *exportPtr); +LONG ProcessNAB_GetFirstAddressBookEntry(NAB_GetFirstAddressBookEntryType *getPtr); +LONG ProcessNAB_GetNextAddressBookEntry(NAB_GetNextAddressBookEntryType *getPtr); +LONG ProcessNAB_FindAddressBookEntry(NAB_FindAddressBookEntryType *findPtr); +LONG ProcessNAB_InsertAddressBookEntry(NAB_InsertAddressBookEntryType *insertPtr); +LONG ProcessNAB_UpdateAddressBookEntry(NAB_UpdateAddressBookEntryType *updatePtr); +LONG ProcessNAB_DeleteAddressBookEntry(NAB_DeleteAddressBookEntryType *deletePtr); + +// +// This will store the CFrameWnd we startup for the Address Book API and if we +// do start an instance of the browser, we will close it out when we leave. +// +static CFrameWnd *pNABFrameWnd = NULL; + +void +StoreNABFrameWnd(CFrameWnd *pFrameWnd) +{ + pNABFrameWnd = pFrameWnd; +} + +// This is the result of a WM_COPYDATA message. This will be used to send requests +// into Communicator for Address Book API requests. +// +// The description of the parameters coming into this call are: +// +// wParam = (WPARAM) (HWND) hwnd; // handle of sending window +// lParam = (LPARAM) (PCOPYDATASTRUCT) pcds; // pointer to structure with data +// context= needed for the context we will use in Communicator +// +// typedef struct tagCOPYDATASTRUCT { // cds +// DWORD dwData; // the ID of the request +// DWORD cbData; // the size of the argument +// PVOID lpData; // Chunk of information defined specifically for each of the +// // Address Book API Calls. +// } COPYDATASTRUCT; +// +// Returns: The Address Book API Return code for the operation: +// +// +LONG +ProcessNetscapeNABHook(WPARAM wParam, LPARAM lParam) +{ + PCOPYDATASTRUCT pcds = (PCOPYDATASTRUCT) lParam; + NABIPCType *ipcInfo; +#ifdef WIN32 + HANDLE hSharedMemory; +#endif + + if (lParam == NULL) + { + return(NAB_FAILURE); + } + + // + // Get shared memory info structure pointer... + // + ipcInfo = (NABIPCType *)pcds->lpData; + if (ipcInfo == NULL) + { + return(NAB_FAILURE); + } + + // + // Now connect to shared memory...or just set the + // pointer for Win16 + // +#ifdef WIN32 + CSharedMem *sMem = NSOpenExistingSharedMemory((LPCTSTR) ipcInfo->smemName, &hSharedMemory); + if (!sMem) + { + return(NAB_FAILURE); + } +#else + if (!ipcInfo->lpsmem) + { + return(NAB_FAILURE); + } +#endif + + TRACE("NAB: NABHook Message ID = %d\n", pcds->dwData); + switch (pcds->dwData) + { + ////////////////////////////////////////////////////////////////////// + // NAB_Open + ////////////////////////////////////////////////////////////////////// + case NSCP_NAB_Open: + { + NAB_OpenType *openInfo; +#ifdef WIN32 + openInfo = (NAB_OpenType *) &(sMem->m_buf[0]); +#else + openInfo = (NAB_OpenType *) ipcInfo->lpsmem; +#endif + + if (!openInfo) + { + return(NAB_FAILURE); + } + + openInfo->ipcWorked = 1; // Set the worked flag + return(ProcessNAB_Open(openInfo)); + break; + } + + ////////////////////////////////////////////////////////////////////// + // NAB_Close + ////////////////////////////////////////////////////////////////////// + case NSCP_NAB_Close: + { + NAB_CloseType *closeInfo; +#ifdef WIN32 + closeInfo = (NAB_CloseType *) &(sMem->m_buf[0]); +#else + closeInfo = (NAB_CloseType *) ipcInfo->lpsmem; +#endif + + if (!closeInfo) + { + return(NAB_FAILURE); + } + + closeInfo->ipcWorked = 1; // Set the worked flag + return(ProcessNAB_Close(closeInfo)); + break; + } + + ////////////////////////////////////////////////////////////////////// + // NAB_GetAddressBookList + ////////////////////////////////////////////////////////////////////// + case NSCP_NAB_GetAddressBookList: + { + NAB_GetAddressBookListType *getInfo; +#ifdef WIN32 + getInfo = (NAB_GetAddressBookListType *) &(sMem->m_buf[0]); +#else + getInfo = (NAB_GetAddressBookListType *) ipcInfo->lpsmem; +#endif + + if (!getInfo) + { + return(NAB_FAILURE); + } + + getInfo->ipcWorked = 1; // Set the worked flag + return(ProcessNAB_GetAddressBookList(getInfo)); + break; + } + + ////////////////////////////////////////////////////////////////////// + // NAB_GetDefaultAddressBook + ////////////////////////////////////////////////////////////////////// + case NSCP_NAB_GetDefaultAddressBook: + { + NAB_GetDefaultAddressBookType *getInfo; +#ifdef WIN32 + getInfo = (NAB_GetDefaultAddressBookType *) &(sMem->m_buf[0]); +#else + getInfo = (NAB_GetDefaultAddressBookType *) ipcInfo->lpsmem; +#endif + + if (!getInfo) + { + return(NAB_FAILURE); + } + + getInfo->ipcWorked = 1; // Set the worked flag + return(ProcessNAB_GetDefaultAddressBook(getInfo)); + break; + } + + ////////////////////////////////////////////////////////////////////// + // NAB_CreatePersonalAddressBook + ////////////////////////////////////////////////////////////////////// + case NSCP_NAB_CreatePersonalAddressBook: + { + NAB_CreatePersonalAddressBookType *createInfo; +#ifdef WIN32 + createInfo = (NAB_CreatePersonalAddressBookType *) &(sMem->m_buf[0]); +#else + createInfo = (NAB_CreatePersonalAddressBookType *) ipcInfo->lpsmem; +#endif + + if (!createInfo) + { + return(NAB_FAILURE); + } + + createInfo->ipcWorked = 1; // Set the worked flag + return(ProcessNAB_CreatePersonalAddressBook(createInfo)); + break; + } + + ////////////////////////////////////////////////////////////////////// + // NAB_SaveAddressBookToHTML + ////////////////////////////////////////////////////////////////////// + case NSCP_NAB_SaveAddressBookToHTML: + { + NAB_SaveAddressBookToHTMLType *saveInfo; +#ifdef WIN32 + saveInfo = (NAB_SaveAddressBookToHTMLType *) &(sMem->m_buf[0]); +#else + saveInfo = (NAB_SaveAddressBookToHTMLType *) ipcInfo->lpsmem; +#endif + + if (!saveInfo) + { + return(NAB_FAILURE); + } + + saveInfo->ipcWorked = 1; // Set the worked flag + return(ProcessNAB_SaveAddressBookToHTML(saveInfo)); + break; + } + + ////////////////////////////////////////////////////////////////////// + // NAB_ImportLDIFFile + ////////////////////////////////////////////////////////////////////// + case NSCP_NAB_ImportLDIFFile: + { + NAB_ImportLDIFFileType *importInfo; +#ifdef WIN32 + importInfo = (NAB_ImportLDIFFileType *) &(sMem->m_buf[0]); +#else + importInfo = (NAB_ImportLDIFFileType *) ipcInfo->lpsmem; +#endif + + if (!importInfo) + { + return(NAB_FAILURE); + } + + importInfo->ipcWorked = 1; // Set the worked flag + return(ProcessNAB_ImportLDIFFile(importInfo)); + break; + } + + ////////////////////////////////////////////////////////////////////// + // NAB_ExportLDIFFile + ////////////////////////////////////////////////////////////////////// + case NSCP_NAB_ExportLDIFFile: + { + NAB_ExportLDIFFileType *exportInfo; +#ifdef WIN32 + exportInfo = (NAB_ExportLDIFFileType *) &(sMem->m_buf[0]); +#else + exportInfo = (NAB_ExportLDIFFileType *) ipcInfo->lpsmem; +#endif + + if (!exportInfo) + { + return(NAB_FAILURE); + } + + exportInfo->ipcWorked = 1; // Set the worked flag + return(ProcessNAB_ExportLDIFFile(exportInfo)); + break; + } + + ////////////////////////////////////////////////////////////////////// + // NAB_GetFirstAddressBookEntry + ////////////////////////////////////////////////////////////////////// + case NSCP_NAB_GetFirstAddressBookEntry: + { + NAB_GetFirstAddressBookEntryType *getInfo; +#ifdef WIN32 + getInfo = (NAB_GetFirstAddressBookEntryType *) &(sMem->m_buf[0]); +#else + getInfo = (NAB_GetFirstAddressBookEntryType *) ipcInfo->lpsmem; +#endif + + if (!getInfo) + { + return(NAB_FAILURE); + } + + getInfo->ipcWorked = 1; // Set the worked flag + return(ProcessNAB_GetFirstAddressBookEntry(getInfo)); + break; + } + + ////////////////////////////////////////////////////////////////////// + // NAB_GetNextAddressBookEntry + ////////////////////////////////////////////////////////////////////// + case NSCP_NAB_GetNextAddressBookEntry: + { + NAB_GetNextAddressBookEntryType *getInfo; +#ifdef WIN32 + getInfo = (NAB_GetNextAddressBookEntryType *) &(sMem->m_buf[0]); +#else + getInfo = (NAB_GetNextAddressBookEntryType *) ipcInfo->lpsmem; +#endif + + if (!getInfo) + { + return(NAB_FAILURE); + } + + getInfo->ipcWorked = 1; // Set the worked flag + return(ProcessNAB_GetNextAddressBookEntry(getInfo)); + break; + } + + ////////////////////////////////////////////////////////////////////// + // NAB_FindAddressBookEntry + ////////////////////////////////////////////////////////////////////// + case NSCP_NAB_FindAddressBookEntry: + { + NAB_FindAddressBookEntryType *findInfo; +#ifdef WIN32 + findInfo = (NAB_FindAddressBookEntryType *) &(sMem->m_buf[0]); +#else + findInfo = (NAB_FindAddressBookEntryType *) ipcInfo->lpsmem; +#endif + + if (!findInfo) + { + return(NAB_FAILURE); + } + + findInfo->ipcWorked = 1; // Set the worked flag + return(ProcessNAB_FindAddressBookEntry(findInfo)); + break; + } + + ////////////////////////////////////////////////////////////////////// + // NAB_InsertAddressBookEntry + ////////////////////////////////////////////////////////////////////// + case NSCP_NAB_InsertAddressBookEntry: + { + NAB_InsertAddressBookEntryType *insertInfo; +#ifdef WIN32 + insertInfo = (NAB_InsertAddressBookEntryType *) &(sMem->m_buf[0]); +#else + insertInfo = (NAB_InsertAddressBookEntryType *) ipcInfo->lpsmem; +#endif + + if (!insertInfo) + { + return(NAB_FAILURE); + } + + insertInfo->ipcWorked = 1; // Set the worked flag + return(ProcessNAB_InsertAddressBookEntry(insertInfo)); + break; + } + + ////////////////////////////////////////////////////////////////////// + // NAB_UpdateAddressBookEntry + ////////////////////////////////////////////////////////////////////// + case NSCP_NAB_UpdateAddressBookEntry: + { + NAB_UpdateAddressBookEntryType *updateInfo; +#ifdef WIN32 + updateInfo = (NAB_UpdateAddressBookEntryType *) &(sMem->m_buf[0]); +#else + updateInfo = (NAB_UpdateAddressBookEntryType *) ipcInfo->lpsmem; +#endif + + if (!updateInfo) + { + return(NAB_FAILURE); + } + + updateInfo->ipcWorked = 1; // Set the worked flag + return(ProcessNAB_UpdateAddressBookEntry(updateInfo)); + break; + } + + ////////////////////////////////////////////////////////////////////// + // NAB_DeleteAddressBookEntry + ////////////////////////////////////////////////////////////////////// + case NSCP_NAB_DeleteAddressBookEntry: + { + NAB_DeleteAddressBookEntryType *deleteInfo; +#ifdef WIN32 + deleteInfo = (NAB_DeleteAddressBookEntryType *) &(sMem->m_buf[0]); +#else + deleteInfo = (NAB_DeleteAddressBookEntryType *) ipcInfo->lpsmem; +#endif + + if (!deleteInfo) + { + return(NAB_FAILURE); + } + + deleteInfo->ipcWorked = 1; // Set the worked flag + return(ProcessNAB_DeleteAddressBookEntry(deleteInfo)); + break; + } + + case NSCP_NAB_FreeMemory: // This should never hit Communicator, but if it does + return(NAB_SUCCESS); // Just return + + default: + return(NAB_FAILURE); // Should never hit here! + break; + } + + return(NAB_SUCCESS); +} + +// +// CNABConnections object...should be in their own file... +// +CNABConnection::CNABConnection ( LONG id ) +{ +int result; + + m_nameIndex = 0; + m_nameTotalCount = 0; + m_sessionCount = 1; + m_defaultConnection = FALSE; + m_ID = (id + 1); // since zero is invalid, but have to make sure we + // decrement by one when it is passed in again. + m_nameIndex = -1; // For tracing our way through the FindNext operation + + m_ContainerPane = NULL; // Container Pane... + m_ContainerArray = NULL; // The array of container info pointers... + m_AddressBookPane = NULL; + m_ContainerArrayCount = 0; + + result = AB_CreateContainerPane(&m_ContainerPane, + theApp.m_pAddressContext, + WFE_MSGGetMaster()) ; + if (result) + return; + + result = AB_InitializeContainerPane(m_ContainerPane); + if (result) + { + AB_ClosePane (m_ContainerPane); + m_ContainerPane = NULL; + return; + } + + RefreshABContainerList(); +} + +CNABConnection::~CNABConnection ( ) +{ + AB_ClosePane( m_ContainerPane ); + m_ContainerPane = NULL; + + if (m_ContainerArray != NULL) + { + free(m_ContainerArray); + m_ContainerArray = NULL; + } + + m_ContainerArrayCount = 0; + + if (m_AddressBookPane != NULL) + { + AB_ClosePane(m_AddressBookPane); + m_AddressBookPane = NULL; + } +} + +BOOL +CNABConnection::RefreshABContainerList ( void ) +{ +int result; + + if (m_ContainerArray != NULL) + { + free(m_ContainerArray); + m_ContainerArray = NULL; + m_ContainerArrayCount = 0; + } + + // Do the querying stuff for AB API! + result = AB_GetNumRootContainers(m_ContainerPane, &m_ContainerArrayCount); // FE allocated. BE fills with the number of root containers + if (result) + { + AB_ClosePane (m_ContainerPane); + m_ContainerPane = NULL; + return FALSE; + } + + if (m_ContainerArrayCount <= 0) + { + m_ContainerArrayCount = 0; + if (m_ContainerArray != NULL) + { + free(m_ContainerArray); + m_ContainerArray = NULL; + } + return TRUE; + } + + m_ContainerArray = (AB_ContainerInfo **) + malloc(sizeof(AB_ContainerInfo *) * m_ContainerArrayCount); + if (!m_ContainerArray) + { + m_ContainerArrayCount = 0; + return FALSE; + } + + result = AB_GetOrderedRootContainers(m_ContainerPane, m_ContainerArray, + &m_ContainerArrayCount); + if (result) + { + AB_ClosePane (m_ContainerPane); + m_ContainerPane = NULL; + free(m_ContainerArray); + return FALSE; + } + + return TRUE; +} + +AB_ContainerInfo * +CNABConnection::FindContainerInfo( LPSTR containerName, LPSTR containerFileName) +{ + int result; + AB_ContainerAttribValue *value; + + // Check the input name... + if ((!containerName) || (*containerName == '\0')) + return(NULL); + + if ((!containerFileName) || (*containerFileName == '\0')) + return(NULL); + + // Check object state for AB... + if ( (!m_ContainerPane) || (m_ContainerArrayCount <= 0) || (!m_ContainerArray) ) + return(NULL); + + for (int j=0; ju.containerType != AB_PABContainer) // AB_LDAPContainer, AB_MListContainer, a mailing list, AB_PABContainer + { + AB_FreeContainerAttribValue(value); + continue; + } + + AB_FreeContainerAttribValue(value); + + // This is valid! Now get the info! + result = AB_GetContainerAttribute(m_ContainerArray[j], attribName, &value); + if (result) + continue; + + if ( (!value->u.string) || (!*(value->u.string)) ) + { + AB_FreeContainerAttribValue(value); + continue; + } + + TRACE("CURRENT NAME IS = %s\n", (char *)value->u.string); + if (strcmp(containerName, (char *)value->u.string) == 0) + { + DIR_Server *dir = AB_GetDirServerForContainer(m_ContainerArray[j]); + if ( (dir) && (dir->fileName != NULL) && (dir->fileName[0] != '\0') ) + { + char *fullName; + + DIR_GetServerFileName(&fullName, dir->fileName); + if ( (fullName != NULL) && (fullName[0] != '\0') ) + { + if ( strcmp(fullName, containerFileName) == 0) + { + XP_FREE(fullName); + AB_FreeContainerAttribValue(value); + return( m_ContainerArray[j] ); + } + else + { + XP_FREE(fullName); + } + } + } + } + + AB_FreeContainerAttribValue(value); + } + + return(NULL); +} + +// +// Various utility functions... +// +static LPSTR +CheckNull(LPSTR inStr) +{ + static UCHAR str[1]; + + str[0] = '\0'; + if (inStr == NULL) + return((LPSTR)str); + else + return(inStr); +} + +static LONG +WriteTheMemoryBufferToDisk(LPSTR fName, UINT bufSize, LPSTR buf) +{ + if (!buf) + { + return(-1); + } + + HFILE hFile = _lcreat(fName, 0); + if (hFile == HFILE_ERROR) + { + return(-1); + } + + UINT writeCount = _lwrite(hFile, buf, bufSize); + _lclose(hFile); + + if (writeCount != bufSize) + { + _unlink(fName); + return(-1); + } + + return(0); +} + +static LPSTR +GetTheTempDirectoryOnTheSystem(void) +{ + static UCHAR retPath[_MAX_PATH]; + + if (getenv("TEMP")) + { + lstrcpy((LPSTR) retPath, getenv("TEMP")); // environmental variable + } + else if (getenv("TMP")) + { + lstrcpy((LPSTR) retPath, getenv("TMP")); // How about this environmental variable? + } + else + { + GetWindowsDirectory((LPSTR) retPath, sizeof(retPath)); + } + + return((LPSTR) &(retPath[0])); +} + +#ifdef WIN16 +static int ISGetTempFileName(LPCSTR a_pDummyPath, LPCSTR a_pPrefix, UINT a_uUnique, LPSTR a_pResultName) +{ +#ifdef GetTempFileName // we need the real thing comming up next... +#undef GetTempFileName +#endif + return GetTempFileName(0, a_pPrefix, a_uUnique, a_pResultName); +} +#endif + +static DWORD +ValidateFile(LPCSTR szFile) +{ + struct _stat buf; + int result; + + result = _stat( szFile, &buf ); + if (result != 0) + return(1); + + if (!(buf.st_mode & S_IREAD)) + return(2); + + return(0); +} + +static LONG +GetTempAttachmentName(LPSTR fName) +{ + UINT res; + static UINT uUnique = 1; + + if (!fName) + return(-1); + + LPSTR szTempPath = GetTheTempDirectoryOnTheSystem(); + +TRYAGAIN: +#ifdef WIN32 + res = GetTempFileName(szTempPath, "MAPI", uUnique++, fName); +#else + res = ISGetTempFileName(szTempPath, "MAPI", uUnique++, fName); +#endif + + if (ValidateFile(fName) != 1) + { + if (uUnique < 32000) + { + goto TRYAGAIN; + } + else + { + return(-1); + } + } + + return 0; +} + +// +// Find the default session for Communicator... +// +static CNABConnection +*GetDefaultSession(void) +{ + int i; + + for (i=0; i < MAX_CON; i++) + { + if (nabConnections[i] != NULL) + { + if (nabConnections[i]->IsDefault()) + { + return nabConnections[i]; + } + } + } + + return(NULL); +} + +// +// Find an open session slot... +// +static LONG +GetOpenSessionSlot( void ) +{ + int i; + + for (i=0; i < MAX_CON; i++) + { + if (nabConnections[i] == NULL) + { + return(i); + } + } + + return(-1); +} + +// +// Returns TRUE if it was reassigned and FALSE if not. +// +static BOOL +TryDefaultReassignment(void) +{ + int i; + int loc = -1; + + // Find any sessions left? + for (i=0; i < MAX_CON; i++) + { + if (nabConnections[i] != NULL) + { + loc = i; + break; + } + } + + // Set default state on the object to true + if (loc >= 0) + { + nabConnections[loc]->SetDefault(TRUE); + return(TRUE); + } + else + { + return(FALSE); + } +} + +// +// APISTART - Where it all begins... +// +LONG +ProcessNAB_Open(NAB_OpenType *openPtr) +{ + CNABConnection *defSession = GetDefaultSession(); + TRACE("MAPI: ProcessNAB_Open()\n"); + + // Reset values... + openPtr->abConnection = 0; + openPtr->majorVerNumber = 0; + openPtr->minorVerNumber = 0; + + // + // Make sure only MAX_CONN are allowed... + // + LONG slot; + if ((slot = GetOpenSessionSlot()) == -1) + { + return(NAB_MAXCON_EXCEEDED); + } + + // + // Create a new session! + // + nabConnections[slot] = new CNABConnection(slot); + if (!nabConnections[slot]) + { + return(NAB_MEMORY_FAILURE); + } + + // Assign the default session... + if (defSession == NULL) + { + nabConnections[slot]->SetDefault(TRUE); + } + + // Set the values the API needs to return... + openPtr->abConnection = nabConnections[slot]->GetID(); + openPtr->majorVerNumber = NAB_VERSION_MAJOR; + openPtr->minorVerNumber = NAB_VERSION_MINOR; + + return(NAB_SUCCESS); +} + +LONG +ProcessNAB_Close(NAB_CloseType *closePtr) +{ + TRACE("MAPI: ProcessNAB_Close() Connection ID = [%d]\n", closePtr->abConnection); + // + // Verify the session handle... + // + if (( (closePtr->abConnection-1) >= MAX_CON) || ( (closePtr->abConnection-1) < 0)) + { + return(NAB_INVALID_CONNID); + } + + CNABConnection *closeSession = nabConnections[(closePtr->abConnection - 1)]; + if (closeSession == NULL) + { + return(NAB_INVALID_CONNID); + } + + // + // Decrement the session count, then if this is the last one + // connected to this session, then kill it... + // + closeSession->DecrementSessionCount(); + if (closeSession->GetSessionCount() <= 0) + { + // + // If this was the default session "holder", then we need to + // assign that task to a new one if it exists, if not, then + // it is all back to null. + // + BOOL needToReassign = closeSession->IsDefault(); + + delete closeSession; + nabConnections[closePtr->abConnection - 1] = NULL; + + if (needToReassign) + { + TRACE("NAB: ProcessNAB_Close() Need to reassign default\n"); + if (!TryDefaultReassignment()) + { + if (pNABFrameWnd != NULL) + { + pNABFrameWnd->PostMessage(WM_CLOSE); + pNABFrameWnd = NULL; + } + } + } + } + + return(NAB_SUCCESS); +} + +// +// Actually process the get address book list request... +// +LONG +ProcessNAB_GetAddressBookList(NAB_GetAddressBookListType *getPtr) +{ + CNABConnection *mySession; + + TRACE("NAB: ProcessNAB_GetAddressBookList() Connection ID = [%d]\n", getPtr->abConnection); + + // Reset some values... + getPtr->abookCount = 0; + getPtr->blobFileName[0] = '\0'; + + // + // Verify the session handle... + // + if (((getPtr->abConnection-1) >= MAX_CON) || ((getPtr->abConnection-1) < 0)) + return(NAB_INVALID_CONNID); + + mySession = nabConnections[(getPtr->abConnection - 1)]; + if (mySession == NULL) + return(NAB_FAILURE); + + TRACE("NAB: ProcessNAB_GetAddressBookList() Temp File = [%s]\n", getPtr->blobFileName); + LPSTR *abookArray; + LONG realAbookCount = 0; + int result; + + if (mySession->RefreshABContainerList ( ) == FALSE) + return(NAB_FAILURE); + + if (mySession->m_ContainerArrayCount <= 0) + return(NAB_SUCCESS); + + if (!(mySession->m_ContainerArray)) + return(NAB_MEMORY_FAILURE); + + if (GetTempAttachmentName((LPSTR) getPtr->blobFileName) < 0) + return(NAB_FILE_ERROR); + + int j; + AB_ContainerAttribValue *value; + LONG currentLocation = 0; + + abookArray = (LPSTR *) malloc( (size_t) sizeof(LPSTR) * ((mySession->m_ContainerArrayCount * 2) + 1) ); + if (!abookArray) + return(NAB_MEMORY_FAILURE); + + for (j=0; j < mySession->m_ContainerArrayCount; j++) + { + result = AB_GetContainerAttribute(mySession->m_ContainerArray[j], attribContainerType, &value); + if (result) + continue; + + if (value->u.containerType != AB_PABContainer) // AB_LDAPContainer, AB_MListContainer, a mailing list, AB_PABContainer + { + AB_FreeContainerAttribValue(value); + continue; + } + + AB_FreeContainerAttribValue(value); + + TRACE("THIS IS A PERSONAL ADDRESS BOOK\n"); + result = AB_GetContainerAttribute(mySession->m_ContainerArray[j], attribName, &value); + if (result) + continue; + + TRACE("NAME OF THE VALUE IS = %s\n", (char *)value->u.string); + abookArray[currentLocation++] = strdup((char *)value->u.string); + AB_FreeContainerAttribValue(value); + + // Now get the file name... + DIR_Server *dir = AB_GetDirServerForContainer(mySession->m_ContainerArray[j]); + if ( (dir) && (dir->fileName != NULL) && (dir->fileName[0] != '\0') ) + { + char *fullName; + DIR_GetServerFileName(&fullName, dir->fileName); + if ( (fullName != NULL) && (fullName[0] != '\0') ) + { + abookArray[currentLocation++] = fullName; + } + else + { + abookArray[currentLocation++] = ""; + } + } + else + abookArray[currentLocation++] = ""; + + ++realAbookCount; + } + + abookArray[currentLocation] = NULL; + + // Create the string sequence + NSstringSeq strSeq = NSStrSeqNew(abookArray); + + // Now just cleanup the array.... + for (int i=0; iblobFileName, + (UINT) NSStrSeqSize(strSeq), strSeq) != 0) + { + NSStrSeqDelete(strSeq); + return(NAB_MEMORY_FAILURE); + } + + NSStrSeqDelete(strSeq); + getPtr->abookCount = realAbookCount; + return(NAB_SUCCESS); +} + +// +// Get the default address book type... +// +LONG +ProcessNAB_GetDefaultAddressBook(NAB_GetDefaultAddressBookType *getPtr) +{ + CNABConnection *mySession; + TRACE("NAB: ProcessNAB_GetDefaultAddressBook() Connection ID = [%d]\n", getPtr->abConnection); + + // Reset some values... + getPtr->abookDesc[0] = '\0'; + getPtr->abookFileName[0] = '\0'; + + // + // Verify the session handle... + // + if (((getPtr->abConnection-1) >= MAX_CON) || ((getPtr->abConnection-1) < 0)) + { + return(NAB_INVALID_CONNID); + } + + mySession = nabConnections[(getPtr->abConnection - 1)]; + if (mySession == NULL) + return(NAB_FAILURE); + + int j; + int result; + AB_ContainerAttribValue *value; + + if (mySession->RefreshABContainerList ( ) == FALSE) + return(NAB_FAILURE); + + if (mySession->m_ContainerArrayCount <= 0) + return(NAB_SUCCESS); + + if (!(mySession->m_ContainerArray)) + return(NAB_MEMORY_FAILURE); + + for (j=0; j < mySession->m_ContainerArrayCount; j++) + { + result = AB_GetContainerAttribute(mySession->m_ContainerArray[j], attribContainerType, &value); + if (result) + continue; + + if (value->u.containerType != AB_PABContainer) // AB_LDAPContainer, AB_MListContainer, a mailing list, AB_PABContainer + { + AB_FreeContainerAttribValue(value); + continue; + } + + AB_FreeContainerAttribValue(value); + + TRACE("THIS IS A PERSONAL ADDRESS BOOK\n"); + result = AB_GetContainerAttribute(mySession->m_ContainerArray[j], attribName, &value); + if (result) + continue; + + getPtr->defaultFound = TRUE; + TRACE("NAME OF THE DEFAULT IS = %s\n", (char *)value->u.string); + lstrcpy((LPSTR) getPtr->abookDesc, (char *)value->u.string); + + // Now get the file name... + DIR_Server *dir = AB_GetDirServerForContainer(mySession->m_ContainerArray[j]); + if (dir) + { + char *fullName; + DIR_GetServerFileName(&fullName, dir->fileName); + if ( (fullName != NULL) && (fullName[0] != '\0') ) + { + lstrcpy((LPSTR) getPtr->abookFileName, fullName); + XP_FREE(fullName); + } + } + + AB_FreeContainerAttribValue(value); + break; + } + + if (getPtr->defaultFound) + return(NAB_SUCCESS); + else + return(NAB_NOT_FOUND); +} + +NAB_CreatePersonalAddressBookType *createPtr = NULL; + +int +MyCallbackForCreateAddressBook( + DIR_Server *server, /* BE will create a DIR_Server and pass it here or pass in an existing DIR_Server if AB_PropertiesCmd */ + MWContext *context, + MSG_Pane *srcPane, /* the pane that caused this call back */ + XP_Bool newDirectory) /* BE will set this argument to TRUE if the FE is showing a new DIR_Server and FALSE otherwise */ +{ + int result; + if (!createPtr) + return(1); + + // Set the description in the DIR_Server structure... + server->description = strdup(createPtr->abookName); + + result = AB_UpdateDIRServerForContainerPane(srcPane, server); + + if ( (server->fileName != NULL) && (server->fileName[0] != '\0') ) + { + char *fullName; + DIR_GetServerFileName(&fullName, server->fileName); + + if ( (fullName != NULL) && (fullName[0] != '\0') ) + { + lstrcpy((LPSTR) createPtr->abookFileName, fullName); + } + else + { + lstrcpy((LPSTR) createPtr->abookFileName, (LPSTR)server->fileName); + } + } + + return(0); +} + +// +// Create the address book... +// +LONG +ProcessNAB_CreatePersonalAddressBook(NAB_CreatePersonalAddressBookType *shmPtr) +{ + CNABConnection *mySession; + TRACE("NAB: ProcessNAB_CreatePersonalAddressBook() Connection ID = [%d]\n", shmPtr->abConnection); + TRACE("NAB: Address Book Name [%s]\n", shmPtr->abookName); + + // Reset some values... + shmPtr->abookFileName[0] = '\0'; + + // + // Verify the session handle... + // + if (((shmPtr->abConnection-1) >= MAX_CON) || ((shmPtr->abConnection-1) < 0)) + { + return(NAB_INVALID_CONNID); + } + + mySession = nabConnections[(shmPtr->abConnection - 1)]; + if (mySession == NULL) + { + return(NAB_FAILURE); + } + + int result; + createPtr = shmPtr; + result = AB_SetShowPropertySheetForDirFunc(mySession->m_ContainerPane, + MyCallbackForCreateAddressBook); + if (result) + { + return(NAB_FAILURE); + } + + // Create the address book! + result = AB_CommandAB2(mySession->m_ContainerPane, AB_NewAddressBook, 0, 0); + if (result) + { + return(NAB_FAILURE); + } + else + { + AB_InitializeContainerPane(mySession->m_ContainerPane); + return(NAB_SUCCESS); + } +} + +// +// Save the address book to HTML... +// +LONG +ProcessNAB_SaveAddressBookToHTML(NAB_SaveAddressBookToHTMLType *shmPtr) +{ + CNABConnection *mySession; + TRACE("NAB: ProcessNAB_SaveAddressBookToHTML() Connection ID = [%d]\n", shmPtr->abConnection); + TRACE("NAB: HTML File Name [%s]\n", shmPtr->HTMLFileName); + // + // Verify the session handle... + // + if (((shmPtr->abConnection-1) >= MAX_CON) || ((shmPtr->abConnection-1) < 0)) + { + return(NAB_INVALID_CONNID); + } + + mySession = nabConnections[(shmPtr->abConnection - 1)]; + if (mySession == NULL) + { + return(NAB_FAILURE); + } + + TRACE("ADDRESS BOOK NAME : [%s]\n", shmPtr->abookName); + TRACE("HTML FILE NAME : [%s]\n", shmPtr->abookFileName); + LONG result; + MSG_Pane *addressBookPane; // Container info for a particular addr book + LONG currentLocation; + + TRACE("HTML FILENAME: [%s]\n", shmPtr->HTMLFileName); + if ( ValidateFile(shmPtr->HTMLFileName) != 1) + return(NAB_FILE_ERROR); + + if (! OpenNABAPIHTMLFile(shmPtr->HTMLFileName, shmPtr->abookName) ) + return(NAB_FILE_ERROR); + + AB_ContainerInfo *conInfo = mySession->FindContainerInfo(shmPtr->abookName, + shmPtr->abookFileName); + if (!conInfo) + { + (void) CloseNABAPIHTMLFile(); + unlink(shmPtr->abookFileName); + return(NAB_INVALID_ABOOK); + } + + result = AB_CreateABPane(&addressBookPane, theApp.m_pAddressContext, WFE_MSGGetMaster()); + if (result) + { + (void) CloseNABAPIHTMLFile(); + unlink(shmPtr->abookFileName); + return(NAB_FAILURE); + } + + result = AB_InitializeABPane(addressBookPane, conInfo); + if (result) + { + (void) CloseNABAPIHTMLFile(); + AB_ClosePane(addressBookPane); + unlink(shmPtr->abookFileName); + return(NAB_INVALID_ABOOK); + } + + LONG lineCount = MSG_GetNumLines(addressBookPane); + currentLocation = 0; + + while ( currentLocation < lineCount ) + { + (void) DumpHTMLTableLineForUser(addressBookPane, currentLocation); + // Increment for next call... + currentLocation++; + } + + AB_ClosePane(addressBookPane); + if (CloseNABAPIHTMLFile()) + { + return(NAB_SUCCESS); + } + else + { + unlink(shmPtr->abookFileName); + return(NAB_FAILURE); + } +} + +// +// Import LDIF file... +// +LONG +ProcessNAB_ImportLDIFFile(NAB_ImportLDIFFileType *shmPtr) +{ + CNABConnection *mySession; + TRACE("NAB: ProcessNAB_ImportLDIFFile() Connection ID = [%d]\n", shmPtr->abConnection); + TRACE("NAB: LDIF File to Import [%s]\n", shmPtr->LDIFFileName); + // + // Verify the session handle... + // + if (((shmPtr->abConnection-1) >= MAX_CON) || ((shmPtr->abConnection-1) < 0)) + { + return(NAB_INVALID_CONNID); + } + + mySession = nabConnections[(shmPtr->abConnection - 1)]; + if (mySession == NULL) + { + return(NAB_FAILURE); + } + + TRACE("LDIF Import File: [%s]\n", shmPtr->LDIFFileName); + if ( ValidateFile(shmPtr->LDIFFileName) != 0) + { + return(NAB_FILE_NOT_FOUND); + } + + TRACE("SPECIFY ADDRESS BOOK NAME: [%s]\n", shmPtr->abookName); + AB_ContainerInfo *conInfo = mySession->FindContainerInfo(shmPtr->abookName, + shmPtr->abookFileName); + if (!conInfo) + return(NAB_INVALID_ABOOK); + + char *tPtr = shmPtr->LDIFFileName; + int failed = AB_ImportData(conInfo, tPtr, 0, AB_Filename); + if (!failed) + { + if (shmPtr->deleteImportFile) + { + unlink(shmPtr->abookFileName); + } + return(NAB_SUCCESS); + } + else + { + return(NAB_FAILURE); + } +} + +// +// Export LDIF file... +// +LONG +ProcessNAB_ExportLDIFFile(NAB_ExportLDIFFileType *shmPtr) +{ + CNABConnection *mySession; + TRACE("NAB: ProcessNAB_ExportLDIFFile() Connection ID = [%d]\n", shmPtr->abConnection); + TRACE("NAB: LDIF File Name [%s]\n", shmPtr->LDIFFileName); + // + // Verify the session handle... + // + if (((shmPtr->abConnection-1) >= MAX_CON) || ((shmPtr->abConnection-1) < 0)) + { + return(NAB_INVALID_CONNID); + } + + mySession = nabConnections[(shmPtr->abConnection - 1)]; + if (mySession == NULL) + { + return(NAB_FAILURE); + } + + TRACE("ADDRESS BOOK FILENAME: [%s]\n", shmPtr->abookFileName); + if ( ValidateFile(shmPtr->abookFileName) != 1) + { + return(NAB_FILE_ERROR); + } + + TRACE("SPECIFY ADDRESS BOOK NAME: [%s]\n", shmPtr->abookName); + AB_ContainerInfo *conInfo = mySession->FindContainerInfo(shmPtr->abookName, + shmPtr->abookFileName); + if (!conInfo) + return(NAB_INVALID_ABOOK); + + char *tPtr = shmPtr->LDIFFileName; + int failed = AB_ExportData(conInfo, &(tPtr), 0, AB_Filename); + if (!failed) + { + return(NAB_SUCCESS); + } + else + { + return(NAB_FILE_ERROR); + } +} + +// +// Get first address book entry... +// +LONG +ProcessNAB_GetFirstAddressBookEntry(NAB_GetFirstAddressBookEntryType *shmPtr) +{ + CNABConnection *mySession; + TRACE("NAB: ProcessNAB_GetFirstAddressBookEntry() Connection ID = [%d]\n", shmPtr->abConnection); + + // Reset some values... + shmPtr->userID = 0; + shmPtr->updateTime = 0; + shmPtr->userInfo[0] = '\0'; + + // + // Verify the session handle... + // + if (((shmPtr->abConnection-1) >= MAX_CON) || ((shmPtr->abConnection-1) < 0)) + { + return(NAB_INVALID_CONNID); + } + + mySession = nabConnections[(shmPtr->abConnection - 1)]; + if (mySession == NULL) + { + return(NAB_FAILURE); + } + + TRACE("ADDRESS BOOK NAME : [%s]\n", shmPtr->abookName); + TRACE("ADDRESS BOOK FILENAME: [%s]\n", shmPtr->abookFileName); + NABUserID userID; + NABUpdateTime updtTime; + LONG result; + + if (mySession->m_AddressBookPane != NULL) + { + AB_ClosePane(mySession->m_AddressBookPane); + mySession->m_AddressBookPane = NULL; + } + + TRACE("RICHIE-TODO: GOTTA GET UPDATE TIME\n"); + AB_ContainerInfo *conInfo = mySession->FindContainerInfo(shmPtr->abookName, + shmPtr->abookFileName); + if (!conInfo) + return(NAB_INVALID_ABOOK); + + result = AB_CreateABPane(&(mySession->m_AddressBookPane), theApp.m_pAddressContext, WFE_MSGGetMaster()); + if (result) + return(NAB_FAILURE); + + result = AB_InitializeABPane(mySession->m_AddressBookPane, conInfo); + if (result) + return(NAB_FAILURE); + + LONG lineCount = MSG_GetNumLines(mySession->m_AddressBookPane); + mySession->SetNameIndex( 0 ); + mySession->SetNameTotalCount( lineCount ); + + BOOL found = FALSE; + + while ( mySession->GetNameIndex() < mySession->GetNameTotalCount() ) + { + found = GetLDIFLineForUser(mySession->m_AddressBookPane, + mySession->GetNameIndex(), + shmPtr->userInfo, &userID, + &updtTime); + // Increment for next call... + mySession->IncrementNameIndex(); + + if (found) + break; + } + + if (found) + { + shmPtr->userID = userID; + shmPtr->updateTime = updtTime; + return(NAB_SUCCESS); + } + else + return(NAB_NOT_FOUND); +} + +// +// Get next address book entry... +// +LONG +ProcessNAB_GetNextAddressBookEntry(NAB_GetNextAddressBookEntryType *shmPtr) +{ + CNABConnection *mySession; + TRACE("NAB: ProcessNAB_GetNextAddressBookEntry() Connection ID = [%d]\n", shmPtr->abConnection); + + // Reset some values... + shmPtr->userID = 0; + shmPtr->updateTime = 0; + shmPtr->userInfo[0] = '\0'; + + // + // Verify the session handle... + // + if (((shmPtr->abConnection-1) >= MAX_CON) || ((shmPtr->abConnection-1) < 0)) + { + return(NAB_INVALID_CONNID); + } + + mySession = nabConnections[(shmPtr->abConnection - 1)]; + if (mySession == NULL) + { + return(NAB_FAILURE); + } + + TRACE("RICHIE-TODO: GOTTA GET UPDATE TIME\n"); + NABUserID userID; + NABUpdateTime updtTime; + + if (mySession->m_AddressBookPane == NULL) + return(NAB_INVALID_ABOOK); + + LONG myIndex = mySession->GetNameIndex(); + if ( myIndex >= mySession->GetNameTotalCount() ) + { + if (mySession->m_AddressBookPane != NULL) + { + AB_ClosePane(mySession->m_AddressBookPane); + mySession->m_AddressBookPane = NULL; + } + + return(NAB_NOT_FOUND); + } + + BOOL found = FALSE; + while ( mySession->GetNameIndex() < mySession->GetNameTotalCount() ) + { + found = GetLDIFLineForUser(mySession->m_AddressBookPane, + mySession->GetNameIndex(), + shmPtr->userInfo, &userID, + &updtTime); + // Increment for next call... + mySession->IncrementNameIndex(); + + if (found) + break; + } + + if (found) + { + shmPtr->userID = userID; + shmPtr->updateTime = updtTime; + return(NAB_SUCCESS); + } + else + return(NAB_NOT_FOUND); +} + +// +// Find a particular address book entry... +// +LONG +ProcessNAB_FindAddressBookEntry(NAB_FindAddressBookEntryType *shmPtr) +{ + CNABConnection *mySession; + TRACE("NAB: ProcessNAB_FindAddressBookEntry() Connection ID = [%d]\n", shmPtr->abConnection); + + // Reset some values... + shmPtr->userInfo[0] = '\0'; + shmPtr->updateTime = 0; + + if (shmPtr->ldifSearchAttribute[0] != '\0') + shmPtr->userID = 0; + + // + // Verify the session handle... + // + if (((shmPtr->abConnection-1) >= MAX_CON) || ((shmPtr->abConnection-1) < 0)) + { + return(NAB_INVALID_CONNID); + } + + mySession = nabConnections[(shmPtr->abConnection - 1)]; + if (mySession == NULL) + { + return(NAB_FAILURE); + } + + int result; + AB_ContainerAttribValue *value; + BOOL found = FALSE; + + // Check object state for AB... + if ( (!(mySession->m_ContainerPane)) || + ((mySession->m_ContainerArrayCount) <= 0) || + (!(mySession->m_ContainerArray)) ) + return(NAB_INVALID_ABOOK); + + // Now either search a specific address book, OR ALL ADDRESS BOOKS! + if (shmPtr->abookName[0] == '\0') + { + for (int j=0; jm_ContainerArrayCount; j++) + { + // First check the type for the container... + result = AB_GetContainerAttribute(mySession->m_ContainerArray[j], attribContainerType, &value); + if (result) + continue; + + if (value->u.containerType != AB_PABContainer) // AB_LDAPContainer, AB_MListContainer, a mailing list, AB_PABContainer + { + AB_FreeContainerAttribValue(value); + continue; + } + AB_FreeContainerAttribValue(value); + + if (SearchABForAttrib( mySession->m_ContainerArray[j], + shmPtr->ldifSearchAttribute, + shmPtr->userInfo, + &(shmPtr->userID), + &(shmPtr->updateTime)) ) + { + found = TRUE; + break; + } + } + } + else + { + TRACE("SEARCHING ADDRESS BOOK NAME : [%s]\n", shmPtr->abookName); + AB_ContainerInfo *conInfo = mySession->FindContainerInfo(shmPtr->abookName, + shmPtr->abookFileName); + if (!conInfo) + return(NAB_INVALID_ABOOK); + + if (SearchABForAttrib( conInfo, shmPtr->ldifSearchAttribute, + shmPtr->userInfo, &(shmPtr->userID), &(shmPtr->updateTime)) ) + { + found = TRUE; + } + } + + if (found) + return(NAB_SUCCESS); + else + return(NAB_NOT_FOUND); +/* + NAB_FAILURE - General failure performing the query + NAB_NOT_FOUND - The requested entry was not found + NAB_NOT_OPEN - This will be returned when a call is made to the API without NAB_Open() being called first. + NAB_INVALID_ABOOK - Address book specified is invalid + NAB_INVALID_CONNID - Invalid connection ID + NAB_MULTIPLE_USERS_FOUND - More than one person found with current search criteria + NAB_INVALID_SEARCH_ATTRIB - Invalid search criteria +*/ +} + +// +// Insert an address book entry... +// +LONG +ProcessNAB_InsertAddressBookEntry(NAB_InsertAddressBookEntryType *shmPtr) +{ + CNABConnection *mySession; + TRACE("NAB: ProcessNAB_InsertAddressBookEntry() Connection ID = [%d]\n", shmPtr->abConnection); + + // Check for SOME input + if (shmPtr->userInfo[0] == '\0') + return NAB_FAILURE; + + // + // Verify the session handle... + // + if (((shmPtr->abConnection-1) >= MAX_CON) || ((shmPtr->abConnection-1) < 0)) + { + return(NAB_INVALID_CONNID); + } + + mySession = nabConnections[(shmPtr->abConnection - 1)]; + if (mySession == NULL) + { + return(NAB_FAILURE); + } + + TRACE("ADDRESS BOOK NAME : [%s]\n", shmPtr->abookName); + AB_ContainerInfo *conInfo = mySession->FindContainerInfo(shmPtr->abookName, + shmPtr->abookFileName); + if (!conInfo) + { + return(NAB_INVALID_ABOOK); + } + + ABID userID; + + TRACE("INSERT THE ADDRESS BOOK ENTRY: [%s]\n", shmPtr->userInfo); + NABError result = InsertEntryToAB(conInfo, shmPtr->userInfo, + FALSE, &userID); + shmPtr->userID = userID; + return(result); +} + +// +// Updating an address book entry... +// +LONG +ProcessNAB_UpdateAddressBookEntry(NAB_UpdateAddressBookEntryType *shmPtr) +{ + CNABConnection *mySession; + TRACE("NAB: ProcessNAB_UpdateAddressBookEntry() Connection ID = [%d]\n", shmPtr->abConnection); + + // + // Verify the session handle... + // + if (((shmPtr->abConnection-1) >= MAX_CON) || ((shmPtr->abConnection-1) < 0)) + { + return(NAB_INVALID_CONNID); + } + + mySession = nabConnections[(shmPtr->abConnection - 1)]; + if (mySession == NULL) + { + return(NAB_FAILURE); + } + + TRACE("ADDRESS BOOK NAME : [%s]\n", shmPtr->abookName); + TRACE("ADDRESS BOOK FILENAME: [%s]\n", shmPtr->abookFileName); + TRACE("UPDATING ADDRESS BOOK ENTRY: [%d]\n", shmPtr->userID); + LONG result; + MSG_Pane *addressBookPane; // Container info for a particular addr book + + AB_ContainerInfo *conInfo = mySession->FindContainerInfo(shmPtr->abookName, + shmPtr->abookFileName); + if (!conInfo) + { + return(NAB_INVALID_ABOOK); + } + + result = AB_CreateABPane(&addressBookPane, theApp.m_pAddressContext, WFE_MSGGetMaster()); + if (result) + { + return(NAB_FAILURE); + } + + result = AB_InitializeABPane(addressBookPane, conInfo); + if (result) + { + AB_ClosePane(addressBookPane); + return(NAB_INVALID_ABOOK); + } + + DWORD lineCount = MSG_GetNumLines(addressBookPane); + MSG_ViewIndex currentLocation = 0; + ABID id; + BOOL found = FALSE; + + while ( currentLocation < lineCount ) + { + result = AB_GetABIDForIndex(addressBookPane, currentLocation, &id); + if (result) + continue; + + if (id == (ABID) shmPtr->userID) + { + found = TRUE; + break; + } + + // Increment for next call... + currentLocation++; + } + + if (!found) + return(NAB_NOT_FOUND); + + result = InsertEntryToAB(conInfo, shmPtr->userInfo, TRUE, &id); + return(result); +} + +// +// Deleting an address book entry... +// +LONG +ProcessNAB_DeleteAddressBookEntry(NAB_DeleteAddressBookEntryType *shmPtr) +{ + CNABConnection *mySession; + TRACE("NAB: ProcessNAB_DeleteAddressBookEntry() Connection ID = [%d]\n", shmPtr->abConnection); + + // + // Verify the session handle... + // + if (((shmPtr->abConnection-1) >= MAX_CON) || ((shmPtr->abConnection-1) < 0)) + { + return(NAB_INVALID_CONNID); + } + + mySession = nabConnections[(shmPtr->abConnection - 1)]; + if (mySession == NULL) + { + return(NAB_FAILURE); + } + + TRACE("ADDRESS BOOK NAME : [%s]\n", shmPtr->abookName); + TRACE("ADDRESS BOOK FILENAME: [%s]\n", shmPtr->abookFileName); + TRACE("DELETING ADDRESS BOOK ENTRY: [%d]\n", shmPtr->userID); + LONG result; + MSG_Pane *addressBookPane; // Container info for a particular addr book + + AB_ContainerInfo *conInfo = mySession->FindContainerInfo(shmPtr->abookName, + shmPtr->abookFileName); + if (!conInfo) + { + return(NAB_INVALID_ABOOK); + } + + result = AB_CreateABPane(&addressBookPane, theApp.m_pAddressContext, WFE_MSGGetMaster()); + if (result) + { + return(NAB_FAILURE); + } + + result = AB_InitializeABPane(addressBookPane, conInfo); + if (result) + { + AB_ClosePane(addressBookPane); + return(NAB_INVALID_ABOOK); + } + + DWORD lineCount = MSG_GetNumLines(addressBookPane); + MSG_ViewIndex currentLocation = 0; + ABID id; + BOOL found = FALSE; + + while ( currentLocation < lineCount ) + { + result = AB_GetABIDForIndex(addressBookPane, currentLocation, &id); + if (result) + continue; + + if (id == (ABID) shmPtr->userID) + { + found = TRUE; + break; + } + + // Increment for next call... + currentLocation++; + } + + if (!found) + return(NAB_NOT_FOUND); + + MSG_ViewIndex indices[1]; + + indices[0] = currentLocation; + if (AB_CommandAB2( addressBookPane, AB_DeleteCmd, indices, 1) ) + { + AB_ClosePane(addressBookPane); + return(NAB_FAILURE); + } + else + { + AB_ClosePane(addressBookPane); + return(NAB_SUCCESS); + } +} diff --git a/mozilla/cmd/winfe/abhook.h b/mozilla/cmd/winfe/abhook.h new file mode 100644 index 00000000000..2a39745233c --- /dev/null +++ b/mozilla/cmd/winfe/abhook.h @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#ifndef _NAB_HOOK_H_ +#define _NAB_HOOK_H_ + +//#include // for MWContext + +// +// This is the entry point to the MAPI session manager that lives +// inside of Communicator. +// +LONG ProcessNetscapeNABHook(WPARAM wParam, LPARAM lParam); + +#endif // _NAB_HOOK_H_ \ No newline at end of file diff --git a/mozilla/cmd/winfe/abutils.cpp b/mozilla/cmd/winfe/abutils.cpp new file mode 100644 index 00000000000..2bdb8cb20f4 --- /dev/null +++ b/mozilla/cmd/winfe/abutils.cpp @@ -0,0 +1,700 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +// +// Address Book API Utility Functions +// Written by: Rich Pizzarro (rhp@netscape.com) +// March 1998 + +#include "stdafx.h" +#include +#include +#include +#include + +#include "wfemsg.h" // for WFE_MSGGetMaster() +#include "nsstrseq.h" +#include "abapi.h" +#include "abhook.h" +#include "nabapi.h" +#include "mapismem.h" +#include "hiddenfr.h" +#include "msgcom.h" +#include "abutils.h" +#include "abcom.h" + +extern CNetscapeApp theApp; + +typedef struct { + BOOL foundInSearch; + int attribType; + AB_AttribID attribID; + char *ldifName; +} attribPairsType; + +// I18N: Please don't touch these strings! +#define ATTRIB_COUNT 19 +attribPairsType attribArray[] = { + // FALSE, dn: cn=first last,mail=email + FALSE, CHAR_VALUE, AB_attribDisplayName, /*AB_attribFullName,*/ "cn: ", + FALSE, CHAR_VALUE, AB_attribFamilyName, "sn: ", + FALSE, CHAR_VALUE, AB_attribGivenName, "givenname: ", + // FALSE, CHAR_VALUE, "top", "objectclass: ", + // FALSE, CHAR_VALUE, "person", "objectclass: ", + FALSE, CHAR_VALUE, AB_attribInfo, "description: ", + FALSE, CHAR_VALUE, AB_attribLocality, "locality: ", + FALSE, CHAR_VALUE, AB_attribRegion, "st: ", + FALSE, CHAR_VALUE, AB_attribEmailAddress, "mail: ", + FALSE, CHAR_VALUE, AB_attribTitle, "title: ", + FALSE, CHAR_VALUE, AB_attribPOAddress, "postOfficeBox: ", + FALSE, CHAR_VALUE, AB_attribStreetAddress, "streetaddress: ", + FALSE, CHAR_VALUE, AB_attribZipCode, "postalcode: ", + FALSE, CHAR_VALUE, AB_attribCountry, "countryname: ", + FALSE, CHAR_VALUE, AB_attribWorkPhone, "telephonenumber: ", + FALSE, CHAR_VALUE, AB_attribFaxPhone, "facsimiletelephonenumber: ", + FALSE, CHAR_VALUE, AB_attribHomePhone, "homephone: ", + FALSE, CHAR_VALUE, AB_attribCompanyName, "o: ", + FALSE, CHAR_VALUE, AB_attribNickName, "xmozillanickname: ", + FALSE, BOOL_VALUE, AB_attribHTMLMail, "xmozillausehtmlmail: ", + FALSE, INT_VALUE, AB_attribUseServer, "xmozillauseconferenceserver: "}; + +BOOL +TackOnAttributeValuePair(MSG_Pane *abPane, LONG userIndex, + LPSTR outString, AB_AttribID attribID, LPSTR stringKey, + DWORD typeOfValue, LPSTR concatString) +{ + int result; + AB_AttributeValue *value; + + if (!outString) + return FALSE; + + result = AB_GetEntryAttributeForPane(abPane, userIndex, attribID, &value); + if (result != 0) + return FALSE; + + + if (typeOfValue == CHAR_VALUE) + { + // Do the key name first... + if ( (value->u.string != NULL) && (value->u.string[0] != '\0') ) + { + lstrcat(outString, stringKey); + lstrcat(outString, (char *)value->u.string); + + if (concatString) + lstrcat(outString, concatString); + } + } + else if (typeOfValue == BOOL_VALUE) + { + // Do the key name first... + lstrcat(outString, stringKey); + + if (value->u.boolValue) + lstrcat(outString, "TRUE"); + else + lstrcat(outString, "FALSE"); + + if (concatString) + lstrcat(outString, concatString); + } + else if (typeOfValue == INT_VALUE) + { + char tval[16]; + + // Do the key name first... + lstrcat(outString, stringKey); + + wsprintf(tval, "%d", value->u.shortValue); + lstrcat(outString, tval); + if (concatString) + lstrcat(outString, concatString); + } + + AB_FreeEntryAttributeValue(value); + return(TRUE); +} + +BOOL +GetLDIFLineForUser(MSG_Pane *abPane, LONG userIndex, LPSTR outString, + NABUserID *userID, NABUpdateTime *updtTime) +{ + ABID id; + int i; + BOOL rc; + + if (!outString) + return FALSE; + + outString[0] = '\0'; + lstrcpy(outString, "dn: "); + + rc = TackOnAttributeValuePair(abPane, userIndex, outString, AB_attribFullName, + "cn: ", CHAR_VALUE, ","); + if (!rc) + return FALSE; + rc = TackOnAttributeValuePair(abPane, userIndex, outString, AB_attribEmailAddress, "mail: ", + CHAR_VALUE, NAB_CRLF); + if (!rc) + return FALSE; + + for (i=0; iu.string != NULL) && (value->u.string[0] != '\0') ) + { + if ( _lwrite(hHTMLFile, value->u.string, lstrlen(value->u.string)) == HFILE_ERROR) + return FALSE; + else + return TRUE; + } + else + return FALSE; +} + +BOOL +DumpHTMLTableLineForUser(MSG_Pane *abPane, LONG userIndex) +{ + DumpHTMLForTable(HTMLNEWROW); + + DumpHTMLForTable(HTMLSTART); + DumpHTMLValue(abPane, userIndex, AB_attribFamilyName); + DumpHTMLForTable(HTMLEND); + + DumpHTMLForTable(HTMLSTART); + DumpHTMLValue(abPane, userIndex, AB_attribGivenName); + DumpHTMLForTable(HTMLEND); + + DumpHTMLForTable(HTMLSTART); + DumpHTMLValue(abPane, userIndex, AB_attribEmailAddress); + DumpHTMLForTable(HTMLEND); + + DumpHTMLForTable(HTMLSTART); + if (DumpHTMLValue(abPane, userIndex, AB_attribCompanyName)) + DumpHTMLForTable(HTMLBREAK); + DumpHTMLValue(abPane, userIndex, AB_attribTitle); + DumpHTMLForTable(HTMLEND); + + DumpHTMLForTable(HTMLSTART); + DumpHTMLValue(abPane, userIndex, AB_attribWorkPhone); + DumpHTMLForTable(HTMLEND); + + DumpHTMLForTable(HTMLSTART); + DumpHTMLValue(abPane, userIndex, AB_attribFaxPhone); + DumpHTMLForTable(HTMLEND); + + DumpHTMLForTable(HTMLSTART); + DumpHTMLValue(abPane, userIndex, AB_attribHomePhone); + DumpHTMLForTable(HTMLEND); + + DumpHTMLForTable(HTMLSTART); + if (DumpHTMLValue(abPane, userIndex, AB_attribPOAddress)) + DumpHTMLForTable(HTMLBREAK); + if (DumpHTMLValue(abPane, userIndex, AB_attribStreetAddress)) + DumpHTMLForTable(HTMLBREAK); + + if (DumpHTMLValue(abPane, userIndex, AB_attribLocality)) + DumpHTMLForTable(HTMLCOMMA); + DumpHTMLValue(abPane, userIndex, AB_attribRegion); + if (DumpHTMLValue(abPane, userIndex, AB_attribZipCode)) + DumpHTMLForTable(HTMLBREAK); + DumpHTMLValue(abPane, userIndex, AB_attribCountry); + DumpHTMLForTable(HTMLEND); + + DumpHTMLForTable(HTMLENDROW); + return TRUE; +} + +BOOL +FindAttributeInLine(LPSTR attribName, LPSTR addLine) +{ + DWORD totlen, complen, i; + + if ( (!attribName) || !(*attribName) ) + return FALSE; + + if ( (!addLine) || !(*addLine) ) + return FALSE; + + i = 0; + totlen = strlen(addLine); + complen = strlen(attribName); + + while ( (i + complen) <= totlen ) + { + if (strncmp( attribName, addLine+i, complen ) == 0) + { + return TRUE; + } + + i++; + } + + return FALSE; +} + +LPSTR +ExtractAttribValue(LPSTR attribName, LPSTR searchLine) +{ + DWORD totlen, complen, i; + + if ( (!attribName) || !(*searchLine) ) + return NULL; + + if ( (!searchLine) || !(*searchLine) ) + return NULL; + + i = 0; + totlen = strlen(searchLine); + complen = strlen(attribName); + + while ( (i + complen) <= totlen ) + { + if (strncmp( attribName, searchLine+i, complen ) == 0) + { + if (i+complen == totlen) // Check if we are at the end... + return NULL; + + LPSTR newPtr; + LPSTR startPtr; + LPSTR endPtr; + DWORD totalSize; + + // Have to add this hack for the fact we have an attribute that + // is a subset of another (i.e. "mail: " and "xmozillausehtmlmail: ") + if ( + (strcmp(attribName, "mail: ") == 0) && (i > 0) && + (*(searchLine+i-1) == 'l' ) + ) + { + i++; + continue; + } + // end of hack + + startPtr = searchLine + (i + complen); + endPtr = startPtr; + + while ( ((*endPtr) != '\0') && ((*endPtr) != '\r') ) + { + endPtr = (endPtr + 1); + } + + totalSize = (endPtr - startPtr) + 1; + newPtr = (LPSTR) malloc(totalSize); + if (!newPtr) + return NULL; + + memset(newPtr, 0, totalSize); + strncpy(newPtr, startPtr, (totalSize - 1)); + return newPtr; + } + + i++; + } + + return NULL; +} + +BOOL +ThisIsAStringAttrib(AB_AttribID attrib) +{ + int i; + + for (i=0; iu.string != NULL) && (value->u.string[0] != '\0') ) + { + if ( strlen(value->u.string) >= strlen(searchValue) ) + { + if (_strnicmp(value->u.string, searchValue, strlen(searchValue)) == 0) + { + found = TRUE; + } + } + } + + AB_FreeEntryAttributeValue(value); + return(found); +} + +BOOL +SearchABForAttrib(AB_ContainerInfo *abContainer, + LPSTR searchAttrib, + LPSTR ldifInfo, + NABUserID *userID, + NABUpdateTime *updtTime) +{ + int result; + MSG_Pane *addressBookPane; // Container info for a particular addr book + + result = AB_CreateABPane(&addressBookPane, theApp.m_pAddressContext, WFE_MSGGetMaster()); + if (result) + { + return FALSE; + } + + result = AB_InitializeABPane(addressBookPane, abContainer); + if (result) + { + AB_ClosePane(addressBookPane); + return(FALSE); + } + + DWORD id; + int currentLocation = 0; + LONG lineCount = MSG_GetNumLines(addressBookPane); + LPSTR searchValue = NULL; + BOOL found = FALSE; + + // + // Support lookups by ABID's... + if ((searchAttrib == NULL) || (searchAttrib[0] == '\0')) + { + while ( currentLocation < lineCount ) + { + ABID id; + + // Get the ABID... + int result = AB_GetABIDForIndex(addressBookPane, currentLocation, &id); + if (result != 0) + { + ++currentLocation; + continue; + } + + if (id == *userID) + { + found = TRUE; + if (!GetLDIFLineForUser(addressBookPane, currentLocation, ldifInfo, userID, updtTime)) + { + found = FALSE; + currentLocation++; + continue; + } + + break; + } + + // Increment for next call... + currentLocation++; + } + + AB_ClosePane(addressBookPane); + return found; + } + // Support lookups by ABID's... + // + + if (!GetIDSearchField(searchAttrib, &id, &searchValue)) + { + AB_ClosePane(addressBookPane); + return(FALSE); + } + + while ( currentLocation < lineCount ) + { + found = FindValueForIDInLine(addressBookPane, currentLocation, + (AB_AttribID)id, searchValue); + if (found) + { + if (!GetLDIFLineForUser(addressBookPane, currentLocation, ldifInfo, userID, updtTime)) + { + found = FALSE; + currentLocation++; + continue; + } + break; + } + + // Increment for next call... + currentLocation++; + } + + if (searchValue != NULL) + free(searchValue); + + AB_ClosePane(addressBookPane); + return found; +} + diff --git a/mozilla/cmd/winfe/abutils.h b/mozilla/cmd/winfe/abutils.h new file mode 100644 index 00000000000..4e28e946df9 --- /dev/null +++ b/mozilla/cmd/winfe/abutils.h @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#ifndef _NAB_UTILS_H_ +#define _NAB_UTILS_H_ + +#define CHAR_VALUE 0 +#define BOOL_VALUE 1 +#define INT_VALUE 2 + +BOOL GetLDIFLineForUser(MSG_Pane *abPane, LONG userIndex, + LPSTR outString, NABUserID *userID, + NABUpdateTime *updtTime); + +// For creating HTML output file... +BOOL OpenNABAPIHTMLFile(LPSTR fName, LPSTR title); +BOOL CloseNABAPIHTMLFile(void); +BOOL DumpHTMLTableLineForUser(MSG_Pane *abPane, LONG userIndex); +BOOL SearchABForAttrib(AB_ContainerInfo *abContainer, + LPSTR searchAttrib, + LPSTR ldifInfo, + NABUserID *userID, + NABUpdateTime *updtTime); + +// For insert entries... +NABError InsertEntryToAB(AB_ContainerInfo *abContainer, LPSTR newLine, + BOOL updateOnly, ABID *updateID); + +#define HTML_HEAD1 \ +"\n\ +\n\ + \n\ + " + +#define HMTL_HEAD2 \ +"\n\ +\n\ + \n\ +\n" + +#define HTML_TAIL "\ +
\n\ + \n\ +\n\ +\n" + +#endif // _NAB_UTILS_H_ + +/* +stuff... +AB_attribFamilyName + +AB_attribGivenName + +AB_attribEmailAddress + +AB_attribCompanyName +
AB_attribTitle + +AB_attribWorkPhone + +AB_attribFaxPhone + +AB_attribHomePhone + +AB_attribPOAddress +
AB_attribStreetAddress +
AB_attribLocality, AB_attribRegion AB_attribZipCode +
AB_attribCountry +...end of stuff.... +*/ \ No newline at end of file diff --git a/mozilla/cmd/winfe/addrprop.cpp b/mozilla/cmd/winfe/addrprop.cpp new file mode 100644 index 00000000000..1330da08ad2 --- /dev/null +++ b/mozilla/cmd/winfe/addrprop.cpp @@ -0,0 +1,2905 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +// addrprop.cpp : implementation file for property dialogs from +// the address book +// +#include "stdafx.h" +#include "rosetta.h" +#include "addrprop.h" +#include "msgnet.h" + + +#ifdef MOZ_NEWADDR + +#include "addrfrm.h" +#include "template.h" +#include "xpgetstr.h" +#include "wfemsg.h" +#include "dirprefs.h" +#include "nethelp.h" +#include "xp_help.h" +#include "prefapi.h" +#include "intlwin.h" +#include "srchdlg.h" +#include "intl_csi.h" +#include "mailpriv.h" +#include "mnprefs.h" + +//RHP - Need this include for the call in confhook.cpp +#include "confhook.h" + +extern "C" { +#include "xpgetstr.h" +extern int MK_ADDR_BOOK_CARD; + +#define NUM_ADDRESS_USER_ATTRIBUTES 9 +#define NUM_ADDRESS_CONTACT_ATTRIBUTES 11 + +BOOL IsNumeric(char* pStr); +}; + +//////////////////////////////////////////////////////////////////////////// +// CAddrEditProperities + +CAddrEditProperties::CAddrEditProperties (CAddrFrame* frameref, + LPCTSTR lpszCaption, CWnd * parent, + MSG_Pane* pane, + MWContext* context, BOOL bNew) : + CNetscapePropertySheet ( lpszCaption, parent ) +{ + m_pUserProperties = NULL; + m_pContact = NULL; + m_pSecurity = NULL; + m_pCooltalk = NULL; + m_context = context; + m_pPane = pane; + m_frame = frameref; + m_bNew = bNew; +} + +CAddrEditProperties::~CAddrEditProperties ( ) +{ + if (m_pFont) { + theApp.ReleaseAppFont(m_pFont); + } + if ( m_pUserProperties ) + delete m_pUserProperties; + if ( m_pContact ) + delete m_pContact; + if ( m_pSecurity ) + delete m_pSecurity; + if ( m_pCooltalk ) + delete m_pCooltalk; +} + +BOOL CAddrEditProperties::Create(CWnd* pParentWnd, DWORD dwStyle, DWORD dwExStyle) +{ + BOOL ret = FALSE; + if (m_MailNewsResourceSwitcher.Initialize ()) { + m_pUserProperties = new CAddressUser (this, m_bNew); + m_pContact = new CAddressContact (this); + m_pSecurity = NULL; + m_pCooltalk = new CAddressCooltalk (this); + AddPage( m_pUserProperties ); + AddPage( m_pContact ); + AddPage( m_pCooltalk ); + SetAttributes(m_pPane); + CNetscapePropertySheet::Create (pParentWnd, dwStyle, dwExStyle); + ret = TRUE; + } + return ret; +} + + +int CAddrEditProperties::DoModal() +{ + if (!m_MailNewsResourceSwitcher.Initialize()) + return -1; + m_pUserProperties = new CAddressUser (this, m_bNew); + m_pContact = new CAddressContact (this); + m_pSecurity = NULL; + m_pCooltalk = new CAddressCooltalk (this); + AddPage( m_pUserProperties ); + AddPage( m_pContact ); + AddPage( m_pCooltalk ); + SetAttributes(m_pPane); + return CNetscapePropertySheet::DoModal(); +} + +void CAddrEditProperties::OnHelp() +{ + if (m_entryID == 0) + { + if (GetActivePage() == m_pUserProperties) + NetHelp(HELP_ADD_USER_PROPS); + else if (GetActivePage() == m_pContact) + NetHelp(HELP_ADD_USER_CONTACT); + HG21511 + else if (GetActivePage() == m_pCooltalk) + NetHelp(HELP_ADD_USER_NETSCAPE_COOLTALK); + } + else + { + if (GetActivePage() == m_pUserProperties) + NetHelp(HELP_EDIT_USER); + else if (GetActivePage() == m_pContact) + NetHelp(HELP_EDIT_USER_CONTACT); + HG19721 + else if (GetActivePage() == m_pCooltalk) + NetHelp(HELP_EDIT_USER_CALLPOINT); + } +} + +BEGIN_MESSAGE_MAP(CAddrEditProperties, CNetscapePropertySheet) + //{{AFX_MSG_MAP(CAddrEditProperties) + // NOTE: the ClassWizard will add message map macros here + ON_BN_CLICKED(IDC_TO, CloseWindow) + ON_WM_CREATE() + ON_WM_DESTROY() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +void CAddrEditProperties::SetAttributes(MSG_Pane* pane) +{ + // otherwise just switch the contents of the sheet + ((CAddressUser*)m_pUserProperties)->SetAttributes(pane); + ((CAddressContact*)m_pContact)->SetAttributes(pane); + ((CAddressCooltalk*)m_pCooltalk)->SetAttributes(pane); +} + +int CAddrEditProperties::OnCreate( LPCREATESTRUCT lpCreateStruct ) +{ + m_MailNewsResourceSwitcher.Reset(); + if (CNetscapePropertySheet::OnCreate(lpCreateStruct) == -1) + return -1; +#ifdef XP_WIN16 + OnInitDialog(); +#endif + return 0; +} + +static int rgiButtons[] = { IDOK, IDC_TO, IDCANCEL, ID_APPLY_NOW, IDHELP }; + + + +BOOL CAddrEditProperties::OnInitDialog ( ) +{ + BOOL bResult = TRUE; + CString label; + + m_MailNewsResourceSwitcher.Reset(); + int16 guicsid = 0; + if (m_context) + { + INTL_CharSetInfo csi = LO_GetDocumentCharacterSetInfo(m_context); + guicsid = INTL_GetCSIWinCSID(csi); + } + else + guicsid = CIntlWin::GetSystemLocaleCsid(); + + HDC hDC = ::GetDC(m_hWnd); + LOGFONT lf; + memset(&lf,0,sizeof(LOGFONT)); + + lf.lfPitchAndFamily = FF_SWISS; + lf.lfWeight = FW_NORMAL; + lf.lfCharSet = IntlGetLfCharset(CIntlWin::GetSystemLocaleCsid()); + if (CIntlWin::GetSystemLocaleCsid() == CS_LATIN1) + _tcscpy(lf.lfFaceName, "MS Sans Serif"); + else + _tcscpy(lf.lfFaceName, IntlGetUIPropFaceName(CIntlWin::GetSystemLocaleCsid())); + lf.lfHeight = -MulDiv(9, ::GetDeviceCaps(hDC, LOGPIXELSY), 72); + m_pFont = theApp.CreateAppFont( lf ); + + ::ReleaseDC(m_hWnd,hDC); + + + bResult = CNetscapePropertySheet::OnInitDialog(); + + if (m_bModeless) + { + // layout property sheet so button area IS accounted for + CRect rectWnd; + GetWindowRect(rectWnd); + CRect rectButton; + HWND hWnd = ::GetDlgItem(m_hWnd, IDOK); + ASSERT(hWnd != NULL); + ::GetWindowRect(hWnd, rectButton); + + SetWindowPos(NULL, 0, 0, + rectWnd.Width(), rectButton.bottom - rectWnd.top + 8, + SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + + // We don't use apply + CWnd* hWnd2 = GetDlgItem(ID_APPLY_NOW); + if (hWnd2) + hWnd2->DestroyWindow (); + + CWnd* widget; + CRect rect2, rect3; + + // create the CLOSE button + widget = GetDlgItem(IDCANCEL); + widget->GetWindowRect(&rect2); + widget->GetClientRect(&rect3); + ScreenToClient(&rect2); + + widget->DestroyWindow (); + + CButton * pButton = new CButton; + label.LoadString (IDS_CANCEL_BUTTON); + pButton->Create( label, + WS_CHILD | WS_VISIBLE | WS_TABSTOP, + rect3, this, IDC_TO); + pButton->MoveWindow(&rect2, TRUE); + pButton->SetFont(GetDlgItem(IDOK)->GetFont(), TRUE); + + // readd some of the standard buttons for modeless dialogs + for (int i = 0; i < sizeof(rgiButtons)/sizeof(rgiButtons[0]); i++) + { + HWND hWnd = ::GetDlgItem(m_hWnd, rgiButtons[i]); + if (hWnd != NULL) + { + ::ShowWindow(hWnd, SW_SHOW); + ::EnableWindow(hWnd, TRUE); + } + } + } + + return bResult; +} + +void CAddrEditProperties::OnDestroy( ) +{ + + CAddrFrame::HandleErrorReturn(AB_ClosePane(m_pPane)); + + +} + + +void CAddrEditProperties::CloseWindow() +{ + CButton * pButton = NULL; + pButton = (CButton *) GetDlgItem (IDC_TO); + if (pButton) + delete pButton; + +#ifdef XP_WIN16 + if (m_bModeless) + { + pButton = (CButton *) GetDlgItem (IDHELP); + if (pButton) + delete pButton; + pButton = (CButton *) GetDlgItem (IDOK); + if (pButton) + delete pButton; + } +#endif +#ifdef XP_WIN32 + if (m_bModeless) + OnClose(); + else + EndDialog(IDOK); +#else + if (m_bModeless) + DestroyWindow(); + else + EndDialog (IDOK); +#endif + +} + +/**************************************************************************** +* +* CAddrEditProperties::OnOK +* +* PARAMETERS: +* None +* +* RETURNS: +* void +* +* DESCRIPTION: +* We override this function because we are a modeless window. +* +****************************************************************************/ + +void CAddrEditProperties::OnOK() +{ + PersonEntry person; + int errorID = 0; + + // add or modify a user + person.Initialize(); + + ((CAddressUser*)m_pUserProperties)->PerformOnOK(m_pPane); + ((CAddressContact*)m_pContact)->PerformOnOK(m_pPane); + ((CAddressCooltalk*)m_pCooltalk)->PerformOnOK(m_pPane); + + AB_CommitChanges(m_pPane); + + // if this is a new user then we will need to create an entry +/* if (GetEntryID() == NULL) { + ABID entryID; + if ((errorID = AB_AddUser (m_dir, theApp.m_pABook, &person, &entryID)) != 0) { + CString s; + CAddrFrame::HandleErrorReturn(errorID, GetParent()); + SetCurrentPage(0); + m_pUserProperties->GetDlgItem(IDC_FIRSTNAME)->SetFocus(); + return; + } + } + else { + // we are potentially modifying a user so we will edit them + if ((errorID = AB_ModifyUser(m_dir, theApp.m_pABook, GetEntryID(), &person)) != 0) { + CString s; + CAddrFrame::HandleErrorReturn(errorID, GetParent()); + SetCurrentPage(0); + m_pUserProperties->GetDlgItem(IDC_FIRSTNAME)->SetFocus(); + return; + } + } +*/ + CButton * pButton = NULL; + pButton = (CButton *) GetDlgItem (IDC_TO); + if (pButton) + delete pButton; + +#ifdef XP_WIN16 + if (m_bModeless) + { + pButton = (CButton *) GetDlgItem (IDHELP); + if (pButton) + delete pButton; + pButton = (CButton *) GetDlgItem (IDOK); + if (pButton) + delete pButton; + } +#endif + + if (m_bModeless) + DestroyWindow(); + else + EndDialog (IDOK); + + +} // END OF FUNCTION CAddrEditProperities::OnOK() + + +///////////////////////////////////////////////////////////////////////////// +// CAddressUser property page + +CAddressUser::CAddressUser(CWnd *pParent, BOOL bNew) + : CNetscapePropertyPage(CAddressUser::IDD) +{ + //{{AFX_DATA_INIT(CAddressUser) + m_address = _T(""); + m_description = _T(""); + m_firstname = _T(""); + m_lastname = _T(""); + m_nickname = _T(""); + m_useHTML = 0; + m_company = _T(""); + m_title = _T(""); + m_department = _T(""); + m_displayname = _T(""); + //}}AFX_DATA_INIT + m_bActivated = FALSE; + m_bNew = bNew; //is this a new card + m_bUserChangedDisplay = FALSE; //has the user typed in the display name field yet. +} + +CAddressUser::~CAddressUser() +{ +} + +void CAddressUser::DoDataExchange(CDataExchange* pDX) +{ + CNetscapePropertyPage::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAddressUser) + DDX_Text(pDX, IDC_COMPANY_NAME, m_company); + DDX_Text(pDX, IDC_TITLE, m_title); + DDX_Text(pDX, IDC_ADDRESS, m_address); + DDX_Text(pDX, IDC_Description, m_description); + DDX_Text(pDX, IDC_FIRSTNAME, m_firstname); + DDX_Text(pDX, IDC_LASTNAME, m_lastname); + DDX_Text(pDX, IDC_NICKNAME, m_nickname); + DDX_Check(pDX, IDC_CHECK1, m_useHTML); + DDX_Text(pDX, IDC_DEPARTMENT, m_department); + DDX_Text(pDX, IDC_DISPLAYNAME, m_displayname); + //}}AFX_DATA_MAP +} + +void CAddressUser::SetFonts(HFONT pFont) +{ + ::SendMessage(::GetDlgItem(m_hWnd, IDC_COMPANY_NAME), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_TITLE), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_ADDRESS), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_Description), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_FIRSTNAME), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_LASTNAME), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_NICKNAME), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_DEPARTMENT), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_DISPLAYNAME), WM_SETFONT, (WPARAM)pFont, FALSE); + +} + + +BEGIN_MESSAGE_MAP(CAddressUser, CNetscapePropertyPage) + //{{AFX_MSG_MAP(CAddressUser) + // NOTE: the ClassWizard will add message map macros here + ON_BN_CLICKED(IDC_TO, OnCloseWindow) + ON_EN_CHANGE( IDC_FIRSTNAME, OnNameTextChange ) + ON_EN_CHANGE( IDC_LASTNAME, OnNameTextChange) + ON_EN_CHANGE( IDC_DISPLAYNAME, OnDisplayNameTextChange) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CAddressUser message handlers + +void CAddressUser::OnNameTextChange() +{ + + if(m_bNew && !m_bUserChangedDisplay) + { + UpdateData(TRUE); + + char *displayName = NULL; + AB_GenerateDefaultDisplayName(m_firstname, m_lastname, &displayName); + if(displayName) + { + m_displayname = displayName; + UpdateData(FALSE); + XP_FREE(displayName); + } + } +} + +void CAddressUser::OnDisplayNameTextChange() +{ + + if(m_bNew) + { + m_bUserChangedDisplay = TRUE; + } +} + +BOOL CAddressUser::OnInitDialog() +{ + // TODO: Add your specialized code here and/or call the base class + CNetscapePropertyPage::OnInitDialog(); + SetFonts (((CAddrEditProperties*) GetParent())->GetHFont()); + return TRUE; +} + +void CAddressUser::SetAttributes (MSG_Pane *pane) +{ + + uint16 numAttributes = NUM_ADDRESS_USER_ATTRIBUTES; + + AB_AttribID* attribs = new AB_AttribID[NUM_ADDRESS_USER_ATTRIBUTES]; + + attribs[0] = AB_attribGivenName; + attribs[1] = AB_attribNickName; + attribs[2] = AB_attribFamilyName; + attribs[3] = AB_attribEmailAddress; + attribs[4] = AB_attribCompanyName; + attribs[5] = AB_attribTitle; + attribs[6] = AB_attribInfo; + attribs[7] = AB_attribHTMLMail; + attribs[8] = AB_attribDisplayName; + + AB_AttributeValue* values; + + AB_GetPersonEntryAttributes(pane, attribs, &values, &numAttributes); + + for(int i = 0; i < numAttributes; i++) + { + switch(values[i].attrib) + { + case AB_attribGivenName: m_firstname = values[i].u.string; + break; + case AB_attribNickName: m_nickname = values[i].u.string; + break; + case AB_attribFamilyName: m_lastname = values[i].u.string; + break; + case AB_attribEmailAddress: m_address = values[i].u.string; + break; + case AB_attribCompanyName: m_company = values[i].u.string; + break; + case AB_attribTitle: m_title = values[i].u.string; + break; + case AB_attribInfo: m_description = values[i].u.string; + break; + case AB_attribHTMLMail: m_useHTML = values[i].u.boolValue; + break; + case AB_attribDisplayName: m_displayname = values[i].u.string; + break; + } + + + + } + + AB_FreeEntryAttributeValues(values, numAttributes); + +/* // if it is an entry that hasn't been added to the database yet then return + if (person) { + if (person->pGivenName) + m_firstname = person->pGivenName; + if (person->pNickName) + m_nickname = person->pNickName; + if (person->pFamilyName) + m_lastname = person->pFamilyName; + if (person->pEmailAddress) + m_address = person->pEmailAddress; + if (person->pCompanyName) + m_company = person->pCompanyName; + if (person->pTitle) + m_title = person->pTitle; + if (person->pInfo) + m_description = person->pInfo; + if (person->HTMLmail) + m_useHTML = person->HTMLmail; + return; + } + + if (entryID) { + // otherwise update the fields + AB_GetGivenName(m_dir, theApp.m_pABook, entryID, m_firstname.GetBuffer(kMaxNameLength)); + m_firstname.ReleaseBuffer(-1); + AB_GetNickname(m_dir, theApp.m_pABook, entryID, m_nickname.GetBuffer(kMaxNameLength)); + m_nickname.ReleaseBuffer(-1); + AB_GetFamilyName(m_dir, theApp.m_pABook, entryID, m_lastname.GetBuffer(kMaxNameLength)); + m_lastname.ReleaseBuffer(-1); + AB_GetEmailAddress(m_dir, theApp.m_pABook, entryID, m_address.GetBuffer(kMaxEmailAddressLength)); + m_address.ReleaseBuffer(-1); + AB_GetInfo(m_dir, theApp.m_pABook, entryID, m_description.GetBuffer(kMaxInfo)); + m_description.ReleaseBuffer(-1); + XP_Bool useHTML = FALSE; + AB_GetHTMLMail(m_dir, theApp.m_pABook, entryID, &useHTML); + m_useHTML = useHTML; + AB_GetCompanyName(m_dir, theApp.m_pABook, entryID, m_company.GetBuffer(kMaxCompanyLength)); + m_company.ReleaseBuffer(-1); + AB_GetTitle(m_dir, theApp.m_pABook, entryID, m_title.GetBuffer(kMaxTitle)); + m_title.ReleaseBuffer(-1); + } + */ +} + + +void CAddressUser::OnCloseWindow() +{ + ((CAddrEditProperties*) GetParent())->CloseWindow(); +} + + +BOOL CAddressUser::PerformOnOK(MSG_Pane *pane) +{ + // TODO: Add your specialized code here and/or call the base class + + // never visited this page so don't bother + if (m_bActivated) + UpdateData(TRUE); + + // remember to free old memory + AB_AttributeValue *values = new AB_AttributeValue[NUM_ADDRESS_USER_ATTRIBUTES]; + + values[0].attrib = AB_attribGivenName; + values[0].u.string = XP_STRDUP(m_firstname.GetBuffer(0)); + + values[1].attrib = AB_attribNickName; + values[1].u.string = XP_STRDUP(m_nickname.GetBuffer(0)); + + values[2].attrib = AB_attribFamilyName; + values[2].u.string = XP_STRDUP(m_lastname.GetBuffer(0)); + + values[3].attrib = AB_attribEmailAddress; + values[3].u.string = XP_STRDUP(m_address.GetBuffer(0)); + + values[4].attrib = AB_attribCompanyName; + values[4].u.string = XP_STRDUP(m_company.GetBuffer(0)); + + values[5].attrib = AB_attribTitle; + values[5].u.string = XP_STRDUP(m_title.GetBuffer(0)); + + values[6].attrib = AB_attribInfo; + values[6].u.string = XP_STRDUP(m_description.GetBuffer(0)); + + values[7].attrib = AB_attribHTMLMail; + values[7].u.boolValue = m_useHTML; + + values[8].attrib = AB_attribDisplayName; + values[8].u.string = XP_STRDUP(m_displayname.GetBuffer(0)); + + AB_SetPersonEntryAttributes(pane, values, NUM_ADDRESS_USER_ATTRIBUTES); + + for(int i = 0; i < NUM_ADDRESS_USER_ATTRIBUTES; i++) + { + if(AB_IsStringEntryAttributeValue(&values[i])) + { + XP_FREE(values[i].u.string); + } + } + + delete values; +/* + person->pNickName = m_nickname.GetBuffer(0); + person->pGivenName = m_firstname.GetBuffer(0); + person->pFamilyName = m_lastname.GetBuffer(0); + person->pEmailAddress = m_address.GetBuffer(0); + person->pInfo = m_description.GetBuffer(0); + person->HTMLmail = m_useHTML; + person->pCompanyName = m_company.GetBuffer(0); + person->pTitle = m_title.GetBuffer(0); +*/ + return TRUE; +} + +BOOL CAddressUser::OnKillActive( ) +{ + CString formattedString; + + if(!CNetscapePropertyPage::OnKillActive()) + return(FALSE); + + formattedString.Format(XP_GetString (MK_ADDR_BOOK_CARD), m_firstname + " " + m_lastname); + GetParent()->SetWindowText(formattedString); + return (TRUE); +} + + +BOOL CAddressUser::OnSetActive() +{ + // TODO: Add your specialized code here and/or call the base class + if(!CNetscapePropertyPage::OnSetActive()) + return(FALSE); + + if(m_bActivated) + return(TRUE); + m_bActivated = TRUE; + return(TRUE); +} + + +///////////////////////////////////////////////////////////////////////////// +// CAddressContact property page + +CAddressContact::CAddressContact(CWnd *pParent) + : CNetscapePropertyPage(CAddressContact::IDD) +{ + //{{AFX_DATA_INIT(CAddressContact) + m_poaddress = _T(""); + m_country = _T(""); + m_address = _T(""); + m_locality = _T(""); + m_region = _T(""); + m_zip = _T(""); + m_work = _T(""); + m_fax = _T(""); + m_home = _T(""); + m_pager = _T(""); + m_cellular = _T(""); + //}}AFX_DATA_INIT + m_bActivated = FALSE; +} + +CAddressContact::~CAddressContact() +{ +} + +void CAddressContact::DoDataExchange(CDataExchange* pDX) +{ + CNetscapePropertyPage::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAddressContact) + DDX_Text(pDX, IDC_STREET2, m_address); + DDX_Text(pDX, IDC_LOCALITY, m_locality); + DDX_Text(pDX, IDC_REGION, m_region); + DDX_Text(pDX, IDC_COUNTRY, m_country); +// DDX_Text(pDX, IDC_POBOX, m_poaddress); + DDX_Text(pDX, IDC_ZIP, m_zip); + DDX_Text(pDX, IDC_PHONE1, m_work); + DDX_Text(pDX, IDC_FAX, m_fax); + DDX_Text(pDX, IDC_HOME1, m_home); + DDX_Text(pDX, IDC_PAGER, m_pager); + DDX_Text(pDX, IDC_CELLULAR, m_cellular); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CAddressContact, CNetscapePropertyPage) + //{{AFX_MSG_MAP(CAddressContact) + // NOTE: the ClassWizard will add message map macros here + ON_BN_CLICKED(IDC_TO, OnCloseWindow) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CAddressContact message handlers + +BOOL CAddressContact::OnInitDialog() +{ + // TODO: Add your specialized code here and/or call the base class + CNetscapePropertyPage::OnInitDialog(); + SetFonts (((CAddrEditProperties*) GetParent())->GetHFont()); + return TRUE; +} + +void CAddressContact::SetFonts(HFONT pFont) +{ + ::SendMessage(::GetDlgItem(m_hWnd, IDC_STREET2), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_LOCALITY), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_REGION), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_COUNTRY), WM_SETFONT, (WPARAM)pFont, FALSE); +// ::SendMessage(::GetDlgItem(m_hWnd, IDC_POBOX), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_ZIP), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_PHONE1), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_FAX), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_HOME1), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_PAGER), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_CELLULAR), WM_SETFONT, (WPARAM)pFont, FALSE); + +} + + +void CAddressContact::SetAttributes (MSG_Pane *pane) +{ + + uint16 numAttributes = NUM_ADDRESS_CONTACT_ATTRIBUTES; + + AB_AttribID* attribs = new AB_AttribID[NUM_ADDRESS_CONTACT_ATTRIBUTES]; + + attribs[0] = AB_attribPOAddress; + attribs[1] = AB_attribStreetAddress; + attribs[2] = AB_attribLocality; + attribs[3] = AB_attribRegion; + attribs[4] = AB_attribZipCode; + attribs[5] = AB_attribCountry; + attribs[6] = AB_attribWorkPhone; + attribs[7] = AB_attribFaxPhone; + attribs[8] = AB_attribHomePhone; + attribs[9] = AB_attribPager; + attribs[10] = AB_attribCellularPhone; + + AB_AttributeValue *values; + + AB_GetPersonEntryAttributes(pane, attribs, &values, &numAttributes); + + for(int i = 0; i < numAttributes; i++) + { + switch(values[i].attrib) + { + case AB_attribPOAddress: m_poaddress = values[i].u.string; + break; + case AB_attribStreetAddress: m_address = values[i].u.string; + break; + case AB_attribLocality: m_locality = values[i].u.string; + break; + case AB_attribRegion: m_region = values[i].u.string; + break; + case AB_attribZipCode: m_zip = values[i].u.string; + break; + case AB_attribCountry: m_country = values[i].u.string; + break; + case AB_attribWorkPhone: m_work = values[i].u.string; + break; + case AB_attribFaxPhone: m_fax = values[i].u.string; + break; + case AB_attribHomePhone: m_home = values[i].u.string; + break; + case AB_attribPager: m_pager = values[i].u.string; + break; + case AB_attribCellularPhone: m_cellular = values[i].u.string; + break; + } + + + + } + + AB_FreeEntryAttributeValues(values, numAttributes); + + +/* if (person) { + if (person->pPOAddress) + m_poaddress = person->pPOAddress; + if (person->pAddress) + m_address = person->pAddress; + if (person->pLocality) + m_locality = person->pLocality; + if (person->pRegion) + m_region = person->pRegion; + if (person->pZipCode) + m_zip = person->pZipCode; + if (person->pCountry) + m_country = person->pCountry; + if (person->pWorkPhone) + m_work = person->pWorkPhone; + if (person->pFaxPhone) + m_fax = person->pFaxPhone; + if (person->pHomePhone) + m_home = person->pHomePhone; + return; + } + + if (entryID) + { + // otherwise update the fields + AB_GetPOAddress(m_dir, theApp.m_pABook, entryID, m_poaddress.GetBuffer(kMaxAddress)); + m_poaddress.ReleaseBuffer(-1); + AB_GetStreetAddress(m_dir, theApp.m_pABook, entryID, m_address.GetBuffer(kMaxAddress)); + m_address.ReleaseBuffer(-1); + AB_GetLocality(m_dir, theApp.m_pABook, entryID, m_locality.GetBuffer(kMaxLocalityLength)); + m_locality.ReleaseBuffer(-1); + AB_GetRegion(m_dir, theApp.m_pABook, entryID, m_region.GetBuffer(kMaxRegionLength)); + m_region.ReleaseBuffer(-1); + AB_GetZipCode(m_dir, theApp.m_pABook, entryID, m_zip.GetBuffer(kMaxZipCode)); + m_zip.ReleaseBuffer(-1); + AB_GetCountry(m_dir, theApp.m_pABook, entryID, m_country.GetBuffer(kMaxAddress)); + m_country.ReleaseBuffer(-1); + AB_GetWorkPhone(m_dir, theApp.m_pABook, entryID, m_work.GetBuffer(kMaxPhone)); + m_work.ReleaseBuffer(-1); + AB_GetFaxPhone(m_dir, theApp.m_pABook, entryID, m_fax.GetBuffer(kMaxPhone)); + m_fax.ReleaseBuffer(-1); + AB_GetHomePhone(m_dir, theApp.m_pABook, entryID, m_home.GetBuffer(kMaxPhone)); + m_home.ReleaseBuffer(-1); + } + */ + +} + + +void CAddressContact::OnCloseWindow() +{ + ((CAddrEditProperties*) GetParent())->CloseWindow(); +} + + +BOOL CAddressContact::PerformOnOK(MSG_Pane *pane) +{ + // TODO: Add your specialized code here and/or call the base class + + if (m_bActivated) + UpdateData(TRUE); + + AB_AttributeValue *values = new AB_AttributeValue[NUM_ADDRESS_CONTACT_ATTRIBUTES]; + + values[0].attrib = AB_attribPOAddress; + values[0].u.string = XP_STRDUP(m_poaddress.GetBuffer(0)); + + values[1].attrib = AB_attribCountry; + values[1].u.string = XP_STRDUP(m_country.GetBuffer(0)); + + values[2].attrib = AB_attribStreetAddress; + values[2].u.string = XP_STRDUP(m_address.GetBuffer(0)); + + values[3].attrib = AB_attribLocality; + values[3].u.string = XP_STRDUP(m_locality.GetBuffer(0)); + + values[4].attrib = AB_attribRegion; + values[4].u.string = XP_STRDUP(m_region.GetBuffer(0)); + + values[5].attrib = AB_attribZipCode; + values[5].u.string = XP_STRDUP(m_zip.GetBuffer(0)); + + values[6].attrib = AB_attribWorkPhone; + values[6].u.string = XP_STRDUP(m_work.GetBuffer(0)); + + values[7].attrib = AB_attribFaxPhone; + values[7].u.string = XP_STRDUP(m_fax.GetBuffer(0)); + + values[8].attrib = AB_attribHomePhone; + values[8].u.string = XP_STRDUP(m_home.GetBuffer(0)); + + values[9].attrib = AB_attribPager; + values[9].u.string = XP_STRDUP(m_pager.GetBuffer(0)); + + values[10].attrib = AB_attribCellularPhone; + values[10].u.string = XP_STRDUP(m_cellular.GetBuffer(0)); + + + AB_SetPersonEntryAttributes(pane, values, NUM_ADDRESS_CONTACT_ATTRIBUTES); + + for(int i = 0; i < NUM_ADDRESS_CONTACT_ATTRIBUTES; i++) + { + if(AB_IsStringEntryAttributeValue(&values[i])) + { + XP_FREE(values[i].u.string); + } + } + + delete values; + +/* + person->pPOAddress = m_poaddress.GetBuffer(0); + person->pCountry = m_country.GetBuffer(0); + person->pAddress = m_address.GetBuffer(0); + person->pLocality = m_locality.GetBuffer(0); + person->pRegion = m_region.GetBuffer(0); + person->pZipCode = m_zip.GetBuffer(0); + person->pWorkPhone = m_work.GetBuffer(0); + person->pFaxPhone = m_fax.GetBuffer(0); + person->pHomePhone = m_home.GetBuffer(0); +*/ + return TRUE; +} + +BOOL CAddressContact::OnSetActive() +{ + // TODO: Add your specialized code here and/or call the base class + if(!CNetscapePropertyPage::OnSetActive()) + return(FALSE); + + + if(m_bActivated) + return(TRUE); + m_bActivated = TRUE; + return(TRUE); +} + +///////////////////////////////////////////////////////////////////////////// +// CAddressCooltalk property page + +CAddressCooltalk::CAddressCooltalk(CWnd *pParent) + : CNetscapePropertyPage(CAddressCooltalk::IDD) +{ + //{{AFX_DATA_INIT(CAddressCooltalk) + m_ipaddress = _T(""); + m_iUseServer = 0; + //}}AFX_DATA_INIT + m_bActivated = FALSE; +} + +CAddressCooltalk::~CAddressCooltalk() +{ +} + +void CAddressCooltalk::DoDataExchange(CDataExchange* pDX) +{ + CNetscapePropertyPage::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAddressCooltalk) + DDX_Text(pDX, IDC_IP_ADDRESS, m_ipaddress); + DDX_CBIndex(pDX, IDC_CoolServer, m_iUseServer); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CAddressCooltalk, CNetscapePropertyPage) + //{{AFX_MSG_MAP(CAddressCooltalk) + ON_BN_CLICKED(IDC_TO, OnCloseWindow) + ON_CBN_SELENDOK(IDC_CoolServer, OnSelendokServer) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +void CAddressCooltalk::SetFonts(HFONT pFont) +{ + ::SendMessage(::GetDlgItem(m_hWnd, IDC_IP_ADDRESS), WM_SETFONT, (WPARAM)pFont, FALSE); +} + + +///////////////////////////////////////////////////////////////////////////// +// CAddressCooltalk message handlers + +void CAddressCooltalk::SetAttributes (MSG_Pane *pane) +{ + short useServer = 0; + + // if it is an entry that hasn't been added to the database yet then return +/* if (person) + { + m_iUseServer = person->UseServer; + + if (m_iUseServer == 1) { + m_specificDLS = person->pCoolAddress; + m_ipaddress = m_specificDLS; + } + + if (m_iUseServer == 2) { + m_hostorIP = person->pCoolAddress; + m_ipaddress = m_hostorIP; + } + return; + } + + if (entryID) + { + AB_GetUseServer(m_dir, theApp.m_pABook, entryID, &useServer); + m_iUseServer = useServer; + + if (m_iUseServer == 1) { + AB_GetCoolAddress(m_dir, theApp.m_pABook, entryID, m_specificDLS.GetBuffer(kMaxCoolAddress)); + m_specificDLS.ReleaseBuffer(-1); + m_ipaddress = m_specificDLS; + } + + if (m_iUseServer == 2) { + AB_GetCoolAddress(m_dir, theApp.m_pABook, entryID, m_hostorIP.GetBuffer(kMaxCoolAddress)); + m_hostorIP.ReleaseBuffer(-1); + m_ipaddress = m_hostorIP; + } + } + */ +} + +void CAddressCooltalk::SetExplanationText() +{ + CString expl; + switch (m_iUseServer) + { + case kDefaultDLS: + expl = ""; + break; + + case kSpecificDLS: + expl.LoadString(IDS_EXAMPLESPECIFICDLS); + break; + + case kHostOrIPAddress: + expl.LoadString(IDS_EXAMPLEHOSTNAME); + break; + } + SetDlgItemText(IDC_EXPLANATION1, expl); +} + +void CAddressCooltalk::OnSelendokServer() +{ + switch (m_iUseServer) + { + case kDefaultDLS: + break; + + case kSpecificDLS: + GetDlgItem(IDC_IP_ADDRESS)->GetWindowText(m_specificDLS); + break; + + case kHostOrIPAddress: + GetDlgItem(IDC_IP_ADDRESS)->GetWindowText(m_hostorIP); + break; + } + + UpdateData(); + switch (m_iUseServer) + { + case kDefaultDLS: + GetDlgItem(IDC_IP_ADDRESS)->EnableWindow(FALSE); + SetDlgItemText(IDC_IP_ADDRESS, ""); + break; + + case kSpecificDLS: + GetDlgItem(IDC_IP_ADDRESS)->EnableWindow(TRUE); + SetDlgItemText(IDC_IP_ADDRESS, m_specificDLS); + break; + + case kHostOrIPAddress: + GetDlgItem(IDC_IP_ADDRESS)->EnableWindow(TRUE); + SetDlgItemText(IDC_IP_ADDRESS, m_hostorIP ); + break; + } + SetExplanationText(); +} + +void CAddressCooltalk::OnCloseWindow() +{ + ((CAddrEditProperties*) GetParent())->CloseWindow(); +} + +BOOL CAddressCooltalk::OnInitDialog() +{ + // TODO: Add your specialized code here and/or call the base class + CNetscapePropertyPage::OnInitDialog(); + UpdateData (FALSE); + + if (m_iUseServer == 0) + GetDlgItem(IDC_IP_ADDRESS)->EnableWindow(FALSE); + SetExplanationText(); + SetFonts (((CAddrEditProperties*) GetParent())->GetHFont()); + + return TRUE; +} + +BOOL CAddressCooltalk::PerformOnOK(MSG_Pane *pane) +{ + // TODO: Add your specialized code here and/or call the base class + if (m_bActivated) + UpdateData(); + +/* person->pCoolAddress = m_ipaddress.GetBuffer(0); + person->UseServer = m_iUseServer; +*/ + return TRUE; +} + +BOOL CAddressCooltalk::OnSetActive() +{ + // TODO: Add your specialized code here and/or call the base class + if(!CNetscapePropertyPage::OnSetActive()) + return(FALSE); + + if(m_bActivated) + return(TRUE); + SetFonts (((CAddrEditProperties*) GetParent())->GetHFont()); + m_bActivated = TRUE; + return(TRUE); +} + + +//////////////////////////////////////////////////////////////////////////// +// CAddrLDAPProperties + +CAddrLDAPProperties::CAddrLDAPProperties (CWnd * parent, + MWContext* context, + DIR_Server* dir, + LPCTSTR lpszCaption) : + CNetscapePropertySheet ( lpszCaption, parent ) +{ + + // for New server only + DIR_InitServer(&m_serverInfo); + m_serverInfo.dirType = LDAPDirectory; + m_serverInfo.saveResults = TRUE; + m_pLDAPProperties = NULL; + m_pOfflineProperties = NULL; + m_pExistServer = dir; + m_context = context; +} + +CAddrLDAPProperties::~CAddrLDAPProperties ( ) +{ + if (m_pFont) { + theApp.ReleaseAppFont(m_pFont); + } + if ( m_pLDAPProperties ) + delete m_pLDAPProperties; + if ( m_pOfflineProperties ) + delete m_pOfflineProperties; +} + +void CAddrLDAPProperties::OnHelp() +{ + if (GetActivePage() == m_pLDAPProperties) + NetHelp(HELP_EDIT_USER); + else if (GetActivePage() == m_pOfflineProperties) + NetHelp(HELP_EDIT_USER_CONTACT); +} + +BEGIN_MESSAGE_MAP(CAddrLDAPProperties, CNetscapePropertySheet) + //{{AFX_MSG_MAP(CAddrLDAPProperties) + // NOTE: the ClassWizard will add message map macros here + ON_WM_CREATE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +int CAddrLDAPProperties::OnCreate( LPCREATESTRUCT lpCreateStruct ) +{ + m_MailNewsResourceSwitcher.Reset(); + if (CNetscapePropertySheet::OnCreate(lpCreateStruct) == -1) + return -1; + return 0; +} + +int CAddrLDAPProperties::DoModal() +{ + if (!m_MailNewsResourceSwitcher.Initialize()) + return -1; + + m_pLDAPProperties = new CServerDialog (this, m_pExistServer, &m_serverInfo); + m_pOfflineProperties = new CServerOfflineDialog (this, m_pExistServer, &m_serverInfo); + AddPage( m_pLDAPProperties ); + AddPage( m_pOfflineProperties ); + + return CNetscapePropertySheet::DoModal(); +} + + +BOOL CAddrLDAPProperties::OnInitDialog ( ) +{ + BOOL bResult = TRUE; + CString label; + + m_MailNewsResourceSwitcher.Reset(); + int16 guicsid = 0; + if (m_context) + { + INTL_CharSetInfo csi = LO_GetDocumentCharacterSetInfo(m_context); + guicsid = INTL_GetCSIWinCSID(csi); + } + else + guicsid = CIntlWin::GetSystemLocaleCsid(); + + HDC hDC = ::GetDC(m_hWnd); + LOGFONT lf; + memset(&lf,0,sizeof(LOGFONT)); + + lf.lfPitchAndFamily = FF_SWISS; + lf.lfWeight = FW_NORMAL; + lf.lfCharSet = IntlGetLfCharset(CIntlWin::GetSystemLocaleCsid()); + if (CIntlWin::GetSystemLocaleCsid() == CS_LATIN1) + _tcscpy(lf.lfFaceName, "MS Sans Serif"); + else + _tcscpy(lf.lfFaceName, IntlGetUIPropFaceName(CIntlWin::GetSystemLocaleCsid())); + lf.lfHeight = -MulDiv(9, ::GetDeviceCaps(hDC, LOGPIXELSY), 72); + m_pFont = theApp.CreateAppFont( lf ); + + ::ReleaseDC(m_hWnd,hDC); +#ifdef _WIN32 + bResult = CNetscapePropertySheet::OnInitDialog(); +#endif + + if (m_pExistServer) + { + label.LoadString(IDS_LDAP_SERVER_PROPERTY); + SetWindowText(LPCTSTR(label)); + } + + return bResult; +} + + +///////////////////////////////////////////////////////////////////////////// +// CServerDialog +CServerDialog::CServerDialog(CWnd *pParent, DIR_Server *pExistServer, + DIR_Server *pNewServer) + : CNetscapePropertyPage(CServerDialog::IDD) +{ + m_pExistServer = pExistServer; + + // for New server only + m_serverInfo =pNewServer; + m_bActivated = FALSE; +} + +BOOL CServerDialog::OnInitDialog() +{ + BOOL ret = CNetscapePropertyPage::OnInitDialog(); + + if (m_pExistServer) + { + SetDlgItemText(IDC_EDIT_DESCRIPTION, m_pExistServer->description); + if (m_pExistServer->dirType != PABDirectory) + { + SetDlgItemText(IDC_EDIT_SERVER, m_pExistServer->serverName); + SetDlgItemText(IDC_EDIT_ROOT, m_pExistServer->searchBase); + SetDlgItemInt(IDC_EDIT_PORT_NO, m_pExistServer->port); + SetDlgItemInt(IDC_EDIT_MAX_HITS, m_pExistServer->maxHits); + HG19511 + CheckDlgButton(IDC_SAVE_PASSWORD, m_pExistServer->savePassword); + CheckDlgButton(IDC_LOGIN_LDAP, m_pExistServer->enableAuth); + } + else + { + GetDlgItem(IDC_EDIT_SERVER)->EnableWindow(FALSE); + GetDlgItem(IDC_EDIT_ROOT)->EnableWindow(FALSE); + GetDlgItem(IDC_EDIT_PORT_NO)->EnableWindow(FALSE); + GetDlgItem(IDC_EDIT_MAX_HITS)->EnableWindow(FALSE); + HG17271 + GetDlgItem(IDC_SAVE_PASSWORD)->EnableWindow(FALSE); + GetDlgItem(IDC_LOGIN_LDAP)->EnableWindow(FALSE); + GetDlgItem(IDC_STATIC_SERVER)->EnableWindow(FALSE); + GetDlgItem(IDC_STATIC_ROOT)->EnableWindow(FALSE); + GetDlgItem(IDC_STATIC_PORT)->EnableWindow(FALSE); + GetDlgItem(IDC_STATIC_MAX_HITS)->EnableWindow(FALSE); + } + } + else + { + SetDlgItemInt(IDC_EDIT_MAX_HITS, 100); + SetDlgItemInt(IDC_EDIT_PORT_NO, LDAP_PORT); + } +#ifdef _WIN32 + ((CEdit*)GetDlgItem(IDC_EDIT_DESCRIPTION))->SetLimitText(MAX_DESCRIPTION_LEN - 1); + ((CEdit*)GetDlgItem(IDC_EDIT_SERVER))->SetLimitText(MAX_HOSTNAME_LEN - 1); +#else + ((CEdit*)GetDlgItem(IDC_EDIT_DESCRIPTION))->LimitText(MAX_DESCRIPTION_LEN - 1); + ((CEdit*)GetDlgItem(IDC_EDIT_SERVER))->LimitText(MAX_HOSTNAME_LEN - 1); + // we aren't doing secure ldap on win16 so hide the checkbox + HG18671 +#endif + ((CEdit*)GetDlgItem(IDC_EDIT_DESCRIPTION))->SetFocus(); + OnEnableLoginLDAP(); + return 0; +} + +BOOL CServerDialog::OnSetActive() +{ + // TODO: Add your specialized code here and/or call the base class + if(!CNetscapePropertyPage::OnSetActive()) + return(FALSE); + + if(m_bActivated) + return(TRUE); + m_bActivated = TRUE; + return(TRUE); +} + +BOOL CServerDialog::OnKillActive( ) +{ + if (!ValidDataInput()) + return FALSE; + return TRUE; +} + +void CServerDialog::OnOK() +{ + CNetscapePropertyPage::OnOK(); + + char text[MAX_DESCRIPTION_LEN]; + if (m_pExistServer) + { + if (GetDlgItemText(IDC_EDIT_DESCRIPTION, text, MAX_DESCRIPTION_LEN)) + { + XP_FREE(m_pExistServer->description); + m_pExistServer->description = XP_STRDUP(text); + } + if (m_pExistServer->dirType == PABDirectory) + return; + if (GetDlgItemText(IDC_EDIT_SERVER, text, MAX_DESCRIPTION_LEN)) + { + XP_FREE(m_pExistServer->serverName); + m_pExistServer->serverName = XP_STRDUP(text); + } + if (GetDlgItemText(IDC_EDIT_ROOT, text, MAX_DESCRIPTION_LEN)) + { + XP_FREE(m_pExistServer->searchBase); + m_pExistServer->searchBase = XP_STRDUP(text); + } + m_pExistServer->port = (int)GetDlgItemInt(IDC_EDIT_PORT_NO); + m_pExistServer->maxHits = (int)GetDlgItemInt(IDC_EDIT_MAX_HITS); + HG19616 + if (IsDlgButtonChecked(IDC_SAVE_PASSWORD)) + m_pExistServer->savePassword = TRUE; + else + m_pExistServer->savePassword = FALSE; + if (IsDlgButtonChecked(IDC_LOGIN_LDAP)) + m_pExistServer->enableAuth = TRUE; + else + m_pExistServer->enableAuth = FALSE; + } + else + { + char port[16]; + if (GetDlgItemText(IDC_EDIT_DESCRIPTION, text, MAX_DESCRIPTION_LEN)) + m_serverInfo->description = XP_STRDUP(text); + if (GetDlgItemText(IDC_EDIT_SERVER, text, MAX_DESCRIPTION_LEN)) + m_serverInfo->serverName = XP_STRDUP(text); + if (GetDlgItemText(IDC_EDIT_ROOT, text, MAX_DESCRIPTION_LEN)) + m_serverInfo->searchBase = XP_STRDUP(text); + HG19879 + if (GetDlgItemText(IDC_EDIT_PORT_NO, port, 16) > 0) + m_serverInfo->port = atoi(port); + else + { + HG17922 + m_serverInfo->port = LDAP_PORT; + } + m_serverInfo->maxHits = (int)GetDlgItemInt(IDC_EDIT_MAX_HITS); + if (IsDlgButtonChecked(IDC_SAVE_PASSWORD)) + m_serverInfo->savePassword = TRUE; + else + m_serverInfo->savePassword = FALSE; + if (IsDlgButtonChecked(IDC_LOGIN_LDAP)) + m_serverInfo->enableAuth = TRUE; + else + m_serverInfo->enableAuth = FALSE; + } +} + +void CServerDialog::OnCheckSecure() +{ + HG98219 +} + +void CServerDialog::OnEnableLoginLDAP() +{ + if (IsDlgButtonChecked(IDC_LOGIN_LDAP)) + GetDlgItem(IDC_SAVE_PASSWORD)->EnableWindow(TRUE); + else { + GetDlgItem(IDC_SAVE_PASSWORD)->EnableWindow(FALSE); + CButton* button = (CButton*) GetDlgItem(IDC_SAVE_PASSWORD); + button->SetCheck(FALSE); + } +} + +BOOL CServerDialog::ValidDataInput() +{ + char text[MAX_DESCRIPTION_LEN]; + if (0 == GetDlgItemText(IDC_EDIT_DESCRIPTION, text, MAX_DESCRIPTION_LEN)) + { + CAddrFrame::HandleErrorReturn(0, this, IDS_EMPTY_STRING); + ((CEdit*)GetDlgItem(IDC_EDIT_DESCRIPTION))->SetFocus(); + return FALSE; + } + + if (m_pExistServer && m_pExistServer->dirType == PABDirectory) + return TRUE; + + if (0 == GetDlgItemText(IDC_EDIT_SERVER, text, MAX_DESCRIPTION_LEN)) + { + CAddrFrame::HandleErrorReturn(0, this, IDS_EMPTY_STRING); + ((CEdit*)GetDlgItem(IDC_EDIT_SERVER))->SetFocus(); + return FALSE; + } + if (GetDlgItemText(IDC_EDIT_PORT_NO, text, MAX_DESCRIPTION_LEN)) + { + int nPort = GetDlgItemInt(IDC_EDIT_PORT_NO); + if (nPort < 0 && nPort> MAX_PORT_NUMBER) + { + CAddrFrame::HandleErrorReturn(0, this, IDS_PORT_RANGE); + ((CEdit*)GetDlgItem(IDC_EDIT_PORT_NO))->SetFocus(); + ((CEdit*)GetDlgItem(IDC_EDIT_PORT_NO))->SetSel((DWORD)MAKELONG(0, -1)); + return FALSE; + } + if (!::IsNumeric(text)) + { + CAddrFrame::HandleErrorReturn(0, this, IDS_NUMBERS_ONLY); + ((CEdit*)GetDlgItem(IDC_EDIT_PORT_NO))->SetFocus(); + ((CEdit*)GetDlgItem(IDC_EDIT_PORT_NO))->SetSel((DWORD)MAKELONG(0, -1)); + return FALSE; + } + } + if (GetDlgItemText(IDC_EDIT_MAX_HITS, text, MAX_DESCRIPTION_LEN)) + { + if (!::IsNumeric(text)) + { + CAddrFrame::HandleErrorReturn(0, this, IDS_NUMBERS_ONLY); + ((CEdit*)GetDlgItem(IDC_EDIT_MAX_HITS))->SetFocus(); + ((CEdit*)GetDlgItem(IDC_EDIT_MAX_HITS))->SetSel((DWORD)MAKELONG(0, -1)); + return FALSE; + } + } + return TRUE; +} + +void CServerDialog::DoDataExchange(CDataExchange* pDX) +{ + CNetscapePropertyPage::DoDataExchange(pDX); +} + +void CServerDialog::OnHelp() +{ + NetHelp(HELP_LDAP_SERVER_PROPS); +} + +BEGIN_MESSAGE_MAP(CServerDialog, CNetscapePropertyPage) + ON_BN_CLICKED(IDC_SECURE, OnCheckSecure) + ON_BN_CLICKED(IDC_LOGIN_LDAP, OnEnableLoginLDAP) + ON_BN_CLICKED(ID_HELP, OnHelp) +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// CServerOfflineDialog +CServerOfflineDialog::CServerOfflineDialog(CWnd *pParent, DIR_Server *pExistServer, + DIR_Server *pNewServer) + : CNetscapePropertyPage(CServerOfflineDialog::IDD) +{ + m_pExistServer = pExistServer; + + // for New server only + m_serverInfo = pNewServer; + m_bActivated = FALSE; +} + +BOOL CServerOfflineDialog::OnInitDialog() +{ + BOOL ret = CNetscapePropertyPage::OnInitDialog(); + + if (m_pExistServer) + { + CheckDlgButton(IDC_CHECK1, DIR_TestFlag (m_pExistServer, DIR_REPLICATION_ENABLED)); + } + else + { + CheckDlgButton(IDC_CHECK1, FALSE); + } + + return 0; +} + +BOOL CServerOfflineDialog::OnSetActive() +{ + // TODO: Add your specialized code here and/or call the base class + if(!CNetscapePropertyPage::OnSetActive()) + return(FALSE); + + if(m_bActivated) + return(TRUE); + m_bActivated = TRUE; + return(TRUE); +} + +void CServerOfflineDialog::OnOK() +{ + CNetscapePropertyPage::OnOK(); + + if (m_bActivated) + { + if (m_pExistServer) + { + if (IsDlgButtonChecked(IDC_CHECK1)) + DIR_SetFlag (m_pExistServer, DIR_REPLICATION_ENABLED); + else + DIR_ClearFlag (m_pExistServer, DIR_REPLICATION_ENABLED); + } + else + { + if (IsDlgButtonChecked(IDC_CHECK1)) + DIR_SetFlag (m_pExistServer, DIR_REPLICATION_ENABLED); + else + DIR_ClearFlag (m_pExistServer, DIR_REPLICATION_ENABLED); + } + } +} + +void CServerOfflineDialog::DoDataExchange(CDataExchange* pDX) +{ + CNetscapePropertyPage::DoDataExchange(pDX); +} + +void CServerOfflineDialog::OnHelp() +{ + NetHelp(HELP_LDAP_SERVER_PROPS); +} + +void CServerOfflineDialog::OnUpdateNow() +{ + BOOL bDownload = FALSE; + + DIR_Server *pServer = (m_pExistServer) ? m_pExistServer : m_serverInfo; + + if(pServer) + { + DIR_SetFlag (pServer, DIR_REPLICATION_ENABLED); + NET_ReplicateDirectory(NULL, pServer); + } + + GetParent()->PostMessage(WM_COMMAND, IDOK, 0); + +} + +BEGIN_MESSAGE_MAP(CServerOfflineDialog, CNetscapePropertyPage) + ON_BN_CLICKED(IDC_UPDATE_NOW, OnUpdateNow) + ON_BN_CLICKED(ID_HELP, OnHelp) +END_MESSAGE_MAP() + + + +#else // MOZ_NEWADDR + +// addrprop.cpp : implementation file for property dialogs from +// the address book +// + +#include "addrfrm.h" +#include "template.h" +#include "xpgetstr.h" +#include "wfemsg.h" +#include "dirprefs.h" +#include "nethelp.h" +#include "xp_help.h" +#include "prefapi.h" +#include "intlwin.h" +#include "srchdlg.h" +#include "intl_csi.h" +#include "mailpriv.h" +#include "mnprefs.h" + +//RHP - Need this include for the call in confhook.cpp +#include "confhook.h" + +extern "C" { +#include "xpgetstr.h" +extern int MK_ADDR_BOOK_CARD; + +BOOL IsNumeric(char* pStr); +}; + +//////////////////////////////////////////////////////////////////////////// +// CAddrEditProperities + +CAddrEditProperties::CAddrEditProperties (CAddrFrame* frameref, + DIR_Server* dir, + LPCTSTR lpszCaption, CWnd * parent, + ABID entryID, + PersonEntry* person, + MWContext* context) : + CNetscapePropertySheet ( lpszCaption, parent ) +{ + + m_pUserProperties = NULL; + m_pContact = NULL; + m_pSecurity = NULL; + m_pCooltalk = NULL; + m_entryID = entryID; + m_dir = dir; + m_context = context; + m_pPerson = person; + m_frame = frameref; +} + +CAddrEditProperties::~CAddrEditProperties ( ) +{ + if (m_pFont) { + theApp.ReleaseAppFont(m_pFont); + } + if ( m_pUserProperties ) + delete m_pUserProperties; + if ( m_pContact ) + delete m_pContact; + if ( m_pSecurity ) + delete m_pSecurity; + if ( m_pCooltalk ) + delete m_pCooltalk; +} + +BOOL CAddrEditProperties::Create(CWnd* pParentWnd, DWORD dwStyle, DWORD dwExStyle) +{ + BOOL ret = FALSE; + if (m_MailNewsResourceSwitcher.Initialize ()) { + m_pUserProperties = new CAddressUser (this); + m_pContact = new CAddressContact (this); + m_pSecurity = NULL; + m_pCooltalk = new CAddressCooltalk (this); + AddPage( m_pUserProperties ); + AddPage( m_pContact ); + AddPage( m_pCooltalk ); + SetEntryID (m_dir, m_entryID, m_pPerson); + CNetscapePropertySheet::Create (pParentWnd, dwStyle, dwExStyle); + ret = TRUE; + } + return ret; +} + +int CAddrEditProperties::DoModal() +{ + if (!m_MailNewsResourceSwitcher.Initialize()) + return -1; + m_pUserProperties = new CAddressUser (this); + m_pContact = new CAddressContact (this); + m_pSecurity = NULL; + m_pCooltalk = new CAddressCooltalk (this); + AddPage( m_pUserProperties ); + AddPage( m_pContact ); + AddPage( m_pCooltalk ); + SetEntryID (m_dir, m_entryID, m_pPerson); + return CNetscapePropertySheet::DoModal(); +} + + +void CAddrEditProperties::OnHelp() +{ + if (m_entryID == 0) + { + if (GetActivePage() == m_pUserProperties) + NetHelp(HELP_ADD_USER_PROPS); + else if (GetActivePage() == m_pContact) + NetHelp(HELP_ADD_USER_CONTACT); + HG92710 + else if (GetActivePage() == m_pCooltalk) + NetHelp(HELP_ADD_USER_NETSCAPE_COOLTALK); + } + else + { + if (GetActivePage() == m_pUserProperties) + NetHelp(HELP_EDIT_USER); + else if (GetActivePage() == m_pContact) + NetHelp(HELP_EDIT_USER_CONTACT); + HG27626 + else if (GetActivePage() == m_pCooltalk) + NetHelp(HELP_EDIT_USER_CALLPOINT); + } +} + +BEGIN_MESSAGE_MAP(CAddrEditProperties, CNetscapePropertySheet) + //{{AFX_MSG_MAP(CAddrEditProperties) + // NOTE: the ClassWizard will add message map macros here + ON_BN_CLICKED(IDC_TO, CloseWindow) + ON_WM_CREATE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +void CAddrEditProperties::SetEntryID (DIR_Server* dir, ABID entryID, PersonEntry* person) +{ + // otherwise just switch the contents of the sheet + ((CAddressUser*)m_pUserProperties)->SetEntryID(m_dir, entryID, person); + ((CAddressContact*)m_pContact)->SetEntryID(m_dir, entryID, person); + ((CAddressCooltalk*)m_pCooltalk)->SetEntryID(m_dir, entryID, person); +} + +int CAddrEditProperties::OnCreate( LPCREATESTRUCT lpCreateStruct ) +{ + m_MailNewsResourceSwitcher.Reset(); + if (CNetscapePropertySheet::OnCreate(lpCreateStruct) == -1) + return -1; +#ifdef XP_WIN16 + OnInitDialog(); +#endif + return 0; +} + +static int rgiButtons[] = { IDOK, IDC_TO, IDCANCEL, ID_APPLY_NOW, IDHELP }; + + + +BOOL CAddrEditProperties::OnInitDialog ( ) +{ + BOOL bResult = TRUE; + CString label; + + m_MailNewsResourceSwitcher.Reset(); + int16 guicsid = 0; + if (m_context) + { + INTL_CharSetInfo csi = LO_GetDocumentCharacterSetInfo(m_context); + guicsid = INTL_GetCSIWinCSID(csi); + } + else + guicsid = CIntlWin::GetSystemLocaleCsid(); + + HDC hDC = ::GetDC(m_hWnd); + LOGFONT lf; + memset(&lf,0,sizeof(LOGFONT)); + + lf.lfPitchAndFamily = FF_SWISS; + lf.lfWeight = FW_NORMAL; + lf.lfCharSet = IntlGetLfCharset(CIntlWin::GetSystemLocaleCsid()); + if (CIntlWin::GetSystemLocaleCsid() == CS_LATIN1) + _tcscpy(lf.lfFaceName, "MS Sans Serif"); + else + _tcscpy(lf.lfFaceName, IntlGetUIPropFaceName(CIntlWin::GetSystemLocaleCsid())); + lf.lfHeight = -MulDiv(9, ::GetDeviceCaps(hDC, LOGPIXELSY), 72); + m_pFont = theApp.CreateAppFont( lf ); + + ::ReleaseDC(m_hWnd,hDC); + + bResult = CNetscapePropertySheet::OnInitDialog(); + + if (m_bModeless) + { + // layout property sheet so button area IS accounted for + CRect rectWnd; + GetWindowRect(rectWnd); + CRect rectButton; + HWND hWnd = ::GetDlgItem(m_hWnd, IDOK); + ASSERT(hWnd != NULL); + ::GetWindowRect(hWnd, rectButton); + + SetWindowPos(NULL, 0, 0, + rectWnd.Width(), rectButton.bottom - rectWnd.top + 8, + SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + + // We don't use apply + CWnd* hWnd2 = GetDlgItem(ID_APPLY_NOW); + if (hWnd2) + hWnd2->DestroyWindow (); + + CWnd* widget; + CRect rect2, rect3; + + // create the CLOSE button + widget = GetDlgItem(IDCANCEL); + widget->GetWindowRect(&rect2); + widget->GetClientRect(&rect3); + ScreenToClient(&rect2); + + widget->DestroyWindow (); + + CButton * pButton = new CButton; + label.LoadString (IDS_CANCEL_BUTTON); + pButton->Create( label, + WS_CHILD | WS_VISIBLE | WS_TABSTOP, + rect3, this, IDC_TO); + pButton->MoveWindow(&rect2, TRUE); + pButton->SetFont(GetDlgItem(IDOK)->GetFont(), TRUE); + + // readd some of the standard buttons for modeless dialogs + for (int i = 0; i < sizeof(rgiButtons)/sizeof(rgiButtons[0]); i++) + { + HWND hWnd = ::GetDlgItem(m_hWnd, rgiButtons[i]); + if (hWnd != NULL) + { + ::ShowWindow(hWnd, SW_SHOW); + ::EnableWindow(hWnd, TRUE); + } + } + } + + + return bResult; +} + +void CAddrEditProperties::CloseWindow() +{ + CButton * pButton = NULL; + pButton = (CButton *) GetDlgItem (IDC_TO); + if (pButton) + delete pButton; + +#ifdef XP_WIN16 + if (m_bModeless) + { + pButton = (CButton *) GetDlgItem (IDHELP); + if (pButton) + delete pButton; + pButton = (CButton *) GetDlgItem (IDOK); + if (pButton) + delete pButton; + } +#endif +#ifdef XP_WIN32 + if (m_bModeless) + OnClose(); + else + EndDialog(IDOK); +#else + if (m_bModeless) + DestroyWindow(); + else + EndDialog (IDOK); +#endif +} + +/**************************************************************************** +* +* CAddrEditProperties::OnOK +* +* PARAMETERS: +* None +* +* RETURNS: +* void +* +* DESCRIPTION: +* We override this function because we are a modeless window. +* +****************************************************************************/ + +void CAddrEditProperties::OnOK() +{ + PersonEntry person; + int errorID = 0; + + // add or modify a user + person.Initialize(); + + ((CAddressUser*)m_pUserProperties)->PerformOnOK(&person); + ((CAddressContact*)m_pContact)->PerformOnOK(&person); + ((CAddressCooltalk*)m_pCooltalk)->PerformOnOK(&person); + + // if this is a new user then we will need to create an entry + if (GetEntryID() == NULL) { + ABID entryID; + if ((errorID = AB_AddUser (m_dir, theApp.m_pABook, &person, &entryID)) != 0) { + CString s; + CAddrFrame::HandleErrorReturn(errorID, GetParent()); + SetCurrentPage(0); + m_pUserProperties->GetDlgItem(IDC_FIRSTNAME)->SetFocus(); + return; + } + } + else { + // we are potentially modifying a user so we will edit them + if ((errorID = AB_ModifyUser(m_dir, theApp.m_pABook, GetEntryID(), &person)) != 0) { + CString s; + CAddrFrame::HandleErrorReturn(errorID, GetParent()); + SetCurrentPage(0); + m_pUserProperties->GetDlgItem(IDC_FIRSTNAME)->SetFocus(); + return; + } + } + + CButton * pButton = NULL; + pButton = (CButton *) GetDlgItem (IDC_TO); + if (pButton) + delete pButton; + +#ifdef XP_WIN16 + if (m_bModeless) + { + pButton = (CButton *) GetDlgItem (IDHELP); + if (pButton) + delete pButton; + pButton = (CButton *) GetDlgItem (IDOK); + if (pButton) + delete pButton; + } +#endif + + if (m_bModeless) + DestroyWindow(); + else + EndDialog (IDOK); + +} // END OF FUNCTION CAddrEditProperities::OnOK() + + +///////////////////////////////////////////////////////////////////////////// +// CAddressUser property page + +CAddressUser::CAddressUser(CWnd *pParent) + : CNetscapePropertyPage(CAddressUser::IDD) +{ + //{{AFX_DATA_INIT(CAddressUser) + m_address = _T(""); + m_description = _T(""); + m_firstname = _T(""); + m_lastname = _T(""); + m_nickname = _T(""); + m_useHTML = 0; + m_company = _T(""); + m_title = _T(""); + //}}AFX_DATA_INIT + m_bActivated = FALSE; +} + +CAddressUser::~CAddressUser() +{ +} + +void CAddressUser::DoDataExchange(CDataExchange* pDX) +{ + CNetscapePropertyPage::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAddressUser) + DDX_Text(pDX, IDC_COMPANY_NAME, m_company); + DDX_Text(pDX, IDC_TITLE, m_title); + DDX_Text(pDX, IDC_ADDRESS, m_address); + DDX_Text(pDX, IDC_Description, m_description); + DDX_Text(pDX, IDC_FIRSTNAME, m_firstname); + DDX_Text(pDX, IDC_LASTNAME, m_lastname); + DDX_Text(pDX, IDC_NICKNAME, m_nickname); + DDX_Check(pDX, IDC_CHECK1, m_useHTML); + DDX_Text(pDX, IDC_DEPARTMENT, m_department); + DDX_Text(pDX, IDC_DISPLAYNAME, m_displayname); + //}}AFX_DATA_MAP + +} + +void CAddressUser::SetFonts(HFONT pFont) +{ + ::SendMessage(::GetDlgItem(m_hWnd, IDC_COMPANY_NAME), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_TITLE), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_ADDRESS), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_Description), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_FIRSTNAME), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_LASTNAME), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_NICKNAME), WM_SETFONT, (WPARAM)pFont, FALSE); +} + + +BEGIN_MESSAGE_MAP(CAddressUser, CNetscapePropertyPage) + //{{AFX_MSG_MAP(CAddressUser) + // NOTE: the ClassWizard will add message map macros here + ON_BN_CLICKED(IDC_TO, OnCloseWindow) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CAddressUser message handlers + +BOOL CAddressUser::OnInitDialog() +{ + // TODO: Add your specialized code here and/or call the base class + CNetscapePropertyPage::OnInitDialog(); + SetFonts (((CAddrEditProperties*) GetParent())->GetHFont()); + return TRUE; +} + +void CAddressUser::SetEntryID (DIR_Server* dir, ABID entryID, PersonEntry* person) +{ + m_dir = dir; + + // if it is an entry that hasn't been added to the database yet then return + if (person) { + if (person->pGivenName) + m_firstname = person->pGivenName; + if (person->pNickName) + m_nickname = person->pNickName; + if (person->pFamilyName) + m_lastname = person->pFamilyName; + if (person->pEmailAddress) + m_address = person->pEmailAddress; + if (person->pCompanyName) + m_company = person->pCompanyName; + if (person->pTitle) + m_title = person->pTitle; + if (person->pInfo) + m_description = person->pInfo; + if (person->HTMLmail) + m_useHTML = person->HTMLmail; + return; + } + + if (entryID) { + // otherwise update the fields + AB_GetGivenName(m_dir, theApp.m_pABook, entryID, m_firstname.GetBuffer(kMaxNameLength)); + m_firstname.ReleaseBuffer(-1); + AB_GetNickname(m_dir, theApp.m_pABook, entryID, m_nickname.GetBuffer(kMaxNameLength)); + m_nickname.ReleaseBuffer(-1); + AB_GetFamilyName(m_dir, theApp.m_pABook, entryID, m_lastname.GetBuffer(kMaxNameLength)); + m_lastname.ReleaseBuffer(-1); + AB_GetEmailAddress(m_dir, theApp.m_pABook, entryID, m_address.GetBuffer(kMaxEmailAddressLength)); + m_address.ReleaseBuffer(-1); + AB_GetInfo(m_dir, theApp.m_pABook, entryID, m_description.GetBuffer(kMaxInfo)); + m_description.ReleaseBuffer(-1); + XP_Bool useHTML = FALSE; + AB_GetHTMLMail(m_dir, theApp.m_pABook, entryID, &useHTML); + m_useHTML = useHTML; + AB_GetCompanyName(m_dir, theApp.m_pABook, entryID, m_company.GetBuffer(kMaxCompanyLength)); + m_company.ReleaseBuffer(-1); + AB_GetTitle(m_dir, theApp.m_pABook, entryID, m_title.GetBuffer(kMaxTitle)); + m_title.ReleaseBuffer(-1); + } +} + + +void CAddressUser::OnCloseWindow() +{ + ((CAddrEditProperties*) GetParent())->CloseWindow(); +} + + +BOOL CAddressUser::PerformOnOK(PersonEntry* person) +{ + // TODO: Add your specialized code here and/or call the base class + + // never visited this page so don't bother + if (m_bActivated) + UpdateData(TRUE); + + person->pNickName = m_nickname.GetBuffer(0); + person->pGivenName = m_firstname.GetBuffer(0); + person->pFamilyName = m_lastname.GetBuffer(0); + person->pEmailAddress = m_address.GetBuffer(0); + person->pInfo = m_description.GetBuffer(0); + person->HTMLmail = m_useHTML; + person->pCompanyName = m_company.GetBuffer(0); + person->pTitle = m_title.GetBuffer(0); + + return TRUE; +} + +BOOL CAddressUser::OnKillActive( ) +{ + CString formattedString; + + if(!CNetscapePropertyPage::OnKillActive()) + return(FALSE); + + formattedString.Format(XP_GetString (MK_ADDR_BOOK_CARD), m_firstname + " " + m_lastname); + GetParent()->SetWindowText(formattedString); + return (TRUE); +} + + +BOOL CAddressUser::OnSetActive() +{ + // TODO: Add your specialized code here and/or call the base class + if(!CNetscapePropertyPage::OnSetActive()) + return(FALSE); + + if(m_bActivated) + return(TRUE); + m_bActivated = TRUE; + return(TRUE); +} + + +///////////////////////////////////////////////////////////////////////////// +// CAddressContact property page + +CAddressContact::CAddressContact(CWnd *pParent) + : CNetscapePropertyPage(CAddressContact::IDD) +{ + //{{AFX_DATA_INIT(CAddressContact) + m_poaddress = _T(""); + m_country = _T(""); + m_address = _T(""); + m_locality = _T(""); + m_region = _T(""); + m_zip = _T(""); + m_work = _T(""); + m_fax = _T(""); + m_home = _T(""); + //}}AFX_DATA_INIT + m_bActivated = FALSE; +} + +CAddressContact::~CAddressContact() +{ +} + +void CAddressContact::DoDataExchange(CDataExchange* pDX) +{ + CNetscapePropertyPage::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAddressContact) + DDX_Text(pDX, IDC_STREET2, m_address); + DDX_Text(pDX, IDC_LOCALITY, m_locality); + DDX_Text(pDX, IDC_REGION, m_region); + DDX_Text(pDX, IDC_COUNTRY, m_country); + //DDX_Text(pDX, IDC_POBOX, m_poaddress); + DDX_Text(pDX, IDC_ZIP, m_zip); + DDX_Text(pDX, IDC_PHONE1, m_work); + DDX_Text(pDX, IDC_FAX, m_fax); + DDX_Text(pDX, IDC_HOME1, m_home); + DDX_Text(pDX, IDC_PAGER, m_pager); + DDX_Text(pDX, IDC_CELLULAR, m_cellular); + //}}AFX_DATA_MAP + +} + + +BEGIN_MESSAGE_MAP(CAddressContact, CNetscapePropertyPage) + //{{AFX_MSG_MAP(CAddressContact) + // NOTE: the ClassWizard will add message map macros here + ON_BN_CLICKED(IDC_TO, OnCloseWindow) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CAddressContact message handlers + +BOOL CAddressContact::OnInitDialog() +{ + // TODO: Add your specialized code here and/or call the base class + CNetscapePropertyPage::OnInitDialog(); + SetFonts (((CAddrEditProperties*) GetParent())->GetHFont()); + return TRUE; +} + +void CAddressContact::SetFonts(HFONT pFont) +{ + ::SendMessage(::GetDlgItem(m_hWnd, IDC_STREET2), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_LOCALITY), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_REGION), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_COUNTRY), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_POBOX), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_ZIP), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_PHONE1), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_FAX), WM_SETFONT, (WPARAM)pFont, FALSE); + ::SendMessage(::GetDlgItem(m_hWnd, IDC_HOME1), WM_SETFONT, (WPARAM)pFont, FALSE); +} + + +void CAddressContact::SetEntryID (DIR_Server* dir, ABID entryID, PersonEntry* person) +{ + m_dir = dir; + + if (person) { + if (person->pPOAddress) + m_poaddress = person->pPOAddress; + if (person->pAddress) + m_address = person->pAddress; + if (person->pLocality) + m_locality = person->pLocality; + if (person->pRegion) + m_region = person->pRegion; + if (person->pZipCode) + m_zip = person->pZipCode; + if (person->pCountry) + m_country = person->pCountry; + if (person->pWorkPhone) + m_work = person->pWorkPhone; + if (person->pFaxPhone) + m_fax = person->pFaxPhone; + if (person->pHomePhone) + m_home = person->pHomePhone; + return; + } + + if (entryID) + { + // otherwise update the fields + AB_GetPOAddress(m_dir, theApp.m_pABook, entryID, m_poaddress.GetBuffer(kMaxAddress)); + m_poaddress.ReleaseBuffer(-1); + AB_GetStreetAddress(m_dir, theApp.m_pABook, entryID, m_address.GetBuffer(kMaxAddress)); + m_address.ReleaseBuffer(-1); + AB_GetLocality(m_dir, theApp.m_pABook, entryID, m_locality.GetBuffer(kMaxLocalityLength)); + m_locality.ReleaseBuffer(-1); + AB_GetRegion(m_dir, theApp.m_pABook, entryID, m_region.GetBuffer(kMaxRegionLength)); + m_region.ReleaseBuffer(-1); + AB_GetZipCode(m_dir, theApp.m_pABook, entryID, m_zip.GetBuffer(kMaxZipCode)); + m_zip.ReleaseBuffer(-1); + AB_GetCountry(m_dir, theApp.m_pABook, entryID, m_country.GetBuffer(kMaxAddress)); + m_country.ReleaseBuffer(-1); + AB_GetWorkPhone(m_dir, theApp.m_pABook, entryID, m_work.GetBuffer(kMaxPhone)); + m_work.ReleaseBuffer(-1); + AB_GetFaxPhone(m_dir, theApp.m_pABook, entryID, m_fax.GetBuffer(kMaxPhone)); + m_fax.ReleaseBuffer(-1); + AB_GetHomePhone(m_dir, theApp.m_pABook, entryID, m_home.GetBuffer(kMaxPhone)); + m_home.ReleaseBuffer(-1); + } +} + + +void CAddressContact::OnCloseWindow() +{ + ((CAddrEditProperties*) GetParent())->CloseWindow(); +} + + +BOOL CAddressContact::PerformOnOK(PersonEntry* person) +{ + // TODO: Add your specialized code here and/or call the base class + + if (m_bActivated) + UpdateData(TRUE); + + person->pPOAddress = m_poaddress.GetBuffer(0); + person->pCountry = m_country.GetBuffer(0); + person->pAddress = m_address.GetBuffer(0); + person->pLocality = m_locality.GetBuffer(0); + person->pRegion = m_region.GetBuffer(0); + person->pZipCode = m_zip.GetBuffer(0); + person->pWorkPhone = m_work.GetBuffer(0); + person->pFaxPhone = m_fax.GetBuffer(0); + person->pHomePhone = m_home.GetBuffer(0); + + return TRUE; +} + +BOOL CAddressContact::OnSetActive() +{ + // TODO: Add your specialized code here and/or call the base class + if(!CNetscapePropertyPage::OnSetActive()) + return(FALSE); + + + if(m_bActivated) + return(TRUE); + m_bActivated = TRUE; + return(TRUE); +} + +///////////////////////////////////////////////////////////////////////////// +// CAddressCooltalk property page + +CAddressCooltalk::CAddressCooltalk(CWnd *pParent) + : CNetscapePropertyPage(CAddressCooltalk::IDD) +{ + //{{AFX_DATA_INIT(CAddressCooltalk) + m_ipaddress = _T(""); + m_iUseServer = 0; + //}}AFX_DATA_INIT + m_bActivated = FALSE; +} + +CAddressCooltalk::~CAddressCooltalk() +{ +} + +void CAddressCooltalk::DoDataExchange(CDataExchange* pDX) +{ + CNetscapePropertyPage::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAddressCooltalk) + DDX_Text(pDX, IDC_IP_ADDRESS, m_ipaddress); + DDX_CBIndex(pDX, IDC_CoolServer, m_iUseServer); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CAddressCooltalk, CNetscapePropertyPage) + //{{AFX_MSG_MAP(CAddressCooltalk) + ON_BN_CLICKED(IDC_TO, OnCloseWindow) + ON_CBN_SELENDOK(IDC_CoolServer, OnSelendokServer) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +void CAddressCooltalk::SetFonts(HFONT pFont) +{ + ::SendMessage(::GetDlgItem(m_hWnd, IDC_IP_ADDRESS), WM_SETFONT, (WPARAM)pFont, FALSE); +} + + +///////////////////////////////////////////////////////////////////////////// +// CAddressCooltalk message handlers + +void CAddressCooltalk::SetEntryID(DIR_Server* dir, ABID entryID, PersonEntry* person) +{ + m_dir = dir; + short useServer = 0; + + // if it is an entry that hasn't been added to the database yet then return + if (person) + { + m_iUseServer = person->UseServer; + + if (m_iUseServer == 1) { + m_specificDLS = person->pCoolAddress; + m_ipaddress = m_specificDLS; + } + + if (m_iUseServer == 2) { + m_hostorIP = person->pCoolAddress; + m_ipaddress = m_hostorIP; + } + return; + } + + if (entryID) + { + AB_GetUseServer(m_dir, theApp.m_pABook, entryID, &useServer); + m_iUseServer = useServer; + + if (m_iUseServer == 1) { + AB_GetCoolAddress(m_dir, theApp.m_pABook, entryID, m_specificDLS.GetBuffer(kMaxCoolAddress)); + m_specificDLS.ReleaseBuffer(-1); + m_ipaddress = m_specificDLS; + } + + if (m_iUseServer == 2) { + AB_GetCoolAddress(m_dir, theApp.m_pABook, entryID, m_hostorIP.GetBuffer(kMaxCoolAddress)); + m_hostorIP.ReleaseBuffer(-1); + m_ipaddress = m_hostorIP; + } + } +} + +void CAddressCooltalk::SetExplanationText() +{ + CString expl; + switch (m_iUseServer) + { + case kDefaultDLS: + expl = ""; + break; + + case kSpecificDLS: + expl.LoadString(IDS_EXAMPLESPECIFICDLS); + break; + + case kHostOrIPAddress: + expl.LoadString(IDS_EXAMPLEHOSTNAME); + break; + } + SetDlgItemText(IDC_EXPLANATION1, expl); +} + +void CAddressCooltalk::OnSelendokServer() +{ + switch (m_iUseServer) + { + case kDefaultDLS: + break; + + case kSpecificDLS: + GetDlgItem(IDC_IP_ADDRESS)->GetWindowText(m_specificDLS); + break; + + case kHostOrIPAddress: + GetDlgItem(IDC_IP_ADDRESS)->GetWindowText(m_hostorIP); + break; + } + + UpdateData(); + switch (m_iUseServer) + { + case kDefaultDLS: + GetDlgItem(IDC_IP_ADDRESS)->EnableWindow(FALSE); + SetDlgItemText(IDC_IP_ADDRESS, ""); + break; + + case kSpecificDLS: + GetDlgItem(IDC_IP_ADDRESS)->EnableWindow(TRUE); + SetDlgItemText(IDC_IP_ADDRESS, m_specificDLS); + break; + + case kHostOrIPAddress: + GetDlgItem(IDC_IP_ADDRESS)->EnableWindow(TRUE); + SetDlgItemText(IDC_IP_ADDRESS, m_hostorIP ); + break; + } + SetExplanationText(); +} + +void CAddressCooltalk::OnCloseWindow() +{ + ((CAddrEditProperties*) GetParent())->CloseWindow(); +} + +BOOL CAddressCooltalk::OnInitDialog() +{ + // TODO: Add your specialized code here and/or call the base class + CNetscapePropertyPage::OnInitDialog(); + UpdateData (FALSE); + + if (m_iUseServer == 0) + GetDlgItem(IDC_IP_ADDRESS)->EnableWindow(FALSE); + SetExplanationText(); + SetFonts (((CAddrEditProperties*) GetParent())->GetHFont()); + + return TRUE; +} + +BOOL CAddressCooltalk::PerformOnOK(PersonEntry* person) +{ + // TODO: Add your specialized code here and/or call the base class + if (m_bActivated) + UpdateData(); + + person->pCoolAddress = m_ipaddress.GetBuffer(0); + person->UseServer = m_iUseServer; + + return TRUE; +} + +BOOL CAddressCooltalk::OnSetActive() +{ + // TODO: Add your specialized code here and/or call the base class + if(!CNetscapePropertyPage::OnSetActive()) + return(FALSE); + + if(m_bActivated) + return(TRUE); + SetFonts (((CAddrEditProperties*) GetParent())->GetHFont()); + m_bActivated = TRUE; + return(TRUE); +} + + +//////////////////////////////////////////////////////////////////////////// +// CAddrLDAPProperties + +CAddrLDAPProperties::CAddrLDAPProperties (CWnd * parent, + MWContext* context, + DIR_Server* dir, + LPCTSTR lpszCaption) : + CNetscapePropertySheet ( lpszCaption, parent ) +{ + + // for New server only + DIR_InitServer(&m_serverInfo); + m_serverInfo.dirType = LDAPDirectory; + m_serverInfo.saveResults = TRUE; + m_pLDAPProperties = NULL; + m_pOfflineProperties = NULL; + m_pExistServer = dir; + m_context = context; +} + +CAddrLDAPProperties::~CAddrLDAPProperties ( ) +{ + if (m_pFont) { + theApp.ReleaseAppFont(m_pFont); + } + if ( m_pLDAPProperties ) + delete m_pLDAPProperties; + if ( m_pOfflineProperties ) + delete m_pOfflineProperties; +} + +void CAddrLDAPProperties::OnHelp() +{ + if (GetActivePage() == m_pLDAPProperties) + NetHelp(HELP_EDIT_USER); + else if (GetActivePage() == m_pOfflineProperties) + NetHelp(HELP_EDIT_USER_CONTACT); +} + +BEGIN_MESSAGE_MAP(CAddrLDAPProperties, CNetscapePropertySheet) + //{{AFX_MSG_MAP(CAddrLDAPProperties) + // NOTE: the ClassWizard will add message map macros here + ON_WM_CREATE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +int CAddrLDAPProperties::DoModal() +{ + if (!m_MailNewsResourceSwitcher.Initialize()) + return -1; + m_pLDAPProperties = new CServerDialog (this, m_pExistServer, &m_serverInfo); + m_pOfflineProperties = new CServerOfflineDialog (this, m_pExistServer, &m_serverInfo); + AddPage( m_pLDAPProperties ); + AddPage( m_pOfflineProperties ); + + return CNetscapePropertySheet::DoModal(); +} + + +int CAddrLDAPProperties::OnCreate( LPCREATESTRUCT lpCreateStruct ) +{ + m_MailNewsResourceSwitcher.Reset(); + if (CNetscapePropertySheet::OnCreate(lpCreateStruct) == -1) + return -1; + return 0; +} + + +BOOL CAddrLDAPProperties::OnInitDialog ( ) +{ + BOOL bResult = TRUE; + CString label; + + m_MailNewsResourceSwitcher.Reset(); + int16 guicsid = 0; + if (m_context) + { + INTL_CharSetInfo csi = LO_GetDocumentCharacterSetInfo(m_context); + guicsid = INTL_GetCSIWinCSID(csi); + } + else + guicsid = CIntlWin::GetSystemLocaleCsid(); + + HDC hDC = ::GetDC(m_hWnd); + LOGFONT lf; + memset(&lf,0,sizeof(LOGFONT)); + + lf.lfPitchAndFamily = FF_SWISS; + lf.lfWeight = FW_NORMAL; + lf.lfCharSet = IntlGetLfCharset(CIntlWin::GetSystemLocaleCsid()); + if (CIntlWin::GetSystemLocaleCsid() == CS_LATIN1) + _tcscpy(lf.lfFaceName, "MS Sans Serif"); + else + _tcscpy(lf.lfFaceName, IntlGetUIPropFaceName(CIntlWin::GetSystemLocaleCsid())); + lf.lfHeight = -MulDiv(9, ::GetDeviceCaps(hDC, LOGPIXELSY), 72); + m_pFont = theApp.CreateAppFont( lf ); + + ::ReleaseDC(m_hWnd,hDC); +#ifdef _WIN32 + bResult = CNetscapePropertySheet::OnInitDialog(); +#endif + + if (m_pExistServer) + { + label.LoadString(IDS_LDAP_SERVER_PROPERTY); + SetWindowText(LPCTSTR(label)); + } + + return bResult; +} + + +/**************************************************************************** +* +* CAddrLDAPProperties::OnOK +* +* PARAMETERS: +* None +* +* RETURNS: +* void +* +* DESCRIPTION: +* We override this function because we are a modeless window. +* +****************************************************************************/ + + +///////////////////////////////////////////////////////////////////////////// +// CServerDialog +CServerDialog::CServerDialog(CWnd *pParent, DIR_Server *pExistServer, + DIR_Server *pNewServer) + : CNetscapePropertyPage(CServerDialog::IDD) +{ + m_pExistServer = pExistServer; + + // for New server only + m_serverInfo =pNewServer; + m_bActivated = FALSE; +} + +BOOL CServerDialog::OnInitDialog() +{ + BOOL ret = CNetscapePropertyPage::OnInitDialog(); + + if (m_pExistServer) + { + SetDlgItemText(IDC_EDIT_DESCRIPTION, m_pExistServer->description); + if (m_pExistServer->dirType != PABDirectory) + { + SetDlgItemText(IDC_EDIT_SERVER, m_pExistServer->serverName); + SetDlgItemText(IDC_EDIT_ROOT, m_pExistServer->searchBase); + SetDlgItemInt(IDC_EDIT_PORT_NO, m_pExistServer->port); + SetDlgItemInt(IDC_EDIT_MAX_HITS, m_pExistServer->maxHits); + HG28981 + CheckDlgButton(IDC_SAVE_PASSWORD, m_pExistServer->savePassword); + CheckDlgButton(IDC_LOGIN_LDAP, m_pExistServer->enableAuth); + } + else + { + GetDlgItem(IDC_EDIT_SERVER)->EnableWindow(FALSE); + GetDlgItem(IDC_EDIT_ROOT)->EnableWindow(FALSE); + GetDlgItem(IDC_EDIT_PORT_NO)->EnableWindow(FALSE); + GetDlgItem(IDC_EDIT_MAX_HITS)->EnableWindow(FALSE); + HG72186 + GetDlgItem(IDC_SAVE_PASSWORD)->EnableWindow(FALSE); + GetDlgItem(IDC_LOGIN_LDAP)->EnableWindow(FALSE); + GetDlgItem(IDC_STATIC_SERVER)->EnableWindow(FALSE); + GetDlgItem(IDC_STATIC_ROOT)->EnableWindow(FALSE); + GetDlgItem(IDC_STATIC_PORT)->EnableWindow(FALSE); + GetDlgItem(IDC_STATIC_MAX_HITS)->EnableWindow(FALSE); + } + } + else + { + SetDlgItemInt(IDC_EDIT_MAX_HITS, 100); + SetDlgItemInt(IDC_EDIT_PORT_NO, LDAP_PORT); + } +#ifdef _WIN32 + ((CEdit*)GetDlgItem(IDC_EDIT_DESCRIPTION))->SetLimitText(MAX_DESCRIPTION_LEN - 1); + ((CEdit*)GetDlgItem(IDC_EDIT_SERVER))->SetLimitText(MAX_HOSTNAME_LEN - 1); +#else + ((CEdit*)GetDlgItem(IDC_EDIT_DESCRIPTION))->LimitText(MAX_DESCRIPTION_LEN - 1); + ((CEdit*)GetDlgItem(IDC_EDIT_SERVER))->LimitText(MAX_HOSTNAME_LEN - 1); + // we aren't doing secure ldap on win16 so hide the checkbox + +#endif + ((CEdit*)GetDlgItem(IDC_EDIT_DESCRIPTION))->SetFocus(); + OnEnableLoginLDAP(); + return 0; +} + +BOOL CServerDialog::OnSetActive() +{ + // TODO: Add your specialized code here and/or call the base class + if(!CNetscapePropertyPage::OnSetActive()) + return(FALSE); + + if(m_bActivated) + return(TRUE); + m_bActivated = TRUE; + return(TRUE); +} + +BOOL CServerDialog::OnKillActive( ) +{ + if (!ValidDataInput()) + return FALSE; + return TRUE; +} + + +void CServerDialog::OnOK() +{ + CNetscapePropertyPage::OnOK(); + + char text[MAX_DESCRIPTION_LEN]; + if (m_pExistServer) + { + if (GetDlgItemText(IDC_EDIT_DESCRIPTION, text, MAX_DESCRIPTION_LEN)) + { + XP_FREE(m_pExistServer->description); + m_pExistServer->description = XP_STRDUP(text); + } + if (m_pExistServer->dirType == PABDirectory) + return; + if (GetDlgItemText(IDC_EDIT_SERVER, text, MAX_DESCRIPTION_LEN)) + { + XP_FREE(m_pExistServer->serverName); + m_pExistServer->serverName = XP_STRDUP(text); + } + if (GetDlgItemText(IDC_EDIT_ROOT, text, MAX_DESCRIPTION_LEN)) + { + XP_FREE(m_pExistServer->searchBase); + m_pExistServer->searchBase = XP_STRDUP(text); + } + m_pExistServer->port = (int)GetDlgItemInt(IDC_EDIT_PORT_NO); + m_pExistServer->maxHits = (int)GetDlgItemInt(IDC_EDIT_MAX_HITS); + HG90271 + if (IsDlgButtonChecked(IDC_SAVE_PASSWORD)) + m_pExistServer->savePassword = TRUE; + else + m_pExistServer->savePassword = FALSE; + if (IsDlgButtonChecked(IDC_LOGIN_LDAP)) + m_pExistServer->enableAuth = TRUE; + else + m_pExistServer->enableAuth = FALSE; + } + else + { + char port[16]; + if (GetDlgItemText(IDC_EDIT_DESCRIPTION, text, MAX_DESCRIPTION_LEN)) + m_serverInfo->description = XP_STRDUP(text); + if (GetDlgItemText(IDC_EDIT_SERVER, text, MAX_DESCRIPTION_LEN)) + m_serverInfo->serverName = XP_STRDUP(text); + if (GetDlgItemText(IDC_EDIT_ROOT, text, MAX_DESCRIPTION_LEN)) + m_serverInfo->searchBase = XP_STRDUP(text); + HG92177 + if (GetDlgItemText(IDC_EDIT_PORT_NO, port, 16) > 0) + m_serverInfo->port = atoi(port); + else + { + HG98216 + m_serverInfo->port = LDAP_PORT; + } + m_serverInfo->maxHits = (int)GetDlgItemInt(IDC_EDIT_MAX_HITS); + if (IsDlgButtonChecked(IDC_SAVE_PASSWORD)) + m_serverInfo->savePassword = TRUE; + else + m_serverInfo->savePassword = FALSE; + if (IsDlgButtonChecked(IDC_LOGIN_LDAP)) + m_serverInfo->enableAuth = TRUE; + else + m_serverInfo->enableAuth = FALSE; + } +} + +void CServerDialog::OnCheckSecure() +{ + HG91761 +} + +void CServerDialog::OnEnableLoginLDAP() +{ + if (IsDlgButtonChecked(IDC_LOGIN_LDAP)) + GetDlgItem(IDC_SAVE_PASSWORD)->EnableWindow(TRUE); + else { + GetDlgItem(IDC_SAVE_PASSWORD)->EnableWindow(FALSE); + CButton* button = (CButton*) GetDlgItem(IDC_SAVE_PASSWORD); + button->SetCheck(FALSE); + } +} + +BOOL CServerDialog::ValidDataInput() +{ + char text[MAX_DESCRIPTION_LEN]; + if (0 == GetDlgItemText(IDC_EDIT_DESCRIPTION, text, MAX_DESCRIPTION_LEN)) + { + CAddrFrame::HandleErrorReturn(0, this, IDS_EMPTY_STRING); + ((CEdit*)GetDlgItem(IDC_EDIT_DESCRIPTION))->SetFocus(); + return FALSE; + } + + if (m_pExistServer && m_pExistServer->dirType == PABDirectory) + return TRUE; + + if (0 == GetDlgItemText(IDC_EDIT_SERVER, text, MAX_DESCRIPTION_LEN)) + { + CAddrFrame::HandleErrorReturn(0, this, IDS_EMPTY_STRING); + ((CEdit*)GetDlgItem(IDC_EDIT_SERVER))->SetFocus(); + return FALSE; + } + if (GetDlgItemText(IDC_EDIT_PORT_NO, text, MAX_DESCRIPTION_LEN)) + { + int nPort = GetDlgItemInt(IDC_EDIT_PORT_NO); + if (nPort < 0 && nPort> MAX_PORT_NUMBER) + { + CAddrFrame::HandleErrorReturn(0, this, IDS_PORT_RANGE); + ((CEdit*)GetDlgItem(IDC_EDIT_PORT_NO))->SetFocus(); + ((CEdit*)GetDlgItem(IDC_EDIT_PORT_NO))->SetSel((DWORD)MAKELONG(0, -1)); + return FALSE; + } + if (!::IsNumeric(text)) + { + CAddrFrame::HandleErrorReturn(0, this, IDS_NUMBERS_ONLY); + ((CEdit*)GetDlgItem(IDC_EDIT_PORT_NO))->SetFocus(); + ((CEdit*)GetDlgItem(IDC_EDIT_PORT_NO))->SetSel((DWORD)MAKELONG(0, -1)); + return FALSE; + } + } + if (GetDlgItemText(IDC_EDIT_MAX_HITS, text, MAX_DESCRIPTION_LEN)) + { + if (!::IsNumeric(text)) + { + CAddrFrame::HandleErrorReturn(0, this, IDS_NUMBERS_ONLY); + ((CEdit*)GetDlgItem(IDC_EDIT_MAX_HITS))->SetFocus(); + ((CEdit*)GetDlgItem(IDC_EDIT_MAX_HITS))->SetSel((DWORD)MAKELONG(0, -1)); + return FALSE; + } + } + return TRUE; +} + +void CServerDialog::DoDataExchange(CDataExchange* pDX) +{ + CNetscapePropertyPage::DoDataExchange(pDX); +} + +void CServerDialog::OnHelp() +{ + NetHelp(HELP_LDAP_SERVER_PROPS); +} + +BEGIN_MESSAGE_MAP(CServerDialog, CNetscapePropertyPage) + ON_BN_CLICKED(IDC_SECURE, OnCheckSecure) + ON_BN_CLICKED(IDC_LOGIN_LDAP, OnEnableLoginLDAP) + ON_BN_CLICKED(ID_HELP, OnHelp) +END_MESSAGE_MAP() + + +///////////////////////////////////////////////////////////////////////////// +// CServerOfflineDialog +CServerOfflineDialog::CServerOfflineDialog(CWnd *pParent, DIR_Server *pExistServer, + DIR_Server *pNewServer) + : CNetscapePropertyPage(CServerOfflineDialog::IDD) +{ + m_pExistServer = pExistServer; + + // for New server only + m_serverInfo = pNewServer; + m_bActivated = FALSE; +} + +BOOL CServerOfflineDialog::OnInitDialog() +{ + BOOL ret = CNetscapePropertyPage::OnInitDialog(); + + if (m_pExistServer) + { + CheckDlgButton(IDC_CHECK1, DIR_TestFlag (m_pExistServer, DIR_REPLICATION_ENABLED)); + } + else + { + CheckDlgButton(IDC_CHECK1, FALSE); + } + + return 0; +} + +BOOL CServerOfflineDialog::OnSetActive() +{ + // TODO: Add your specialized code here and/or call the base class + if(!CNetscapePropertyPage::OnSetActive()) + return(FALSE); + + if(m_bActivated) + return(TRUE); + m_bActivated = TRUE; + return(TRUE); +} + +void CServerOfflineDialog::OnOK() +{ + CNetscapePropertyPage::OnOK(); + + if (m_bActivated) + { + if (m_pExistServer) + { + if (IsDlgButtonChecked(IDC_CHECK1)) + DIR_SetFlag (m_pExistServer, DIR_REPLICATION_ENABLED); + else + DIR_ClearFlag (m_pExistServer, DIR_REPLICATION_ENABLED); + } + else + { + if (IsDlgButtonChecked(IDC_CHECK1)) + DIR_SetFlag (m_pExistServer, DIR_REPLICATION_ENABLED); + else + DIR_ClearFlag (m_pExistServer, DIR_REPLICATION_ENABLED); + } + } +} + +void CServerOfflineDialog::DoDataExchange(CDataExchange* pDX) +{ + CNetscapePropertyPage::DoDataExchange(pDX); +} + +void CServerOfflineDialog::OnHelp() +{ + NetHelp(HELP_LDAP_SERVER_PROPS); +} + +void CServerOfflineDialog::OnUpdateNow() +{ + +} + +BEGIN_MESSAGE_MAP(CServerOfflineDialog, CNetscapePropertyPage) + ON_BN_CLICKED(IDC_UPDATE_NOW, OnUpdateNow) + ON_BN_CLICKED(ID_HELP, OnHelp) +END_MESSAGE_MAP() + +#endif //MOZ_NEWADDR + + diff --git a/mozilla/cmd/winfe/addrprop.h b/mozilla/cmd/winfe/addrprop.h new file mode 100644 index 00000000000..ea7570c1d7d --- /dev/null +++ b/mozilla/cmd/winfe/addrprop.h @@ -0,0 +1,871 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "addrfrm.h" + +#ifdef MOZ_NEWADDR + +#ifndef _addrprop_h_ +#define _addrprop_h_ + +// ADDRFRM.H +// +// DESCRIPTION: +// This file contains the declarations of the various address book related +// classes. +// + +#include "outliner.h" +#include "apimsg.h" +#include "xp_core.h" +#include "abcom.h" +#include "property.h" +#include "abmldlg.h" +#include "mailfrm.h" +#include "mnrccln.h" + +// above the range for normal EN_ messages +#define PEN_ILLEGALCHAR 0x8000 + // sent to parent when illegal character hit + // return 0 if you want parsed edit to beep + +/**************************************************************************** +* +* Class: CAddrEditProperties +* +* DESCRIPTION: +* This class is the property sheet window for editing all the attributes +* of the people types in the address book +* +****************************************************************************/ +class CAddrEditProperties : public CNetscapePropertySheet +{ +protected: + ABID m_entryID; + + CPropertyPage * m_pUserProperties; + CPropertyPage * m_pContact; + CPropertyPage * m_pSecurity; + CPropertyPage * m_pCooltalk; + CMailNewsResourceSwitcher m_MailNewsResourceSwitcher; + MSG_Pane * m_pPane; + //is this a new card + BOOL m_bNew; + +public: + MWContext* m_context; + HFONT m_pFont; + +public: + CAddrEditProperties ( CAddrFrame* frameref, LPCTSTR lpszCaption, + CWnd * parent = NULL, MSG_Pane * pane = NULL, + MWContext* context = NULL, BOOL bNew = FALSE); + virtual ~CAddrEditProperties ( ); + virtual void OnHelp(); + virtual int DoModal(); + virtual BOOL Create( CWnd* pParentWnd = NULL, DWORD dwStyle = (DWORD)1, DWORD dwExStyle = 0 ); + + void SetAttributes(MSG_Pane *pane); + HFONT GetHFont() { return (m_pFont); } + void CloseWindow (); + +protected: + CAddrFrame* m_frame; +// virtual ~CAddrEditProperties ( ); + + // Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CAddrEditProperties) + public: + virtual void OnOK(); + afx_msg void OnDestroy( ); + virtual BOOL OnInitDialog(); + //}}AFX_VIRTUAL + + // Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CAddrEditProperties) + // NOTE: the ClassWizard will add member functions here + afx_msg int OnCreate( LPCREATESTRUCT ); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + + +/**************************************************************************** +* +* Class: CAddressUser +* +* DESCRIPTION: +* This class is the property page for editing the most common attributes +* of the people types in the address book +* +****************************************************************************/ +class CAddressUser : public CNetscapePropertyPage +{ + +// Construction +public: + CAddressUser(CWnd *pParent, BOOL bNew); + virtual ~CAddressUser(); + + BOOL m_bActivated; + BOOL m_bNew; + BOOL m_bUserChangedDisplay; + +// Dialog Data + //{{AFX_DATA(CAddressUser) + enum { IDD = IDD_ADDRESS_USER }; + CString m_address; + CString m_description; + CString m_firstname; + CString m_lastname; + CString m_nickname; + int m_useHTML; + CString m_company; + CString m_title; + CString m_displayname; + CString m_department; + //}}AFX_DATA + + void SetAttributes(MSG_Pane *pane); + BOOL PerformOnOK(MSG_Pane *pane); + void SetFonts( HFONT pFont ); + +// Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CAddressUser) + public: + virtual BOOL OnInitDialog(); + virtual BOOL OnSetActive(); + virtual BOOL OnKillActive(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CAddressUser) + // NOTE: the ClassWizard will add member functions here + afx_msg void OnCloseWindow(); + afx_msg void OnNameTextChange(); + afx_msg void OnDisplayNameTextChange(); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() + +}; + + +/**************************************************************************** +* +* Class: CAddressContact +* +* DESCRIPTION: +* This class is the property page for editing the contact info +* of the people types in the address book +* +****************************************************************************/ +class CAddressContact : public CNetscapePropertyPage +{ + +// Construction +public: + CAddressContact(CWnd *pParent); + virtual ~CAddressContact(); + + BOOL m_bActivated; + +// Dialog Data + //{{AFX_DATA(CAddressContact) + enum { IDD = IDD_ADDRESS_CONTACT }; + CString m_poaddress; + CString m_address; + CString m_locality; + CString m_region; + CString m_zip; + CString m_country; + CString m_work; + CString m_fax; + CString m_home; + CString m_pager; + CString m_cellular; + //}}AFX_DATA + + void SetAttributes(MSG_Pane *pane); + BOOL PerformOnOK(MSG_Pane *pane); + void SetFonts( HFONT pFont ); + +// Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CAddressContact) + public: + virtual BOOL OnInitDialog(); + virtual BOOL OnSetActive(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CAddressContact) + // NOTE: the ClassWizard will add member functions here + afx_msg void OnCloseWindow(); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() + +}; + +/**************************************************************************** +* +* Class: CAddressCooltalk +* +* DESCRIPTION: +* This class is the property page for editing and accessing the cool +* talk attributes for a person object. +* +****************************************************************************/ +class CAddressCooltalk : public CNetscapePropertyPage +{ +// Construction +public: + CAddressCooltalk(CWnd *pParent); + virtual ~CAddressCooltalk(); + + BOOL m_bActivated; + + void SetAttributes(MSG_Pane *pane); + BOOL PerformOnOK(MSG_Pane *pane); + void SetExplanationText(); + void SetFonts( HFONT pFont ); + +// Dialog Data + //{{AFX_DATA(CAddressCooltalk) + enum { IDD = IDD_ADDRESS_COOLTALK }; + CString m_ipaddress; + CString m_specificDLS; + CString m_hostorIP; + int m_iUseServer; + //}}AFX_DATA + + +// Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CAddressCooltalk) + public: + virtual BOOL OnInitDialog(); + virtual BOOL OnSetActive(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CAddressCooltalk) + afx_msg void OnCloseWindow(); + afx_msg void OnSelendokServer(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +}; + +/**************************************************************************** +* +* Class: CAddressSecurity +* +* DESCRIPTION: +* This class is the property page for editing and accessing the security +* attributes for a person object. +* +****************************************************************************/ +class CAddressSecurity : public CNetscapePropertyPage +{ +// Construction +public: + CAddressSecurity(CWnd *pParent); // standard constructor + virtual ~CAddressSecurity(); + + BOOL m_bActivated; + + void SetAttributes(MSG_Pane *pane); + BOOL PerformOnOK(MSG_Pane *pane); + +// Dialog Data + //{{AFX_DATA(CAddressSecurity) + enum { IDD = IDD_ADDRESS_SECURITY }; + CString m_explain1; + CString m_explain2; + CString m_explain3; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CAddressSecurity) + public: + virtual BOOL OnInitDialog(); + virtual BOOL OnSetActive(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CAddressSecurity) + // NOTE: the ClassWizard will add member functions here + afx_msg void OnCloseWindow(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +/**************************************************************************** +* +* Class: CAddrLDAPProperties +* +* DESCRIPTION: +* This class is the property sheet window for editing all the attributes +* of the people types in the address book +* +****************************************************************************/ +class CAddrLDAPProperties : public CNetscapePropertySheet +{ +protected: + CPropertyPage * m_pLDAPProperties; + CPropertyPage * m_pOfflineProperties; + CMailNewsResourceSwitcher m_MailNewsResourceSwitcher; + +public: + DIR_Server* m_pExistServer; + DIR_Server m_serverInfo; + HFONT m_pFont; + MWContext* m_context; + +public: + CAddrLDAPProperties (CWnd * parent, + MWContext* context, + DIR_Server* dir = NULL, + LPCTSTR lpszCaption = NULL); + virtual ~CAddrLDAPProperties ( ); + virtual void OnHelp(); + virtual int DoModal(); + HFONT GetHFont() { return (m_pFont); } + +protected: + // Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CAddrLDAPProperties) + public: + virtual BOOL OnInitDialog(); + //}}AFX_VIRTUAL + + // Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CAddrLDAPProperties) + // NOTE: the ClassWizard will add member functions here + afx_msg int OnCreate( LPCREATESTRUCT ); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + +class CServerDialog : public CNetscapePropertyPage +{ +// Attributes +public: + + CServerDialog(CWnd* pParent = NULL, DIR_Server *pExistServer = NULL, DIR_Server *pNewServer = NULL); + + enum { IDD = IDD_PREF_LDAP_SERVER}; + + DIR_Server *m_pExistServer; + DIR_Server *m_serverInfo; + BOOL m_bActivated; + + //{{AFX_VIRTUAL(CServerDialog) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + BOOL ValidDataInput(); + + // Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CServerDialog) + public: + virtual void OnOK(); + virtual BOOL OnSetActive(); + virtual BOOL OnKillActive(); + virtual BOOL OnInitDialog(); + //}}AFX_VIRTUAL + + afx_msg void OnCheckSecure(); + afx_msg void OnEnableLoginLDAP(); + afx_msg void OnHelp(); + DECLARE_MESSAGE_MAP() +}; + +class CServerOfflineDialog : public CNetscapePropertyPage +{ +// Attributes +public: + + CServerOfflineDialog(CWnd* pParent = NULL, DIR_Server *pServer = NULL, DIR_Server *pNewServer = NULL); + + enum { IDD = IDD_PREF_LDAP_OFFLINE_SETTINGS}; + + DIR_Server *m_pExistServer; + DIR_Server *m_serverInfo; + BOOL m_bActivated; + + //{{AFX_VIRTUAL(CServerOfflineDialog) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CServerOfflineDialog) + public: + virtual void OnOK(); + virtual BOOL OnSetActive(); + virtual BOOL OnInitDialog(); + //}}AFX_VIRTUAL + + afx_msg void OnUpdateNow(); + afx_msg void OnHelp(); + DECLARE_MESSAGE_MAP() +}; + + +#endif + + +#else //MOZ_NEWADDR +#ifndef _addrprop_h_ +#define _addrprop_h_ + +// ADDRFRM.H +// +// DESCRIPTION: +// This file contains the declarations of the various address book related +// classes. +// + +#include "outliner.h" +#include "apimsg.h" +#include "xp_core.h" +#include "addrbook.h" +#include "property.h" +#include "abmldlg.h" +#include "mailfrm.h" +#include "mnrccln.h" + +// above the range for normal EN_ messages +#define PEN_ILLEGALCHAR 0x8000 + // sent to parent when illegal character hit + // return 0 if you want parsed edit to beep + +/**************************************************************************** +* +* Class: CAddrEditProperties +* +* DESCRIPTION: +* This class is the property sheet window for editing all the attributes +* of the people types in the address book +* +****************************************************************************/ +class CAddrEditProperties : public CNetscapePropertySheet +{ +protected: + ABID m_entryID; + + CPropertyPage * m_pUserProperties; + CPropertyPage * m_pContact; + CPropertyPage * m_pSecurity; + CPropertyPage * m_pCooltalk; + CMailNewsResourceSwitcher m_MailNewsResourceSwitcher; + +public: + DIR_Server* m_dir; + MWContext* m_context; + HFONT m_pFont; + PersonEntry* m_pPerson; + +public: + CAddrEditProperties ( CAddrFrame* frameref, DIR_Server* dir, + LPCTSTR lpszCaption, + CWnd * parent = NULL, ABID entryID = NULL, + PersonEntry* person = NULL, + MWContext* context = NULL); + virtual ~CAddrEditProperties ( ); + virtual void OnHelp(); + virtual int DoModal(); + virtual BOOL Create( CWnd* pParentWnd = NULL, DWORD dwStyle = (DWORD)1, DWORD dwExStyle = 0 ); + + void SetEntryID(DIR_Server* dir, ABID entryID = NULL, PersonEntry* person = NULL); + ABID GetEntryID() { return (m_entryID); } + DIR_Server* GetDirectoryServer() { return (m_dir); } + HFONT GetHFont() { return (m_pFont); } + void CloseWindow (); + +protected: + CAddrFrame* m_frame; +// virtual ~CAddrEditProperties ( ); + + // Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CAddrEditProperties) + public: + virtual void OnOK(); + virtual BOOL OnInitDialog(); + //}}AFX_VIRTUAL + + // Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CAddrEditProperties) + // NOTE: the ClassWizard will add member functions here + afx_msg int OnCreate( LPCREATESTRUCT ); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + + +/**************************************************************************** +* +* Class: CAddressUser +* +* DESCRIPTION: +* This class is the property page for editing the most common attributes +* of the people types in the address book +* +****************************************************************************/ +class CAddressUser : public CNetscapePropertyPage +{ + +// Construction +public: + CAddressUser(CWnd *pParent); + virtual ~CAddressUser(); + + BOOL m_bActivated; + DIR_Server* m_dir; + +// Dialog Data + //{{AFX_DATA(CAddressUser) + enum { IDD = IDD_ADDRESS_USER }; + CString m_address; + CString m_description; + CString m_firstname; + CString m_lastname; + CString m_nickname; + int m_useHTML; + CString m_company; + CString m_title; + CString m_department; + CString m_displayname; + //}}AFX_DATA + + void SetEntryID(DIR_Server* dir, ABID entryID = NULL, PersonEntry* person = NULL); + BOOL PerformOnOK(PersonEntry* person); + void SetFonts( HFONT pFont ); + +// Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CAddressUser) + public: + virtual BOOL OnInitDialog(); + virtual BOOL OnSetActive(); + virtual BOOL OnKillActive(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CAddressUser) + // NOTE: the ClassWizard will add member functions here + afx_msg void OnCloseWindow(); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() + +}; + + +/**************************************************************************** +* +* Class: CAddressContact +* +* DESCRIPTION: +* This class is the property page for editing the contact info +* of the people types in the address book +* +****************************************************************************/ +class CAddressContact : public CNetscapePropertyPage +{ + +// Construction +public: + CAddressContact(CWnd *pParent); + virtual ~CAddressContact(); + + BOOL m_bActivated; + DIR_Server* m_dir; + +// Dialog Data + //{{AFX_DATA(CAddressContact) + enum { IDD = IDD_ADDRESS_CONTACT }; + CString m_poaddress; + CString m_address; + CString m_locality; + CString m_region; + CString m_zip; + CString m_country; + CString m_work; + CString m_fax; + CString m_home; + CString m_pager; + CString m_cellular; + //}}AFX_DATA + + void SetEntryID(DIR_Server* dir, ABID entryID = NULL, PersonEntry* person = NULL); + BOOL PerformOnOK(PersonEntry* person); + void SetFonts( HFONT pFont ); + +// Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CAddressContact) + public: + virtual BOOL OnInitDialog(); + virtual BOOL OnSetActive(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CAddressContact) + // NOTE: the ClassWizard will add member functions here + afx_msg void OnCloseWindow(); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() + +}; + +/**************************************************************************** +* +* Class: CAddressCooltalk +* +* DESCRIPTION: +* This class is the property page for editing and accessing the cool +* talk attributes for a person object. +* +****************************************************************************/ +class CAddressCooltalk : public CNetscapePropertyPage +{ +// Construction +public: + CAddressCooltalk(CWnd *pParent); + virtual ~CAddressCooltalk(); + + BOOL m_bActivated; + DIR_Server* m_dir; + + void SetEntryID(DIR_Server* dir, ABID entryID = NULL, PersonEntry* person = NULL); + BOOL PerformOnOK(PersonEntry* person); + void SetExplanationText(); + void SetFonts( HFONT pFont ); + +// Dialog Data + //{{AFX_DATA(CAddressCooltalk) + enum { IDD = IDD_ADDRESS_COOLTALK }; + CString m_ipaddress; + CString m_specificDLS; + CString m_hostorIP; + int m_iUseServer; + //}}AFX_DATA + + +// Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CAddressCooltalk) + public: + virtual BOOL OnInitDialog(); + virtual BOOL OnSetActive(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CAddressCooltalk) + afx_msg void OnCloseWindow(); + afx_msg void OnSelendokServer(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +}; + + +/**************************************************************************** +* +* Class: CAddrLDAPProperties +* +* DESCRIPTION: +* This class is the property sheet window for editing all the attributes +* of the people types in the address book +* +****************************************************************************/ +class CAddrLDAPProperties : public CNetscapePropertySheet +{ +protected: + CPropertyPage * m_pLDAPProperties; + CPropertyPage * m_pOfflineProperties; + CMailNewsResourceSwitcher m_MailNewsResourceSwitcher; + +public: + DIR_Server* m_pExistServer; + DIR_Server m_serverInfo; + HFONT m_pFont; + MWContext* m_context; + +public: + CAddrLDAPProperties (CWnd * parent, + MWContext* context, + DIR_Server* dir = NULL, + LPCTSTR lpszCaption = NULL); + virtual ~CAddrLDAPProperties ( ); + virtual void OnHelp(); + virtual int DoModal(); + + HFONT GetHFont() { return (m_pFont); } + +protected: + // Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CAddrLDAPProperties) + public: + virtual BOOL OnInitDialog(); + //}}AFX_VIRTUAL + + // Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CAddrLDAPProperties) + // NOTE: the ClassWizard will add member functions here + afx_msg int OnCreate( LPCREATESTRUCT ); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + +class CServerDialog : public CNetscapePropertyPage +{ +// Attributes +public: + + CServerDialog(CWnd* pParent = NULL, DIR_Server *pExistServer = NULL, DIR_Server *pNewServer = NULL); + + enum { IDD = IDD_PREF_LDAP_SERVER}; + + DIR_Server *m_pExistServer; + DIR_Server *m_serverInfo; + BOOL m_bActivated; + + //{{AFX_VIRTUAL(CServerDialog) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + BOOL ValidDataInput(); + + // Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CServerDialog) + public: + virtual void OnOK(); + virtual BOOL OnKillActive(); + virtual BOOL OnSetActive(); + virtual BOOL OnInitDialog(); + //}}AFX_VIRTUAL + + afx_msg void OnCheckSecure(); + afx_msg void OnEnableLoginLDAP(); + afx_msg void OnHelp(); + DECLARE_MESSAGE_MAP() +}; + +class CServerOfflineDialog : public CNetscapePropertyPage +{ +// Attributes +public: + + CServerOfflineDialog(CWnd* pParent = NULL, DIR_Server *pServer = NULL, DIR_Server *pNewServer = NULL); + + enum { IDD = IDD_PREF_LDAP_OFFLINE_SETTINGS}; + + DIR_Server *m_pExistServer; + DIR_Server *m_serverInfo; + BOOL m_bActivated; + + //{{AFX_VIRTUAL(CServerOfflineDialog) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // Overrides + // ClassWizard generate virtual function overrides + //{{AFX_VIRTUAL(CServerOfflineDialog) + public: + virtual void OnOK(); + virtual BOOL OnSetActive(); + virtual BOOL OnInitDialog(); + //}}AFX_VIRTUAL + + afx_msg void OnUpdateNow(); + afx_msg void OnHelp(); + DECLARE_MESSAGE_MAP() +}; + + +#endif +#endif // MOZ_NEWADDR diff --git a/mozilla/cmd/winfe/advprosh.cpp b/mozilla/cmd/winfe/advprosh.cpp new file mode 100644 index 00000000000..0d061a78f24 --- /dev/null +++ b/mozilla/cmd/winfe/advprosh.cpp @@ -0,0 +1,532 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "stdafx.h" +#include "windowsx.h" + +#include "wfemsg.h" +#include "prefapi.h" + +#include "nethelp.h" +#include "advprosh.h" + +/*-----------------------------------------------------------------*/ +/* */ +/* Search options page */ +/* */ +/*-----------------------------------------------------------------*/ + +CSearchOptionsPage::CSearchOptionsPage(CWnd *pParent, UINT nID) + : CNetscapePropertyPage(nID) +{ + m_pParent = pParent; + m_bHasBeenViewed = FALSE; +} + +CSearchOptionsPage::~CSearchOptionsPage() +{ +} + +void CSearchOptionsPage::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAdvSearchOptionsDlg) + DDX_Check(pDX, IDC_CHECK_SUBFOLDERS, m_bIncludeSubfolders); + DDX_Check(pDX, IDC_CHECK_SEARCH_LOCALLY, m_iSearchArea); + //}}AFX_DATA_MAP +} + +BOOL CSearchOptionsPage::OnInitDialog() +{ + XP_Bool bSubFolders = FALSE; + XP_Bool bSearchServer = FALSE; + + PREF_GetBoolPref("mailnews.searchSubFolders",&bSubFolders); + m_bIncludeSubfolders = bSubFolders; + + m_bOffline = NET_IsOffline(); + + if(!m_bOffline) + PREF_GetBoolPref("mailnews.searchServer",&bSearchServer); + else + { + CWnd * pCheck = GetDlgItem(IDC_CHECK_SEARCH_LOCALLY); + if(pCheck != NULL) + pCheck->EnableWindow(FALSE); + } + + m_iSearchArea = bSearchServer ? 0 : 1; + + UpdateData(FALSE); + + m_bHasBeenViewed = TRUE; + + BOOL ret = CNetscapePropertyPage::OnInitDialog(); + return ret; +} + +void CSearchOptionsPage::OnOK() +{ + if(!m_bHasBeenViewed) + return; + UpdateData(); + PREF_SetBoolPref("mailnews.searchSubFolders",(XP_Bool)m_bIncludeSubfolders); + + if(!m_bOffline) + { + XP_Bool bSearchServer = m_iSearchArea ? 0 : 1; + PREF_SetBoolPref("mailnews.searchServer", bSearchServer); + } +} + +void CSearchOptionsPage::OnCancel() +{ + CNetscapePropertyPage::OnCancel(); +} + +BOOL CSearchOptionsPage::OnSetActive() +{ + if(!CNetscapePropertyPage::OnSetActive()) + return FALSE; + + return TRUE ; +} + +BOOL CSearchOptionsPage::OnKillActive() +{ + return CNetscapePropertyPage::OnKillActive(); +} + + +BEGIN_MESSAGE_MAP(CSearchOptionsPage, CNetscapePropertyPage) +END_MESSAGE_MAP() + +/*-----------------------------------------------------------------*/ +/* */ +/* Edit headers page */ +/* */ +/*-----------------------------------------------------------------*/ + +CCustomHeadersPage::CCustomHeadersPage(CWnd *pParent, UINT nID) + : CNetscapePropertyPage(nID) +{ + m_pParent = pParent; + m_bHasBeenViewed = FALSE; +} + +CCustomHeadersPage::~CCustomHeadersPage() +{ +} + +void CCustomHeadersPage::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); +} + +BOOL CCustomHeadersPage::OnInitDialog() +{ + MSG_FolderInfo *pInbox = NULL; + MSG_GetFoldersWithFlag (WFE_MSGGetMaster(), MSG_FOLDER_FLAG_INBOX, &pInbox, 1); + uint16 numItems; + MSG_GetNumAttributesForFilterScopes (WFE_MSGGetMaster(), scopeMailFolder, (void**)&pInbox, 1, &numItems); + MSG_SearchMenuItem * pHeaderItems = new MSG_SearchMenuItem [numItems]; + + if (!pHeaderItems) + return FALSE; + + MSG_GetAttributesForFilterScopes (WFE_MSGGetMaster(), scopeMailFolder, (void**)&pInbox, 1, pHeaderItems, &numItems); + + CListBox * pList = (CListBox *)GetDlgItem(IDC_HEADER_LIST); + + for (int i=0; i < numItems; i++) + { + if ( (pHeaderItems[i].attrib == attribOtherHeader) && pHeaderItems[i].isEnabled ) + pList->AddString(pHeaderItems[i].name); + } + + delete pHeaderItems; + + updateUI(); + + m_bHasBeenViewed = TRUE; + + return CNetscapePropertyPage::OnInitDialog(); +} + +void CCustomHeadersPage::OnAddHeader() +{ + CEdit * pEdit = (CEdit *)GetDlgItem(IDC_EDIT_NEW_HEADER); + CListBox * pList = (CListBox *)GetDlgItem(IDC_HEADER_LIST); + + char szString[256] = {'\0'}; + pEdit->GetWindowText(szString, sizeof(szString)); + if(strlen(szString) == 0) + return; + pList->AddString(szString); + pEdit->SetWindowText(""); + pEdit->SetFocus(); +} + +void CCustomHeadersPage::OnRemoveHeader() +{ + CListBox * pList = (CListBox *)GetDlgItem(IDC_HEADER_LIST); + + int iSel = pList->GetCurSel(); + if(iSel < 0) + return; + + pList->DeleteString(iSel); + + int iNewCount = pList->GetCount(); + + if(iNewCount > 0) + pList->SetCurSel((iSel >= iNewCount) ? iNewCount - 1 : iSel); + + OnSelChangeHeaderList(); + + if(iNewCount == 0) + { + CEdit * pEdit = (CEdit *)GetDlgItem(IDC_EDIT_NEW_HEADER); + pEdit->SetFocus(); + } +} + +void CCustomHeadersPage::OnReplaceHeader() +{ + CEdit * pEdit = (CEdit *)GetDlgItem(IDC_EDIT_NEW_HEADER); + CListBox * pList = (CListBox *)GetDlgItem(IDC_HEADER_LIST); + + char szString[256] = {'\0'}; + pEdit->GetWindowText(szString, sizeof(szString)); + if(strlen(szString) == 0) + return; + + int iSel = pList->GetCurSel(); + if(iSel != LB_ERR) + { + pList->DeleteString(iSel); + pList->InsertString(iSel, szString); + } + else + pList->AddString(szString); + + updateUI(); + pEdit->SetFocus(); +} + +void CCustomHeadersPage::OnChangeEditHeader() +{ + updateUI(); + char szString[256]; + GetDlgItemText(IDC_EDIT_NEW_HEADER, szString, sizeof(szString)); + if(strlen(szString) > 0) + SetDefID(IDC_ADD_HEADER); +} + +void CCustomHeadersPage::OnSelChangeHeaderList() +{ + CEdit * pEdit = (CEdit *)GetDlgItem(IDC_EDIT_NEW_HEADER); + CListBox * pList = (CListBox *)GetDlgItem(IDC_HEADER_LIST); + + char szString[256] = {'\0'}; + + int iSel = pList->GetCurSel(); + + if(iSel >= 0) + { + pList->GetText(iSel, szString); + pEdit->SetWindowText(szString); + } + else + pEdit->SetWindowText(""); + + updateUI(); +} + +BOOL CCustomHeadersPage::OnSetActive() +{ + if(!CNetscapePropertyPage::OnSetActive()) + return FALSE; + + return TRUE ; +} + +BOOL CCustomHeadersPage::OnKillActive() +{ + return CNetscapePropertyPage::OnKillActive(); +} + +void CCustomHeadersPage::OnOK() +{ + if(!m_bHasBeenViewed) + return; + + CEdit * pEdit = (CEdit *)GetDlgItem(IDC_EDIT_NEW_HEADER); + CListBox * pList = (CListBox *)GetDlgItem(IDC_HEADER_LIST); + + int iCount = pList->GetCount(); + + if(iCount != 0) + { + CString strTemp; + pList->GetText(0, strTemp); + CString strHeaderPrefList = strTemp; + + for (int i = 1; i < iCount; i++) + { + pList->GetText(i,strTemp); + strHeaderPrefList += ": " + strTemp; + } + PREF_SetCharPref("mailnews.customHeaders", strHeaderPrefList); + } + else + { + PREF_SetCharPref("mailnews.customHeaders",""); + } + CPropertyPage::OnOK(); +} + +void CCustomHeadersPage::OnCancel() +{ + CNetscapePropertyPage::OnCancel(); +} + +void CCustomHeadersPage::enableDlgItem(int iId, BOOL bEnable) +{ + CWnd * pItem = GetDlgItem(iId); + if(pItem == NULL) + return; + pItem->EnableWindow(bEnable); +} + +void CCustomHeadersPage::updateUI() +{ + CListBox * pList = (CListBox *)GetDlgItem(IDC_HEADER_LIST); + + char szString[256] = {'\0'}; + int iSel = pList->GetCurSel(); + + GetDlgItemText(IDC_EDIT_NEW_HEADER, szString, sizeof(szString)); + enableDlgItem(IDC_ADD_HEADER, (strlen(szString) > 0)); + + enableDlgItem(IDC_REMOVE_HEADER, (iSel >= 0)); + + char szToReplace[256] = {'\0'}; + pList->GetText(iSel, szToReplace); + enableDlgItem(IDC_REPLACE_HEADER, ((strlen(szString) > 0) && (iSel >= 0) && (0 != strcmp(szString, szToReplace)))); +} + +BEGIN_MESSAGE_MAP(CCustomHeadersPage, CNetscapePropertyPage) + ON_BN_CLICKED(IDC_ADD_HEADER, OnAddHeader) + ON_BN_CLICKED(IDC_REMOVE_HEADER, OnRemoveHeader) + ON_BN_CLICKED(IDC_REPLACE_HEADER, OnReplaceHeader) + ON_EN_CHANGE(IDC_EDIT_NEW_HEADER, OnChangeEditHeader) + ON_LBN_SELCHANGE(IDC_HEADER_LIST, OnSelChangeHeaderList) +END_MESSAGE_MAP() + +/*-----------------------------------------------------------------*/ +/* */ +/* Advanced search property sheet */ +/* */ +/*-----------------------------------------------------------------*/ + +CAdvancedOptionsPropertySheet::CAdvancedOptionsPropertySheet(CWnd *pParent, + const char* pName, + DWORD dwPagesBitFlag) + : CNetscapePropertySheet(pName, pParent, 0, NULL), + m_pParent(pParent), + m_iReturnCode(IDCANCEL), + m_dwPagesBitFlag(dwPagesBitFlag), + m_OptionsPage(NULL), + m_HeadersPage(NULL) +{ + assert(m_dwPagesBitFlag != AOP_NOPAGES); + + if(m_dwPagesBitFlag & AOP_SEARCH_OPTIONS) + { + m_OptionsPage = new CSearchOptionsPage(pParent, IDD_ADVANCED_SEARCH); + AddPage(m_OptionsPage); + } + + if(m_dwPagesBitFlag & AOP_CUSTOM_HEADERS) + { + m_HeadersPage = new CCustomHeadersPage(pParent, IDD_HEADER_LIST); + AddPage(m_HeadersPage); + } +} + +CAdvancedOptionsPropertySheet::~CAdvancedOptionsPropertySheet() +{ + if(m_OptionsPage!= NULL) + delete m_OptionsPage; + if(m_HeadersPage!= NULL) + delete m_HeadersPage; +} + +CNetscapePropertyPage * CAdvancedOptionsPropertySheet::GetCurrentPage() +{ + return (CNetscapePropertyPage *)GetActivePage(); +} + +BOOL CAdvancedOptionsPropertySheet::OnCommand(WPARAM wParam, LPARAM lParam) +{ +#ifdef _WIN32 + int id = LOWORD(wParam); +#else + int id = (int)wParam; +#endif + + if((id == IDOK) || (id == IDCANCEL)) + { + m_iReturnCode = id; + OnClose(); + return TRUE; + } + + return CNetscapePropertySheet::OnCommand(wParam, lParam); +} + +#define BUTTON_CONTAINER 33 +#define BUTTON_WIDTH 75 +#define BUTTON_HEIGHT 23 +#define BUTTON_TO_RIGHT 9 +#define BUTTON_TO_BOTTOM 10 +#define BUTTON_TO_BUTTON 6 +#define X_ADJUSTMENT 9 +#define Y_ADJUSTMENT 3 + +#define SetWinFont(hwnd, hfont, fRedraw) FORWARD_WM_SETFONT((hwnd), (hfont), (fRedraw), ::SendMessage) +#define GetWinFont(hwnd) FORWARD_WM_GETFONT((hwnd), ::SendMessage) + +#ifdef _WIN32 + +BOOL CAdvancedOptionsPropertySheet::OnInitDialog() +{ + BOOL ret = CNetscapePropertySheet::OnInitDialog(); + +#else + +int CAdvancedOptionsPropertySheet::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + int ret = CNetscapePropertySheet::OnCreate(lpCreateStruct); + +#endif + + if(m_pParent && ::IsWindow(m_pParent->m_hWnd)) + m_pParent->EnableWindow(FALSE); + + EnableWindow(TRUE); + + RECT rc; + GetWindowRect(&rc); + int iWidth = rc.right - rc.left; + int iHeight = rc.bottom - rc.top + BUTTON_CONTAINER; + SetWindowPos(NULL,0,0, iWidth, iHeight, SWP_NOZORDER | SWP_NOMOVE); + + DWORD dwStyle = WS_VISIBLE | WS_CHILD; + HINSTANCE hInst = AfxGetInstanceHandle(); + + GetClientRect(&rc); + HFONT hFont = GetWinFont(m_hWnd); + + int y = rc.bottom - rc.top - BUTTON_TO_BOTTOM - BUTTON_HEIGHT + Y_ADJUSTMENT; + int w = BUTTON_WIDTH; + int h = BUTTON_HEIGHT; + int x; + HWND hWnd; + + x = rc.right - rc.left - BUTTON_TO_RIGHT - (BUTTON_TO_BUTTON + BUTTON_WIDTH) + X_ADJUSTMENT; + hWnd = CreateWindow("button", "Help", dwStyle, x,y, w, h, m_hWnd, (HMENU)IDHELP, hInst, NULL); + SetWinFont(hWnd, hFont, TRUE); + + x -= BUTTON_TO_BUTTON + BUTTON_WIDTH; + hWnd = CreateWindow("button", "Cancel", dwStyle, x,y, w, h, m_hWnd, (HMENU)IDCANCEL, hInst, NULL); + SetWinFont(hWnd, hFont, TRUE); + + x -= BUTTON_TO_BUTTON + BUTTON_WIDTH; + hWnd = CreateWindow("button", "OK", dwStyle, x,y, w, h, m_hWnd, (HMENU)IDOK, hInst, NULL); + SetWinFont(hWnd, hFont, TRUE); + + return ret; +} + +void CAdvancedOptionsPropertySheet::OnDestroy() +{ + CNetscapePropertySheet::OnDestroy(); +} + +void CAdvancedOptionsPropertySheet::OnClose() +{ + if(m_dwPagesBitFlag & AOP_SEARCH_OPTIONS) + { + if(m_iReturnCode == IDOK) + m_OptionsPage->OnOK(); + m_pParent->PostMessage(WM_ADVANCED_OPTIONS_DONE, 0, m_iReturnCode); + } + + if(m_dwPagesBitFlag & AOP_CUSTOM_HEADERS) + { + if(m_iReturnCode == IDOK) + m_HeadersPage->OnOK(); + m_pParent->PostMessage(WM_EDIT_CUSTOM_DONE, 0, m_iReturnCode); + } + + if(m_pParent && ::IsWindow(m_pParent->m_hWnd)) + m_pParent->EnableWindow(TRUE); + + DestroyWindow(); +} + +void CAdvancedOptionsPropertySheet::OnNcDestroy() +{ + delete this; +} + +void CAdvancedOptionsPropertySheet::OnHelp() +{ + if(GetActivePage() == m_OptionsPage) + NetHelp(HELP_SEARCH_MAILNEWS_OPTIONS); + if(GetActivePage() == m_HeadersPage) + NetHelp(HELP_SEARCH_MAILNEWS_HEADERS); +} + +BOOL CAdvancedOptionsPropertySheet::PreTranslateMessage(MSG * pMsg) +{ + if(pMsg->message == WM_KEYDOWN) + { + switch ((int)pMsg->wParam) + { + case VK_ESCAPE: + OnClose(); + break; + default: + return CNetscapePropertySheet::PreTranslateMessage(pMsg); + break; + } + return TRUE; + } + return CNetscapePropertySheet::PreTranslateMessage(pMsg); +} + +BEGIN_MESSAGE_MAP(CAdvancedOptionsPropertySheet, CNetscapePropertySheet) +#ifndef _WIN32 + ON_WM_CREATE() +#endif + ON_WM_CLOSE() + ON_WM_DESTROY() +END_MESSAGE_MAP() diff --git a/mozilla/cmd/winfe/advprosh.h b/mozilla/cmd/winfe/advprosh.h new file mode 100644 index 00000000000..1d0fe207d74 --- /dev/null +++ b/mozilla/cmd/winfe/advprosh.h @@ -0,0 +1,137 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#ifndef ADVPROPSHEET_H +#define ADVPROPSHEET_H + +#include "property.h" +#include "msg_filt.h" + +#define WM_ADVANCED_OPTIONS_DONE (WM_USER + 1555) +#define WM_EDIT_CUSTOM_DONE (WM_USER + 1556) + +class CAdvancedOptionsPropertySheet; + +class CSearchOptionsPage : public CNetscapePropertyPage +{ +public: + CSearchOptionsPage(CWnd *pParent, UINT nID); + ~CSearchOptionsPage(); + + virtual BOOL OnInitDialog(); + virtual BOOL OnSetActive(); + virtual BOOL OnKillActive( ); + virtual void OnOK(); + virtual void OnCancel(); + + //{{AFX_DATA(CSearchOptionsPage) + enum { IDD = IDD_ADVANCED_SEARCH }; + BOOL m_bIncludeSubfolders; + int m_iSearchArea; + //}}AFX_DATA + + //{{AFX_VIRTUAL(CSearchOptionsPage) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +protected: + CWnd * m_pParent; + BOOL m_bHasBeenViewed; + BOOL m_bOffline; + + + DECLARE_MESSAGE_MAP() +}; + +class CCustomHeadersPage : public CNetscapePropertyPage +{ +public: + CCustomHeadersPage(CWnd *pParent, UINT nID); + ~CCustomHeadersPage(); + + virtual BOOL OnInitDialog(); + virtual BOOL OnSetActive(); + virtual BOOL OnKillActive(); + virtual void OnAddHeader(); + virtual void OnRemoveHeader(); + virtual void OnReplaceHeader(); + virtual void OnChangeEditHeader(); + virtual void OnSelChangeHeaderList(); + virtual void OnOK(); + virtual void OnCancel(); + + void updateUI(); + void enableDlgItem(int iId, BOOL bEnable); + + //{{AFX_DATA(CCustomHeadersPage) + enum { IDD = IDD_HEADER_LIST }; + //}}AFX_DATA + //{{AFX_VIRTUAL(CCustomHeadersPage) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +protected: + CWnd * m_pParent; + BOOL m_bHasBeenViewed; + + DECLARE_MESSAGE_MAP() +}; + +class CAdvancedOptionsPropertySheet : public CNetscapePropertySheet +{ +public: + + CAdvancedOptionsPropertySheet(CWnd *pParent, const char* pName, DWORD dwPagesBitFlag = 0L); + ~CAdvancedOptionsPropertySheet(); + + CNetscapePropertyPage * GetCurrentPage(); + + virtual void OnHelp(); + +protected: + CWnd * m_pParent; + int m_iReturnCode; + DWORD m_dwPagesBitFlag; + + CSearchOptionsPage * m_OptionsPage; + CCustomHeadersPage * m_HeadersPage; + + virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + virtual void OnNcDestroy(); + virtual void OnClose(); + virtual BOOL PreTranslateMessage(MSG * pMsg); + +#ifdef _WIN32 + virtual BOOL OnInitDialog(); +#else + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); +#endif + + virtual void OnDestroy(); + + DECLARE_MESSAGE_MAP() +}; + +// Advanced Options pages (bitflags) +#define AOP_NOPAGES 0x00000000 +#define AOP_SEARCH_OPTIONS 0x00000001 +#define AOP_CUSTOM_HEADERS 0x00000002 + +#endif // ADVPROPSHEET_H \ No newline at end of file diff --git a/mozilla/cmd/winfe/mkfiles32/mozilla.mak b/mozilla/cmd/winfe/mkfiles32/mozilla.mak index 2d09c31335c..1ac8679ad80 100644 --- a/mozilla/cmd/winfe/mkfiles32/mozilla.mak +++ b/mozilla/cmd/winfe/mkfiles32/mozilla.mak @@ -416,7 +416,16 @@ LINK_LIBS= \ $(DIST)\lib\netutil.lib \ $(DIST)\lib\network.lib \ $(DIST)\lib\cnetinit.lib \ +!ifdef MOZ_MAIL_NEWS + $(DIST)\lib\nntpurl.lib \ + $(DIST)\lib\smtpurl.lib \ + $(DIST)\lib\pop3url.lib \ + $(DIST)\lib\mailbxurl.lib \ + $(DIST)\lib\imap4url.lib \ + $(DIST)\lib\certurl.lib \ +!endif !ifdef MOZ_LDAP + $(DIST)\lib\ldapurl.lib \ !ifdef MOZ_JAVA $(DIST)\lib\nsldap32v30.lib \ !endif @@ -881,6 +890,8 @@ $(OUTDIR)\mozilla.dep: $(DEPTH)\cmd\winfe\mkfiles32\mozilla.mak $(DEPTH)\lib\libmime\mimedrft.c $(DEPTH)\lib\libmisc\mime.c $(DEPTH)\lib\libmisc\dirprefs.c +!endif +!ifdef MOZ_CALENDAR $(DEPTH)\lib\libmime\mimecal.c !endif @@ -923,9 +934,8 @@ $(OUTDIR)\mozilla.dep: $(DEPTH)\cmd\winfe\mkfiles32\mozilla.mak !ifdef MOZ_MAIL_NEWS $(DEPTH)\lib\libmsg\ad_strm.c + $(DEPTH)\lib\libmsg\addrutil.cpp $(DEPTH)\lib\libmsg\msgppane.cpp - $(DEPTH)\lib\libmsg\addr.c - $(DEPTH)\lib\libmsg\addrutil.c $(DEPTH)\lib\libmsg\ap_decod.c $(DEPTH)\lib\libmsg\ap_encod.c $(DEPTH)\lib\libmsg\appledbl.c @@ -1002,7 +1012,6 @@ $(OUTDIR)\mozilla.dep: $(DEPTH)\cmd\winfe\mkfiles32\mozilla.mak $(DEPTH)\lib\libmsg\thrhead.cpp $(DEPTH)\lib\libmsg\thrlstst.cpp $(DEPTH)\lib\libmsg\thrnewvw.cpp - $(DEPTH)\lib\libmsg\mozdb.cpp !endif !ifndef MOZ_NGLAYOUT @@ -2458,6 +2467,17 @@ BUILD_SOURCE: $(OBJ_FILES) !ifdef MOZ_MAIL_NEWS $(DIST)\lib\mnrc16.lib + !endif +!ifdef MOZ_MAIL_NEWS + $(DIST)\lib\nntpurl.lib + + $(DIST)\lib\smtpurl.lib + + $(DIST)\lib\pop3url.lib + + $(DIST)\lib\mailbxurl.lib + + $(DIST)\lib\imap4url.lib + + $(DIST)\lib\certurl.lib + +!endif +!ifdef MOZ_LDAP + $(DIST)\lib\ldapurl.lib + +!endif !ifdef MOZ_CALENDAR $(DIST)\lib\cal3240.lib + $(DIST)\lib\nsfmt3230.lib + diff --git a/mozilla/cmd/winfe/mozilla.cpp b/mozilla/cmd/winfe/mozilla.cpp index 5143cc47cb6..6f751619145 100644 --- a/mozilla/cmd/winfe/mozilla.cpp +++ b/mozilla/cmd/winfe/mozilla.cpp @@ -1584,6 +1584,7 @@ BOOL CNetscapeApp::InitInstance() } if (ldapFile) XP_FREE(ldapFile); +#ifndef MOZ_NEWADDR DIR_Server* pab = NULL; DIR_GetPersonalAddressBook (theApp.m_directories, &pab); @@ -1592,6 +1593,7 @@ BOOL CNetscapeApp::InitInstance() msg = m_UserDirectory; msg += "\\address.htm"; AB_InitializeAddressBook(pab, &m_pABook, (const char *) msg); +#endif #endif /* MOZ_LDAP */ // Initialize slave URL loading context. @@ -2270,8 +2272,10 @@ int CNetscapeApp::ExitInstance() NET_ShutdownNetLib(); #ifdef MOZ_MAIL_NEWS +#ifndef MOZ_LITE if (m_pABook) AB_CloseAddressBook(&m_pABook); +#endif WFE_MSGShutdown(); #endif /* MOZ_MAIL_NEWS */ diff --git a/mozilla/cmd/winfe/mozilla.rc b/mozilla/cmd/winfe/mozilla.rc index 8eca3443ada..c8f962ca2e4 100644 --- a/mozilla/cmd/winfe/mozilla.rc +++ b/mozilla/cmd/winfe/mozilla.rc @@ -1014,6 +1014,11 @@ BEGIN EDITTEXT IDC_EDIT_HOST,34,8,142,14,ES_AUTOHSCROLL END +// XXX - spider - the resource linker is blowing up thinking id 230 is duplicate, +// but I cannot find it. Can the module owner reviewing this code +// help me track down this dup id. The resource compiler is happy, not +// even an error, but the linker dies with corrupt file +#ifndef MOZ_MAIL_NEWS IDD_ADDRESSBOOK DIALOG DISCARDABLE 0, 0, 332, 34 STYLE WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN FONT 8, "MS Sans Serif" @@ -1025,6 +1030,7 @@ BEGIN EDITTEXT IDC_ADDRNAME,134,16,133,13,ES_AUTOHSCROLL PUSHBUTTON "Search...",IDC_DIRSEARCH,277,16,50,13 END +#endif 1536 DIALOG DISCARDABLE 0, 0, 181, 132 @@ -2261,7 +2267,6 @@ BEGIN TOPMARGIN, 7 BOTTOMMARGIN, 64 END -END IDD_NETPROFILE_LOGIN, DIALOG BEGIN diff --git a/mozilla/cmd/winfe/nabapi.h b/mozilla/cmd/winfe/nabapi.h new file mode 100644 index 00000000000..1871a1f7715 --- /dev/null +++ b/mozilla/cmd/winfe/nabapi.h @@ -0,0 +1,491 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#ifndef __NABAPI_H__ +#define __NABAPI_H__ + +/******************************************************** + * Structures, Type Definitions & Return Codes + ********************************************************/ + +#ifndef _UINT_16 +typedef unsigned short UINT_16; +#endif +#ifndef _UINT_32 +typedef unsigned long UINT_32; +#endif +#ifndef _INT_16 +typedef short INT_16; +#endif +#ifndef _INT_32 +typedef long INT_32; +#endif + +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (1) +#endif +#ifndef NULL +#define NULL (0L) +#endif + +typedef unsigned char NABBool; +typedef long NABError; +typedef short NABReason; +typedef long NABAddrBookID; +typedef long NABUserID; +typedef long NABFieldMask; +typedef unsigned long NABUpdateTime; +typedef unsigned long NABConnectionID; + +typedef enum { + NAB_SUCCESS, /* The API call succeeded. */ + NAB_FAILURE, /* This is a generic API failure. */ + NAB_INVALID_CONNID, /* Invalid Connection ID for request */ + NAB_NOT_OPEN, /* This will be returned when a call is made to the API without NAB_Open() being called first. */ + NAB_NOT_FOUND, /* The requested information was not found. */ + NAB_PERMISSION_DENIED, /* User does not have write permission to perform the operation */ + NAB_DISK_FULL, /* Disk full condition encountered attempting to perform the operation */ + NAB_INVALID_NAME, /* The name specified for the operation is invalid */ + NAB_INVALID_ABOOK, /* The address book specified is invalid */ + NAB_FILE_NOT_FOUND, /* The disk file for the operation was not found */ + NAB_ALREADY_EXISTS, /* The user entry already exists in the address book being updated */ + NAB_MAXCON_EXCEEDED,/* The maximum number of API connections has been exceeded */ + NAB_MEMORY_FAILURE, /* Memory condition, such as malloc() failures */ + NAB_FILE_ERROR, /* Error on file creation */ + NAB_INVALID_ENTRY, /* Invalid entry for operation */ + NAB_MULTIPLE_USERS_FOUND, /* More than one person found with current search criteria */ + NAB_INVALID_SEARCH_ATTRIB /* Invalid search criteria */ +} NAB_ERROR_CODE_TYPES; + +typedef struct +{ + char *description; // Description of address book - MUST BE UNIQUE + char *fileName; // The file name of the address book +} NABAddrBookDescType; + +// Used to separate return values for the LDIF formatted lines +#define NAB_CRLF "\r\n" + +/******************************************************** + * Initialization Calls + ********************************************************/ +/* + * NAB_Open() will start the connection to the API. It will initialize any necessary + * internal variables and return the major and minor version as defined here. This + * will allow client applications to verify the version they are running against. + * + * Return Value: + * NAB_SUCCESS - The API was opened successfully and is ready to process other API calls. + * NAB_FAILURE - The API open operation failed. + * NAB_MAXCON_EXCEEDED - The maximum number of connections to the API has been exceeded. + * + * Parameters: + * id - the pointer to a connection ID that will be filled out by the open call. This needs + * to be used in other API calls. ZERO is invalid. + * majorVerNumber - this is a pointer to a UNIT_16 for the MAJOR version number of the API + * minorVerNumber - this is a pointer to a UNIT_16 for the MINOR version number of the API + */ +NABError NAB_Open(NABConnectionID *id, UINT_16 *majorVerNumber, UINT_16 *minorVerNumber); + +/* + * This closes the connection to the API. Any subsequent call to the NABAPI will fail with + * an NAB_NOT_OPEN return code. + * + * Return Value: + * NAB_SUCCESS - The API was opened successfully and is ready to process other API calls. + * NAB_FAILURE - The API open operation failed. + * Parameters: + * id - the connection ID returned by the NAB_Open() call + */ +NABError NAB_Close(NABConnectionID id); + +/******************************************************** + * Address Book Information Calls + ********************************************************/ + +/* + * This call will return a list of pointers to the list of available address + * books in NABAddrBookDescType format. The memory for this array will be allocated + * by the NABAPI and the caller is responsible for freeing the memory with a call + * to the NAB_FreeMemory() function call. NOTE: The call to NAB_FreeMemory() must be + * made for each entry in the returned array and not just a pointer to the array itself. + * The number of address books found will also be returned in the abookCount + * argument to the call. If the returned list is NULL or the abookCount value is + * less than or equal to ZERO, no address books were found. + * + * Return Value: + * NAB_SUCCESS - The operation succeeded and the next entry was found. + * NAB_FAILURE - General failure getting the next address book entry + * NAB_NOT_FOUND - No address books were found + * NAB_NOT_OPEN - This will be returned when a call is made to the API without NAB_Open() being called first. + * NAB_INVALID_CONNID - Invalid connection ID + * + * Parameters: + * id - the connection ID returned by the NAB_Open() call + * abookCount - a pointer to a UINT_32 where the API will store the number of + * address books found and returned to the caller + * aBooks - A pointer to an array of NABAddrBookDescType structures. If no entries are + * available, a NULL list is returned to the caller. + */ +NABError NAB_GetAddressBookList(NABConnectionID id, UINT_32 *abookCount, NABAddrBookDescType *aBooks[]); + +/* + * This call will return the default address book for the client. If the + * returned value is NULL, no default address book was found. The memory + * for this return value will be allocated by the ABAPI and the caller is + * responsible for freeing the memory with a call to the NAB_FreeMemory() + * function call. + * + * Return Value: + * NAB_SUCCESS - The operation succeeded and the next entry was found. + * NAB_FAILURE - General failure getting the next address book entry + * NAB_NOT_FOUND - No address books were found + * NAB_NOT_OPEN - This will be returned when a call is made to the API without NAB_Open() being called first. + * NAB_INVALID_CONNID - Invalid connection ID + * + * Parameters: + * id - the connection ID returned by the NAB_Open() call + * aBook - A pointer to an NABAddrBookDescType structure. If no default address + * book is found, NULL returned to the caller. + */ +NABError NAB_GetDefaultAddressBook(NABConnectionID id, NABAddrBookDescType **aBook); + +/* + * This call will create a local/personal address book with the name given. + * + * Return Value: + * NAB_SUCCESS - The personal address book was created successfully. + * NAB_FAILURE - General failure to create the address book + * NAB_NOT_OPEN - This will be returned when a call is made to the API without NAB_Open() being called first. + * NAB_PERMISSION_DENIED - User does not have write permission to create an address book + * NAB_DISK_FULL - Disk full condition encountered attempting to create address book + * NAB_INVALID_NAME - The name for the address book is invalid or already exists + * NAB_INVALID_ABOOK - Address book specified is invalid + * + * Parameters: + * id - the connection ID returned by the NAB_Open() call + * addrBookName - the description name for the address book to be created + * addrBook - a pointer to the addrBook entry that will be populated by the API for the newly created API - if + * the create call fails, this pointer is set to NULL - + * [memory allocated by API and must be freed by a call to NAB_FreeMemory()] + */ +NABError NAB_CreatePersonalAddressBook(NABConnectionID id, char *addrBookName, NABAddrBookDescType **addrBook); + +/******************************************************** + * Memory Handling Calls + ********************************************************/ + +/* + * This call frees memory allocated by the NABAPI. + * + * Return Value: + * NAB_SUCCESS - The memory pointed to by ptr was freed successfully. + * NAB_FAILURE - The ptr parameter was NULL or was unable to be free'd + * + * Parameters: + * ptr - this is a pointer to the memory allocated by the NABAPI. + */ +NABError NAB_FreeMemory(void *ptr); + +/******************************************************** + * Utility Calls + ********************************************************/ + +/* + * This will save the address book specified by the addrBook parameter into a + * table formatted, HTML file. The fields to be saved should be passed into + * the call via the ldifFields parameter. + * + * Return Value: + * NAB_SUCCESS - The address book was saved to an HTML file. + * NAB_FAILURE - General failure to create save the address book + * NAB_NOT_OPEN - This will be returned when a call is made to the API + * without NAB_Open() being called first. + * NAB_PERMISSION_DENIED - User does not have write permission to create + * an address book + * NAB_DISK_FULL - Disk full condition encountered attempting to create address book + * NAB_INVALID_NAME - The name for the address book is invalid or already exists + * NAB_INVALID_ABOOK - Address book specified is invalid + * + * Parameters: + * id - the connection ID returned by the NAB_Open() call + * addrBook - a pointer to a NABAddrBookDescType structure for the particular + * address book for the operation + * fileName - the name for the output HTML file on disk + */ +NABError NAB_SaveAddressBookToHTML(NABConnectionID id, NABAddrBookDescType *addrBook, + char *fileName); + +/* + * This call will import an LDIF formatted file from a disk file to the address + * book specified by the addrBook parameter. + * + * Return Value: + * NAB_SUCCESS - The import operation succeeded. + * NAB_FAILURE - General failure to during the import operation + * NAB_NOT_OPEN - This will be returned when a call is made to the API without + * NAB_Open() being called first. + * NAB_PERMISSION_DENIED - User does not have write permission to the address + * book or to read the import disk file + * NAB_DISK_FULL - Disk full condition encountered attempting to import the address book + * NAB_INVALID_ABOOK - Address book specified is invalid + * NAB_FILE_NOT_FOUND - The disk file was not found + * + * Parameters: + * id - the connection ID returned by the NAB_Open() call + * addrBook - a pointer to a NABAddrBookDescType structure for the particular address + * book for the operation + * fileName - the disk file name for the import operation + * deleteFileWhenFinished - if this is true, the temp file will be deleted upon exit + */ +NABError NAB_ImportLDIFFile(NABConnectionID id, NABAddrBookDescType *addrBook, + char *fileName, NABBool deleteFileWhenFinished); + +/* + * This call will export the contents of an address book specified by the + * addrBook parameter to an LDIF formatted disk file. + * + * Return Value: + * NAB_SUCCESS - The export operation succeeded. + * NAB_FAILURE - General failure to during the export operation + * NAB_NOT_OPEN - This will be returned when a call is made to the API + * without NAB_Open() being called first. + * NAB_PERMISSION_DENIED - User does not have write permission for the output + * disk file + * NAB_DISK_FULL - Disk full condition encountered attempting to export the + * address book + * NAB_INVALID_ABOOK - Address book specified is invalid + * + * Parameters: + * id - the connection ID returned by the NAB_Open() call + * addrBook - a pointer to a NABAddrBookDescType structure for the particular address book + * for the operation + * fileName - the disk file name for the export operation + */ +NABError NAB_ExportLDIFFile(NABConnectionID id, NABAddrBookDescType *addrBook, char *fileName); + +/* + * This is a convenience call that will format an LDIF string for use with calls such + * as NAB_InsertAddressBookEntry() and NAB_UpdateAddressBookEntry(). The memory for + * the line is allocated by the NAB_FormatLDIFLine() and must be freed by the caller + * with the NAB_FreeMemory() call. + * + * Return Value: + * NULL - the call failed for one of the following reasons: + * + * - There were not valid parameters passed into the call + * - The memory allocation failed + * + * char * - a pointer to the allocated string + * + * Parameters: + * firstName (sn) - persons first name + * lastName (givenname) - last name + * generalNotes (description) - general notes for entry + * city (locality) - City + * state (st) - State + * email (mail) - Email Address + * title (title) - Job Title or position + * addrLine1 (postOfficeBox) - Address line #1 + * addrLine2 (streetaddress) - Address line #2 + * zipCode (postalcode) - Zip Code + * country (countryname) - Country Name + * businessPhone (telephonenumber) - Business Telephone Number + * faxPhone (facsimiletelephonenumber) - FAX Number + * homePhone (homephone) - Home Phone Number + * organization (o) - Organization or company name + * nickname (xmozillanickname) - Communicator Nickname + * useHTML (xmozillausehtmlmail) - Preference for HTML composition + */ +char *NAB_FormatLDIFLine(char *firstName, + char *lastName, + char *generalNotes, + char *city, + char *state, + char *email, + char *title, + char *addrLine1, + char *addrLine2, + char *zipCode, + char *country, + char *businessPhone, + char *faxPhone, + char *homePhone, + char *organization, + char *nickname, + char *useHTML); + +/******************************************************** + * Individual Entry Calls + ********************************************************/ + +/* + * This call will get the User ID for the first person in the specified address + * book and it will also return the attributes for the user in the ldifEntry field. The memory for + * this field must be freed by a call to NAB_FreeMemory(). The addrBook argument is + * passed back via the calls that return NABAddrBookDescType structures. + * The call will also return the time this entry was last updated via the NABUpdateTime + * paramenter. The memory for this structure should be allocated by the + * calling application. It will return NAB_SUCCESS if an entry was found or NAB_NOT_FOUND + * if no entries are found. If a user is found, ID for the user returned is + * stored in the userID argument. + * + * Return Value: + * NAB_SUCCESS - The operation succeeded and an entry was found. + * NAB_FAILURE - General failure getting the first address book entry + * NAB_NOT_OPEN - This will be returned when a call is made to the API without + * NAB_Open() being called first. + * NAB_INVALID_ABOOK - Address book specified is invalid + * + * Parameters: + * id - the connection ID returned by the NAB_Open() call + * addrBook - a pointer to a NABAddrBookDescType structure for the particular + * address book for the operation + * userID - a pointer to a NABUserID value that will be filled out by the API with the ID + * for the found user + * ldifEntry - the ldif formatted information for the found user [memory allocated by API + * and must be freed by a call to NAB_FreeMemory()] + * time - a pointer to the the NABUpdateTime value for the time of last update for the individual entry + */ +NABError NAB_GetFirstAddressBookEntry(NABConnectionID id, NABAddrBookDescType *addrBook, NABUserID *userID, + char **ldifEntry, NABUpdateTime *updTime); + +/* + * This call will get the next address book entry from the specified address book and it will also + * return the attributes for the user in the ldifEntry field. The memory for + * this field must be freed by a call to NAB_FreeMemory(). The addrBook argument is passed back + * via the calls that return NABAddrBookDescType structures. The call will also return the time this + * entry was last updated via the NABUpdateTime paramenter. The memory for this structure should be allocated by the + * calling application. It will return NAB_SUCCESS if an entry was found or NAB_NOT_FOUND if the + * final entry has already been returned. If a user is found, ID for the user returned is stored + * in the userID argument. + * + * Return Value: + * NAB_SUCCESS - The operation succeeded and the next entry was found. + * NAB_FAILURE - General failure getting the next address book entry + * NAB_NOT_FOUND - The final entry was already returned + * NAB_NOT_OPEN - This will be returned when a call is made to the API without NAB_Open() being called first. + * NAB_INVALID_ABOOK - NAB_GetFirstAddressBookEntry() probably wasn't called + * + * Parameters: + * id - the connection ID returned by the NAB_Open() call + * userID - a pointer to a NABUserID value that will be filled out by the API with the ID for the found user + * ldifEntry - the ldif formatted information for the found user [memory allocated by API and must + * be freed by a call to NAB_FreeMemory()] + * time - a pointer to the the NABUpdateTime value for the time of last update for the individual entry + */ +NABError NAB_GetNextAddressBookEntry(NABConnectionID id, NABUserID *userID, + char **ldifEntry, NABUpdateTime *updTime); + +/* + * This call will perform a query on the specified ldif attribute pair. The memory for the ldifEntry + * will be allocated by the API and must be freed by a call to NAB_FreeMemory(). The addrBook argument + * is passed back via the calls that return NABAddrBookDescType structures. The call will also return the time + * this entry was last updated via the NABUpdateTime paramenter. The memory for this structure should be + * allocated by the calling application. It will return NAB_SUCCESS if an entry was found or NAB_NOT_FOUND + * if the search failed. + * + * Return Value: + * NAB_SUCCESS - The operation succeeded and the requested entry was found. + * NAB_FAILURE - General failure performing the query + * NAB_NOT_FOUND - The requested entry was not found + * NAB_NOT_OPEN - This will be returned when a call is made to the API without NAB_Open() being called first. + * NAB_INVALID_ABOOK - Address book specified is invalid + * + * Parameters: + * id - the connection ID returned by the NAB_Open() call + * addrBook - a pointer to a NABAddrBookDescType structure for the particular address book for the operation + * ldifSearchAttribute - the attribute/value pairing for the search operation + * ldifEntry - the ldif formatted information for the user entry found [memory allocated by API and + * must be freed by a call to NAB_FreeMemory()] + * time - a pointer to the the NABUpdateTime value for the time of last update for the individual entry + */ +NABError NAB_FindAddressBookEntry(NABConnectionID id, NABAddrBookDescType *addrBook, + NABUserID *userID, char *ldifSearchAttribute, + char **ldifEntry, NABUpdateTime *updTime); + +/* + * This call will insert an individual address book entry (in LDIF format) into the (addrBookID) address + * book. The addrBook argument is passed back via the calls that return NABAddrBookDescType structures. + * If this user already exists in the specified address book, the operation will fail. + * + * Return Value: + * NAB_SUCCESS - The insert operation succeeded. + * NAB_FAILURE - General failure to during the insert operation + * NAB_NOT_OPEN - This will be returned when a call is made to the API without NAB_Open() being called first. + * NAB_PERMISSION_DENIED - User does not have write permission to the address book to perform the insertion operation + * NAB_DISK_FULL - Disk full condition encountered attempting to insert the entry into the address book + * NAB_INVALID_ABOOK - Address book specified is invalid + * NAB_ALREADY_EXISTS - The user entry already exists in the address book + * + * Parameters: + * id - the connection ID returned by the NAB_Open() call + * addrBook - a pointer to a NABAddrBookDescType structure for the particular address book for the operation + * ldifEntry - the entry to be inserted into the identified address book + * userID - a pointer to a NABUserID value that will be filled out by the API with the ID for the new user + */ +NABError NAB_InsertAddressBookEntry(NABConnectionID id, NABAddrBookDescType *addrBook, char *ldifEntry, NABUserID *userID); + +/* This call will update the specified address book entry in the specified address book with the + * information passed in via the LDIF formatted entry. Attributes that exists will be replaced by the + * new entry and new attributes will be added to the user record. The addrBook argument is passed back + * via the calls that return NABAddrBookDescType structures. If the entry is not found, the call will fail. + * + * Return Value: + * NAB_SUCCESS - The update operation succeeded. + * NAB_FAILURE - General failure to during the update operation + * NAB_NOT_OPEN - This will be returned when a call is made to the API without NAB_Open() being called first. + * NAB_PERMISSION_DENIED - User does not have write permission to the address book to perform the update operation + * NAB_DISK_FULL - Disk full condition encountered attempting to update the entry into the address book + * NAB_INVALID_ABOOK - Address book specified is invalid + * NAB_NOT_FOUND - The user entry to update does not exist in the address book + * + * Parameters: + * id - the connection ID returned by the NAB_Open() call + * addrBook - a pointer to a NABAddrBookDescType structure for the particular address book for the operation + * userID - the specific user ID for the entry to be updated + * ldifEntry - the attribute pairs to be updated in the identified address book + */ +NABError NAB_UpdateAddressBookEntry(NABConnectionID id, NABAddrBookDescType *addrBook, NABUserID userID, + char *ldifEntry); + +/* + * This call will delete the specified address book entry in the specified address book. The addrBook + * argument is passed back via the calls that return NABAddrBookDescType structures. If the entry is + * not found, the call will fail. + * + * Return Value: + * NAB_SUCCESS - The delete operation succeeded. + * NAB_FAILURE - General failure to during the delete operation + * NAB_NOT_OPEN - This will be returned when a call is made to the API without NAB_Open() being called first. + * NAB_PERMISSION_DENIED - User does not have write permission to the address book to perform the delete operation + * NAB_DISK_FULL - Disk full condition encountered attempting to delete the entry into the address book + * NAB_INVALID_ABOOK - Address book specified is invalid + * NAB_NOT_FOUND - The user entry to delete does not exist in the address book + * + * Parameters: + * id - the connection ID returned by the NAB_Open() call + * addrBook - a pointer to a NABAddrBookDescType structure for the particular address book for the operation + * userID - the specific user ID for the entry to be updated + */ +NABError NAB_DeleteAddressBookEntry(NABConnectionID id, NABAddrBookDescType *addrBook, NABUserID userID); + + +#endif // ifdef __NABAPI_H__ diff --git a/mozilla/cmd/winfe/offlndlg.cpp b/mozilla/cmd/winfe/offlndlg.cpp new file mode 100644 index 00000000000..9c782442400 --- /dev/null +++ b/mozilla/cmd/winfe/offlndlg.cpp @@ -0,0 +1,588 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "stdafx.h" +#include "mailpriv.h" +#include "wfemsg.h" +#include "prefapi.h" +#include "nethelp.h" +#include "xp_help.h" +#include "offpkdlg.h" +#include "offlndlg.h" + + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// COfflineInfo +// Note bGoOffline is true if we need to go offline, false if we need +// to go online +// bChangeState is TRUE if the bGoOffline parameter is to be used, +// FALSE if it will be ignored. +COfflineInfo::COfflineInfo(BOOL bDownloadMail, BOOL bDownloadNews, + BOOL bDownloadDirectories, + BOOL bSendMail, BOOL bDownloadFlagged, + BOOL bGoOffline, BOOL bChangeState) +{ + m_bDownloadMail = bDownloadMail; + m_bDownloadNews = bDownloadNews; + m_bDownloadDirectories = bDownloadDirectories; + m_bSendMail = bSendMail; + m_bDownloadFlagged = bDownloadFlagged; + m_bGoOffline = bGoOffline; + m_bChangeState = bChangeState; +} + +BOOL COfflineInfo::DownloadMail() +{ + return m_bDownloadMail; +} + +BOOL COfflineInfo::DownloadNews() +{ + return m_bDownloadNews; +} + +BOOL COfflineInfo::DownloadDirectories() +{ + return m_bDownloadDirectories; +} + +BOOL COfflineInfo::SendMail() +{ + return m_bSendMail; +} + +BOOL COfflineInfo::DownloadFlagged() +{ + return m_bDownloadFlagged; +} + +BOOL COfflineInfo::GoOffline() +{ + return m_bGoOffline; +} + +BOOL COfflineInfo::ChangeState() +{ + return m_bChangeState; +} +///////////////////////////////////////////////////////////////////////////// +// COfflineDlg dialog + + +COfflineDlg::COfflineDlg(CWnd* pParent /*=NULL*/) + : CDialog(COfflineDlg::IDD, pParent) +{ + XP_Bool bDiscussions = 0; + XP_Bool bMail = 0; + XP_Bool bMessages = 0; + XP_Bool bDirectories = 0; + XP_Bool bFlagged = 0; + XP_Bool bGoOffline = 0; + + PREF_GetBoolPref("offline.download_discussions",&bDiscussions); + PREF_GetBoolPref("offline.download_mail",&bMail); + PREF_GetBoolPref("offline.download_messages",&bMessages); + PREF_GetBoolPref("offline.download_directories",&bDirectories); +// PREF_GetBoolPref("offline.download_flagged", &bFlagged); + PREF_GetBoolPref("offline.download_gooffline",&bGoOffline); + + + + m_bDownLoadDiscussions = bDiscussions; + m_bDownLoadMail = bMail; + m_bSendMessages = bMessages; + m_bDownLoadDirectories = bDirectories; +// m_bDownLoadFlagged = bFlagged; + m_bGoOffline = bGoOffline; + + m_bMode = NET_IsOffline(); +} + +void COfflineDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Check(pDX, IDC_DWNLD_CHECK_MAIL, m_bDownLoadMail); + DDX_Check(pDX, IDC_DWNLD_CHECK_DISCUSSIONS, m_bDownLoadDiscussions); + DDX_Check(pDX, IDC_DWNLD_CHECK_DIRECTORIES, m_bDownLoadDirectories); + DDX_Check(pDX, IDC_DWNLD_CHECK_SENDMAIL, m_bSendMessages); +// DDX_Check(pDX, IDC_DOWNLOADFLAGGED, m_bDownLoadFlagged); + DDX_Check(pDX, IDC_DWNLD_CHECK_GO_OFFLINE, m_bGoOffline); + +} + + +BEGIN_MESSAGE_MAP(COfflineDlg, CDialog) + //{{AFX_MSG_MAP(COfflineDlg) + ON_BN_CLICKED(IDC_DWNLD_BUTTON_SELECT, OnButtonSelect) + ON_BN_CLICKED(IDC_DWNLD_HELP_GO_OFFLINE, OnHelp) + ON_WM_PAINT() + ON_WM_DESTROY() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COfflineDlg message handlers + +void COfflineDlg::OnButtonSelect() +{ + CDlgOfflinePicker OfflinePickerDlg(this); + OfflinePickerDlg.DoModal(); + /* + CDlgSelectGroups dlgSelectGroups(this); + dlgSelectGroups.DoModal(); + CWnd *pWnd = GetDlgItem(IDC_TEXT_DISCUSSIONS_SELECTED); + CString strCountText; + CString strTemp; + + if (pWnd) + { + strTemp.LoadString(IDS_TEXT_DISCUSSIONS_SELECTED); + char buf[20]; + strCountText = itoa(dlgSelectGroups.GetSelectionCount(),buf,10) + (CString)" "+ strTemp; + pWnd->SetWindowText(strCountText); + } + + pWnd = GetDlgItem(IDC_TEXT_FOLDERS_SELECTED); + if (pWnd) + { + strTemp.LoadString(IDS_TEXT_FOLDERS_SELECTED); + char buf[20]; + strCountText = itoa(dlgSelectGroups.GetMailSelectionCount(),buf,10) + (CString)" "+ strTemp; + pWnd->SetWindowText(strCountText); + } + */ +} + +void COfflineDlg::ShutDownFrameCallBack(HWND hwnd, MSG_Pane *pane, void * closure) +{ + if (::IsWindow(hwnd)) { + ::ShowWindow(hwnd,SW_SHOW); + ::UpdateWindow(hwnd); + } + + if (pane) + { + COfflineInfo *info = (COfflineInfo *) closure; + + + BOOL bGoOffline = info->ChangeState() && info->GoOffline(); + MSG_SynchronizeOffline(WFE_MSGGetMaster(), pane, info->DownloadNews(), + info->DownloadMail(), info->SendMail(), info->DownloadDirectories(), + bGoOffline); + + } +} + +BOOL COfflineDlg::ShowOnlineCallBack(HWND hWnd, MSG_Pane *pane, void * closure) +{ + BOOL bSendMail = FALSE; + if (pane) + { + COfflineInfo *info = (COfflineInfo *) closure; + + bSendMail = info->SendMail(); + + //handle going online: + if(info->ChangeState() && !info->GoOffline()) + { + + XP_Bool bSelectable; + MSG_COMMAND_CHECK_STATE selected; + const char * pString; + XP_Bool bPlural; + + MSG_CommandStatus (pane,MSG_DeliverQueuedMessages, + NULL, 0,&bSelectable, &selected, &pString, + &bPlural); + int32 prefInt; + + + PREF_GetIntPref("offline.send.unsent_messages", &prefInt); + + //check for unsent messages. + if(prefInt == 0) + { + bSendMail = FALSE; + if(bSelectable) + { + int result = ::MessageBox(hWnd, ::szLoadString(IDS_SENDONLINE), + ::szLoadString(IDS_GOONLINE), + MB_YESNO | MB_APPLMODAL); + + bSendMail = (result == IDYES); + } + + } + else if (prefInt == 1) + { + //if there's mail send it otherwise don't. + bSendMail = bSelectable; + } + else + { + bSendMail = FALSE; + } + } + + + } + //if bSendMail is true we want to bring up a progress pane + return bSendMail; +} + +void WFE_Synchronize(CWnd *pParent, BOOL bExitAfterSynchronizing) +{ + if(bExitAfterSynchronizing) + { + theApp.HideFrames(); + } + + BOOL bOnline; + COfflineDlg rDlg(pParent); + PREF_GetBoolPref("network.online", &bOnline); + rDlg.InitDialog(!bOnline); + int result = rDlg.DoModal(); + + if(result == IDOK && rDlg.DownloadItems()) + { + //note we only change state if we go offline. + COfflineInfo info(rDlg.m_bDownLoadMail, rDlg.m_bDownLoadDiscussions, + rDlg.m_bDownLoadDirectories, + rDlg.m_bSendMessages, FALSE, + rDlg.m_bGoOffline, rDlg.m_bGoOffline); + + theApp.m_bSynchronizing = TRUE; + new COfflineProgressDialog(pParent, NULL,COfflineDlg::ShutDownFrameCallBack, + &info,szLoadString(IDS_SYNCHRONIZING), NULL, bExitAfterSynchronizing); + } + else + { + if(bExitAfterSynchronizing) + { + theApp.CommonAppExit(); + } + } +} + +/* rhp - This is for doing MAPI Synchronizing... */ +void +WFE_MAPISynchronize(CWnd *pParent) +{ + BOOL bOnline; + COfflineDlg rDlg(pParent); + PREF_GetBoolPref("network.online", &bOnline); + rDlg.InitDialog(!bOnline); + // int result = rDlg.DoModal(); - no need for showing dialog... + + // we only change state if we go offline. + COfflineInfo info(rDlg.m_bDownLoadMail, rDlg.m_bDownLoadDiscussions, + rDlg.m_bDownLoadDirectories, + rDlg.m_bSendMessages, FALSE, + rDlg.m_bGoOffline, rDlg.m_bGoOffline); + + theApp.m_bSynchronizing = TRUE; + new COfflineProgressDialog(pParent, NULL,COfflineDlg::ShutDownFrameCallBack, + &info,szLoadString(IDS_SYNCHRONIZING), NULL, FALSE); +} + +/* + * Return if there are items to download based on this dialog's current settings + */ +BOOL COfflineDlg::DownloadItems() +{ + + return((m_bDownLoadDiscussions || m_bDownLoadMail || m_bSendMessages || + m_bDownLoadDirectories )); + +} + +void COfflineDlg::OnOK() +{ + // TODO: Add extra validation here + UpdateData(); + +/* if ((m_bDownLoadDiscussions || m_bDownLoadMail || m_bSendMessages || + m_bDownLoadDirectories )) + { + new CProgressDialog(this, NULL,ShutDownFrameCallBack, + this,szLoadString(IDS_DOWNLOADINGARTICLES)); // need correct window title + ;//DownLoad!!!!!!! + } + else + { + PREF_SetBoolPref("network.online", m_bMode); + } +*/ + if (!m_bDownLoadDiscussions && !m_bDownLoadMail && !m_bDownLoadDirectories && !m_bSendMessages) + PREF_SetBoolPref("network.online", !m_bGoOffline); + + PREF_SetBoolPref("offline.download_discussions",(XP_Bool)m_bDownLoadDiscussions); + PREF_SetBoolPref("offline.download_mail",(XP_Bool)m_bDownLoadMail); + PREF_SetBoolPref("offline.download_directories",(XP_Bool)m_bDownLoadDirectories); + PREF_SetBoolPref("offline.download_messages",(XP_Bool)m_bSendMessages); +// PREF_SetBoolPref("offline.download_flagged", (XP_Bool)m_bDownLoadFlagged); + + CDialog::OnOK(); +} + +void COfflineDlg::OnHelp() +{ + NetHelp(HELP_MAILNEWS_SYNCHRONIZE); +} + +int COfflineDlg::DoModal() +{ + if (!m_MNResourceSwitcher.Initialize()) + return -1; + return CDialog::DoModal(); +} + +BOOL COfflineDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + m_MNResourceSwitcher.Reset(); + + CWnd *pWnd1 = GetDlgItem(IDC_DWNLD_STATIC_MGD); + if (!pWnd1 ) + return FALSE; + + HFONT hFont = (HFONT)this->SendMessage(WM_GETFONT); + if (hFont != NULL) + { //make the title bold + VERIFY(::GetObject(hFont, sizeof(LOGFONT), &m_LogFont)); + m_LogFont.lfWeight=FW_BOLD; + m_Font.CreateFontIndirect(&m_LogFont); + + pWnd1->SetFont(&m_Font); + } + + return TRUE; // return TRUE unless you set the focus to a control +} + + + +void COfflineDlg::OnDestroy( ) +{ + int i = 0; +} + + + +/////////////////////////////////////////////////////////////////////// +////CGoOfflineAndSynchDlg + +CGoOfflineAndSynchDlg::CGoOfflineAndSynchDlg(CWnd* pParent /*=NULL*/) + : CDialog(CGoOfflineAndSynchDlg::IDD, pParent) +{ + XP_Bool bDiscussions = 0; + XP_Bool bMail = 0; + XP_Bool bMessages = 0; +#ifdef WIN32 + XP_Bool bPages = 0; + XP_Bool bChannels = 0; +#endif + XP_Bool bDirectories = 0; + XP_Bool bGoOffline = 0; + + PREF_GetBoolPref("offline.download_discussions",&bDiscussions); + PREF_GetBoolPref("offline.download_mail",&bMail); + PREF_GetBoolPref("offline.download_messages",&bMessages); + +#ifdef WIN32 + PREF_GetBoolPref("offline.download_pages",&bPages); + PREF_GetBoolPref("offline.download_channels",&bChannels); +#endif + PREF_GetBoolPref("offline.download_directories",&bDirectories); + PREF_GetBoolPref("offline.download_gooffline",&bGoOffline); + + m_bDownLoadDiscussions = bDiscussions; + m_bDownLoadMail = bMail; + m_bSendMessages = bMessages; + +#ifdef WIN32 + m_bDownLoadPages = bPages; + m_bDownLoadChannels = bChannels; +#endif + m_bDownLoadDirectories = bDirectories; + m_bGoOffline = bGoOffline; + + m_bMode = NET_IsOffline();//get offline mode +} + +BEGIN_MESSAGE_MAP(CGoOfflineAndSynchDlg, CDialog) + //{{AFX_MSG_MAP(CGoOfflineAndSynchDlg) + ON_BN_CLICKED(IDCANCEL, OnCancel) + ON_BN_CLICKED(IDC_HELP_DOWNLOAD, OnHelp) + ON_BN_CLICKED(IDC_BUTTON_NO, OnNo) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COfflineDlg message handlers + +void CGoOfflineAndSynchDlg::OnCancel() +{ + CDialog::OnCancel(); +} + +void CGoOfflineAndSynchDlg::OnNo() +{ + PREF_SetBoolPref("network.online", m_bMode); + CDialog::OnOK(); +} + +void CGoOfflineAndSynchDlg::ShutDownFrameCallBack(HWND hwnd, MSG_Pane *pane, void * closure) +{ + if (::IsWindow(hwnd)) { + ::ShowWindow(hwnd,SW_SHOW); + ::UpdateWindow(hwnd); + } + + if (pane) + { + CGoOfflineAndSynchDlg *SynchronizeDlg = (CGoOfflineAndSynchDlg *) closure; + MSG_GoOffline(WFE_MSGGetMaster(), pane, SynchronizeDlg->m_bDownLoadDiscussions, SynchronizeDlg->m_bDownLoadMail, + SynchronizeDlg->m_bSendMessages, FALSE /*getDirectories*/); + } +} + +void CGoOfflineAndSynchDlg::OnOK() +{ + // TODO: Add extra validation here +#ifdef WIN32 + if ((m_bDownLoadDiscussions || m_bDownLoadMail || m_bSendMessages || + m_bDownLoadDirectories || m_bDownLoadPages || m_bDownLoadChannels)) +#else + if ((m_bDownLoadDiscussions || m_bDownLoadMail || m_bSendMessages || + m_bDownLoadDirectories )) +#endif + { + new CProgressDialog(this, NULL,ShutDownFrameCallBack, + this,szLoadString(IDS_DOWNLOADINGARTICLES)); // need correct window title + ;//DownLoad!!!!!!! + // somehow we need to wait until downloading is done. + } + else + { + PREF_SetBoolPref("network.online", m_bMode); + } + + PREF_SetBoolPref("offline.download_discussions",(XP_Bool)m_bDownLoadDiscussions); + PREF_SetBoolPref("offline.download_mail",(XP_Bool)m_bDownLoadMail); + PREF_SetBoolPref("offline.download_directories",(XP_Bool)m_bDownLoadDirectories); + PREF_SetBoolPref("offline.download_messages",(XP_Bool)m_bSendMessages); + + CDialog::OnOK(); +} + +void CGoOfflineAndSynchDlg::OnHelp() +{ + NetHelp(HELP_OFFLINE_DOWNLOAD); +} + + +int CGoOfflineAndSynchDlg::DoModal() +{ + if (!m_MNResourceSwitcher.Initialize()) + return -1; + return CDialog::DoModal(); +} + +BOOL CGoOfflineAndSynchDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + m_MNResourceSwitcher.Reset(); + + CWnd *pWnd1 = GetDlgItem(IDC_STATIC_SYNC_QUESTION); + if (!pWnd1) + return FALSE; + + HFONT hFont = (HFONT)this->SendMessage(WM_GETFONT); + if (hFont != NULL) + { //make the title bold + VERIFY(::GetObject(hFont, sizeof(LOGFONT), &m_LogFont)); + m_LogFont.lfWeight=FW_BOLD; + m_Font.CreateFontIndirect(&m_LogFont); + pWnd1->SetFont(&m_Font); + } + + return TRUE; // return TRUE unless you set the focus to a control +} + +///////////////////////////////////////////////////////////////////////////// +// CAskSynchronizeExitDlg dialog + + +CAskSynchronizeExitDlg::CAskSynchronizeExitDlg(CWnd* pParent /*=NULL*/) + : CDialog(CAskSynchronizeExitDlg::IDD, pParent) +{ + +} + +void CAskSynchronizeExitDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Check(pDX, IDC_DONTASKAGAIN, m_bDontAskAgain); + +} + + +BEGIN_MESSAGE_MAP(CAskSynchronizeExitDlg, CDialog) + //{{AFX_MSG_MAP(CAskSynchronizeExitDlg) + ON_BN_CLICKED(IDYES, OnYes) + ON_BN_CLICKED(IDNO, OnNo) + ON_WM_PAINT() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CAskSynchronizeExitDlg message handlers + + + +void CAskSynchronizeExitDlg::OnYes() +{ + CommonClose(); + EndDialog(IDYES); +} + +void CAskSynchronizeExitDlg::OnNo() +{ + CommonClose(); + EndDialog(IDNO); +} + +void CAskSynchronizeExitDlg::CommonClose() +{ + // TODO: Add extra validation here + UpdateData(); + + + if(m_bDontAskAgain) + { + PREF_SetBoolPref("offline.prompt_synch_on_exit",(XP_Bool)!m_bDontAskAgain); + } +} + + diff --git a/mozilla/cmd/winfe/offlndlg.h b/mozilla/cmd/winfe/offlndlg.h new file mode 100644 index 00000000000..2ca039b77f3 --- /dev/null +++ b/mozilla/cmd/winfe/offlndlg.h @@ -0,0 +1,174 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +// offlndlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// COfflineDlg dialog + +#ifndef OFFLNDLG_H +#define OFFLNDLG_H + +#include "mnrccln.h" + +class COfflineInfo +{ + +protected: + BOOL m_bDownloadMail; + BOOL m_bDownloadNews; + BOOL m_bDownloadDirectories; + BOOL m_bSendMail; + BOOL m_bDownloadFlagged; + BOOL m_bGoOffline; + BOOL m_bChangeState; + +public: + COfflineInfo(BOOL bDownloadMail, BOOL bDownloadNews, BOOL bDownloadDirectoires, + BOOL bSendMail, BOOL bDownloadFlagged, BOOL bGoOffline, BOOL bChangeState); + + BOOL DownloadMail(); + BOOL DownloadNews(); + BOOL DownloadDirectories(); + BOOL SendMail(); + BOOL DownloadFlagged(); + BOOL GoOffline(); + BOOL ChangeState(); + +}; + +class COfflineDlg : public CDialog +{ + +public: + LOGFONT m_LogFont; + CFont m_Font; + CFont m_Font2; + BOOL m_bMode; + int m_nNumberSelected; + + CMailNewsResourceSwitcher m_MNResourceSwitcher; +// Construction +public: + COfflineDlg(CWnd* pParent = NULL); // standard constructor + + void InitDialog(BOOL bMode) {m_bMode = bMode;}; //Sets button window font sizes + static void ShutDownFrameCallBack(HWND hwnd, MSG_Pane *pane, void * closure); + static BOOL ShowOnlineCallBack(HWND hWnd, MSG_Pane *pane, void *closure); + BOOL DownloadItems(); // should anything be downloaded? + +// Dialog Data +#ifdef WIN32 + enum { IDD = IDD_DOWNLOAD_CONTROL_PANEL }; +#else + enum { IDD = IDD_DWNLD_CTRL_PANEL_W16 }; +#endif + BOOL m_bDownLoadDiscussions; + BOOL m_bDownLoadMail; + BOOL m_bDownLoadDirectories; + BOOL m_bSendMessages; + BOOL m_bDownLoadFlagged; + BOOL m_bGoOffline; + + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + public: + virtual int DoModal( ); + +// Implementation +protected: + afx_msg void OnButtonSelect(); + virtual void OnOK(); + virtual BOOL OnInitDialog(); + virtual void OnHelp(); + afx_msg void OnDestroy( ); + DECLARE_MESSAGE_MAP() +}; + +//CGoOfflineAndSynchDlg prompts user to synchronize before going offline. +class CGoOfflineAndSynchDlg : public CDialog +{ + +public: + LOGFONT m_LogFont; + CFont m_Font; + CFont m_Font2; + BOOL m_bMode; + + CMailNewsResourceSwitcher m_MNResourceSwitcher; +// Construction +public: + CGoOfflineAndSynchDlg(CWnd* pParent = NULL); // standard constructor + + void InitDialog(BOOL bMode) {m_bMode = bMode;}; //Sets button window font sizes + static void ShutDownFrameCallBack(HWND hwnd, MSG_Pane *pane, void * closure); +// Dialog Data + enum { IDD = IDD_SYNCHRONIZE_ONOFF }; + BOOL m_bDownLoadDiscussions; + BOOL m_bDownLoadMail; + BOOL m_bDownLoadDirectories; +#ifdef WIN32 + BOOL m_bDownLoadPages; + BOOL m_bDownLoadChannels; +#endif + BOOL m_bSendMessages; + BOOL m_bGoOffline; + + +// Implementation +public: + virtual int DoModal( ); + +protected: + virtual void OnOK(); + afx_msg void OnNo(); + virtual void OnCancel(); + virtual void OnHelp(); + virtual BOOL OnInitDialog(); + DECLARE_MESSAGE_MAP() +}; + + +class CAskSynchronizeExitDlg : public CDialog +{ + +public: + +// Construction +public: + CAskSynchronizeExitDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + enum { IDD = IDD_SYNCHRONIZE_EXIT }; + + BOOL m_bDontAskAgain; + + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + void CommonClose(); +// Implementation +protected: + virtual void OnYes(); + virtual void OnNo(); + DECLARE_MESSAGE_MAP() +}; + +#endif + + diff --git a/mozilla/cmd/winfe/offpkdlg.cpp b/mozilla/cmd/winfe/offpkdlg.cpp new file mode 100644 index 00000000000..8a0dfc4e5b9 --- /dev/null +++ b/mozilla/cmd/winfe/offpkdlg.cpp @@ -0,0 +1,680 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +// offpkdlg.cpp : Offline picker dialog implementation file +// + +#include "stdafx.h" +#include "msgcom.h" +#include "mailmisc.h" +#include "xp_time.h" +#include "xplocale.h" +#include "wfemsg.h" +#include "dateedit.h" +#include "nethelp.h" +#include "prefapi.h" +#include "nethelp.h" +#include "xp_help.h" +#include "offpkdlg.h" + + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char BASED_CODE THIS_FILE[] = __FILE__; +#endif + + +#define LEVEL_ROOT 0 +#define LEVEL_1 1 + +extern "C" BOOL IsNumeric(char* pStr); +extern "C" void HelperInitFonts( HDC hdc , HFONT *phFont, HFONT *phBoldFont); + + + + +///////////////////////////////////////////////////////////////////////////// +// COfflinePickerCX +COfflinePickerCX::COfflinePickerCX(ContextType type, CDlgOfflinePicker *pDialog) +: CStubsCX(type, MWContextMail) +{ + m_pDialog = (CDlgOfflinePicker*)pDialog; +} + + +///////////////////////////////////////////////////////////////////////////// +// COfflineList +STDMETHODIMP COfflineList::QueryInterface(REFIID refiid, LPVOID * ppv) +{ + *ppv = NULL; + if (IsEqualIID(refiid,IID_IUnknown)) + *ppv = (LPUNKNOWN) this; + else if (IsEqualIID(refiid,IID_IMsgList)) + *ppv = (LPMSGLIST) this; + + if (*ppv != NULL) { + ((LPUNKNOWN) *ppv)->AddRef(); + return NOERROR; + } + + return ResultFromScode(E_NOINTERFACE); +} + +STDMETHODIMP_(ULONG) COfflineList::AddRef(void) +{ + return ++m_ulRefCount; +} + +STDMETHODIMP_(ULONG) COfflineList::Release(void) +{ + ULONG ulRef; + ulRef = --m_ulRefCount; + if (m_ulRefCount == 0) + delete this; + return ulRef; +} + +void COfflineList::ListChangeStarting( MSG_Pane* pane, XP_Bool asynchronous, + MSG_NOTIFY_CODE notify, MSG_ViewIndex where, + int32 num) +{ + if (m_pDialog) + m_pDialog->ListChangeStarting( pane, asynchronous, + notify, where, num ); +} + +void COfflineList::ListChangeFinished( MSG_Pane* pane, XP_Bool asynchronous, + MSG_NOTIFY_CODE notify, MSG_ViewIndex where, + int32 num) +{ + if (m_pDialog) + m_pDialog->ListChangeFinished( pane, asynchronous, + notify, where, num ); +} + +void COfflineList::GetSelection( MSG_Pane* pane, MSG_ViewIndex **indices, int *count, + int *focus) +{ +} + +void COfflineList::SelectItem( MSG_Pane* pane, int item ) +{ +} + +////////////////////////////////////////////////////////////////////////////// +// COfflinePickerOutlinerParent + +BEGIN_MESSAGE_MAP(COfflinePickerOutlinerParent, COutlinerParent) +END_MESSAGE_MAP() + +COfflinePickerOutlinerParent::COfflinePickerOutlinerParent() +{ +} + + +COfflinePickerOutlinerParent::~COfflinePickerOutlinerParent() +{ +} + +// Draw Column text and Sort indicator +BOOL COfflinePickerOutlinerParent::RenderData (int idColumn, CRect & rect, CDC &dc, const char * text) +{ + COfflinePickerOutliner* pOutliner = (COfflinePickerOutliner*) m_pOutliner; + + // Draw Text String + if (idColumn == ID_COL_DOWNLOAD) + dc.DrawText(text, _tcslen(text), &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER); + else + dc.DrawText(text, _tcslen(text), &rect, DT_SINGLELINE | DT_VCENTER); + + return TRUE; +} + + +COutliner * COfflinePickerOutlinerParent::GetOutliner ( void ) +{ + return new COfflinePickerOutliner; +} + +void COfflinePickerOutlinerParent::CreateColumns ( void ) +{ + CString text, newName; + CRect colRect; + int col1, col2, nPos; + int pos1, pos2 ; + + col2 = m_pDialog->nameWidth; + col1 = m_pDialog->downloadWidth; + pos2 = m_pDialog->namePos; + pos1 = m_pDialog->downloadPos; + + + text.LoadString(IDS_DOWNLOAD); + nPos = text.Find('&'); + if (nPos >= 0) + newName = text.Left(nPos) + text.Right(text.GetLength() - nPos - 1); + else + newName = text; + m_pOutliner->AddColumn (newName, ID_COL_DOWNLOAD, col1, colRect.right, + ColumnVariable, col1, FALSE); + + text.LoadString(IDS_DOWNLOAD_LIST); + m_pOutliner->AddColumn (text, ID_COL_NAME, col2, colRect.right, + ColumnVariable, col2, FALSE); + + m_pOutliner->SetColumnPos( ID_COL_DOWNLOAD, pos1 ); + m_pOutliner->SetColumnPos( ID_COL_NAME, pos2 ); + + m_pOutliner->SetImageColumn( ID_COL_NAME ); +} + + +////////////////////////////////////////////////////////////////////////////// +// COfflinePickerOutliner + +COfflinePickerOutliner::COfflinePickerOutliner() +{ + ApiApiPtr(api); + m_pUnkUserImage = api->CreateClassInstance(APICLASS_IMAGEMAP); + if (m_pUnkUserImage) { + m_pUnkUserImage->QueryInterface(IID_IImageMap,(LPVOID*)&m_pIUserImage); + ASSERT(m_pIUserImage); + m_pIUserImage->Initialize(IDB_MAILNEWS,16,16); + } + + m_pAncestor = NULL; + m_pszExtraText = new char[256]; + m_pDialog = NULL; + +} + +COfflinePickerOutliner::~COfflinePickerOutliner ( ) +{ + if (m_pUnkUserImage) { + if (m_pIUserImage) + m_pUnkUserImage->Release(); + } + + delete [] m_pAncestor; + delete [] m_pszExtraText; + +} + + +int COfflinePickerOutliner::GetDepth( int iLine ) +{ + MSG_FolderLine folderLine; + MSG_GetFolderLineByIndex( m_pPane, (MSG_ViewIndex) iLine, 1, &folderLine ); + return folderLine.level - 1; +} + +int COfflinePickerOutliner::GetNumChildren( int iLine ) +{ + MSG_FolderLine folderLine; + MSG_GetFolderLineByIndex( m_pPane, (MSG_ViewIndex) iLine, 1, &folderLine ); + return folderLine.numChildren; +} + +BOOL COfflinePickerOutliner::IsCollapsed( int iLine ) +{ + MSG_FolderLine folderLine; + MSG_GetFolderLineByIndex( m_pPane, (MSG_ViewIndex) iLine, 1, &folderLine ); + return folderLine.flags & MSG_FOLDER_FLAG_ELIDED ? TRUE : FALSE; +} + +BOOL COfflinePickerOutliner::RenderData(UINT iColumn, CRect &rect, CDC &dc, const char * text ) +{ + if (iColumn != ID_COL_DOWNLOAD) + return FALSE; + + if ((m_FolderLine.flags & MSG_FOLDER_FLAG_NEWS_HOST) || + (m_FolderLine.flags & MSG_FOLDER_FLAG_IMAP_SERVER) || + ((m_FolderLine.flags & MSG_FOLDER_FLAG_MAIL) && + !(m_FolderLine.flags & MSG_FOLDER_FLAG_IMAPBOX))) + return TRUE; + + int idxImage = -1; + + if (MSG_GetFolderPrefFlags(m_FolderLine.id) & MSG_FOLDER_PREF_OFFLINE) + idxImage = IDX_CHECKMARK; + else + idxImage = IDX_CHECKBOX; + + m_pIUserImage->DrawImage(idxImage, rect.left + ( ( rect.Width ( ) - 16 ) / 2 ), + rect.top, &dc, FALSE); + return TRUE; +} + + +int COfflinePickerOutliner::TranslateIcon(void * pLineData) +{ + ASSERT(pLineData); + MSG_FolderLine * pFolder = (MSG_FolderLine*)pLineData; + + BOOL bOpen = pFolder->numChildren <= 0 || + pFolder->flags & MSG_FOLDER_FLAG_ELIDED ? FALSE : TRUE; + + return WFE_MSGTranslateFolderIcon( pFolder->level, pFolder->flags, bOpen ); +} + +int COfflinePickerOutliner::TranslateIconFolder (void * pData) +{ + ASSERT(pData); + MSG_FolderLine * pFolder = (MSG_FolderLine*)pData; + + if (pFolder->numChildren > 0) { + if (pFolder->flags & MSG_FOLDER_FLAG_ELIDED) { + return(OUTLINER_CLOSEDFOLDER); + } else { + return(OUTLINER_OPENFOLDER); + } + } + return (OUTLINER_ITEM); +} + +BOOL COfflinePickerOutliner::ColumnCommand(int iColumn, int iLine) +{ + if (iColumn == ID_COL_DOWNLOAD) + { + //MSG_ViewIndex indices; + //indices = (MSG_ViewIndex)iLine; + MSG_GetFolderLineByIndex(m_pPane, iLine, 1, &m_FolderLine ); + if ( MSG_GetFolderPrefFlags(m_FolderLine.id) & MSG_FOLDER_PREF_OFFLINE ) + { + MSG_SetFolderPrefFlags(m_FolderLine.id, !MSG_FOLDER_PREF_OFFLINE); + } + else + { + MSG_SetFolderPrefFlags(m_FolderLine.id, MSG_FOLDER_PREF_OFFLINE); + } + //MSG_Command(GetPane(), MSG_ToggleSubscribed, &indices, 1); + } + return FALSE; +} + + +void* COfflinePickerOutliner::AquireLineData(int line) +{ + delete [] m_pAncestor; + m_pAncestor = NULL; + + m_pszExtraText[ 0 ] = '\0'; + if ( line >= m_iTotalLines) + return NULL; + if ( !MSG_GetFolderLineByIndex(m_pPane, line, 1, &m_FolderLine )) + return NULL; + + return &m_FolderLine; +} + + +void COfflinePickerOutliner::GetTreeInfo( int iLine, uint32 * pFlags, int* pDepth, + OutlinerAncestorInfo** pAncestor) +{ + delete [] m_pAncestor; + m_pAncestor = NULL; + + if (pAncestor) { + if ( m_FolderLine.level > 0 ) { + m_pAncestor = new OutlinerAncestorInfo[m_FolderLine.level]; + } else { + m_pAncestor = new OutlinerAncestorInfo[1]; + } + + int i = m_FolderLine.level - 1; + int idx = iLine + 1; + while ( i > 0 ) { + int level; + if ( idx < m_iTotalLines ) { + level = MSG_GetFolderLevelByIndex( m_pPane, idx ); + if ( (level - 1) == i ) { + m_pAncestor[i].has_prev = TRUE; + m_pAncestor[i].has_next = TRUE; + i--; + idx++; + } else if ( (level - 1) < i ) { + m_pAncestor[i].has_prev = FALSE; + m_pAncestor[i].has_next = FALSE; + i--; + } else { + idx++; + } + } else { + m_pAncestor[i].has_prev = FALSE; + m_pAncestor[i].has_next = FALSE; + i--; + } + } + m_pAncestor[0].has_prev = FALSE; + m_pAncestor[0].has_next = FALSE; + + *pAncestor = m_pAncestor; + } + + if ( pFlags ) *pFlags = m_FolderLine.flags; + if ( pDepth ) *pDepth = m_FolderLine.level - 1; +} + + +void COfflinePickerOutliner::ReleaseLineData(void *) +{ + delete [] m_pAncestor; + m_pAncestor = NULL; +} + + +LPCTSTR COfflinePickerOutliner::GetColumnText(UINT iColumn, void* pLineData) +{ + MSG_FolderLine* pfolderLine = (MSG_FolderLine*)pLineData; + + memset(m_pszExtraText, '\0', 256); + switch (iColumn) + { + case ID_COL_NAME: + strncpy(m_pszExtraText, pfolderLine->name, strlen(pfolderLine->name)); + break; + default: + break; + } + return m_pszExtraText; +} + +void COfflinePickerOutliner::OnSelChanged() +{ +} + +void COfflinePickerOutliner::OnSelDblClk() +{ +} + +int COfflinePickerOutliner::ToggleExpansion(int iLine) +{ + return CMailNewsOutliner::ToggleExpansion(iLine); +} + +// To select the first item in the outliner, +// so a user can tab through +BOOL COfflinePickerOutliner::SelectInitialItem() +{ + int count; + MSG_ViewIndex *indices; + GetSelection( indices, count ); + + if (GetTotalLines() && !count ) + { + SelectItem(0); + InvalidateLine(0); + return TRUE; + } + if (count) + return TRUE; + return FALSE; +} + +/* + * Returns true if there are items in this outliner that can be + * selected for download. False otherwise. + */ +BOOL COfflinePickerOutliner::HasSelectableItems() +{ + + int numLines = GetTotalLines(); + MSG_FolderLine folderLine; + + for(int i = 0; i < numLines; i++) + { + + if(MSG_GetFolderLineByIndex( m_pPane, (MSG_ViewIndex) i, 1, &folderLine )) + { + // As long as we're not a server or a non IMAP mailbox then + // there is something to download. + if (!((folderLine.flags & MSG_FOLDER_FLAG_NEWS_HOST) || + (folderLine.flags & MSG_FOLDER_FLAG_IMAP_SERVER) || + ((folderLine.flags & MSG_FOLDER_FLAG_MAIL) && + !(folderLine.flags & MSG_FOLDER_FLAG_IMAPBOX)))) + { + return TRUE; + } + + } + + + } + return FALSE; + +} + +///////////////////////////////////////////////////////////////////////////// +// CDlgOfflinePicker dialog + + +CDlgOfflinePicker::CDlgOfflinePicker(CWnd* pParent /*=NULL*/) + : CDialog(CDlgOfflinePicker::IDD, pParent) +{ + //{{AFX_DATA_INIT(CDlgOfflinePicker) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + + m_nDiscussionSelectionCount =0; + m_nMailSelectionCount =0; + m_nDirectorySelectionCount =0; + m_nPublicFolderSelectionCount=0; + m_pMaster = WFE_MSGGetMaster(); + + m_pParent = pParent; + m_bActivated = FALSE; + m_bSelChanged = FALSE; + m_bNotifyAll = FALSE; + m_bDoShowWindow = TRUE; + + m_pOutliner = NULL; + m_pOutlinerParent = NULL; + + m_bProcessGetDeletion = FALSE; + m_bListChangeStarting = FALSE; + nameWidth = 0; + downloadWidth = 0; + namePos = 1; + downloadPos = 0; + + +} + +CDlgOfflinePicker::~CDlgOfflinePicker() +{ + +} + +void CDlgOfflinePicker::ListChangeStarting(MSG_Pane* pane, XP_Bool asynchronous, + MSG_NOTIFY_CODE notify, MSG_ViewIndex where, int32 num) +{ + if ( pane == GetPane() ) + { + m_bListChangeStarting = TRUE; + + if (m_pOutliner) + m_pOutliner->MysticStuffStarting(asynchronous, notify, where, num ); + } +} + + +void CDlgOfflinePicker::ListChangeFinished(MSG_Pane* pane, XP_Bool asynchronous, + MSG_NOTIFY_CODE notify, MSG_ViewIndex where, int32 num) +{ + if ( pane == GetPane() ) + { + m_bListChangeStarting = FALSE; + if (m_pOutliner) + { + m_pOutliner->MysticStuffFinishing(asynchronous, notify, where, num); + m_pOutliner->SetTotalLines(CASTINT(MSG_GetNumLines(GetPane()))); + } + } +} + + +void CDlgOfflinePicker::CleanupOnClose() +{ + COfflineList* pList = GetList(); + if (pList) + { + pList->Release(); + SetList(NULL); + } + + COfflinePickerCX* pCX = GetPickerContext(); + if (pCX) + { + if(!pCX->IsDestroyed()) + pCX->DestroyContext(); + SetPickerContext(NULL); + } + MSG_Pane * pPane = GetPane(); + if (pPane) + { + MSG_DestroyPane(pPane); + SetPane(NULL); + } + + if(m_pOutlinerParent) + { + delete m_pOutlinerParent; + } + +} + + +void CDlgOfflinePicker::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CDlgOfflinePicker) + // NOTE: the ClassWizard will add DDX and DDV calls here + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CDlgOfflinePicker, CDialog) + //{{AFX_MSG_MAP(CDlgOfflinePicker) + ON_BN_CLICKED(IDC_OFFLINE_SELECT_HELP, OnHelp) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CDlgOfflinePicker message handlers + + +void CDlgOfflinePicker::OnOK() +{ + CDialog::OnOK(); + CleanupOnClose(); +} + +int CDlgOfflinePicker::DoModal () +{ + if (!m_MNResourceSwitcher.Initialize()) + return -1; + return CDialog::DoModal(); +} + + +BOOL CDlgOfflinePicker::OnInitDialog() +{ + CDialog::OnInitDialog(); + m_MNResourceSwitcher.Reset(); + + m_iIndex = 0; + +//We need to get a pane and context in order to get updates + COfflinePickerCX* pCX = new COfflinePickerCX(MailCX,this); + + pCX->GetContext()->fancyFTP = TRUE; + pCX->GetContext()->fancyNews = TRUE; + pCX->GetContext()->intrupt = FALSE; + pCX->GetContext()->reSize = FALSE; + pCX->GetContext()->type = MWContextMail; + SetPickerContext(pCX); + + m_pPane = MSG_CreateFolderPane(pCX->GetContext(), m_pMaster); + + if (!m_pPane) + return FALSE; + + //use COfflineList to hook up with the backend + COfflineList *pInstance = new COfflineList(this); + SetList(pInstance); + COfflineList** hList = GetListHandle(); + pInstance->QueryInterface(IID_IMsgList, (LPVOID *)hList); + MSG_SetFEData((MSG_Pane*)m_pPane, (void *)pInstance); +//Done hooking up the pane and context to the list + +//Get the temporary box size for the outliner on the dialog!!! + CRect rect2, rect3, rect4; + CWnd* widget = GetDlgItem(IDC_TEMP_OUTLINER_BOX); + widget->ShowWindow( SW_HIDE ); + widget->GetWindowRect(&rect2); + widget->GetClientRect(&rect3); + GetClientRect(&rect4); + ClientToScreen(&rect4); + rect2.OffsetRect(-rect4.left, -rect4.top); + + downloadWidth = 60; + nameWidth = rect2.Width() -downloadWidth; + + // do this here so that the resource dll switching can take place + m_pOutlinerParent = new COfflinePickerOutlinerParent(); + +#ifdef _WIN32 + m_pOutlinerParent->CreateEx ( WS_EX_CLIENTEDGE, NULL, _T("NSOutlinerParent"), + WS_VISIBLE|WS_CHILD|WS_CLIPCHILDREN|WS_TABSTOP, + rect2.left, rect2.top, + rect2.right - rect2.left, rect2.bottom - rect2.top, + this->m_hWnd, NULL); +#else + m_pOutlinerParent->Create( NULL, _T("NSOutlinerParent"), + WS_VISIBLE|WS_CHILD|WS_CLIPCHILDREN|WS_TABSTOP, + rect2, this, NULL); +#endif + + m_pOutlinerParent->SetDialogOwner((CDlgOfflinePicker*)this); + m_pOutlinerParent->CreateColumns(); + m_pOutliner = (COfflinePickerOutliner *) m_pOutlinerParent->m_pOutliner; + if (m_pOutliner) + { + m_pOutliner->SetPane(m_pPane); + m_pOutliner->SetTotalLines(CASTINT(MSG_GetNumLines(GetPane()))); + m_pOutliner->SetDlg(this); + m_pOutliner->SelectInitialItem(); + } + + if (!m_pOutliner->HasSelectableItems()) + AfxMessageBox(IDS_NOTHING_SUBSCRIBED); + + + return TRUE; +} + +void CDlgOfflinePicker::OnCancel() +{ + CDialog::OnCancel(); + CleanupOnClose(); +} + +void CDlgOfflinePicker::OnHelp() +{ + NetHelp(HELP_MAILNEWS_SELECT_ITEMS); +} diff --git a/mozilla/cmd/winfe/offpkdlg.h b/mozilla/cmd/winfe/offpkdlg.h new file mode 100644 index 00000000000..e6354516dec --- /dev/null +++ b/mozilla/cmd/winfe/offpkdlg.h @@ -0,0 +1,271 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +// offpkdlg.h : header file +// + +#ifndef OFFPKDLG_H +#define OFFPKDLG_H + +#include "property.h" +#include "outliner.h" +#include "apiimg.h" +#include "apimsg.h" +#include "mailmisc.h" +#include "msgcom.h" +#include "mnrccln.h" + + +//offline outliner item data container. +typedef struct ItemData +{ + MSG_FolderInfo *infoData; + BOOL bDownLoad; +}ItemData; + +// Definitions for column headings in the outliner control +#define ID_COL_NAME 1 +#define ID_COL_DOWNLOAD 2 + +class COfflinePickerOutliner; +class CDlgOfflinePicker; +class COfflineList; + + +///////////////////////////////////////////////////////////////////////////// +// Class: COfflinePickerCX +class COfflinePickerCX: public CStubsCX +{ +protected: + CDlgOfflinePicker* m_pDialog; + +public: + COfflinePickerCX(ContextType type,CDlgOfflinePicker* pDialog); + void Alert(MWContext *pContext, const char *pMessage){return;} + + virtual CWnd *GetDialogOwner() const { return (CWnd*)m_pDialog;} + +}; + +///////////////////////////////////////////////////////////////////////////// +// Class: COfflinePickerOutlinerParent +class COfflinePickerOutlinerParent : public COutlinerParent +{ +public: + COfflinePickerOutlinerParent(); + virtual ~COfflinePickerOutlinerParent(); + virtual COutliner * GetOutliner ( ); + virtual void CreateColumns ( ); + virtual BOOL RenderData ( int idColumn, CRect & rect, CDC & dc, const char *); + + void SetDialogOwner(CDlgOfflinePicker* pDialog) { m_pDialog = pDialog; } + +// Implementation +protected: + + CDlgOfflinePicker* m_pDialog; + + DECLARE_MESSAGE_MAP() +}; + + + +///////////////////////////////////////////////////////////////////////////// +// CDlgOfflinePicker dialog + +class CDlgOfflinePicker : public CDialog +{ + +public: + CMailNewsResourceSwitcher m_MNResourceSwitcher; + + int m_nDiscussionSelectionCount; + int m_nMailSelectionCount; + int m_nDirectorySelectionCount; + int m_nPublicFolderSelectionCount; + int m_iIndex; + + int nameWidth; + int downloadWidth; + int namePos; + int downloadPos; + + BOOL m_bListChangeStarting; + BOOL m_bProcessGetDeletion; + + + CWnd *m_pParent; + MSG_Pane *m_pPane; + MSG_Master *m_pMaster; + COfflinePickerCX *m_pOfflineCX; + COfflineList *m_pPickerList; + +// Construction +public: + CDlgOfflinePicker(CWnd* pParent = NULL); // standard constructor + ~CDlgOfflinePicker(); + + MWContext* GetContext(); + COfflinePickerCX* GetPickerContext() {return m_pOfflineCX;}; + + MSG_Pane * GetPane() {return m_pPane;}; + COfflineList* GetList() {return m_pPickerList;}; + COfflineList** GetListHandle() {return &m_pPickerList;}; + COfflinePickerOutliner * GetOutliner() { return m_pOutliner; }; + + void SetPickerContext(COfflinePickerCX* pCX) {m_pOfflineCX = pCX;}; + void SetPane(MSG_Pane *pPane) {m_pPane = pPane;}; + void SetList(COfflineList* pList) {m_pPickerList = pList;}; + + Bool IsOutlinerHasFocus(); + void EnableAllControls(BOOL bEnable); + void DoStopListChange(); + void ClearPickerSelection(); + + virtual void ListChangeStarting(MSG_Pane* pane, XP_Bool asynchronous, + MSG_NOTIFY_CODE notify, MSG_ViewIndex where, + int32 num); + virtual void ListChangeFinished(MSG_Pane* pane, XP_Bool asynchronous, + MSG_NOTIFY_CODE notify, MSG_ViewIndex where, + int32 num); + + // Dialog Data + //{{AFX_DATA(CDlgOfflinePicker) + enum { IDD = IDD_OFFLINE_PICKER }; + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + + int GetSelectionCount() {return m_nDiscussionSelectionCount;}; + int GetMailSelectionCount() {return m_nMailSelectionCount;}; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CDlgOfflinePicker) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + public: + virtual int DoModal (); + //}}AFX_VIRTUAL + +// Implementation +protected: + BOOL m_bActivated; + BOOL m_bSelChanged; + BOOL m_bNotifyAll; //MAG_NotifyALl when outliner is not visible + BOOL m_bInitDialog; + BOOL m_bDoShowWindow; + + COfflinePickerOutlinerParent *m_pOutlinerParent; + COfflinePickerOutliner *m_pOutliner; + + void CleanupOnClose(); + // Generated message map functions + //{{AFX_MSG(CDlgOfflinePicker) + virtual void OnOK(); + virtual BOOL OnInitDialog(); + virtual void OnCancel(); + virtual void OnHelp(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +class COfflinePickerOutliner : public CMailNewsOutliner +{ +friend class COfflinePickerOutlinerParent; + +protected: + char* m_pszExtraText; + OutlinerAncestorInfo * m_pAncestor; + MSG_FolderLine m_FolderLine; + BOOL m_bSelChanged; + CDlgOfflinePicker* m_pDialog; + +public: + COfflinePickerOutliner ( ); + virtual ~COfflinePickerOutliner ( ); + + MSG_Pane * GetPane() {return m_pPane;}; + void SetDlg(CDlgOfflinePicker *pDialog) { m_pDialog = pDialog; } + CDlgOfflinePicker * GetDlg() { return m_pDialog; } + void DeselectItem(); + BOOL SelectInitialItem(); + BOOL HasSelectableItems(); + + virtual void OnSelChanged(); + virtual void OnSelDblClk(); + virtual int ToggleExpansion ( int iLine ); + + virtual int GetDepth( int iLine ); + virtual int GetNumChildren( int iLine ); + virtual BOOL IsCollapsed( int iLine ); + virtual BOOL ColumnCommand(int iColumn, int iLine); + + virtual LPCTSTR GetColumnText ( UINT iColumn, void * pLineData ); + virtual void * AquireLineData ( int iLine ); + virtual void ReleaseLineData ( void * pLineData ); + virtual void GetTreeInfo ( int iLine, uint32 * pFlags, int * iDepth, + OutlinerAncestorInfo ** pAncestor ); + virtual BOOL RenderData ( UINT idColumn, CRect & rect, CDC & dc, const char * text); + virtual int TranslateIcon ( void *); + virtual int TranslateIconFolder ( void *); + +}; + + +///////////////////////////////////////////////////////////////////////////// +// Class: COfflineList +class COfflineList: public IMsgList +{ + + CDlgOfflinePicker* m_pDialog; + + unsigned long m_ulRefCount; + +public: +// IUnknown Interface + STDMETHODIMP QueryInterface(REFIID,LPVOID *); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + +// IMsgList Interface + virtual void ListChangeStarting(MSG_Pane* pane, XP_Bool asynchronous, + MSG_NOTIFY_CODE notify, MSG_ViewIndex where, + int32 num); + virtual void ListChangeFinished(MSG_Pane* pane, XP_Bool asynchronous, + MSG_NOTIFY_CODE notify, MSG_ViewIndex where, + int32 num); + virtual void GetSelection(MSG_Pane* pane, MSG_ViewIndex **indices, int *count, + int *focus); + virtual void SelectItem(MSG_Pane* pane, int item); + virtual void CopyMessagesInto( MSG_Pane *pane, MSG_ViewIndex *indices, int count, + MSG_FolderInfo *folderInfo) {} + virtual void MoveMessagesInto( MSG_Pane *pane, MSG_ViewIndex *indices, int count, + MSG_FolderInfo *folderInfo) {} + + void SetPickerDialog(CDlgOfflinePicker * pDialog) + { m_pDialog = pDialog; } + + COfflineList(CDlgOfflinePicker *pDialog) + { + m_ulRefCount = 0; + m_pDialog = pDialog; + } +}; + +#endif OFFPKDLG_H diff --git a/mozilla/cmd/winfe/prefs/mnpref/src/mnpages.cpp b/mozilla/cmd/winfe/prefs/mnpref/src/mnpages.cpp index edb185c492a..c858fda1ce8 100644 --- a/mozilla/cmd/winfe/prefs/mnpref/src/mnpages.cpp +++ b/mozilla/cmd/winfe/prefs/mnpref/src/mnpages.cpp @@ -16,7 +16,6 @@ * Reserved. */ -#include "xp_help.h" #include "pch.h" #include #include "dllcom.h" @@ -30,6 +29,7 @@ #include "mninterf.h" #include "mnprefid.h" #include "msgcom.h" +#include "xp_help.h" //#define MOZ_NO_LDAP 1 #undef MOZ_LDAP diff --git a/mozilla/cmd/winfe/res/offline.bmp b/mozilla/cmd/winfe/res/offline.bmp new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mozilla/cmd/xfe/Makefile b/mozilla/cmd/xfe/Makefile index e6623259495..23535c3bb11 100644 --- a/mozilla/cmd/xfe/Makefile +++ b/mozilla/cmd/xfe/Makefile @@ -265,6 +265,7 @@ ifdef MOZ_LOC_INDEP BASIC_LIBS += $(DIST)/lib/libli.a endif + ifdef JAVA_OR_OJI JAVA_JMC = $(DIST)/lib/libjmc.a # XXX To be removed... endif @@ -289,9 +290,13 @@ BASIC_LIBS_2 = \ ifdef MOZ_MAIL_NEWS BASIC_LIBS += \ - $(DIST)/lib/libnet.a \ $(DIST)/lib/libns_mime.a \ $(NULL) + +BASIC_LIBS_2 += \ + $(DIST)/lib/libldap.a \ + $(NULL) + endif BASIC_LIBS += \ @@ -304,14 +309,39 @@ BASIC_LIBS += \ $(DIST)/lib/libmozmsg.a \ $(DIST)/lib/libmsg.a \ $(DIST)/lib/libmime.a \ - $(DIST)/lib/libnet.a \ + $(DIST)/lib/libaddr.a \ + $(DIST)/lib/libneo.a \ + $(DIST)/lib/libaddr.a \ + $(NULL) +endif + +ifdef MOZ_LDAP +BASIC_LIBS += $(DIST)/lib/libldap.a $(DIST)/lib/liblber.a +endif + +ifdef MOZ_MAIL_NEWS +BASIC_LIBS += $(DIST)/lib/libnntpurl.a \ + $(DIST)/lib/libsmtpurl.a \ + $(DIST)/lib/libimap4url.a \ + $(DIST)/lib/libpop3url.a \ + $(DIST)/lib/libmailbxurl.a \ + $(DIST)/lib/libcrtldurl.a \ + $(NULL) +endif + +ifdef MOZ_MAIL_NEWS +ifdef MOZ_LDAP +BASIC_LIBS += $(DIST)/lib/libldapurl.a \ + $(NULL) +endif +endif + +ifdef MOZ_CALENDAR +BASIC_LIBS += \ $(DIST)/lib/libjulian.a \ $(DIST)/lib/libnscnv30.a \ $(DIST)/lib/libnsuni30.a \ $(DIST)/lib/libnsfmt30.a \ - $(DIST)/lib/libaddr.a \ - $(DIST)/lib/libneo.a \ - $(DIST)/lib/libaddr.a \ $(NULL) endif @@ -413,10 +443,6 @@ ifndef MOZ_SECURITY EXPORT_LIB += $(DIST)/lib/libsecfree.a endif -ifdef MOZ_LDAP -BASIC_LIBS += $(DIST)/lib/libldap.a $(DIST)/lib/liblber.a -endif - LOCALES = $(LOCALE_MAP) $(MAIL_IM_HACK) $(NEWS_IM_HACK) ALL_EXPORT_LIBS = $(BASIC_LIBS) $(EXPORT_LIB) $(BASIC_LIBS_2) $(NSPR_LIB) diff --git a/mozilla/cmd/xfe/addrbk.c b/mozilla/cmd/xfe/addrbk.c index 7ad01c5fd6e..4a49be942b8 100644 --- a/mozilla/cmd/xfe/addrbk.c +++ b/mozilla/cmd/xfe/addrbk.c @@ -120,14 +120,18 @@ void FE_InitAddrBook() PR_snprintf(tmp, sizeof (tmp), "%.900s/.netscape/address-book.html", home); { +#ifndef MOZ_NEWADDR DIR_Server *pabDir = NULL; DIR_GetPersonalAddressBook(directories, &pabDir); AB_InitializeAddressBook(pabDir, &AddrBook, tmp); +#endif } } void FE_CloseAddrBook() { +#ifndef MOZ_NEWADDR AB_CloseAddressBook(&AddrBook); +#endif } #else diff --git a/mozilla/cmd/xfe/icons/images/MN_MailAttach.gif b/mozilla/cmd/xfe/icons/images/MN_MailAttach.gif new file mode 100644 index 00000000000..bdca2477959 Binary files /dev/null and b/mozilla/cmd/xfe/icons/images/MN_MailAttach.gif differ diff --git a/mozilla/cmd/xfe/icons/images/MN_Template.gif b/mozilla/cmd/xfe/icons/images/MN_Template.gif new file mode 100644 index 00000000000..a32e8e442b0 Binary files /dev/null and b/mozilla/cmd/xfe/icons/images/MN_Template.gif differ diff --git a/mozilla/cmd/xfe/icons/images/MN_TemplateO.gif b/mozilla/cmd/xfe/icons/images/MN_TemplateO.gif new file mode 100644 index 00000000000..52fcd15a8f5 Binary files /dev/null and b/mozilla/cmd/xfe/icons/images/MN_TemplateO.gif differ diff --git a/mozilla/cmd/xfe/icons/images/MN_Templatefile.gif b/mozilla/cmd/xfe/icons/images/MN_Templatefile.gif new file mode 100644 index 00000000000..d4a35de3cb8 Binary files /dev/null and b/mozilla/cmd/xfe/icons/images/MN_Templatefile.gif differ diff --git a/mozilla/cmd/xfe/mozilla.c b/mozilla/cmd/xfe/mozilla.c index 69851a8a4f1..43f228b99a1 100644 --- a/mozilla/cmd/xfe/mozilla.c +++ b/mozilla/cmd/xfe/mozilla.c @@ -39,7 +39,7 @@ #include "secnav.h" #include "secrng.h" #include "mozjava.h" -#ifdef MOZ_MAIL_NEWS +#ifdef MOZ_CALENDAR #include "nlsxp.h" #endif #ifdef MOZ_SMARTUPDATE @@ -2100,7 +2100,7 @@ main mozilla_thread = PR_CurrentThread(); fdset_lock = PR_NewNamedMonitor("mozilla-fdset-lock"); -#ifdef MOZ_MAIL_NEWS +#ifdef MOZ_CALENDAR NLS_EncInitialize(NULL,NULL); #endif /* diff --git a/mozilla/cmd/xfe/prefdialogs.c b/mozilla/cmd/xfe/prefdialogs.c index 56b7162ecf3..6651d6d99a4 100644 --- a/mozilla/cmd/xfe/prefdialogs.c +++ b/mozilla/cmd/xfe/prefdialogs.c @@ -664,7 +664,7 @@ fe_InstallPreferences (MWContext *context) /* spider begin */ FE_SARCacheDir = fe_globalPrefs.sar_cache_dir ; /* spider end */ - NET_DontDiskCacheSSL(!fe_globalPrefs.cache_ssl_p); + HG47991 /* bookmark_file */ diff --git a/mozilla/cmd/xfe/src/ABComplPickerDlg.cpp b/mozilla/cmd/xfe/src/ABComplPickerDlg.cpp new file mode 100644 index 00000000000..07883835bab --- /dev/null +++ b/mozilla/cmd/xfe/src/ABComplPickerDlg.cpp @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- + Created: Tao Cheng , 16-mar-98 + */ + +#include "ABComplPickerDlg.h" +#include "ABComplPickerView.h" + +#include "Frame.h" +#include "ViewGlue.h" + +#include +#include + +#include "xpgetstr.h" +#include "xp_mem.h" + +#include "libi18n.h" +#include "intl_csi.h" +#include "felocale.h" + +extern int XFE_AB_NAME; +extern int XFE_AB_TITLE; +extern int XFE_AB_DEPARTMENT; + +XFE_ABComplPickerDlg::XFE_ABComplPickerDlg(Widget parent, + char *name, + Boolean modal, + MWContext *context, + MSG_Pane *pane, + MWContext *pickerContext, + NCPickerExitFunc func, + void *callData): +#if defined(GLUE_COMPO_CONTEXT) + XFE_ViewDashBDlg(parent, name, context, + True, /* ok */ + True, /* cancel */ + True, /* help */ + False, /* apply; remove */ + modal) +#else + XFE_ViewDialog((XFE_View *) 0, parent, name, + context, + True, /* ok */ + True, /* cancel */ + True, /* help */ + False, /* apply; remove */ + False, /* separator */ + modal) +#endif /* GLUE_COMPO_CONTEXT */ +{ + + m_func = func; + m_cData = callData; + m_okToDestroy = False; + + Arg av [20]; + int ac = 0; + + /* Form: m_chrome is the dialog + */ + ac = 0; + XtSetArg (av [ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg (av [ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg (av [ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg (av [ac], XmNrightAttachment, XmATTACH_FORM); ac++; + XtSetArg (av [ac], XmNresizePolicy, XmRESIZE_NONE); ac++; + Widget form = XmCreateForm (m_chrome, "pickerParentForm", av, ac); +#if defined(GLUE_COMPO_CONTEXT) + m_aboveButtonArea = form; +#endif /* GLUE_COMPO_CONTEXT */ + XtManageChild (form); + XtVaSetValues(m_chrome, /* the dialog */ + // XmNallowShellResize, FALSE, + // XmNnoResize, False, + XmNdefaultButton, NULL, + NULL); + + /* picker pane + */ + MSG_Pane *pickerPane = 0; + if (!pane || !pickerContext) { + m_pickerContext = cloneCntxtNcreatePane(context, &pickerPane); + }/* pane */ + else { + m_pickerContext = pickerContext; + pickerPane = pane; + }/* else */ + + /* The list view + */ + XFE_ABComplPickerView *pickerView = + new XFE_ABComplPickerView(this, + form, + NULL, + m_pickerContext, + pickerPane); + setView(pickerView); + // + pickerView->registerInterest(XFE_ABComplPickerView::confirmed, + this, + (XFE_FunctionNotification)confirmed_cb); +#if defined(GLUE_COMPO_CONTEXT) + if (m_dashboard && pickerView) + m_dashboard->connect2Dashboard(pickerView); + // + attachView(); +#endif /* GLUE_COMPO_CONTEXT */ +} + +XFE_ABComplPickerDlg::~XFE_ABComplPickerDlg() +{ +#if defined(GLUE_COMPO_CONTEXT) + if (m_view && m_dashboard) + m_dashboard->disconnectFromDashboard(m_view); +#endif + m_view->unregisterInterest(XFE_ABComplPickerView::confirmed, + this, + (XFE_FunctionNotification)confirmed_cb); + m_okToDestroy = True; +} + +XFE_CALLBACK_DEFN(XFE_ABComplPickerDlg, confirmed)(XFE_NotificationCenter */*obj*/, + void */*clientData*/, + void */* callData */) +{ + ok(); +} + +void +XFE_ABComplPickerDlg::selectItem(int i) +{ + XFE_Outliner *outliner = ((XFE_MNListView *) m_view)->getOutliner(); + if (i < outliner->getTotalLines()) + outliner->selectItemExclusive(i); +} + +MWContext* +XFE_ABComplPickerDlg::cloneCntxtNcreatePane(MWContext *context, + MSG_Pane **pane) +{ + MWContext *pickerContext = XP_NewContext(); + if (!pickerContext) + return NULL; + + +#if defined(GLUE_COMPO_CONTEXT) + fe_ContextData *fec = XP_NEW_ZAP (fe_ContextData); + XP_ASSERT(fec); + CONTEXT_DATA((pickerContext)) = fec; + pickerContext->funcs = fe_BuildDisplayFunctionTable(); + // hack to identify parent type + XFE_Frame *f = ViewGlue_getFrame(context); + ViewGlue_addMapping(f, (void *)pickerContext); +#else + fe_ContextData *fec = XP_NEW_ZAP (fe_ContextData); + XP_ASSERT(fec); + CONTEXT_DATA((pickerContext)) = fec; + XFE_Frame *f = ViewGlue_getFrame(context); + ViewGlue_addMapping(f, (void *)pickerContext); + pickerContext->funcs = fe_BuildDisplayFunctionTable(); + CONTEXT_WIDGET(pickerContext) = CONTEXT_WIDGET(context); + // fe_InitRemoteServer (XtDisplay (widget)); +#endif /* GLUE_COMPO_CONTEXT */ + + // Initialize doc_csid + INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(pickerContext); + INTL_SetCSIDocCSID(c, fe_LocaleCharSetID); + INTL_SetCSIWinCSID(c, INTL_DocToWinCharSetID(INTL_GetCSIDocCSID(c))); + + // + int error = AB_CreateABPickerPane(pane, + pickerContext, + fe_getMNMaster(), + 8); + XP_ASSERT(*pane); + return pickerContext; +} + +void +XFE_ABComplPickerDlg::cancel() +{ + hide(); +} + +void +XFE_ABComplPickerDlg::ok() +{ + cancel(); + if (m_func) + m_func(((XFE_ABComplPickerView *)m_view)->getSelection(), + m_cData); +} + +/* C API + */ +extern "C" XFE_ABComplPickerDlg* +fe_showComplPickerDlg(Widget toplevel, + MWContext *context, + MSG_Pane *pane, + MWContext *pickerContext, + NCPickerExitFunc func, + void *callData) +{ + XFE_ABComplPickerDlg *dlg = + new XFE_ABComplPickerDlg(toplevel, + "CompletionPickerDlg", + True, + context, + pane, + pickerContext, + func, + callData); +#if 1 + return dlg; +#else + dlg->show(); +#endif +} + diff --git a/mozilla/cmd/xfe/src/ABComplPickerDlg.h b/mozilla/cmd/xfe/src/ABComplPickerDlg.h new file mode 100644 index 00000000000..3b4abb26a85 --- /dev/null +++ b/mozilla/cmd/xfe/src/ABComplPickerDlg.h @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- + Created: Tao Cheng , 16-mar-98 + */ + +#ifndef _ABCOMPLPICKERDLG_H_ +#define _ABCOMPLPICKERDLG_H_ + +#if defined(GLUE_COMPO_CONTEXT) +#include "ViewDashBDlg.h" +#else +#include "ViewDialog.h" +#endif /* GLUE_COMPO_CONTEXT */ + +typedef void (*NCPickerExitFunc)(void *clientData, void *callData); +// +#if defined(GLUE_COMPO_CONTEXT) +class XFE_ABComplPickerDlg: public XFE_ViewDashBDlg +#else +class XFE_ABComplPickerDlg: public XFE_ViewDialog +#endif /* GLUE_COMPO_CONTEXT */ +{ + +public: + + XFE_ABComplPickerDlg(Widget parent, + char *name, + Boolean modal, + MWContext *context, + MSG_Pane *pane, + MWContext *pickerContext, + NCPickerExitFunc func, + void *callData); + + virtual ~XFE_ABComplPickerDlg(); + + void selectItem(int); + + static MWContext* cloneCntxtNcreatePane(MWContext *context, + MSG_Pane **pane); + + XFE_CALLBACK_DECL(confirmed) + +protected: + virtual void cancel(); + virtual void ok(); + + NCPickerExitFunc m_func; + void *m_cData; +private: + + MWContext *m_pickerContext; +}; + +#endif /* _ABCOMPLPICKERDLG_H_ */ diff --git a/mozilla/cmd/xfe/src/ABComplPickerView.cpp b/mozilla/cmd/xfe/src/ABComplPickerView.cpp new file mode 100644 index 00000000000..241aff39681 --- /dev/null +++ b/mozilla/cmd/xfe/src/ABComplPickerView.cpp @@ -0,0 +1,527 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- + Created: Tao Cheng, 16-mar-1998 + */ + + +#include "Frame.h" +#include "ViewGlue.h" +#include "ABComplPickerView.h" + +#define COMPPCIKER_OUTLINER_GEOMETRY_PREF "comppicker.outliner_geometry" + +const char *XFE_ABComplPickerView::confirmed = + "XFE_ABComplPickerView::confirmed"; + +static char a_line[AB_MAX_STRLEN]; + +// icons +fe_icon XFE_ABComplPickerView::m_pabIcon = { 0 }; +fe_icon XFE_ABComplPickerView::m_ldapDirIcon = { 0 }; +fe_icon XFE_ABComplPickerView::m_mListIcon = { 0 }; + +XFE_ABComplPickerView::XFE_ABComplPickerView(XFE_Component *toplevel_component, + Widget parent, + XFE_View *parent_view, + MWContext *context, + MSG_Pane *p): + XFE_MNListView(toplevel_component, parent_view, context, p), + m_numAttribs(0), + m_curIndex(MSG_VIEWINDEXNONE) +{ + XP_ASSERT(p); + setPane(p); + + m_searchingDir = False; + m_frmParent = True; + + /* For outliner + */ + int num_columns = AB_GetNumColumnsForPane(p); + AB_AttribID *attribIDs = + (AB_AttribID *) XP_CALLOC(num_columns, + sizeof(AB_AttribID)); + m_numAttribs = num_columns; + int error = AB_GetColumnAttribIDsForPane(p, + attribIDs, + &m_numAttribs); +#if defined(DEBUG_tao_) + AB_ColumnInfo *cInfo = 0; + for (AB_ColumnID i=AB_ColumnID0; i < num_columns; i++) { + cInfo = AB_GetColumnInfoForPane(p, i); + if (cInfo) { + printf("\nID=%d, attribID=%d, displayString=%s, sortable=%d\n", + i, + cInfo->attribID, cInfo->displayString, cInfo->sortable); + AB_FreeColumnInfo(cInfo); + }/* if */ + }/* for i */ +#endif + + XP_ASSERT(num_columns == m_numAttribs); +#if defined(DEBUG_tao) + printf("\n--XFE_ABComplPickerView: num_columns=%d, m_numAttribs=%d\n", + num_columns, m_numAttribs); +#endif + static int column_widths[] = {3, 25, 25, 25, 8, 10, 12}; +#if 0 + if (num_columns > 4) + num_columns = 4; +#endif + m_outliner = new XFE_Outliner("compPickerList", + this, + toplevel_component, + parent, + False, // constantSize + True, // hasHeadings + num_columns, + 4,// num_visible + column_widths, + COMPPCIKER_OUTLINER_GEOMETRY_PREF); + + m_outliner->setMultiSelectAllowed(True); + m_outliner->setHideColumnsAllowed(True); + + /* BEGIN_3P: XmLGrid + */ + XtVaSetValues(m_outliner->getBaseWidget(), + XmNselectionPolicy, XmSELECT_MULTIPLE_ROW, + XtVaTypedArg, XmNblankBackground, XmRString, "white", 6, + XmNvisibleRows, 8, + NULL); + XtVaSetValues(m_outliner->getBaseWidget(), + XmNcellDefaults, True, + XtVaTypedArg, XmNcellBackground, XmRString, "white", 6, + NULL); + /* END_3P: XmLGrid + */ + + setBaseWidget(m_outliner->getBaseWidget()); + + /* reflect the right count + */ + uint32 count = 0; + count = MSG_GetNumLines(m_pane); + m_outliner->change(0, count, count); + if (count > 1) + m_outliner->selectItemExclusive(1); + + /* + * register interest in getting allconnectionsComplete + */ + MWContext *top = XP_GetNonGridContext (context); + XFE_Frame *f = ViewGlue_getFrame(top); + if (f) { +#if !defined(GLUE_COMPO_CONTEXT) + f->registerInterest(XFE_Frame::allConnectionsCompleteCallback, + this, + (XFE_FunctionNotification)allConnectionsComplete_cb); + +#endif /* GLUE_COMPO_CONTEXT */ + m_frmParent = (f->getType() == FRAME_MAILNEWS_COMPOSE)?True:False; + }/* if */ + +#if defined(GLUE_COMPO_CONTEXT) + /* use view + */ + ViewGlue_addMappingForCompo(this, (void *) context); + CONTEXT_WIDGET(context) = getBaseWidget(); +#endif + + + /* initialize the icons if they haven't already been + */ + Pixel bg_pixel; + XtVaGetValues(m_outliner->getBaseWidget(), XmNbackground, &bg_pixel, 0); + + if (!m_pabIcon.pixmap) + fe_NewMakeIcon(getToplevel()->getBaseWidget(), + /* umm. fix me + */ + BlackPixelOfScreen(XtScreen(m_outliner->getBaseWidget())), + bg_pixel, + &m_pabIcon, + NULL, + MNC_AddressSmall.width, + MNC_AddressSmall.height, + MNC_AddressSmall.mono_bits, + MNC_AddressSmall.color_bits, + MNC_AddressSmall.mask_bits, + FALSE); + + + if (!m_ldapDirIcon.pixmap) + fe_NewMakeIcon(getToplevel()->getBaseWidget(), + /* umm. fix me + */ + BlackPixelOfScreen(XtScreen(m_outliner->getBaseWidget())), + bg_pixel, + &m_ldapDirIcon, + NULL, + MN_FolderServer.width, + MN_FolderServer.height, + MN_FolderServer.mono_bits, + MN_FolderServer.color_bits, + MN_FolderServer.mask_bits, + FALSE); + + + if (!m_mListIcon.pixmap) + fe_NewMakeIcon(getToplevel()->getBaseWidget(), + /* umm. fix me + */ + BlackPixelOfScreen(XtScreen(m_outliner->getBaseWidget())), + bg_pixel, + &m_mListIcon, + NULL, + MN_People.width, + MN_People.height, + MN_People.mono_bits, + MN_People.color_bits, + MN_People.mask_bits, + FALSE); + + +} + +XFE_ABComplPickerView::~XFE_ABComplPickerView() +{ +#if !defined(GLUE_COMPO_CONTEXT) + /* + * un register interest in getting allconnectionsComplete + */ + MWContext *top = XP_GetNonGridContext (m_contextData); + XFE_Frame *f = ViewGlue_getFrame(top); + if (f) + f->unregisterInterest(XFE_Frame::allConnectionsCompleteCallback, + this, + (XFE_FunctionNotification)allConnectionsComplete_cb); +#endif /* GLUE_COMPO_CONTEXT */ + +} + +XFE_CALLBACK_DEFN(XFE_ABComplPickerView, allConnectionsComplete)(XFE_NotificationCenter */*obj*/, + void */*clientData*/, + void *callData) +{ + MWContext *c = (MWContext *) callData; + if (c == m_contextData) { + int error = AB_FinishSearchAB2(m_pane); + }/* if */ +} + +void +XFE_ABComplPickerView::allConnectionsComplete(MWContext *context) +{ + if (context == m_contextData) { + int error = AB_FinishSearchAB2(m_pane); + }/* if */ +} + +void +XFE_ABComplPickerView::listChangeFinished(XP_Bool /* asynchronous */, + MSG_NOTIFY_CODE notify, + MSG_ViewIndex where, + int32 num) +{ + switch (notify) { + case MSG_NotifyLDAPTotalContentChanged: + { +#if defined(DEBUG_tao) + printf("\nXFE_ABComplPickerView::MSG_NotifyLDAPTotalContentChanged, where=%d, num=%d", where, num); +#endif + m_outliner->change(0, num, num); + m_outliner->scroll2Item((int) where); + } + break; + + case MSG_NotifyInsertOrDelete: +#if defined(DEBUG_tao) + printf("\nXFE_ABComplPickerView::MSG_NotifyInsertOrDelete, where=%d, num=%d", where, num); +#endif + if (m_searchingDir) { + int error = AB_LDAPSearchResultsAB2(m_pane, + where, num); + } + break; + }/* switch notify */ +} + +void +XFE_ABComplPickerView::paneChanged(XP_Bool asynchronous, + MSG_PANE_CHANGED_NOTIFY_CODE notify_code, + int32 value) +{ +#if defined(DEBUG_tao) + printf("\nXFE_ABComplPickerView::paneChanged, asynchronous=%d, notify_code=%d, value=0x%x", asynchronous, notify_code, value); +#endif + switch (notify_code) { + case MSG_PaneChanged: + break; + + case MSG_PaneClose: + break; + + case MSG_PaneNotifyStartSearching: + m_searchingDir = True; + //notifyInterested(XFE_Component::progressBarCylonStart); + break; + + case MSG_PaneNotifyStopSearching: + //notifyInterested(XFE_Component::progressBarCylonStop); + m_searchingDir = False; + break; + + }/* notify_code */ +} + +// We're nice to our subclasses :) +char * +XFE_ABComplPickerView::getCellTipString(int row, int column) +{ + if (column == 0 ) { + AB_ContainerAttribValue *value = 0; + int error = + AB_GetContainerAttributeForPane(m_pane, + (MSG_ViewIndex) row, + attribName, + &value); + + XP_SAFE_SPRINTF(a_line, sizeof(a_line), + "%s", + EMPTY_STRVAL(value)?"":value->u.string); +#if defined(DEBUG_tao) + printf("\n##XFE_ABComplPickerView::getCellTipString:%s\n", + a_line); +#endif + if (m_frmParent) + return value?a_line:0; + else { + notifyInterested(XFE_View::statusNeedsUpdating, + value?a_line:""); + + return 0; + }/* else */ + AB_FreeContainerAttribValue(value); + }/* if */ + else + return 0; +} + +Boolean +XFE_ABComplPickerView::handlesCommand(CommandType cmd, void *calldata, + XFE_CommandInfo* i) +{ + return False; +} + +Boolean +XFE_ABComplPickerView::isCommandEnabled(CommandType cmd, void *calldata, + XFE_CommandInfo* i) +{ +} + +Boolean +XFE_ABComplPickerView::isCommandSelected(CommandType cmd, void *calldata, + XFE_CommandInfo* i) +{ +} + +void +XFE_ABComplPickerView::doCommand(CommandType cmd, void *calldata, + XFE_CommandInfo* i) +{ +} + +char * +XFE_ABComplPickerView::commandToString(CommandType cmd, void *calldata, + XFE_CommandInfo* i) +{ +} + +// The Outlinable interface. +char * +XFE_ABComplPickerView::getColumnName(int column) +{ + return getColumnHeaderText(column); +} + +char * +XFE_ABComplPickerView::getColumnHeaderText(int column) +{ + a_line[0] = '\0'; + if (column >= 0 && + column < m_numAttribs) { + AB_ColumnInfo *cInfo = + AB_GetColumnInfoForPane(m_pane, + (AB_ColumnID) column); + if (cInfo) { + XP_SAFE_SPRINTF(a_line, sizeof(a_line), + "%s", + cInfo->displayString?cInfo->displayString:""); + AB_FreeColumnInfo(cInfo); + }/* if */ + }/* if */ + return a_line; +} + +void * +XFE_ABComplPickerView::acquireLineData(int line) +{ +#if 1 + m_curIndex = (MSG_ViewIndex) line; + return (void *) &m_curIndex; // a non-zero val +#else + int error = AB_GetABIDForIndex(m_pane, + (MSG_ViewIndex) line, + &m_entryID); + + if (m_entryID == MSG_VIEWINDEXNONE) + return 0; + else + return (void *) m_entryID; +#endif +} + +char * +XFE_ABComplPickerView::getColumnText(int column) +{ + a_line[0] = '\0'; + AB_AttributeValue *value = 0; + AB_AttribID attrib = AB_attribEntryType; + if (column > 0 && + column < m_numAttribs) { + AB_ColumnInfo *cInfo = + AB_GetColumnInfoForPane(m_pane, + (AB_ColumnID) column); + if (cInfo) { + attrib = cInfo->attribID; + AB_FreeColumnInfo(cInfo); + + if (attrib != AB_attribEntryType) { + int error = + AB_GetEntryAttributeForPane(m_pane, + m_curIndex, + attrib, &value); + + XP_SAFE_SPRINTF(a_line, sizeof(a_line), + "%s", + EMPTY_STRVAL(value)?"":value->u.string); + + AB_FreeEntryAttributeValue(value); + }/* if */ + + }/* if */ + }/* if */ + else if (column == 0 && + !m_frmParent) { + AB_ContainerAttribValue *value = 0; + int error = + AB_GetContainerAttributeForPane(m_pane, + (MSG_ViewIndex) m_curIndex, + attribName, + &value); + + XP_SAFE_SPRINTF(a_line, sizeof(a_line), + "%s", + EMPTY_STRVAL(value)?"":value->u.string); + AB_FreeContainerAttribValue(value); + }/* else if */ + return a_line; +} + +fe_icon * +XFE_ABComplPickerView::getColumnIcon(int column) +{ + fe_icon *myIcon = 0; + if (column == 0) { + AB_ContainerType type = AB_GetEntryContainerType(m_pane, + m_curIndex); + switch (type) { + case AB_LDAPContainer: + myIcon = &m_ldapDirIcon; + break; + + case AB_PABContainer: + myIcon = &m_pabIcon; + break; + + case AB_MListContainer: + myIcon = &m_mListIcon; + break; + }/* switch */ + }/* if */ + return myIcon; +} + +void XFE_ABComplPickerView::doubleClickBody(const OutlineButtonFuncData *data) +{ + // notifyInterest() + notifyInterested(XFE_ABComplPickerView::confirmed); +} + + +void +XFE_ABComplPickerView::Buttonfunc(const OutlineButtonFuncData *data) +{ + int row = data->row, + clicks = data->clicks; + + if (row < 0) { + // clickHeader(data); + return; + } + else { + /* content row + */ + if (clicks == 2) { + m_outliner->selectItemExclusive(data->row); + doubleClickBody(data); + }/* clicks == 2 */ + else if (clicks == 1) { + /* assume single only for now + */ + m_outliner->selectItemExclusive(data->row); + getToplevel()->notifyInterested(XFE_View::chromeNeedsUpdating); + }/* clicks == 1 */ + }/* else */ +} + +void +XFE_ABComplPickerView::releaseLineData() +{ +} + + +AB_NameCompletionCookie * +XFE_ABComplPickerView::getSelection() +{ + int count = 0; + const int *indices = 0; + m_outliner->getSelection(&indices, &count); + + if (count && indices) + return AB_GetNameCompletionCookieForIndex(m_pane, + indices[0]); + else + return AB_GetNameCompletionCookieForIndex(m_pane, + 1); + +} diff --git a/mozilla/cmd/xfe/src/ABComplPickerView.h b/mozilla/cmd/xfe/src/ABComplPickerView.h new file mode 100644 index 00000000000..ba20d40f4a5 --- /dev/null +++ b/mozilla/cmd/xfe/src/ABComplPickerView.h @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- + Created: Tao Cheng, 16-mar-1998 + */ + + +#ifndef _ABCOMPLPICKERVIEW_H_ +#define _ABCOMPLPICKERVIEW_H_ + +#include "MNListView.h" + +#include "addrbk.h" + +class XFE_ABComplPickerView : public XFE_MNListView +{ +public: + XFE_ABComplPickerView(XFE_Component *toplevel_component, + Widget parent, + XFE_View *parent_view, + MWContext *context, + MSG_Pane *p); + virtual ~XFE_ABComplPickerView(); + + // + // callback to be notified when allconnectionsComplete + // + virtual void allConnectionsComplete(MWContext *context); + XFE_CALLBACK_DECL(allConnectionsComplete) + + // + virtual void listChangeFinished(XP_Bool asynchronous, + MSG_NOTIFY_CODE notify, + MSG_ViewIndex where, int32 num); + + virtual void paneChanged(XP_Bool asynchronous, + MSG_PANE_CHANGED_NOTIFY_CODE notify_code, + int32 value); + + // We're nice to our subclasses :) + virtual Boolean handlesCommand(CommandType cmd, void *calldata = NULL, + XFE_CommandInfo* i = NULL); + virtual Boolean isCommandEnabled(CommandType cmd, void *calldata = NULL, + XFE_CommandInfo* i = NULL); + virtual Boolean isCommandSelected(CommandType cmd, void *calldata = NULL, + XFE_CommandInfo* i = NULL); + virtual void doCommand(CommandType cmd, void *calldata = NULL, + XFE_CommandInfo* i = NULL); + virtual char *commandToString(CommandType cmd, void *calldata = NULL, + XFE_CommandInfo* i = NULL); + + // The Outlinable interface. + virtual void *ConvFromIndex(int /* index */){return NULL;}; + virtual int ConvToIndex(void */* item */){return 0;}; + virtual fe_icon *getColumnHeaderIcon(int column){return NULL;}; + virtual EOutlinerTextStyle getColumnHeaderStyle(int){return OUTLINER_Default;}; + + virtual char *getColumnName(int column); + virtual char *getColumnHeaderText(int column); + virtual void *acquireLineData(int line); + virtual char *getColumnText(int column); + virtual fe_icon *getColumnIcon(int column); + + virtual void getTreeInfo(XP_Bool *expandable, XP_Bool *is_expanded, + int *depth, OutlinerAncestorInfo **ancestor) {}; + EOutlinerTextStyle getColumnStyle(int){}; + void Flippyfunc(const OutlineFlippyFuncData *){}; + + virtual void doubleClickBody(const OutlineButtonFuncData *data); + virtual void Buttonfunc(const OutlineButtonFuncData *data); + virtual void releaseLineData(); + + // Get tooltipString & docString; + // returned string shall be freed by the callee + // row < 0 indicates heading row; otherwise it is a content row + // (starting from 0) + // + virtual char *getCellTipString(int /* row */, int /* column */); + virtual char *getCellDocString(int /* row */, int /* column */) { + return NULL;} + + // columns for the Outliner + enum { + OUTLINER_COLUMN_TYPE = 0, + OUTLINER_COLUMN_NAME, + OUTLINER_COLUMN_TITLE, + OUTLINER_COLUMN_DEPARTMENT, + OUTLINER_COLUMN_LAST + }; + + static fe_icon m_pabIcon; + static fe_icon m_ldapDirIcon; + static fe_icon m_mListIcon; + + AB_NameCompletionCookie *getSelection(); + + static const char *confirmed; + +protected: + int m_numAttribs; + ABID m_entryID; /* entryID for current line */ + MSG_ViewIndex m_curIndex; + XP_Bool m_searchingDir; + XP_Bool m_frmParent; +}; +#endif /* _ABCOMPLPICKERVIEW_H_ */ + diff --git a/mozilla/cmd/xfe/src/AddressFolderView.cpp b/mozilla/cmd/xfe/src/AddressFolderView.cpp index 7eb10d04df4..9ff23de57fa 100644 --- a/mozilla/cmd/xfe/src/AddressFolderView.cpp +++ b/mozilla/cmd/xfe/src/AddressFolderView.cpp @@ -1221,6 +1221,7 @@ XFE_AddressFolderView::changedItem(char *pString, int* iconType, extern "C" char * xfe_ExpandName(char * pString, int* iconID, short* xxx, ABook *pAddrBook, DIR_Server *pDirServer) { +#if !defined(MOZ_NEWADDR) ABID entryID; ABID field; char * fullname; @@ -1258,6 +1259,7 @@ extern "C" char * xfe_ExpandName(char * pString, int* iconID, short* xxx, if (fullname) return fullname; } +#endif return NULL; } diff --git a/mozilla/cmd/xfe/src/AddressOutliner.cpp b/mozilla/cmd/xfe/src/AddressOutliner.cpp index 6af5fdefb13..108c9283bea 100644 --- a/mozilla/cmd/xfe/src/AddressOutliner.cpp +++ b/mozilla/cmd/xfe/src/AddressOutliner.cpp @@ -1676,6 +1676,8 @@ extern "C" char * xfe_ExpandForNameCompletion(char * pString, ABook *pAddrBook, DIR_Server *pDirServer) { +#if !defined(MOZ_NEWADDR) + ABID entryID; ABID field; char *pName = XP_STRDUP(pString); @@ -1723,7 +1725,7 @@ extern "C" char * xfe_ExpandForNameCompletion(char * pString, } } - +#endif return NULL; } diff --git a/mozilla/cmd/xfe/src/CategoryView.cpp b/mozilla/cmd/xfe/src/CategoryView.cpp new file mode 100644 index 00000000000..34a678c0478 --- /dev/null +++ b/mozilla/cmd/xfe/src/CategoryView.cpp @@ -0,0 +1,331 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1996 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* + CategoryView.cpp -- presents view of news categories. + Created: Chris Toshok , 29-Aug-96. + */ + +#include "MozillaApp.h" +#include "CategoryView.h" +#include "xpassert.h" + +#include "xpgetstr.h" +extern int XFE_CATEGORY_OUTLINER_COLUMN_NAME; + +#ifdef DEBUG_toshok +#define D(x) x +#else +#define D(x) +#endif + +#define OUTLINER_GEOMETRY_PREF "mail.categorypane.outliner_geometry" + +const int XFE_CategoryView::OUTLINER_COLUMN_NAME = 0; + +const char *XFE_CategoryView::categorySelected = "XFE_CategoryView::categorySelected"; + +XFE_CategoryView::XFE_CategoryView(XFE_Component *toplevel_component, + Widget parent, + XFE_View *parent_view, MWContext *context, + MSG_Pane *p) + : XFE_MNListView(toplevel_component, parent_view, context, p) +{ + int num_columns = 1; + static int default_column_widths[] = { 20 }; + + // create our view if we don't have one already. + if (!p) + setPane(MSG_CreateCategoryPane(m_contextData, + XFE_MNView::m_master)); + + // create our outliner. + m_outliner = new XFE_Outliner("categoryList", + this, + getToplevel(), + parent, + FALSE, // constantSize + TRUE, // hasHeadings + num_columns, + num_columns, + default_column_widths, + OUTLINER_GEOMETRY_PREF); + + m_outliner->setPipeColumn( OUTLINER_COLUMN_NAME ); + m_outliner->setMultiSelectAllowed( True ); + + setBaseWidget(m_outliner->getBaseWidget()); +} + +XFE_CategoryView::~XFE_CategoryView() +{ +D(printf ("In XFE_CategoryView::~XFE_CategoryView()\n");) + + delete m_outliner; + + destroyPane(); +} + +void +XFE_CategoryView::loadFolder(MSG_FolderInfo *folderinfo) +{ + m_folderInfo = folderinfo; + + MSG_LoadNewsGroup(m_pane, m_folderInfo); + + XFE_MozillaApp::theApp()->notifyInterested(XFE_MNView::MNChromeNeedsUpdating); +} + +void +XFE_CategoryView::selectCategory(MSG_FolderInfo *category) +{ + MSG_ViewIndex index; + + index = MSG_GetFolderIndexForInfo(m_pane, category, True); + + if (index != MSG_VIEWINDEXNONE) + m_outliner->selectItemExclusive(index); +} + +Boolean +XFE_CategoryView::isCommandEnabled(CommandType cmd, + void *calldata, XFE_CommandInfo*) +{ + return XFE_MNListView::isCommandEnabled(cmd, calldata); +} + +Boolean +XFE_CategoryView::handlesCommand(CommandType cmd, + void *calldata, XFE_CommandInfo*) +{ + if (cmd == xfeCmdDeleteCategory + || cmd == xfeCmdKillCategory + || cmd == xfeCmdMarkCategoryRead + || cmd == xfeCmdMarkCategoryUnread + || cmd == xfeCmdSelectCategory) + { + return True; + } + else + { + return XFE_MNListView::handlesCommand(cmd, calldata); + } +} + +void +XFE_CategoryView::doCommand(CommandType cmd, void *calldata, XFE_CommandInfo* info) +{ + MSG_CommandType msg_cmd = commandToMsgCmd(cmd); + const int *selected; + int count; + + m_outliner->getSelection(&selected, &count); + + if ((msg_cmd == (MSG_CommandType)~0) || + (msg_cmd == MSG_PostNew) || + (msg_cmd == MSG_MailNew) ) + { + XFE_MNListView::doCommand(cmd, calldata, info); + } + else + { + MSG_Command(m_pane, msg_cmd, (MSG_ViewIndex*)selected, count); + } + + getToplevel()->notifyInterested(XFE_View::chromeNeedsUpdating); +} + +void * +XFE_CategoryView::ConvFromIndex(int index) +{ + return MSG_GetFolderInfo(m_pane, index); +} + +int +XFE_CategoryView::ConvToIndex(void *item) +{ + MSG_ViewIndex index = MSG_GetFolderIndex(m_pane, (MSG_FolderInfo*)item); + + if (index == MSG_VIEWINDEXNONE) + return -1; + else + return index; +} + + +char * +XFE_CategoryView::getColumnName(int column) +{ + // there is only one column in the category view. + XP_ASSERT(column == OUTLINER_COLUMN_NAME); + + return "Category"; +} + +char * +XFE_CategoryView::getColumnHeaderText(int column) +{ + // there is only one column in the category view. + XP_ASSERT(column == OUTLINER_COLUMN_NAME); + + return XP_GetString(XFE_CATEGORY_OUTLINER_COLUMN_NAME); +} + +fe_icon * +XFE_CategoryView::getColumnHeaderIcon(int) +{ + return 0; +} + +EOutlinerTextStyle +XFE_CategoryView::getColumnHeaderStyle(int) +{ + return OUTLINER_Default; +} + +void * +XFE_CategoryView::aquireLineData(int line) +{ + m_ancestorInfo = NULL; + + if (!MSG_GetFolderLineByIndex(m_pane, line, 1, &m_categoryLine)) + return NULL; + + if ( m_categoryLine.level > 0 ) + { + m_ancestorInfo = new OutlinerAncestorInfo[ m_categoryLine.level ]; + } + else + { + m_ancestorInfo = new OutlinerAncestorInfo[ 1 ]; + } + + // ripped straight from the winfe + int i = m_categoryLine.level - 1; + int idx = line + 1; + int total_lines = m_outliner->getTotalLines(); + while ( i > 0 ) { + int level; + if ( idx < total_lines ) { + level = MSG_GetFolderLevelByIndex( m_pane, idx ); + if ( (level - 1) == i ) { + m_ancestorInfo[i].has_prev = TRUE; + m_ancestorInfo[i].has_next = TRUE; + i--; + idx++; + } else if ( (level - 1) < i ) { + m_ancestorInfo[i].has_prev = FALSE; + m_ancestorInfo[i].has_next = FALSE; + i--; + } else { + idx++; + } + } else { + m_ancestorInfo[i].has_prev = FALSE; + m_ancestorInfo[i].has_next = FALSE; + i--; + } + } + + m_ancestorInfo[0].has_prev = FALSE; + m_ancestorInfo[0].has_next = FALSE; + + return &m_categoryLine; +} + +void +XFE_CategoryView::getTreeInfo(XP_Bool *expandable, XP_Bool *is_expanded, + int *depth, OutlinerAncestorInfo **ancestor) +{ + XP_Bool is_line_expandable; + XP_Bool is_line_expanded; + + is_line_expandable = m_categoryLine.numChildren > 0; + + if (is_line_expandable) + { + is_line_expanded = !(m_categoryLine.flags & MSG_FOLDER_FLAG_ELIDED); + } + else + { + is_line_expanded = FALSE; + } + + if (ancestor) + *ancestor = m_ancestorInfo; + + if (depth) + *depth = m_categoryLine.level - 1; + + if (expandable) + *expandable = is_line_expandable; + + if (is_expanded) + *is_expanded = is_line_expanded; +} + +EOutlinerTextStyle +XFE_CategoryView::getColumnStyle(int) +{ + return (m_categoryLine.unseen > 0) ? OUTLINER_Bold : OUTLINER_Default; +} + +char * +XFE_CategoryView::getColumnText(int) +{ + if (m_categoryLine.prettyName) + return (char*)m_categoryLine.prettyName; + else + return (char*)m_categoryLine.name; +} + +fe_icon * +XFE_CategoryView::getColumnIcon(int) +{ + return &newsgroupIcon; +} + +void +XFE_CategoryView::releaseLineData() +{ + delete [] m_ancestorInfo; + m_ancestorInfo = NULL; +} + +void +XFE_CategoryView::Buttonfunc(const OutlineButtonFuncData *data) +{ + MSG_FolderLine line; + + if (!MSG_GetFolderLineByIndex(m_pane, data->row, 1, &line)) return; + + m_outliner->selectItemExclusive(data->row); + + notifyInterested(categorySelected, MSG_GetFolderInfo(m_pane, data->row)); + + getToplevel()->notifyInterested(XFE_View::chromeNeedsUpdating); +} + +void +XFE_CategoryView::Flippyfunc(const OutlineFlippyFuncData *data) +{ + if (MSG_ExpansionDelta(m_pane, data->row) != 0) + MSG_ToggleExpansion(m_pane, data->row, NULL); + getToplevel()->notifyInterested(XFE_View::chromeNeedsUpdating); +} diff --git a/mozilla/cmd/xfe/src/CategoryView.h b/mozilla/cmd/xfe/src/CategoryView.h new file mode 100644 index 00000000000..f2c7548b8fa --- /dev/null +++ b/mozilla/cmd/xfe/src/CategoryView.h @@ -0,0 +1,84 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1996 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* + CategoryView.h -- class definition for CategoryView. + + Created: Chris Toshok , 29-Aug-96. + */ + +#ifndef _xfe_categoryview_h +#define _xfe_categoryview_h + +#include "MNListView.h" +#include "Outlinable.h" + +class XFE_CategoryView : public XFE_MNListView +{ +public: + XFE_CategoryView(XFE_Component *toplevel_component, Widget parent, + XFE_View *parent_view, MWContext *context, + MSG_Pane *p = NULL); + + virtual ~XFE_CategoryView(); + + void loadFolder(MSG_FolderInfo *folderinfo); + + void selectCategory(MSG_FolderInfo *category); + + virtual Boolean isCommandEnabled(CommandType command, void *calldata = NULL, + XFE_CommandInfo* i = NULL); + virtual Boolean handlesCommand(CommandType command, void *calldata = NULL, + XFE_CommandInfo* i = NULL); + virtual void doCommand(CommandType command, void *calldata = NULL, + XFE_CommandInfo* i = NULL); + + /* Outlinable interface methods */ + virtual void *ConvFromIndex(int index); + virtual int ConvToIndex(void *item); + + virtual char *getColumnName(int column); + + virtual char *getColumnHeaderText(int column); + virtual fe_icon *getColumnHeaderIcon(int column); + virtual EOutlinerTextStyle getColumnHeaderStyle(int column); + virtual void *aquireLineData(int line); + virtual void getTreeInfo(XP_Bool *expandable, XP_Bool *is_expanded, int *depth, OutlinerAncestorInfo **ancestor); + virtual EOutlinerTextStyle getColumnStyle(int column); + virtual char *getColumnText(int column); + virtual fe_icon *getColumnIcon(int column); + virtual void releaseLineData(); + + virtual void Buttonfunc(const OutlineButtonFuncData *data); + virtual void Flippyfunc(const OutlineFlippyFuncData *data); + + static const char *categorySelected; + +private: + static const int OUTLINER_COLUMN_NAME; + + MSG_FolderInfo *m_folderInfo; + + // for the outlinable stuff + MSG_FolderLine m_categoryLine; + OutlinerAncestorInfo *m_ancestorInfo; +}; + +#endif /* _xfe_categoryview_h */ + diff --git a/mozilla/cmd/xfe/src/LIConflictDialog.cpp b/mozilla/cmd/xfe/src/LIConflictDialog.cpp new file mode 100644 index 00000000000..a3794b0782e --- /dev/null +++ b/mozilla/cmd/xfe/src/LIConflictDialog.cpp @@ -0,0 +1,160 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1996 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/********************************************************************** + LIConflictDialog.cpp + By Daniel Malmer + 5/18/98 + +**********************************************************************/ + +#include "LIConflictDialog.h" +#include "li_public.h" +#include "xfe.h" + +#include +#include + + +extern "C" void +fe_makeConflictDialog(LIConflictDialogCallback f, void* closure, const char* title, const char* message, const char* left_button, const char* right_button) +{ + XFE_LIConflictDialog* dialog = new XFE_LIConflictDialog(FE_GetToplevelWidget(), title, message, left_button, right_button); + + dialog->show(); + + while ( dialog->selection_made() == 0 ) { + FEU_StayingAlive(); + } + + f(closure, dialog->state()); + + delete(dialog); +} + + +XFE_LIConflictDialog::XFE_LIConflictDialog(Widget parent, const char* title, const char* message, const char* left_button, const char* right_button) : XFE_Dialog(parent, "liConflict", TRUE, TRUE, FALSE, FALSE, TRUE, TRUE) +{ + int ac; + Arg av[16]; + Widget form; + XmString xm_str; + + m_selection_made = 0; + + ac = 0; + form = XmCreateForm(m_chrome, "form", av, ac); + XtManageChild(form); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + m_message_label = XmCreateLabel(form, "messageLabel", av, ac); + XtManageChild(m_message_label); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, m_message_label); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + m_query_label = XmCreateLabel(form, "queryLabel", av, ac); + XtManageChild(m_query_label); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, m_query_label); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + m_always_toggle = XmCreateToggleButtonGadget(form, "alwaysToggle", av, ac); + XtManageChild(m_always_toggle); + + setTitle(title); + + xm_str = XmStringCreate((char*) message, XmFONTLIST_DEFAULT_TAG); + XtVaSetValues(m_message_label, XmNlabelString, xm_str, NULL); + XmStringFree(xm_str); + + xm_str = XmStringCreate((char*) left_button, XmFONTLIST_DEFAULT_TAG); + XtVaSetValues(m_okButton, XmNlabelString, xm_str, NULL); + XmStringFree(xm_str); + + xm_str = XmStringCreate((char*) right_button, XmFONTLIST_DEFAULT_TAG); + XtVaSetValues(m_cancelButton, XmNlabelString, xm_str, NULL); + XmStringFree(xm_str); + + XtAddCallback(m_chrome, XmNokCallback, ok_callback, this); + XtAddCallback(m_chrome, XmNcancelCallback, cancel_callback, this); +} + + +XFE_LIConflictDialog::~XFE_LIConflictDialog() +{ +} + + +void +XFE_LIConflictDialog::ok_callback(Widget w, XtPointer closure, XtPointer data) +{ + ((XFE_LIConflictDialog*) closure)->useLocalCallback(w, data); +} + + +void +XFE_LIConflictDialog::cancel_callback(Widget w, XtPointer closure, XtPointer data) +{ + ((XFE_LIConflictDialog*) closure)->useServerCallback(w, data); +} + + +void +XFE_LIConflictDialog::useLocalCallback(Widget w, XtPointer data) +{ + m_state = KEEP_LOCAL_FILE; + hide(); + m_selection_made = 1; +} + + +void +XFE_LIConflictDialog::useServerCallback(Widget w, XtPointer data) +{ + m_state = DOWNLOAD_SERVER_FILE; + hide(); + m_selection_made = 1; +} + + +int +XFE_LIConflictDialog::state() +{ + return (m_state | + (XmToggleButtonGetState(m_always_toggle) ? APPLY_TO_ALL : 0)); +} + + +void +XFE_LIConflictDialog::setTitle(const char* title) +{ + Widget shell = getBaseWidget(); + + while ( !XtIsShell(shell) && XtParent(shell) ) { + shell = XtParent(shell); + } + XtVaSetValues(shell, XmNtitle, title, NULL); +} + + diff --git a/mozilla/cmd/xfe/src/LIConflictDialog.h b/mozilla/cmd/xfe/src/LIConflictDialog.h new file mode 100644 index 00000000000..82261270ca4 --- /dev/null +++ b/mozilla/cmd/xfe/src/LIConflictDialog.h @@ -0,0 +1,70 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +/********************************************************************** + LIConflictDialog.h + By Daniel Malmer + 5/18/98 + +**********************************************************************/ + +#ifndef __LIConflictDialog_h +#define __LIConflictDialog_h + +#include "Dialog.h" + +class XFE_LIConflictDialog : public XFE_Dialog +{ +public: + + // Constructors, Destructors + + XFE_LIConflictDialog(Widget parent, const char*, const char*, const char*, const char*); + + virtual ~XFE_LIConflictDialog(); + + // Accessor functions + + // Modifier functions + + // virtual void show(); + + // virtual void hide(); + + void useLocalCallback(Widget, XtPointer); + void useServerCallback(Widget, XtPointer); + + static void ok_callback(Widget, XtPointer, XtPointer); + static void cancel_callback(Widget, XtPointer, XtPointer); + + int state(); + + int selection_made() {return m_selection_made;} + +private: + Widget m_message_label; + Widget m_query_label; + Widget m_always_toggle; + int m_state; + int m_selection_made; + void setTitle(const char* title); +}; + +#endif /* __LIConflictDialog_h */ diff --git a/mozilla/cmd/xfe/src/LILoginAdvancedDialog.cpp b/mozilla/cmd/xfe/src/LILoginAdvancedDialog.cpp new file mode 100644 index 00000000000..2c362beca3e --- /dev/null +++ b/mozilla/cmd/xfe/src/LILoginAdvancedDialog.cpp @@ -0,0 +1,93 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/********************************************************************** + LILoginAdvancedDialog.cpp + By Daniel Malmer + 5/4/98 + +**********************************************************************/ + +#include "LILoginAdvancedDialog.h" + + +XFE_LILoginAdvancedDialog::XFE_LILoginAdvancedDialog(Widget parent) : XFE_Dialog(parent, "liLoginOptions", TRUE, TRUE, FALSE, FALSE, FALSE) +{ + Widget form; + + form = XmCreateForm(m_chrome, "prefs", NULL, 0); + XtManageChild(form); + + m_serverFrame = new XFE_PrefsPageLIServer(form); + m_filesFrame = new XFE_PrefsPageLIFiles(form); + + m_serverFrame->create(); + m_serverFrame->init(); + + m_filesFrame->create(); + m_filesFrame->init(); + + XtVaSetValues(m_serverFrame->get_frame(), + XmNbottomAttachment, XmATTACH_NONE, + NULL); + XtVaSetValues(m_filesFrame->get_frame(), + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_serverFrame->get_frame(), + NULL); + + XtAddCallback(m_chrome, XmNokCallback, ok_callback, this); + XtAddCallback(m_chrome, XmNcancelCallback, cancel_callback, this); +} + + +XFE_LILoginAdvancedDialog::~XFE_LILoginAdvancedDialog() +{ +} + + +void +XFE_LILoginAdvancedDialog::ok_callback(Widget w, XtPointer closure, XtPointer data) +{ + ((XFE_LILoginAdvancedDialog*) closure)->okCallback(w, data); +} + + +void +XFE_LILoginAdvancedDialog::cancel_callback(Widget w, XtPointer closure, XtPointer data) +{ + ((XFE_LILoginAdvancedDialog*) closure)->cancelCallback(w, data); +} + + +void +XFE_LILoginAdvancedDialog::okCallback(Widget w, XtPointer data) +{ + m_serverFrame->save(); + m_filesFrame->save(); + cancelCallback(w, data); +} + + +void +XFE_LILoginAdvancedDialog::cancelCallback(Widget w, XtPointer data) +{ + hide(); +} + + diff --git a/mozilla/cmd/xfe/src/LILoginAdvancedDialog.h b/mozilla/cmd/xfe/src/LILoginAdvancedDialog.h new file mode 100644 index 00000000000..3ee18ab590a --- /dev/null +++ b/mozilla/cmd/xfe/src/LILoginAdvancedDialog.h @@ -0,0 +1,64 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +/********************************************************************** + LILoginAdvancedDialog.h + By Daniel Malmer + 5/4/98 + +**********************************************************************/ + +#ifndef __LILoginAdvancedDialog_h +#define __LILoginAdvancedDialog_h + +#include "Dialog.h" +#include "PrefsPageLIServer.h" +#include "PrefsPageLIFiles.h" + +class XFE_LILoginAdvancedDialog : public XFE_Dialog +{ +public: + + // Constructors, Destructors + + XFE_LILoginAdvancedDialog(Widget parent); + + virtual ~XFE_LILoginAdvancedDialog(); + + // Accessor functions + + // Modifier functions + + // virtual void show(); + + // virtual void hide(); + + void okCallback(Widget, XtPointer); + void cancelCallback(Widget, XtPointer); + + static void ok_callback(Widget, XtPointer, XtPointer); + static void cancel_callback(Widget, XtPointer, XtPointer); + +private: + XFE_PrefsPageLIServer* m_serverFrame; + XFE_PrefsPageLIFiles* m_filesFrame; +}; + +#endif /* __LILoginAdvancedDialog_h */ diff --git a/mozilla/cmd/xfe/src/LILoginDialog.cpp b/mozilla/cmd/xfe/src/LILoginDialog.cpp new file mode 100644 index 00000000000..c3a19c9dd36 --- /dev/null +++ b/mozilla/cmd/xfe/src/LILoginDialog.cpp @@ -0,0 +1,134 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/********************************************************************** + LILoginDialog.cpp + By Daniel Malmer + 5/4/98 + +**********************************************************************/ + +#include "LILoginDialog.h" +#include "LILoginAdvancedDialog.h" + +extern "C" void +fe_createLILoginDialog(Widget parent) +{ + XFE_LILoginDialog* dialog = new XFE_LILoginDialog(parent); + + dialog->show(); + + while ( dialog->selection_made() == 0 ) { + FEU_StayingAlive(); + } + + delete(dialog); +} + + +XFE_LILoginDialog::XFE_LILoginDialog(Widget parent) : XFE_Dialog(parent, "liLogin", TRUE, TRUE, FALSE, FALSE, FALSE) +{ + Widget form; + Widget button; + + m_selection_made = 0; + + form = XmCreateForm(m_chrome, "prefs", NULL, 0); + XtManageChild(form); + + m_userFrame = new XFE_PrefsPageLIGeneral(form, XFE_PrefsPageLIGeneral::login); + + m_userFrame->create(); + m_userFrame->init(); + + XtUnmanageChild(m_userFrame->get_top_frame()); + + XtVaSetValues(m_userFrame->get_bottom_frame(), + XmNtopAttachment, XmATTACH_FORM, + NULL); + + button = XmCreatePushButton(form, "liLoginAdvancedButton", NULL, 0); + XtManageChild(button); + + XtVaSetValues(button, XmNrightAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopOffset, 4, + XmNtopWidget, m_userFrame->get_bottom_frame(), + NULL); + + XtAddCallback(button, XmNactivateCallback, advanced_callback, this); + + XtAddCallback(m_chrome, XmNokCallback, ok_callback, this); + XtAddCallback(m_chrome, XmNcancelCallback, cancel_callback, this); +} + + +XFE_LILoginDialog::~XFE_LILoginDialog() +{ +} + + +void +XFE_LILoginDialog::advanced_callback(Widget w, XtPointer closure, XtPointer data) +{ + ((XFE_LILoginDialog*) closure)->advancedCallback(w, data); +} + + +void +XFE_LILoginDialog::advancedCallback(Widget w, XtPointer data) +{ + XFE_LILoginAdvancedDialog* dialog; + + dialog = new XFE_LILoginAdvancedDialog(m_wParent); + + dialog->show(); +} + + +void +XFE_LILoginDialog::ok_callback(Widget w, XtPointer closure, XtPointer data) +{ + ((XFE_LILoginDialog*) closure)->okCallback(w, data); +} + + +void +XFE_LILoginDialog::cancel_callback(Widget w, XtPointer closure, XtPointer data) +{ + ((XFE_LILoginDialog*) closure)->cancelCallback(w, data); +} + + +void +XFE_LILoginDialog::okCallback(Widget w, XtPointer data) +{ + m_userFrame->save(); + cancelCallback(w, data); +} + + +void +XFE_LILoginDialog::cancelCallback(Widget w, XtPointer data) +{ + m_selection_made = 1; + hide(); +} + + diff --git a/mozilla/cmd/xfe/src/LILoginDialog.h b/mozilla/cmd/xfe/src/LILoginDialog.h new file mode 100644 index 00000000000..10e1f0fa343 --- /dev/null +++ b/mozilla/cmd/xfe/src/LILoginDialog.h @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/********************************************************************** + LILoginDialog.h + By Daniel Malmer + 5/4/98 + +**********************************************************************/ + +#ifndef __LILoginDialog_h +#define __LILoginDialog_h + +#include "Dialog.h" +#include "PrefsPageLIGeneral.h" + +class XFE_LILoginDialog : public XFE_Dialog +{ +public: + + // Constructors, Destructors + + XFE_LILoginDialog(Widget parent); + + virtual ~XFE_LILoginDialog(); + + // Accessor functions + + // Modifier functions + + // virtual void show(); + + // virtual void hide(); + + void okCallback(Widget, XtPointer); + void cancelCallback(Widget, XtPointer); + void advancedCallback(Widget, XtPointer); + + static void advanced_callback(Widget, XtPointer, XtPointer); + static void ok_callback(Widget, XtPointer, XtPointer); + static void cancel_callback(Widget, XtPointer, XtPointer); + + int selection_made() {return m_selection_made;} + +private: + XFE_PrefsPageLIGeneral* m_userFrame; + int m_selection_made; +}; + +#endif /* __LILoginDialog_h */ diff --git a/mozilla/cmd/xfe/src/Makefile b/mozilla/cmd/xfe/src/Makefile index b88e0b73705..33627b6d296 100644 --- a/mozilla/cmd/xfe/src/Makefile +++ b/mozilla/cmd/xfe/src/Makefile @@ -51,6 +51,7 @@ REQUIRES =\ style \ xfeicons \ rdf \ + progress \ xpcom \ netcnvts \ msg \ @@ -225,6 +226,10 @@ CPPSRCS += \ PrefsPageNServer.cpp \ ProgressFrame.cpp \ SubUpgradeDialog.cpp \ + TabView.cpp \ + XmLFolderDialog.cpp \ + XmLFolderView.cpp \ + ViewDashBDlg.cpp \ $(NULL) endif diff --git a/mozilla/cmd/xfe/src/PrefsDialog.cpp b/mozilla/cmd/xfe/src/PrefsDialog.cpp index a1ad17e4c23..18ef1354153 100644 --- a/mozilla/cmd/xfe/src/PrefsDialog.cpp +++ b/mozilla/cmd/xfe/src/PrefsDialog.cpp @@ -31,6 +31,9 @@ #include "xpassert.h" #include "xfe.h" #include "PrefsDialog.h" +#include "PrefsPageMessages.h" +#include "PrefsPageNServer.h" +#include "PrefsPageAddress.h" #include "prefapi.h" #include @@ -233,7 +236,7 @@ typedef enum _prefsPageType { PAGE_TYPE_MAILNEWS_MESSAGES, PAGE_TYPE_MAILNEWS_MAILSERVER, PAGE_TYPE_MAILNEWS_NSERVER, - PAGE_TYPE_MAILNEWS_ADDRESS, + PAGE_TYPE_MAILNEWS_ADDRESS, PAGE_TYPE_MAILNEWS_COPIES, PAGE_TYPE_MAILNEWS_HTML, PAGE_TYPE_MAILNEWS_RECEIPTS, @@ -1828,15 +1831,9 @@ void XFE_PrefsDialog::newPage(XFE_PrefsPage *&page, case PAGE_TYPE_MAILNEWS_MAILSERVER: page = new XFE_PrefsPageMailNewsMserver(this); break; - case PAGE_TYPE_MAILNEWS_NEWSSERVER: - page = new XFE_PrefsPageMailNewsNserver(this); - break; case PAGE_TYPE_MAILNEWS_NSERVER: page = new XFE_PrefsPageNServer(this); break; - case PAGE_TYPE_MAILNEWS_ADDRBOOK: - page = new XFE_PrefsPageMailNewsAddrBook(this); - break; case PAGE_TYPE_MAILNEWS_ADDRESS: page = new XFE_PrefsPageAddress(this); break; diff --git a/mozilla/cmd/xfe/src/PrefsDialog.h b/mozilla/cmd/xfe/src/PrefsDialog.h index b60d3beb60e..f951a4f9606 100644 --- a/mozilla/cmd/xfe/src/PrefsDialog.h +++ b/mozilla/cmd/xfe/src/PrefsDialog.h @@ -40,9 +40,9 @@ #include "Outliner.h" #include "Outlinable.h" #include "PrefsData.h" -#ifdef MOZ_LDAP - #include "dirprefs.h" -#endif +/* #ifdef MOZ_LDAP */ /* (rb) This file is required */ +#include "dirprefs.h" +/* #endif */ #ifdef MOZ_MAIL_NEWS #include "PrefsDialogMServer.h" #include "PrefsMailFolderDlg.h" @@ -748,32 +748,7 @@ private: class XFE_PrefsIncomingMServer { public: -<<<<<<< PrefsDialog.h - // Constructors, Destructors - - XFE_PrefsPageMailNewsComposition(XFE_PrefsDialog *dialog); - virtual ~XFE_PrefsPageMailNewsComposition(); - - // Manipulators - - virtual void create(); - virtual void init(); - virtual void install(); - virtual void save(); - - // Gets - - PrefsDataMailNewsComposition *getData(); - - // Callbacks - page Mail News/Composition - - static void cb_more(Widget, XtPointer, XtPointer); - -private: - - // Data -======= XFE_PrefsIncomingMServer(Widget incomingServerBox, XFE_PrefsPage *page); virtual ~XFE_PrefsIncomingMServer(); XFE_PrefsPage *getPage() { return prefsPage; }; @@ -841,12 +816,7 @@ private: XFE_PrefsPage *prefsPage; Widget m_local_dir_text; }; ->>>>>>> 3.1.14.1 -<<<<<<< PrefsDialog.h - PrefsDataMailNewsComposition *m_prefsDataMailNewsComposition; -======= ->>>>>>> 3.1.14.1 // News Servers Data class XFE_PrefsNewsServer @@ -913,43 +883,6 @@ private: class XFE_PrefsPageMailNewsCopies : public XFE_PrefsPage { public: -<<<<<<< PrefsDialog.h - - // Constructors, Destructors - - XFE_PrefsPageMailNewsNserver(XFE_PrefsDialog *dialog); - virtual ~XFE_PrefsPageMailNewsNserver(); - - // Manipulators - - virtual void create(); - virtual void init(); - virtual void install(); - virtual void save(); - virtual Boolean verify(); - - // Gets - - PrefsDataMailNewsNserver *getData(); - - // Callbacks - page Mail News/News Server - - static void cb_browse(Widget, XtPointer, XtPointer); - HG92727 - -private: - - // Data - - PrefsDataMailNewsNserver *m_prefsDataMailNewsNserver; -}; - -// ************************* XFE_PrefsPageMailNewsAddrBook ************************* - -class XFE_PrefsPageMailNewsAddrBook : public XFE_PrefsPage, public XFE_Outlinable -{ -public: -======= XFE_PrefsPageMailNewsCopies(XFE_PrefsDialog *dialog); virtual ~XFE_PrefsPageMailNewsCopies(); @@ -1033,84 +966,9 @@ private: Widget m_nohtml_both_toggle; }; ->>>>>>> 3.1.14.1 - -<<<<<<< PrefsDialog.h - // Constructors, Destructors - - XFE_PrefsPageMailNewsAddrBook(XFE_PrefsDialog *dialog); - virtual ~XFE_PrefsPageMailNewsAddrBook(); - - // Manipulators - - virtual void create(); - virtual void init(); - virtual void install(); - virtual void save(); - - void insertDir(DIR_Server *dir); - void insertDirAtPos(int pos, DIR_Server *dir); - void deleteDirAtPos(int pos); - void swapDirs(int pos1, int pos2, int selPos); - void setSelectionPos(int pos); - void deselectPos(int pos); - - // Gets - - PrefsDataMailNewsAddrBook *getData(); - - // Outlinable interface methods - virtual void *ConvFromIndex(int index); - virtual int ConvToIndex(void *item); - - virtual char *getColumnName(int column); - virtual char *getColumnHeaderText(int column); - virtual fe_icon *getColumnHeaderIcon(int column); - virtual EOutlinerTextStyle getColumnHeaderStyle(int column); - virtual void *acquireLineData(int line); - virtual void getTreeInfo(XP_Bool *expandable, XP_Bool *is_expanded, int *depth, - OutlinerAncestorInfo **ancestor); - virtual EOutlinerTextStyle getColumnStyle(int column); - virtual char *getColumnText(int column); - virtual fe_icon *getColumnIcon(int column); - virtual void releaseLineData(); - - virtual void Buttonfunc(const OutlineButtonFuncData *data); - virtual void Flippyfunc(const OutlineFlippyFuncData *data); - - virtual XFE_Outliner *getOutliner(); - // Get tooltipString & docString; - // returned string shall be freed by the callee - // row < 0 indicates heading row; otherwise it is a content row - // (starting from 0) - // - virtual char *getCellTipString(int /* row */, int /* column */) {return NULL;} - virtual char *getCellDocString(int /* row */, int /* column */) {return NULL;} - // Callbacks - page Mail News/Address Book - static void cb_add(Widget, XtPointer, XtPointer); - static void cb_edit(Widget, XtPointer, XtPointer); - static void cb_delete(Widget, XtPointer, XtPointer); - static void cb_promote(Widget, XtPointer, XtPointer); - static void cb_demote(Widget, XtPointer, XtPointer); - static void cb_toggleNameOrder(Widget, XtPointer, XtPointer); - -private: -======= ->>>>>>> 3.1.14.1 - -<<<<<<< PrefsDialog.h - // Data - - static const int OUTLINER_COLUMN_NAME; - static const int OUTLINER_COLUMN_MAX_LENGTH; - static const int OUTLINER_INIT_POS; - - PrefsDataMailNewsAddrBook *m_prefsDataMailNewsAddrBook; - int m_rowIndex; -======= // ************************* XFE_PrefsPageMailNewsReceipts ****************** @@ -1139,7 +997,6 @@ private: Widget m_sentmail_toggle; Widget m_never_toggle; Widget m_some_toggle; ->>>>>>> 3.1.14.1 }; #endif /* MOZ_MAIL_NEWS */ diff --git a/mozilla/cmd/xfe/src/PrefsDialogMServer.cpp b/mozilla/cmd/xfe/src/PrefsDialogMServer.cpp new file mode 100644 index 00000000000..2b0fbeff272 --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsDialogMServer.cpp @@ -0,0 +1,1319 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- */ +/* + PrefsDialogMServer.cpp -- Multiple mail server preferences dialog + Created: Arun Sharma , Thu Mar 19 14:37:46 PST 1998 + */ + + +#include "rosetta.h" +#include "MozillaApp.h" +#include "prefapi.h" +#include "felocale.h" +#include "xpgetstr.h" +#include "PrefsDialogMServer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// IMAP pref stuff taken from WINFE. This code is XP really. +#define USERNAME ".userName" +#define REMEMBER_PASSWORD ".remember_password" +#define CHECK_NEW_MAIL ".check_new_mail" +#define CHECK_TIME ".check_time" +#define OVERRIDE_NAMESPACES ".override_namespaces" +#define DELETE_MODEL ".delete_model" +#define IS_SECURE ".isSecure" +#define NS_OTHER ".namespace.other_users" +#define NS_PERSONAL ".namespace.personal" +#define NS_PUBLIC ".namespace.public" +#define USE_SUB ".using_subscription" +#define EXPUNGE_QUIT ".cleanup_inbox_on_exit" +#define TRASH_QUIT ".empty_trash_on_exit" +#define IMAP_DOWNLOAD "mail.imap.new_mail_get_headers" + +#define POP_SERVER "network.hosts.pop_server" +#define POP_NAME "mail.pop_name" +#define POP_REMEMBER_PASSWORD "mail.remember_password" +#define POP_CHECK_NEW_MAIL "mail.check_new_mail" +#define POP_CHECK_TIME "mail.check_time" +#define POP_LEAVE_ON_SERVER "mail.leave_on_server" +#define POP_DOWNLOAD "mail.pop3_gets_new_mail" + +#define MAIL_SERVER_TYPE "mail.server_type" + +extern int XFE_GENERAL_TAB; +extern int XFE_ADVANCED_TAB; +extern int XFE_IMAP_TAB; +extern int XFE_POP_TAB; +extern int XFE_MOVEMAIL_TAB; + +static const char PrefTemplate[] = "mail.imap.server."; + +// Make sure XP_FREE() from the caller function; +static char* +IMAP_GetPrefName(const char *host_name, const char *pref) +{ + XP_ASSERT(host_name); + if (!host_name) return NULL; + int pref_size=sizeof(PrefTemplate) + + XP_STRLEN(host_name) + + XP_STRLEN(pref) + 1; + char *pref_name= (char *) XP_CALLOC(pref_size, sizeof(char)); + + XP_STRCPY(pref_name, PrefTemplate); + XP_STRCAT(pref_name, host_name); + XP_STRCAT(pref_name, pref); + + return pref_name; +} + +static XP_Bool +IMAP_PrefIsLocked(const char *host_name, const char* pref) +{ + XP_Bool result; + char* pref_name = IMAP_GetPrefName(host_name, pref); + + if (!pref_name) return FALSE; + + result = PREF_PrefIsLocked(pref_name); + XP_FREE(pref_name); + + return result; +} + +static void +IMAP_SetCharPref(const char *host_name, const char* pref, const char* value) +{ + char* pref_name = IMAP_GetPrefName(host_name, pref); + + if (!pref_name) return; + + PREF_SetCharPref(pref_name, value); + XP_FREE(pref_name); + +} + +static void +IMAP_SetIntPref(const char *host_name, const char* pref, int32 value) +{ + char* pref_name = IMAP_GetPrefName(host_name, pref); + + if (!pref_name) return; + + PREF_SetIntPref(pref_name, value); + XP_FREE(pref_name); +} + +static void +IMAP_SetBoolPref(const char *host_name, const char *pref, XP_Bool value) +{ + char* pref_name = IMAP_GetPrefName(host_name, pref); + + if (!pref_name) return; + + PREF_SetBoolPref(pref_name, value); + XP_FREE(pref_name); +} + +static int +IMAP_CopyCharPref(const char *host_name, const char *pref, char **buf) +{ + char* pref_name = IMAP_GetPrefName(host_name, pref); + if (!pref_name) return PREF_ERROR; + if (pref_name) + { + int retval = PREF_CopyCharPref(pref_name, buf); + XP_FREE(pref_name); + return retval; + } + return PREF_ERROR; +} + +static int +IMAP_GetIntPref(const char *host_name, const char* pref, int32 *intval) +{ + char* pref_name = IMAP_GetPrefName(host_name,pref); + if (!pref_name) return PREF_ERROR; + + int retval = PREF_GetIntPref(pref_name, intval); + XP_FREE(pref_name); + + return retval; +} + +static int +IMAP_GetBoolPref(const char *host_name, const char* pref, XP_Bool *boolval) +{ + char* pref_name = IMAP_GetPrefName(host_name, pref); + if (!pref_name) return PREF_ERROR; + + int retval = PREF_GetBoolPref(pref_name, boolval); + XP_FREE(pref_name); + + return retval; +} + + +XFE_PrefsMServerDialog::XFE_PrefsMServerDialog(Widget parent, + char *server_name, + XP_Bool allow_multiple, + MWContext *context) + : XFE_XmLFolderDialog((XFE_View *) NULL, + parent, + "MailServerInfo", + context, + TRUE, // ok + TRUE, // cancel + FALSE, // help + FALSE, // apply + TRUE, // separator + TRUE // modal + ), + m_allow_multiple(allow_multiple), + m_server_name(server_name) +{ + int32 server_type; + PREF_GetIntPref(MAIL_SERVER_TYPE,&server_type); + m_server_type = (MSG_SERVER_TYPE)server_type; + + create(); + init(); + +} + +void XFE_PrefsMServerDialog::create() { + XFE_XmLFolderView* folder_view = (XFE_XmLFolderView *) m_view; + + + // create tabs + m_general_tab = + new XFE_PrefsMServerGeneralTab(this,folder_view,m_wParent, + m_allow_multiple); + m_imap_tab = new XFE_PrefsMServerIMAPTab(this, folder_view); + m_imap_advanced_tab = new XFE_PrefsMServerAdvancedTab(this, folder_view); + m_pop_tab = new XFE_PrefsMServerPOPTab(this, folder_view); + m_movemail_tab = new XFE_PrefsMServerMovemailTab(this, folder_view); + + // add tabs + folder_view->addTab(m_general_tab); + + // add these tabs but don't show them + folder_view->addTab(m_imap_tab, FALSE); + folder_view->addTab(m_imap_advanced_tab, FALSE); + folder_view->addTab(m_pop_tab, FALSE); + folder_view->addTab(m_movemail_tab, FALSE); + + // start with general tab + m_general_tab->show(); + + m_general_tab->setChangedCallback(changedType, (void *)this); +} + +void XFE_PrefsMServerDialog::init() { + + m_general_tab->init(m_server_name, m_server_type); + ChangedType(m_server_type); + + m_imap_advanced_tab->init(m_server_name); + m_imap_tab->init(m_server_name); + m_pop_tab->init(); + m_movemail_tab->init(); + +} + +void XFE_PrefsMServerDialog::apply() { + + + // we need to tell the other panes what server they should use + // and only apply the relevant panes + char *server_name=m_general_tab->getServerName(); + m_general_tab->apply(); + switch(m_server_type) { + case MSG_Imap4: + m_imap_tab->apply(server_name); + m_imap_advanced_tab->apply(server_name); + break; + case MSG_Pop3: + m_pop_tab->apply(); + break; + case MSG_MoveMail: + m_movemail_tab->apply(); + break; + case MSG_Inbox: + XP_ASSERT(0); + break; + } + +} + +void +XFE_PrefsMServerDialog::changedType(void *closure,MSG_SERVER_TYPE server_type) +{ + ((XFE_PrefsMServerDialog *)closure)->ChangedType(server_type); +} + +void +XFE_PrefsMServerDialog::ChangedType(MSG_SERVER_TYPE server_type) +{ + m_server_type=server_type; + + // show/hide appropriate tabs + if (server_type==MSG_Imap4) { + m_imap_tab->show(); + m_imap_advanced_tab->show(); + } else { + m_imap_tab->hide(); + m_imap_advanced_tab->hide(); + } + + if (server_type==MSG_Pop3) { + m_pop_tab->show(); + } else { + m_pop_tab->hide(); + } + + if (server_type==MSG_MoveMail) { + m_movemail_tab->show(); + } else { + m_movemail_tab->hide(); + } + +} + + +// Create the body of the IMAP General Tab +XFE_PrefsMServerGeneralTab::XFE_PrefsMServerGeneralTab( + XFE_Component *top, + XFE_View *view, + Widget parent, + XP_Bool allow_multiple) + : XFE_XmLTabView(top, view, XFE_GENERAL_TAB), + m_server_label(0), + m_server_text(0), + m_server_type_menu(0), + m_server_type_label(0), + m_server_type_option(0), + m_server_user_label(0), + m_server_user_name(0), + m_remember_password(0), + m_check_mail(0), + m_check_time(0), + m_minute_label(0), + m_imap_button(0), + m_pop_button(0), + m_movemail_button(0), + m_changed_callback(0), + m_changed_closure(0), + m_server_name(0), + m_parent(parent) +{ + create(allow_multiple); +} + +void XFE_PrefsMServerGeneralTab::create(XP_Bool allow_multiple) { + Widget kids[15]; + Arg av[10]; + int ac = 0, i = 0; + Widget form; + + form = getBaseWidget(); + + kids[i++] = m_server_label = + XmCreateLabelGadget(form, "ServerName", av, ac); + + kids[i++] = m_server_text = + fe_CreateTextField(form, "ServerNameText", av, ac); + + + Visual *v = 0; + Colormap cmap = 0; + Cardinal depth = 0; + + ac = 0; + + XtVaGetValues (m_parent, + XtNvisual, &v, + XtNcolormap, &cmap, + XtNdepth, &depth, + 0); + + ac = 0; + kids[i++] = m_server_type_label = + XmCreateLabelGadget(form, "ServerType", av, ac); + + ac = 0; + XtSetArg(av[ac], XmNvisual, v); ac++; + XtSetArg(av[ac], XmNdepth, depth); ac++; + XtSetArg(av[ac], XmNcolormap, cmap); ac++; + m_server_type_menu = XmCreatePulldownMenu (form, "serverTypeMenu", av, ac); + + ac = 0; + XtSetArg (av [ac], XmNsubMenuId, m_server_type_menu); ac++; + kids[i++] = m_server_type_option = + fe_CreateOptionMenu (form, "serverTypeOption", av, ac); + fe_UnmanageChild_safe(XmOptionLabelGadget(m_server_type_option)); + + // Now add the entries + + // POP + if (allow_multiple) { + m_pop_button = XmCreatePushButtonGadget(m_server_type_menu, + "popOption", av, ac); + XtAddCallback(m_pop_button, XmNactivateCallback, + optionPop_cb,(XtPointer)this); + XtManageChild(m_pop_button); + + m_movemail_button = XmCreatePushButtonGadget(m_server_type_menu, + "movemailOption", av, ac); + XtAddCallback(m_movemail_button, XmNactivateCallback, + optionMovemail_cb, (XtPointer)this); + XtManageChild(m_movemail_button); + } + + // IMAP + m_imap_button = XmCreatePushButtonGadget(m_server_type_menu, + "imapOption", av, ac); + XtAddCallback(m_imap_button, XmNactivateCallback, + optionImap_cb,(XtPointer)this); + + XtManageChild(m_imap_button); + + + // IMAP is selected until init() + XtVaSetValues (m_server_type_option, + XmNmenuHistory, m_imap_button, + NULL); + + ac = 0; + kids[i++] = m_server_user_label = + XmCreateLabelGadget(form, "ServerUser", av, ac); + + kids[i++] = m_server_user_name = + fe_CreateTextField(form, "Username", av, ac); + + kids[i++] = m_remember_password = + XmCreateToggleButtonGadget(form, "RememberPass", av, ac); + + kids[i++] = m_check_mail = + XmCreateToggleButtonGadget(form, "CheckMail", av, ac); + + kids[i++] = m_check_time = + fe_CreateTextField(form, "WaitTime", av, ac); + + kids[i++] = m_minute_label = + XmCreateLabelGadget(form, "MinuteLabel", av, ac); + + kids[i++] = m_download_toggle = + XmCreateToggleButtonGadget(form, "downloadToggle", av, ac); + + int max_height1 = XfeVaGetTallestWidget(m_server_label, + m_server_text, + NULL); + int max_height2 = XfeVaGetTallestWidget(m_server_type_label, + m_server_type_option, + NULL) + 15; + int max_height3 = XfeVaGetTallestWidget(m_server_user_label, + m_server_user_name, + NULL); + int max_height5 = XfeVaGetTallestWidget(m_check_mail, + m_check_time, + m_minute_label, + NULL); + // Specify the geometry constraints + XtVaSetValues(m_server_label, + XmNheight, max_height1, + XmNalignment, XmALIGNMENT_END, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_FORM, + NULL); + + XtVaSetValues(m_server_text, + XmNheight, max_height1, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, m_server_label, + XmNrightAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, m_server_label, + NULL); + + XtVaSetValues(m_server_type_label, + XmNheight, max_height2, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_server_label, + NULL); + + XtVaSetValues(m_server_type_option, + XmNheight, max_height2, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, m_server_type_label, + XmNrightAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, m_server_type_label, + NULL); + + XtVaSetValues(m_server_user_label, + XmNheight, max_height3, + XmNalignment, XmALIGNMENT_END, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_server_type_label, + NULL); + + XtVaSetValues(m_server_user_name, + XmNheight, max_height3, + XmNrightAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, m_server_user_label, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, m_server_user_label, + NULL); + + XtVaSetValues(m_remember_password, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_server_user_label, + NULL); + + XtVaSetValues(m_check_mail, + XmNheight, max_height5, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_remember_password, + NULL); + + XtVaSetValues(m_check_time, + XmNheight, max_height5, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, m_check_mail, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, m_check_mail, + NULL); + + XtVaSetValues(m_minute_label, + XmNheight, max_height5, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, m_check_time, + XmNrightWidget, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, m_check_time, + NULL); + + XtVaSetValues(m_download_toggle, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_check_mail, + XmNleftAttachment, XmATTACH_FORM, + NULL); + + // Manage the kids + XtManageChildren(kids, i); +} + +void +XFE_PrefsMServerGeneralTab::init(char *server_name, + MSG_SERVER_TYPE server_type) +{ + + m_is_new=FALSE; + m_server_name=server_name; + m_original_type=server_type; + + setServerType(server_type); + + // this is ugly - m_is_new has to be set correctly + // in order for lockWidgets() to behave correctly + if (!server_name) { + m_is_new=TRUE; + lockWidgets(); + } + + char *user_name=NULL; + XP_Bool remember_password=FALSE; + XP_Bool check_mail=TRUE; + XP_Bool download=TRUE; + int32 check_time=10; + + if (server_name) fe_SetTextField(m_server_text, server_name); + // map Prefs->widget values + switch(m_server_type) { + case MSG_Imap4: + if (server_name) { + IMAP_CopyCharPref(server_name, USERNAME,&user_name); + IMAP_GetBoolPref(server_name, REMEMBER_PASSWORD,&remember_password); + IMAP_GetBoolPref(server_name, CHECK_NEW_MAIL,&check_mail); + IMAP_GetIntPref(server_name, CHECK_TIME, &check_time); + } + PREF_GetBoolPref(IMAP_DOWNLOAD, &download); + break; + + case MSG_Pop3: + PREF_CopyCharPref(POP_NAME,&user_name); + PREF_GetBoolPref(POP_REMEMBER_PASSWORD,&remember_password); + PREF_GetBoolPref(POP_CHECK_NEW_MAIL,&check_mail); + PREF_GetIntPref(POP_CHECK_TIME, &check_time); + PREF_GetBoolPref(POP_DOWNLOAD, &download); + XP_ASSERT(m_pop_button); + break; + + case MSG_MoveMail: + user_name=XP_STRDUP(""); + PREF_GetBoolPref(POP_CHECK_NEW_MAIL,&check_mail); + PREF_GetIntPref(POP_CHECK_TIME, &check_time); + PREF_GetBoolPref(POP_DOWNLOAD, &download); + break; + + case MSG_Inbox: + XP_ASSERT(0); + break; + } + + + if (user_name) fe_SetTextField(m_server_user_name,user_name); + XmToggleButtonGadgetSetState(m_remember_password, remember_password,TRUE); + XmToggleButtonGadgetSetState(m_check_mail, check_mail,TRUE); + XmToggleButtonGadgetSetState(m_download_toggle, download, TRUE); + + + char *check_time_buf=PR_smprintf("%d",check_time); + fe_SetTextField(m_check_time,check_time_buf); + XP_FREE(check_time_buf); + XP_FREE(user_name); + +} + +void +XFE_PrefsMServerGeneralTab::setServerType(MSG_SERVER_TYPE type) +{ + m_server_type=type; + switch(m_server_type) { + case MSG_Imap4: + XtVaSetValues (m_server_type_option, + XmNmenuHistory, m_imap_button, + NULL); + break; + + case MSG_Pop3: + XtVaSetValues (m_server_type_option, + XmNmenuHistory, m_pop_button, + NULL); + break; + + case MSG_MoveMail: + XtVaSetValues (m_server_type_option, + XmNmenuHistory, m_movemail_button, + NULL); + break; + + case MSG_Inbox: + XP_ASSERT(0); + break; + } +} + +void +XFE_PrefsMServerGeneralTab::lockWidgets() +{ + XP_Bool lock_server=FALSE, + lock_user=FALSE, + lock_password=FALSE, + lock_check_mail=FALSE, + lock_download=FALSE, + lock_check_time=FALSE; + + switch (m_server_type) { + case MSG_Imap4: + if (m_is_new || (m_original_type!=MSG_Imap4)) + lock_server = FALSE; + else + lock_server = TRUE; + + lock_download=PREF_PrefIsLocked(IMAP_DOWNLOAD); + if (m_server_name) { + lock_user = IMAP_PrefIsLocked(m_server_name, USERNAME); + lock_password = IMAP_PrefIsLocked(m_server_name, REMEMBER_PASSWORD); + lock_check_mail = IMAP_PrefIsLocked(m_server_name, CHECK_NEW_MAIL); + lock_check_time = IMAP_PrefIsLocked(m_server_name, CHECK_TIME); + } + break; + + case MSG_Pop3: + lock_server = PREF_PrefIsLocked(POP_SERVER); + lock_user = PREF_PrefIsLocked(POP_NAME); + lock_password = PREF_PrefIsLocked(POP_REMEMBER_PASSWORD); + lock_check_mail = PREF_PrefIsLocked(POP_CHECK_NEW_MAIL); + lock_check_time = PREF_PrefIsLocked(POP_CHECK_TIME); + lock_download = PREF_PrefIsLocked(POP_DOWNLOAD); + break; + + case MSG_MoveMail: + lock_server=TRUE; + lock_user=TRUE; + lock_password=TRUE; + lock_check_mail = PREF_PrefIsLocked(POP_CHECK_NEW_MAIL); + lock_check_time = PREF_PrefIsLocked(POP_CHECK_TIME); + lock_download = PREF_PrefIsLocked(POP_DOWNLOAD); + break; + + case MSG_Inbox: + XP_ASSERT(0); + break; + } + + XtSetSensitive(m_server_text, !lock_server); + XtSetSensitive(m_server_user_name, !lock_user); + XtSetSensitive(m_remember_password, !lock_password); + XtSetSensitive(m_check_mail, !lock_check_mail); + XtSetSensitive(m_check_time, !lock_check_time); + XtSetSensitive(m_download_toggle, !lock_download); + +} + + +void +XFE_PrefsMServerGeneralTab::optionImap_cb(Widget /* w */, + XtPointer clientData, + XtPointer /* callData */) +{ + XFE_PrefsMServerGeneralTab *tab=(XFE_PrefsMServerGeneralTab*)clientData; + if (!tab) return; + tab->ChangedType(MSG_Imap4); +} + +void +XFE_PrefsMServerGeneralTab::optionPop_cb(Widget /* w */, + XtPointer clientData, + XtPointer /* callData */) +{ + XFE_PrefsMServerGeneralTab *tab=(XFE_PrefsMServerGeneralTab*)clientData; + if (!tab) return; + tab->ChangedType(MSG_Pop3); +} + +void +XFE_PrefsMServerGeneralTab::optionMovemail_cb(Widget /* w */, + XtPointer clientData, + XtPointer /* callData*/) +{ + XFE_PrefsMServerGeneralTab *tab=(XFE_PrefsMServerGeneralTab*)clientData; + if (!tab) return; + tab->ChangedType(MSG_MoveMail); +} + +void +XFE_PrefsMServerGeneralTab::ChangedType(MSG_SERVER_TYPE type) +{ + m_server_type=type; + if (m_changed_callback) m_changed_callback(m_changed_closure, type); + lockWidgets(); +} + +void +XFE_PrefsMServerGeneralTab::setChangedCallback(serverTypeCallback cb, + void* closure) +{ + m_changed_callback=cb; + m_changed_closure=closure; +} + +void +XFE_PrefsMServerGeneralTab::apply() +{ + + // pull actual values out of the widgets + char *server_name = getServerName(); + char *user_name = getUserName(); + XP_Bool remember_password = getRememberPassword(); + XP_Bool check_mail = getCheckMail(); + XP_Bool download = getDownload(); + int32 check_time = getWaitTime(); + MSG_Master *master = fe_getMNMaster(); + switch (m_server_type) { + case MSG_Imap4: + + if (m_original_type==MSG_Imap4) { + // create if necessary + // use dumb defaults & let pref panes handle it correctly + if (m_is_new) + MSG_CreateIMAPHost(master, server_name, FALSE, user_name, + check_mail, check_time, remember_password, + TRUE, TRUE, "", "",""); + } else { // originally POP or movemail + // implicitly create by setting POP server and switching type + PREF_SetCharPref(POP_SERVER, server_name); + PREF_SetIntPref(MAIL_SERVER_TYPE, (int32)MSG_Imap4); + } + + IMAP_SetCharPref(server_name, USERNAME, user_name); + IMAP_SetBoolPref(server_name, REMEMBER_PASSWORD, remember_password); + IMAP_SetBoolPref(server_name, CHECK_NEW_MAIL, check_mail); + IMAP_SetIntPref(server_name, CHECK_TIME, check_time); + PREF_SetBoolPref(IMAP_DOWNLOAD, download); + break; + + case MSG_Pop3: + + PREF_SetIntPref(MAIL_SERVER_TYPE, (int32)MSG_Pop3); + PREF_SetCharPref(POP_SERVER, server_name); + PREF_SetCharPref(POP_NAME, user_name); + PREF_SetBoolPref(POP_REMEMBER_PASSWORD, remember_password); + PREF_SetBoolPref(POP_CHECK_NEW_MAIL, check_mail); + PREF_SetIntPref(POP_CHECK_TIME, check_time); + PREF_SetBoolPref(POP_DOWNLOAD, download); + break; + + case MSG_MoveMail: + PREF_SetIntPref(MAIL_SERVER_TYPE, (int32)MSG_MoveMail); + PREF_SetBoolPref(POP_CHECK_NEW_MAIL, check_mail); + PREF_SetIntPref(POP_CHECK_TIME, check_time); + PREF_SetBoolPref(POP_DOWNLOAD, download); + break; + + case MSG_Inbox: + XP_ASSERT(0); + } + + // Free all the strings + XtFree(server_name); + XtFree(user_name); + +} + +// Create the body of the Imap Tab +XFE_PrefsMServerIMAPTab::XFE_PrefsMServerIMAPTab( + XFE_Component *top, + XFE_View *view) + : XFE_XmLTabView(top, view, XFE_IMAP_TAB), + m_delete_trash_toggle(0), + m_delete_mark_toggle(0), + m_delete_remove_toggle(0), + m_use_ssl(0) +{ +#if 0 + m_use_sub=0; +#endif + create(); +} + +void +XFE_PrefsMServerIMAPTab::create() { + Widget kids[10]; + int i = 0; + Widget form; + Widget delete_radiobox; + Widget delete_label; + Widget sep1; + + form = getBaseWidget(); + + HG19271 + + kids[i++] = delete_label = + XmCreateLabelGadget(form, "deleteLabel", NULL, 0); + kids[i++] = delete_radiobox = + XmCreateRadioBox(form, "deleteRadioBox", NULL, 0); + + m_delete_trash_toggle = + XmCreateToggleButtonGadget(delete_radiobox, "trashToggle", NULL, 0); + m_delete_mark_toggle = + XmCreateToggleButtonGadget(delete_radiobox, "markToggle", NULL, 0); + m_delete_remove_toggle = + XmCreateToggleButtonGadget(delete_radiobox, "removeToggle", NULL, 0); + + + kids[i++] = m_expunge_toggle = + XmCreateToggleButtonGadget(form, "expungeExitToggle", NULL, 0); + kids[i++] = m_trash_toggle = + XmCreateToggleButtonGadget(form, "trashExitToggle", NULL, 0); + +#if 0 + kids[i++] = m_use_sub = + XmCreateToggleButtonGadget(form, "UseSub", NULL, 0); +#endif + + + HG10977 + + XtVaSetValues(delete_label, + XmNtopAttachment, XmATTACH_WIDGET, + HG18162 + XmNleftAttachment, XmATTACH_FORM, + NULL); + + XtVaSetValues(delete_radiobox, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, delete_label, + XmNleftAttachment, XmATTACH_FORM, + NULL); + + XtVaSetValues(m_expunge_toggle, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, delete_radiobox, + XmNleftWidget, XmATTACH_FORM, + NULL); + + XtVaSetValues(m_trash_toggle, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_expunge_toggle, + XmNleftWidget, XmATTACH_FORM, + NULL); + +#if 0 + XtVaSetValues(m_use_sub, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_trash_toggle, + XmNleftAttachment, XmATTACH_FORM, + NULL); +#endif + + // Manage the kids + XtManageChild(m_delete_trash_toggle); + XtManageChild(m_delete_mark_toggle); + XtManageChild(m_delete_remove_toggle); + XtManageChildren(kids, i); + + // set an initial default (this should be done in init(), + // but I'm not sure how) + XmToggleButtonGadgetSetState(m_delete_trash_toggle, True, True); +} + +void +XFE_PrefsMServerIMAPTab::init(char *server_name) { + + if (!server_name) return; + + XP_Bool HG19711 use_sub; + XP_Bool expunge_quit, trash_quit; + int32 intval; + + IMAP_GetIntPref(server_name, DELETE_MODEL, &intval); + HG19189 + IMAP_GetBoolPref(server_name, USE_SUB, &use_sub); + IMAP_GetBoolPref(server_name, EXPUNGE_QUIT, &expunge_quit); + IMAP_GetBoolPref(server_name, TRASH_QUIT, &trash_quit); + + MSG_IMAPDeleteModel delete_model=(MSG_IMAPDeleteModel)intval; + + switch (delete_model) { + case MSG_IMAPDeleteIsIMAPDelete: + XmToggleButtonGadgetSetState(m_delete_mark_toggle, True, True); + break; + case MSG_IMAPDeleteIsMoveToTrash: + XmToggleButtonGadgetSetState(m_delete_trash_toggle, True, True); + break; + case MSG_IMAPDeleteIsDeleteNoTrash: + XmToggleButtonGadgetSetState(m_delete_remove_toggle, True, True); + break; + } + + HG71676 + XmToggleButtonGadgetSetState(m_expunge_toggle, expunge_quit, TRUE); + XmToggleButtonGadgetSetState(m_trash_toggle, trash_quit, TRUE); +#if 0 + XmToggleButtonGadgetSetState(m_use_sub, use_sub,TRUE); +#endif + +} + +void +XFE_PrefsMServerIMAPTab::apply(char *server_name) +{ + XP_ASSERT(server_name); + if (!server_name) return; + + int32 intval=(int32)MSG_IMAPDeleteIsMoveToTrash; + + if (XmToggleButtonGadgetGetState(m_delete_mark_toggle)) + intval=(int32)MSG_IMAPDeleteIsIMAPDelete; + else if (XmToggleButtonGadgetGetState(m_delete_trash_toggle)) + intval=(int32)MSG_IMAPDeleteIsMoveToTrash; + else if (XmToggleButtonGadgetGetState(m_delete_remove_toggle)) + intval=(int32)MSG_IMAPDeleteIsDeleteNoTrash; + + IMAP_SetIntPref(server_name, DELETE_MODEL, intval); + HG71851 + IMAP_SetBoolPref(server_name, EXPUNGE_QUIT, getExpungeQuit()); + IMAP_SetBoolPref(server_name, TRASH_QUIT, getTrashQuit()); +#if 0 + IMAP_SetBoolPref(server_name, USE_SUB, getUseSub()); +#endif + +} + +// Create the body of the Advanced Tab +XFE_PrefsMServerAdvancedTab::XFE_PrefsMServerAdvancedTab( + XFE_Component *top, + XFE_View *view) + : XFE_XmLTabView(top, view, XFE_ADVANCED_TAB), + m_path_prefs_label(0), + m_personal_dir_label(0), + m_personal_dir_text(0), + m_public_dir_label(0), + m_public_dir_text(0), + m_other_label(0), + m_other_text(0), + m_allow_server(0) +{ + create(); +} + +void XFE_PrefsMServerAdvancedTab::create() { + Widget kids[10]; + Arg av[10]; + int ac = 0, i = 0; + Widget form; + + form = getBaseWidget(); + + kids[i++] = m_path_prefs_label = + XmCreateLabelGadget(form, "PathPrefsLabel", av, ac); + + kids[i++] = m_personal_dir_label = + XmCreateLabelGadget(form, "PersonalDir", av, ac); + + kids[i++] = m_personal_dir_text = + fe_CreateTextField(form, "PersonalDirText", av, ac); + + kids[i++] = m_public_dir_label = + XmCreateLabelGadget(form, "PublicDir", av, ac); + + kids[i++] = m_public_dir_text = + fe_CreateTextField(form, "PublicDirText", av, ac); + + kids[i++] = m_other_label = + XmCreateLabelGadget(form, "OtherUsers", av, ac); + + kids[i++] = m_other_text = + fe_CreateTextField(form, "OtherUsersText", av, ac); + + kids[i++] = m_allow_server = + XmCreateToggleButtonGadget(form, "AllowServer", av, ac); + + // Specify the geometry constraints + + XtVaSetValues(m_path_prefs_label, + XmNalignment, XmALIGNMENT_BEGINNING, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_FORM, + NULL); + + XtVaSetValues(m_personal_dir_label, + XmNalignment, XmALIGNMENT_END, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, m_personal_dir_text, + XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNbottomWidget, m_personal_dir_text, + NULL); + + XtVaSetValues(m_personal_dir_text, + XmNrightAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_path_prefs_label, + NULL); + + XtVaSetValues(m_public_dir_label, + XmNalignment, XmALIGNMENT_END, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, m_public_dir_text, + XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNbottomWidget, m_public_dir_text, + NULL); + + XtVaSetValues(m_public_dir_text, + XmNrightAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_personal_dir_text, + NULL); + + XtVaSetValues(m_other_label, + XmNalignment, XmALIGNMENT_END, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, m_other_text, + XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNbottomWidget, m_other_text, + NULL); + + XtVaSetValues(m_other_text, + XmNrightAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_public_dir_text, + NULL); + + XtVaSetValues(m_allow_server, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_other_label, + NULL); + + // Manage the kids + XtManageChildren(kids, i); +} + +void +XFE_PrefsMServerAdvancedTab::init(char *server_name) { + if (!server_name) return; + + char *personal_dir=NULL, + *public_dir=NULL, + *other_dir=NULL; + XP_Bool override=FALSE; + + if (IMAP_CopyCharPref(server_name, NS_PERSONAL, &personal_dir)==PREF_OK) { + fe_SetTextField(m_personal_dir_text, personal_dir); + XP_FREE(personal_dir); + } + + if (IMAP_CopyCharPref(server_name, NS_PUBLIC, &public_dir)==PREF_OK) { + fe_SetTextField(m_public_dir_text, public_dir); + XP_FREE(public_dir); + } + if (IMAP_CopyCharPref(server_name, NS_OTHER, &other_dir)==PREF_OK) { + fe_SetTextField(m_other_text, other_dir); + XP_FREE(other_dir); + } + + IMAP_GetBoolPref (server_name, OVERRIDE_NAMESPACES, &override); + XmToggleButtonGadgetSetState(m_allow_server, override,TRUE); + +} + + +void +XFE_PrefsMServerAdvancedTab::apply(char *server_name) +{ + + XP_ASSERT(server_name); + if (!server_name) return; + + char *personal_dir = getImapPersonalDir(); + char *public_dir = getImapPublicDir(); + char *others_dir = getImapOthersDir(); + XP_Bool override = getOverrideNamespaces(); + + IMAP_SetCharPref(server_name, NS_PERSONAL, personal_dir); + IMAP_SetCharPref(server_name, NS_PUBLIC, public_dir); + IMAP_SetCharPref(server_name, NS_OTHER, others_dir); + IMAP_SetBoolPref(server_name, OVERRIDE_NAMESPACES, override); + + XtFree(personal_dir); + XtFree(public_dir); + XtFree(others_dir); + +} + + +// Create the body of the POP Tab +XFE_PrefsMServerPOPTab::XFE_PrefsMServerPOPTab(XFE_Component *top, + XFE_View *view) + : XFE_XmLTabView(top, view, XFE_POP_TAB), + m_leave_messages(0) +{ + create(); +} + +void +XFE_PrefsMServerPOPTab::create() { + + Widget kids[10]; + Arg av[10]; + int ac = 0, i = 0; + Widget form; + + form = getBaseWidget(); + + kids[i++] = m_leave_messages = + XmCreateToggleButtonGadget(form, "LeaveMessages", av, ac); + + XtVaSetValues(m_leave_messages, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_FORM, + NULL); + + // Manage the kids + XtManageChildren(kids, i); +} + +void +XFE_PrefsMServerPOPTab::init() { + XP_Bool leave_messages; + PREF_GetBoolPref(POP_LEAVE_ON_SERVER, &leave_messages); + XmToggleButtonGadgetSetState(m_leave_messages, leave_messages,TRUE); +} + +void +XFE_PrefsMServerPOPTab::apply() +{ + PREF_SetBoolPref(POP_LEAVE_ON_SERVER, getLeaveMessages()); +} + +XFE_PrefsMServerMovemailTab::XFE_PrefsMServerMovemailTab(XFE_Component *top, + XFE_View *view) + : XFE_XmLTabView(top, view, XFE_MOVEMAIL_TAB) +{ + create(); +} + +void +XFE_PrefsMServerMovemailTab::create() +{ + Widget kids[5]; + int i; + Widget spacer; + Widget toggle_radiobox; + Widget form = getBaseWidget(); + + Dimension lmargin, spacing; + + i=0; + kids[i++] = toggle_radiobox = + XmCreateRadioBox(form, "movemailRadio", NULL,0); + m_builtin_toggle = + XmCreateToggleButtonGadget(toggle_radiobox,"builtinToggle", NULL,0); + m_external_toggle = + XmCreateToggleButtonGadget(toggle_radiobox,"externalToggle",NULL,0); + + // spacer so text box lines up with label in bottom radio button + kids[i++] = spacer = + XmCreateLabelGadget(form, "", NULL, 0); + kids[i++] = m_program_text = + fe_CreateTextField(form, "externalApp", NULL, 0); + kids[i++] = m_choose_button = + XmCreatePushButtonGadget(form, "appChoose", NULL, 0); + + XtVaGetValues(m_external_toggle, + XmNmarginLeft, &lmargin, + XmNspacing, &spacing, + NULL); + + XtVaSetValues(toggle_radiobox, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + NULL); + XtVaSetValues(spacer, + XmNwidth, lmargin+spacing, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, toggle_radiobox, + XmNleftAttachment, XmATTACH_FORM, + NULL); + XtVaSetValues(m_program_text, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, toggle_radiobox, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, spacer, + NULL); + XtVaSetValues(m_choose_button, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, m_program_text, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, m_program_text, + NULL); + + XtManageChild(m_builtin_toggle); + XtManageChild(m_external_toggle); + XtManageChildren(kids, i); + + XtAddCallback(m_builtin_toggle, XmNvalueChangedCallback, + toggleBuiltin_cb, this); + XtAddCallback(m_choose_button, XmNactivateCallback, + chooseMovemail_cb, this); +} + +void +XFE_PrefsMServerMovemailTab::init() +{ + XP_Bool boolval; + XP_Bool locked; + char *charval; + + locked=PREF_PrefIsLocked("mail.use_builtin_movemail"); + XtSetSensitive(m_builtin_toggle, !locked); + XtSetSensitive(m_external_toggle,!locked); + PREF_GetBoolPref("mail.use_builtin_movemail", &boolval); + XmToggleButtonGadgetSetState(m_builtin_toggle, boolval, True); + XmToggleButtonGadgetSetState(m_external_toggle, !boolval, True); + + locked=PREF_PrefIsLocked("mail.movemail_program"); + XtSetSensitive(m_program_text, !locked); + + PREF_CopyCharPref("mail.movemail_program", &charval); + fe_SetTextField(m_program_text, charval); + XP_FREE(charval); + +} + +void +XFE_PrefsMServerMovemailTab::apply() +{ + XP_Bool boolval = (XP_Bool)XmToggleButtonGetState(m_builtin_toggle); + PREF_SetBoolPref("mail.use_builtin_movemail", boolval); + + char *str=fe_GetTextField(m_program_text); + PREF_SetCharPref("mail.movemail_program", str); + if (str) XtFree(str); + +} + +void +XFE_PrefsMServerMovemailTab::toggleBuiltin_cb(Widget, + XtPointer closure, + XtPointer callData) +{ + XFE_PrefsMServerMovemailTab *tab=(XFE_PrefsMServerMovemailTab *)closure; + tab->toggleBuiltin((XmToggleButtonCallbackStruct *)callData); +} + +void +XFE_PrefsMServerMovemailTab::chooseMovemail_cb(Widget, + XtPointer closure, + XtPointer) +{ + XFE_PrefsMServerMovemailTab *tab=(XFE_PrefsMServerMovemailTab *)closure; + tab->chooseMovemail(); +} + + + +void +XFE_PrefsMServerMovemailTab::toggleBuiltin(XmToggleButtonCallbackStruct *cbs) +{ + XtSetSensitive(m_program_text, !cbs->set); + XtSetSensitive(m_choose_button, !cbs->set); +} + +void +XFE_PrefsMServerMovemailTab::chooseMovemail() +{ + + +} diff --git a/mozilla/cmd/xfe/src/PrefsDialogMServer.h b/mozilla/cmd/xfe/src/PrefsDialogMServer.h new file mode 100644 index 00000000000..77331444584 --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsDialogMServer.h @@ -0,0 +1,269 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- */ +/* + PrefsDialogMServer.h -- Headers for multiple mail server preferences dialog + Created: Arun Sharma , Thu Mar 19 14:37:46 PST 1998 + */ + +#ifndef _xfe_prefsdialog_mserver_h +#define _xfe_prefsdialog_mserver_h + +#include "rosetta.h" +#include "XmLFolderDialog.h" +#include "XmLFolderView.h" +#include "TabView.h" +#include "structs.h" + +#define TYPE_POP 0 +#define TYPE_IMAP 1 + + +class XFE_PrefsMServerGeneralTab : public XFE_XmLTabView +{ +public: + typedef void (serverTypeCallback)(void *, MSG_SERVER_TYPE); + +private: + Widget m_server_label; + Widget m_server_text; + Widget m_server_type_menu; + Widget m_server_type_label; + Widget m_server_type_option; + Widget m_server_user_label; + Widget m_server_user_name; + Widget m_remember_password; + Widget m_check_mail; + Widget m_check_time; + Widget m_minute_label; + Widget m_imap_button; + Widget m_pop_button; + Widget m_movemail_button; + Widget m_download_toggle; + + serverTypeCallback *m_changed_callback; + void *m_changed_closure; + + char *m_server_name; + Widget m_parent; + MSG_SERVER_TYPE m_server_type; + XP_Bool m_is_new; + MSG_SERVER_TYPE m_original_type; + +public: + + XFE_PrefsMServerGeneralTab(XFE_Component *top, XFE_View *view, + Widget parent, XP_Bool allow_multiple); + virtual ~XFE_PrefsMServerGeneralTab() {}; + void create(XP_Bool allow_multiple); + void init(char *server_name, MSG_SERVER_TYPE server_type); + void apply(); + + void setChangedCallback(serverTypeCallback cb, void *closure); + void setServerType(MSG_SERVER_TYPE type); + void lockWidgets(); + XP_Bool getRememberPassword() { + return XmToggleButtonGadgetGetState(m_remember_password); + }; + + XP_Bool getCheckMail() { + return XmToggleButtonGadgetGetState(m_check_mail); + }; + + XP_Bool getDownload() { + return XmToggleButtonGadgetGetState(m_download_toggle); + } + + // Need to be careful here not to leak memory + char *getServerName() { + return XmTextFieldGetString(m_server_text); + }; + + char *getUserName() { + return XmTextFieldGetString(m_server_user_name); + }; + + int getWaitTime() { + char *p = XmTextFieldGetString(m_check_time); + int i = atoi(p); + XtFree(p); + return i; + }; + + static void optionImap_cb(Widget, XtPointer, XtPointer); + static void optionPop_cb(Widget, XtPointer, XtPointer); + static void optionMovemail_cb(Widget, XtPointer, XtPointer); + void ChangedType(MSG_SERVER_TYPE); +}; + +class XFE_PrefsMServerIMAPTab : public XFE_XmLTabView +{ + +private: + Widget m_delete_trash_toggle; + Widget m_delete_mark_toggle; + Widget m_delete_remove_toggle; + HG18966 + Widget m_expunge_toggle; + Widget m_trash_toggle; +#if 0 + Widget m_use_sub; +#endif + +public: + XFE_PrefsMServerIMAPTab(XFE_Component *top, XFE_View *view); + virtual ~XFE_PrefsMServerIMAPTab() {}; + void create(); + void init(char *server_name); + virtual void apply() {}; + void apply(char *server_name); + + HG27311 + +#if 0 + XP_Bool getUseSub() { + return XmToggleButtonGadgetGetState(m_use_sub); + }; +#endif + + XP_Bool getExpungeQuit() { + return XmToggleButtonGadgetGetState(m_expunge_toggle); + } + + XP_Bool getTrashQuit() { + return XmToggleButtonGadgetGetState(m_trash_toggle); + } + +}; + +class XFE_PrefsMServerAdvancedTab : public XFE_XmLTabView +{ +private: + Widget m_path_prefs_label; + Widget m_personal_dir_label; + Widget m_personal_dir_text; + Widget m_public_dir_label; + Widget m_public_dir_text; + Widget m_other_label; + Widget m_other_text; + Widget m_allow_server; + +public: + XFE_PrefsMServerAdvancedTab(XFE_Component *top, XFE_View *view); + virtual ~XFE_PrefsMServerAdvancedTab() {}; + void create(); + void init(char *server_name); + virtual void apply() {}; + void apply(char *server_name); + + char *getImapPersonalDir() { + return XmTextFieldGetString(m_personal_dir_text); + }; + + char *getImapPublicDir() { + return XmTextFieldGetString(m_public_dir_text); + }; + + char *getImapOthersDir() { + return XmTextFieldGetString(m_other_text); + }; + + XP_Bool getOverrideNamespaces() { + return XmToggleButtonGadgetGetState(m_allow_server); + }; + +}; + + +class XFE_PrefsMServerPOPTab : public XFE_XmLTabView +{ +private: + Widget m_leave_messages; + +public: + XFE_PrefsMServerPOPTab(XFE_Component *top, XFE_View *view); + virtual ~XFE_PrefsMServerPOPTab() {}; + + XP_Bool getLeaveMessages() { + return XmToggleButtonGadgetGetState(m_leave_messages); + }; + + void create(); + void init(); + void apply(); +}; + +class XFE_PrefsMServerMovemailTab : public XFE_XmLTabView +{ +private: + Widget m_builtin_toggle; + Widget m_external_toggle; + + Widget m_program_text; + Widget m_choose_button; + +public: + XFE_PrefsMServerMovemailTab(XFE_Component *top, XFE_View *view); + virtual ~XFE_PrefsMServerMovemailTab() {}; + + static void toggleBuiltin_cb(Widget, XtPointer, XtPointer); + static void chooseMovemail_cb(Widget, XtPointer, XtPointer); + + void toggleBuiltin(XmToggleButtonCallbackStruct *); + void chooseMovemail(); + + void create(); + void init(); + void apply(); + +}; + + + +class XFE_PrefsMServerDialog : public XFE_XmLFolderDialog +{ +private: + // Real data + MSG_SERVER_TYPE m_server_type; + XP_Bool m_allow_multiple; + char *m_server_name; + + + // Various tabs + XFE_PrefsMServerGeneralTab *m_general_tab; + XFE_PrefsMServerIMAPTab *m_imap_tab; + XFE_PrefsMServerAdvancedTab *m_imap_advanced_tab; + XFE_PrefsMServerPOPTab *m_pop_tab; + XFE_PrefsMServerMovemailTab *m_movemail_tab; + static void changedType(void *closure, MSG_SERVER_TYPE server_type); + +public: + XFE_PrefsMServerDialog(Widget parent, char *name, + XP_Bool allow_multiple, + MWContext *context); + + Widget getChrome() { return m_chrome; }; + void create(); + void init(); + virtual void apply(); + void ChangedType(MSG_SERVER_TYPE server_type); +}; + +#endif /* _xfe_prefsdialog_mserver_h */ diff --git a/mozilla/cmd/xfe/src/PrefsDialogNServer.cpp b/mozilla/cmd/xfe/src/PrefsDialogNServer.cpp new file mode 100644 index 00000000000..ee135873295 --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsDialogNServer.cpp @@ -0,0 +1,209 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- */ +/* + PrefsDialogMServer.cpp -- Multiple mail server preferences dialog + Created: Alec Flett + */ + +#include "rosetta.h" +#include "PrefsDialogNServer.h" +#include "felocale.h" +#include "prprf.h" +#include "msgcom.h" +#include "Xfe/Geometry.h" + +#include "xfe.h" + +#include +#include +#include +#include +#include + + +XFE_PrefsNServerDialog::XFE_PrefsNServerDialog(Widget parent) + : XFE_Dialog(parent, "NewsServerInfo", + TRUE, TRUE, TRUE, FALSE, TRUE, TRUE), + m_server_text(0), + m_port_text(0), + HG71661 + m_password_toggle(0) +{ + + +} + +void +XFE_PrefsNServerDialog::create() { + Widget kids[7]; + int i=0; + + Widget form = XmCreateForm(m_chrome, "form", NULL, 0); + Widget server_label = kids[i++] = + XmCreateLabelGadget(form, "serverLabel", NULL, 0); + m_server_text = kids[i++] = + XmCreateText(form, "serverText", NULL, 0); + + Widget port_label = kids[i++] = + XmCreateLabelGadget(form, "portLabel", NULL, 0); + m_port_text = kids[i++] = + XmCreateText(form, "portText", NULL, 0); + + HG90177 + m_password_toggle = kids[i++] = + XmCreateToggleButtonGadget(form, "passwordToggle", NULL, 0); + + int max_height1 = XfeVaGetTallestWidget(server_label, m_server_text, NULL); + int max_height2 = XfeVaGetTallestWidget(port_label, m_port_text, NULL); + // now arrange the widgets + XtVaSetValues(server_label, + XmNheight, max_height1, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + NULL); + XtVaSetValues(m_server_text, + XmNheight, max_height1, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, server_label, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, server_label, + XmNrightAttachment, XmATTACH_FORM, + NULL); + XtVaSetValues(port_label, + XmNheight, max_height2, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, server_label, + XmNleftAttachment, XmATTACH_FORM, + NULL); + XtVaSetValues(m_port_text, + XmNheight, max_height2, + XmNcolumns,5, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, port_label, + XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNleftWidget, m_server_text, + NULL); + HG79299 + XtVaSetValues(m_password_toggle, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, HG21897, + XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNleftWidget, HG17661, + NULL); + + XtManageChildren(kids,i); + XtManageChild(form); + + XtAddCallback(m_chrome, XmNokCallback, cb_ok, this); + XtAddCallback(m_chrome, XmNcancelCallback, cb_cancel, this); + HG18261 + + setPort(NEWS_PORT); +} + +void +XFE_PrefsNServerDialog::init(const char *server, int32 port, + XP_Bool xxx, XP_Bool password) +{ + + if (m_server_text) fe_SetTextField(m_server_text, server); + + setPort(port); + + HG19877 + if (m_password_toggle) + XmToggleButtonSetState(m_password_toggle, password, False); + +} + +void +XFE_PrefsNServerDialog::cb_cancel(Widget, XtPointer closure, XtPointer) +{ + XFE_PrefsNServerDialog *theDialog = + (XFE_PrefsNServerDialog *)closure; + + theDialog->hide(); + theDialog->m_retVal=FALSE; + theDialog->m_doneWithLoop=TRUE; +} + +void +XFE_PrefsNServerDialog::cb_ok(Widget, XtPointer closure, XtPointer) +{ + XFE_PrefsNServerDialog *theDialog = + (XFE_PrefsNServerDialog *)closure; + + theDialog->hide(); + theDialog->m_retVal=TRUE; + theDialog->m_doneWithLoop=TRUE; +} + +HG19871 + + + +XP_Bool +XFE_PrefsNServerDialog::prompt() +{ + + m_doneWithLoop=False; + show(); + + while (!m_doneWithLoop) + fe_EventLoop(); + + return m_retVal; +} + +void XFE_PrefsNServerDialog::setPort(int32 port) +{ + HG19773 + + char *charval=PR_smprintf("%d",port); + fe_SetTextField(m_port_text, charval); + XP_FREE(charval); +} + +char * +XFE_PrefsNServerDialog::getServerName() +{ + return fe_GetTextField(m_server_text); +} + +int32 +XFE_PrefsNServerDialog::getPort() +{ + char *charval = fe_GetTextField(m_port_text); + int32 port = XP_ATOI(charval); + XP_FREE(charval); + + return port; +} + + + +XP_Bool +XFE_PrefsNServerDialog::getPassword() +{ + XP_ASSERT(m_password_toggle); + if (!m_password_toggle) return FALSE; + return (XmToggleButtonGetState(m_password_toggle)==True) ? TRUE : FALSE; +} diff --git a/mozilla/cmd/xfe/src/PrefsDialogNServer.h b/mozilla/cmd/xfe/src/PrefsDialogNServer.h new file mode 100644 index 00000000000..a9039dfa56d --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsDialogNServer.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- */ +/* + PrefsDialogNServer.h -- Multiple news server preferences dialog + Created: Alec Flett + */ + +#ifndef _xfe_prefsdialognserver_h +#define _xfe_prefsdialognserver_h + +#include "rosetta.h" +#include "Dialog.h" + +class XFE_PrefsNServerDialog : public XFE_Dialog +{ + public: + + XFE_PrefsNServerDialog(Widget parent); + void create(); + void init(const char *server, int32 port, XP_Bool xxx, XP_Bool password); + void setPort(int32 port); + char *getServerName(); + int32 getPort(); + HG78266 + XP_Bool getPassword(); + XP_Bool prompt(); + + static void cb_cancel(Widget, XtPointer, XtPointer); + static void cb_ok(Widget, XtPointer, XtPointer); + HG18177 + + private: + Widget m_server_text; + Widget m_port_text; + HG18760 + Widget m_password_toggle; + + XP_Bool m_doneWithLoop; + XP_Bool m_retVal; + +}; + + +#endif diff --git a/mozilla/cmd/xfe/src/PrefsDialogSmart.cpp b/mozilla/cmd/xfe/src/PrefsDialogSmart.cpp new file mode 100644 index 00000000000..57e15b29cfd --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsDialogSmart.cpp @@ -0,0 +1,526 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- + PrefsDialogSmart.cpp -- the Smart Browsing preferences pane + Created: Ramiro Estrugo , 20-May-98. + */ + +#include "xpassert.h" +#include "MozillaApp.h" +#include "xfe.h" +#include "prefapi.h" +#include "PrefsDialog.h" +#include "prefapi.h" + +#include +#include +#include + +#include + +// +// Smart Browsing preferences +// + +////////////////////////////////////////////////////////////////////////// +XFE_PrefsPageBrowserSmart::XFE_PrefsPageBrowserSmart(XFE_PrefsDialog *dialog) + : XFE_PrefsPage(dialog), + m_prefsDataBrowserSmart(0) +{ + m_rl_need_chrome_update = FALSE; +} +////////////////////////////////////////////////////////////////////////// +XFE_PrefsPageBrowserSmart::~XFE_PrefsPageBrowserSmart() +{ + delete m_prefsDataBrowserSmart; +} +////////////////////////////////////////////////////////////////////////// +void +XFE_PrefsPageBrowserSmart::create() +{ + Widget kids[100]; + Arg av[50]; + int ac; + int i; + + PrefsDataBrowserSmart *fep = NULL; + + fep = new PrefsDataBrowserSmart; + memset(fep, 0, sizeof(PrefsDataBrowserSmart)); + m_prefsDataBrowserSmart = fep; + + fep->context = getContext(); + fep->prompt_dialog = getPrefsDialog()->getDialogChrome(); + + // Smart browsing page form + Widget form; + + ac = 0; + XtSetArg (av [ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg (av [ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg (av [ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg (av [ac], XmNrightAttachment, XmATTACH_FORM); ac++; + form = XmCreateForm (m_wPageForm, "smart", av, ac); + XtManageChild (form); + m_wPage = fep->page = form; + + Widget frame1; + + // Related links frame + ac = 0; + XtSetArg (av [ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg (av [ac], XmNrightAttachment, XmATTACH_FORM); ac++; + XtSetArg (av [ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg (av [ac], XmNbottomAttachment, XmATTACH_NONE); ac++; + frame1 = XmCreateFrame (form, "relatedFrame", av, ac); + + // Related links box + Widget form1; + + ac = 0; + XtSetArg (av [ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg (av [ac], XmNrightAttachment, XmATTACH_FORM); ac++; + XtSetArg (av [ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg (av [ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + form1 = XmCreateForm (frame1, "relatedBox", av, ac); + + // The "Related Links" frame title + Widget label1; + + ac = 0; + XtSetArg (av [ac], XmNchildType, XmFRAME_TITLE_CHILD); ac++; + label1 = XmCreateLabelGadget (frame1, "relatedBoxLabel", av, ac); + + XtManageChild (label1); + + // top portion + ac = 0; + i = 0; + + // Related links enabled + kids[i++] = fep->rl_enabled_toggle = + XmCreateToggleButtonGadget(form1, "enableRelated", av, ac); + + // Related links autoload label + kids[i++] = fep->rl_autoload_label = + XmCreateLabelGadget(form1, "autoloadLabel", av, ac); + + // Related links autoload always + kids[i++] = fep->rl_autoload_radio_always = + XmCreateToggleButtonGadget(form1, "autoloadAlways", av, ac); + + // Related links autoload adaptive + kids[i++] = fep->rl_autoload_radio_adaptive = + XmCreateToggleButtonGadget(form1, "autoloadAdaptive", av, ac); + + // Related links autoload never + kids[i++] = fep->rl_autoload_radio_never = + XmCreateToggleButtonGadget(form1, "autoloadNever", av, ac); + + // Related links excluded domains label + kids[i++] = fep->rl_excluded_label = + XmCreateLabelGadget(form1, "excludedLabel", av, ac); + + // Related links excluded domains text + kids[i++] = fep->rl_excluded_text = + fe_CreateText(form1, "excludedText", av, ac); + + // Related links enabled + XtVaSetValues(fep->rl_enabled_toggle, + XmNindicatorType, XmN_OF_MANY, + XmNalignment, XmALIGNMENT_BEGINNING, + XmNtopAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_NONE, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + 0); + + // Related links autoload label + XtVaSetValues(fep->rl_autoload_label, + XmNalignment, XmALIGNMENT_BEGINNING, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, fep->rl_enabled_toggle, + XmNtopOffset, 10, + XmNbottomAttachment, XmATTACH_NONE, + XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNleftWidget, fep->rl_enabled_toggle, + XmNrightAttachment, XmATTACH_NONE, + 0); + + // Related links autoload always + XtVaSetValues(fep->rl_autoload_radio_always, + XmNindicatorType, XmONE_OF_MANY, + XmNalignment, XmALIGNMENT_BEGINNING, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, fep->rl_autoload_label, + XmNbottomAttachment, XmATTACH_NONE, + XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNleftWidget, fep->rl_enabled_toggle, + XmNrightAttachment, XmATTACH_NONE, + XmNleftOffset, 16, + 0); + + // Related links autoload adaptive + XtVaSetValues(fep->rl_autoload_radio_adaptive, + XmNindicatorType, XmONE_OF_MANY, + XmNalignment, XmALIGNMENT_BEGINNING, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, fep->rl_autoload_radio_always, + XmNbottomAttachment, XmATTACH_NONE, + XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNleftWidget, fep->rl_enabled_toggle, + XmNrightAttachment, XmATTACH_NONE, + XmNleftOffset, 16, + 0); + + // Related links autoload never + XtVaSetValues(fep->rl_autoload_radio_never, + XmNindicatorType, XmONE_OF_MANY, + XmNalignment, XmALIGNMENT_BEGINNING, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, fep->rl_autoload_radio_adaptive, + XmNbottomAttachment, XmATTACH_NONE, + XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNleftWidget, fep->rl_enabled_toggle, + XmNrightAttachment, XmATTACH_NONE, + XmNleftOffset, 16, + 0); + + // Related links excluded domains label + XtVaSetValues(fep->rl_excluded_label, + XmNalignment, XmALIGNMENT_BEGINNING, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, fep->rl_autoload_radio_never, + XmNtopOffset, 10, + XmNbottomAttachment, XmATTACH_NONE, + XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNleftWidget, fep->rl_enabled_toggle, + XmNrightAttachment, XmATTACH_NONE, + 0); + + // Related links excluded domains text + XtVaSetValues(fep->rl_excluded_text, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, fep->rl_excluded_label, + XmNbottomAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNleftWidget, fep->rl_enabled_toggle, + XmNrightAttachment, XmATTACH_FORM, + + XmNeditable, True, + XmNeditMode, XmMULTI_LINE_EDIT, + XmNrows, 5, + XmNwordWrap, True, + + 0); + + XtManageChildren (kids, i); + XtManageChild (form1); + XtManageChild (frame1); + + // Keywords + Widget frame2; + Widget label2; + Widget form2; + + ac = 0; + XtSetArg (av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg (av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + XtSetArg (av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg (av[ac], XmNtopWidget, frame1); ac++; + XtSetArg (av[ac], XmNtopOffset, 8); ac++; + XtSetArg (av[ac], XmNbottomAttachment, XmATTACH_NONE); ac++; + frame2 = XmCreateFrame (form, "keywordFrame", av, ac); + + ac = 0; + XtSetArg (av [ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg (av [ac], XmNrightAttachment, XmATTACH_FORM); ac++; + XtSetArg (av [ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg (av [ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + form2 = XmCreateForm (frame2, "keywordBox", av, ac); + + ac = 0; + XtSetArg (av [ac], XmNchildType, XmFRAME_TITLE_CHILD); ac++; + label2 = XmCreateLabelGadget (frame2, "keywordBoxLabel", av, ac); + + ac = 0; + i = 0; + + kids[i++] = fep->enable_internet_keywords = + XmCreateToggleButtonGadget(form2, "enableKeywords", av, ac); + + XtVaSetValues(fep->enable_internet_keywords, + XmNindicatorType, XmN_OF_MANY, + XmNalignment, XmALIGNMENT_BEGINNING, + XmNtopAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_NONE, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + NULL); + + XtManageChildren (kids, i); + + XtManageChild (label2); + XtManageChild (form2); + XtManageChild (frame2); + + // Add callbacks to automatic related links toggle button + XtAddCallback(fep->rl_enabled_toggle, + XmNvalueChangedCallback, + cb_toggleRelatedEnabled, + (XtPointer) this); + + // Add callbacks to automatic related links toggle button + XtAddCallback(fep->rl_autoload_radio_always, + XmNvalueChangedCallback, + cb_toggleRelatedAutoload, + (XtPointer) fep); + + XtAddCallback(fep->rl_autoload_radio_adaptive, + XmNvalueChangedCallback, + cb_toggleRelatedAutoload, + (XtPointer) fep); + + XtAddCallback(fep->rl_autoload_radio_never, + XmNvalueChangedCallback, + cb_toggleRelatedAutoload, + (XtPointer) fep); + + // Add callbacks to internet keyword toggle button +// XtAddCallback(fep->enable_internet_keywords, XmNvalueChangedCallback, +// cb_toggleInternetKeywords, fep); + + setCreated(TRUE); +} +////////////////////////////////////////////////////////////////////////// +void +XFE_PrefsPageBrowserSmart::init() +{ + XP_ASSERT( m_prefsDataBrowserSmart != NULL ); + + PrefsDataBrowserSmart * fep = m_prefsDataBrowserSmart; + + // Related links enabled + XtVaSetValues(fep->rl_enabled_toggle, + XmNset, fe_globalPrefs.related_links_enabled, + XmNsensitive, !PREF_PrefIsLocked("browser.related.enabled"), + NULL); + + // Excluded domaings + XmTextSetString(fep->rl_excluded_text, + fe_globalPrefs.related_disabled_domains); + + updateRelatedAutoloadSensitive(); + + updateInternetKeywordsSensitive(); + + setInitialized(TRUE); +} +////////////////////////////////////////////////////////////////////////// +void +XFE_PrefsPageBrowserSmart::install() +{ + if (m_rl_need_chrome_update) + { + // Notify whoever is interested in updating the related links + XFE_MozillaApp::theApp()->notifyInterested(XFE_MozillaApp::updateRelatedLinksShowing); + m_rl_need_chrome_update = FALSE; + } +} +////////////////////////////////////////////////////////////////////////// +void +XFE_PrefsPageBrowserSmart::save() +{ + PrefsDataBrowserSmart * fep = m_prefsDataBrowserSmart; + + XP_ASSERT(fep); + + // Related links + XP_Bool old_related_links_enabled = fe_globalPrefs.related_links_enabled; + + // Related links enabled + fe_globalPrefs.related_links_enabled = + XfeToggleButtonIsSet(fep->rl_enabled_toggle); + + // Related links autoload + if (old_related_links_enabled != fe_globalPrefs.related_links_enabled) + { + m_rl_need_chrome_update = TRUE; + } + + // Related links autoload + if (XfeToggleButtonIsSet(fep->rl_autoload_radio_always)) + { + fe_globalPrefs.related_links_autoload = RELATED_LINKS_AUTOLOAD_ALWAYS; + } + + if (XfeToggleButtonIsSet(fep->rl_autoload_radio_adaptive)) + { + fe_globalPrefs.related_links_autoload = RELATED_LINKS_AUTOLOAD_ADAPTIVE; + } + + if (XfeToggleButtonIsSet(fep->rl_autoload_radio_never)) + { + fe_globalPrefs.related_links_autoload = RELATED_LINKS_AUTOLOAD_NEVER; + } + + // Excluded domains + fe_globalPrefs.related_disabled_domains = + XmTextGetString(fep->rl_excluded_text); + + + // Internet Keywords + fe_globalPrefs.internet_keywords_enabled = + XfeToggleButtonIsSet(fep->enable_internet_keywords); + + + // Install preferences + install(); +} +////////////////////////////////////////////////////////////////////////// +PrefsDataBrowserSmart * +XFE_PrefsPageBrowserSmart::getData() +{ + return m_prefsDataBrowserSmart; +} +////////////////////////////////////////////////////////////////////////// +void +XFE_PrefsPageBrowserSmart::updateRelatedAutoloadSensitive() +{ + XP_ASSERT( m_prefsDataBrowserSmart != NULL ); + + PrefsDataBrowserSmart * fep = m_prefsDataBrowserSmart; + + Boolean sensitive = + !PREF_PrefIsLocked("browser.related.enabled") && + fe_globalPrefs.related_links_enabled; + + // autoload label + XtVaSetValues(fep->rl_autoload_label, + XmNsensitive, sensitive, + NULL); + + // autoload always + XtVaSetValues(fep->rl_autoload_radio_always, + XmNset, (fe_globalPrefs.related_links_autoload == RELATED_LINKS_AUTOLOAD_ALWAYS), + XmNsensitive, sensitive, + NULL); + + // autoload adaptive + XtVaSetValues(fep->rl_autoload_radio_adaptive, + XmNset, (fe_globalPrefs.related_links_autoload == RELATED_LINKS_AUTOLOAD_ADAPTIVE), + XmNsensitive, sensitive, + NULL); + + // autoload never + XtVaSetValues(fep->rl_autoload_radio_never, + XmNset, (fe_globalPrefs.related_links_autoload == RELATED_LINKS_AUTOLOAD_NEVER), + XmNsensitive, sensitive, + NULL); + + // excluded domains label + XtVaSetValues(fep->rl_excluded_label, + XmNsensitive, sensitive, + NULL); + + // excluded domains text + XtVaSetValues(fep->rl_excluded_text, + XmNsensitive, sensitive, + NULL); +} +////////////////////////////////////////////////////////////////////////// +void +XFE_PrefsPageBrowserSmart::updateInternetKeywordsSensitive() +{ + XP_ASSERT( m_prefsDataBrowserSmart != NULL ); + + PrefsDataBrowserSmart * fep = m_prefsDataBrowserSmart; + + Boolean keywords_sensitive = + !PREF_PrefIsLocked("browser.goBrowsing.enabled"); + + // enable keywords + XtVaSetValues(fep->enable_internet_keywords, + XmNset, fe_globalPrefs.internet_keywords_enabled, + XmNsensitive, keywords_sensitive, + NULL); +} +////////////////////////////////////////////////////////////////////////// +void +XFE_PrefsPageBrowserSmart::cb_toggleRelatedEnabled(Widget w, + XtPointer clientData, + XtPointer callData) +{ + XFE_PrefsPageBrowserSmart * obj = + (XFE_PrefsPageBrowserSmart *) clientData; + + XmToggleButtonCallbackStruct * cb = + (XmToggleButtonCallbackStruct *) callData; + + PrefsDataBrowserSmart * fep = obj->m_prefsDataBrowserSmart; + + XP_ASSERT( obj != NULL ); + XP_ASSERT( fep != NULL ); + XP_ASSERT( cb != NULL ); + XP_ASSERT( w == fep->rl_enabled_toggle ); + + fe_globalPrefs.related_links_enabled = (XP_Bool) cb->set; + + obj->m_rl_need_chrome_update = True; + + obj->updateRelatedAutoloadSensitive(); +} +////////////////////////////////////////////////////////////////////////// +void +XFE_PrefsPageBrowserSmart::cb_toggleRelatedAutoload(Widget w, + XtPointer closure, + XtPointer callData) +{ + XmToggleButtonCallbackStruct * cb = + (XmToggleButtonCallbackStruct *) callData; + + PrefsDataBrowserSmart * fep = (PrefsDataBrowserSmart *) closure; + + if (! cb->set) + { + XtVaSetValues(w, XmNset, True, 0); + } + else if (w == fep->rl_autoload_radio_always) + { + XtVaSetValues(fep->rl_autoload_radio_adaptive, XmNset, False, 0); + XtVaSetValues(fep->rl_autoload_radio_never, XmNset, False, 0); + } + else if (w == fep->rl_autoload_radio_adaptive) + { + XtVaSetValues(fep->rl_autoload_radio_always, XmNset, False, 0); + XtVaSetValues(fep->rl_autoload_radio_never, XmNset, False, 0); + } + else if (w == fep->rl_autoload_radio_never) + { + XtVaSetValues(fep->rl_autoload_radio_always, XmNset, False, 0); + XtVaSetValues(fep->rl_autoload_radio_adaptive, XmNset, False, 0); + } + else + { + abort(); + } +} +////////////////////////////////////////////////////////////////////////// + diff --git a/mozilla/cmd/xfe/src/PrefsMailFolderDlg.cpp b/mozilla/cmd/xfe/src/PrefsMailFolderDlg.cpp new file mode 100644 index 00000000000..f1f746d99ae --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsMailFolderDlg.cpp @@ -0,0 +1,349 @@ +/* -*- Mode: C++; tab-width: 4 -*- + PrefsMailFolderDlg.cpp -- Generic dialog for choosing a mail folder + Copyright © 1998 Netscape Communications Corporation, all rights reserved. + Created: Alec Flett , 05-Mar-98 + */ + +#include "MozillaApp.h" +#include "xpgetstr.h" +#include "felocale.h" +#include "msgcom.h" +#include "msgmast.h" +#include "PrefsMailFolderDlg.h" +#include "FolderPromptDialog.h" +#include "ViewGlue.h" + +#include +#include +#include + +extern int XFE_CHOOSE_FOLDER_INSTRUCT; +extern int XFE_FOLDER_ON_FORMAT; +extern int MK_MSG_SENT_L10N_NAME; + +XFE_PrefsMailFolderDialog::XFE_PrefsMailFolderDialog(Widget parent, + MSG_Master *master, + MWContext *context) : + XFE_Dialog(parent,"prefsMailFolderDialog",TRUE, TRUE, // ok and cancel + FALSE, FALSE, FALSE, TRUE), // nothing else + m_l10n_name(0), + m_retVal(0) +{ + m_context=context; + m_master=master; +} + +XFE_PrefsMailFolderDialog::~XFE_PrefsMailFolderDialog() +{ + // should I be deleting FolderDropdowns? + // the delete is also commented out on FolderPromptDialog.cpp, line 81 + // delete m_folderDropDown; + // delete m_serverDropDown; + delete m_l10n_name; +} + +void XFE_PrefsMailFolderDialog::cb_ok(Widget, XtPointer closure, XtPointer) +{ + XFE_PrefsMailFolderDialog *theDialog = + (XFE_PrefsMailFolderDialog *)closure; + + theDialog->hide(); + theDialog->m_retVal=theDialog->getFolder(); + theDialog->m_doneWithLoop=True; +} + +void XFE_PrefsMailFolderDialog::cb_cancel(Widget, XtPointer closure, XtPointer) +{ + XFE_PrefsMailFolderDialog *theDialog = + (XFE_PrefsMailFolderDialog *)closure; + theDialog->hide(); + + theDialog->m_doneWithLoop=True; +} + +void XFE_PrefsMailFolderDialog::cb_newFolder(Widget, XtPointer closure, XtPointer) +{ + XFE_PrefsMailFolderDialog *theDialog = + (XFE_PrefsMailFolderDialog *)closure; + + theDialog->newFolder(); +} + +void XFE_PrefsMailFolderDialog::newFolder() { + + XFE_FolderPromptDialog *fpd = + new XFE_FolderPromptDialog(this->getBaseWidget(), + "newFolderDialog", + NULL, + this); + + MSG_FolderInfo *folder=m_folderDropdown->getSelectedFolder(); + MSG_FolderInfo *newFolder = fpd->prompt(folder); + + if (newFolder) { + m_folderDropdown->resyncDropdown(); + setFolder(newFolder); + } + delete fpd; + +} + +MWContext *XFE_PrefsMailFolderDialog::getContext() +{ + return m_context; +} + + +void XFE_PrefsMailFolderDialog::initPage() +{ + Widget kids[10]; + int i=0; + + Arg av[10]; + Cardinal ac; + + XP_ASSERT(m_chrome); + + ac=0; + Widget form = XmCreateForm(m_chrome, "form", av, ac); + m_instruct_label = + kids[i++] = XmCreateLabelGadget(form, "instructions",av,ac); + + + XtVaSetValues(m_instruct_label, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + + ac=0; + kids[i++] = m_server_toggle = + XmCreateToggleButtonGadget(form, "folderOnServer", av, ac); + kids[i++] = m_folder_toggle = + XmCreateToggleButtonGadget(form, "specificFolder", av, ac); + + m_serverDropdown = + new XFE_FolderDropdown(this,form, // only show servers + TRUE, FALSE, FALSE, FALSE); + Widget server_dropdown = + kids[i++] = m_serverDropdown->getBaseWidget(); + + m_folderDropdown = + new XFE_FolderDropdown(this,form, // only select folders + FALSE, FALSE, FALSE); + Widget folder_dropdown = + kids[i++] = m_folderDropdown->getBaseWidget(); + + kids[i++] = m_new_folder_button = + XmCreatePushButtonGadget(form, "newFolder", av, ac); + + int max_height1=XfeVaGetTallestWidget(m_server_toggle, + server_dropdown, + NULL); + + int max_height2=XfeVaGetTallestWidget(m_folder_toggle, + folder_dropdown, + m_new_folder_button, + NULL); + + XtVaSetValues(m_server_toggle, + XmNheight, max_height1, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_instruct_label, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + XmNindicatorType, XmONE_OF_MANY, + NULL); + + m_serverDropdown->setPopupServer(FALSE); + XtVaSetValues(server_dropdown, + XmNheight, max_height1, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, m_server_toggle, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, m_server_toggle, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + + XtVaSetValues(m_folder_toggle, + XmNheight, max_height2, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_server_toggle, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + XmNindicatorType, XmONE_OF_MANY, + NULL); + + m_folderDropdown->setPopupServer(FALSE); + XtVaSetValues(folder_dropdown, + XmNheight, max_height2, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, m_folder_toggle, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, m_folder_toggle, + XmNrightWidget, XmATTACH_NONE, + XmNbottomWidget, XmATTACH_NONE, + NULL); + + XtVaSetValues(m_new_folder_button, + XmNheight, max_height2, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, m_folder_toggle, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, folder_dropdown, + XmNrightWidget, XmATTACH_FORM, + XmNbottomWidget, XmATTACH_NONE, + NULL); + + XtManageChild(form); + XtManageChild(m_instruct_label); + XtManageChildren(kids, i); + + // callbacks + XtAddCallback(m_chrome, XmNokCallback, cb_ok, this); + XtAddCallback(m_chrome, XmNcancelCallback, cb_cancel, this); + XtAddCallback(m_new_folder_button, XmNactivateCallback, cb_newFolder,this); + XtAddCallback(m_folder_toggle,XmNvalueChangedCallback,cb_folderClick,this); + XtAddCallback(m_server_toggle,XmNvalueChangedCallback,cb_serverClick,this); + +} + + +MSG_FolderInfo *XFE_PrefsMailFolderDialog::getFolder() +{ + if (XmToggleButtonGetState(m_folder_toggle)) { + return m_folderDropdown->getSelectedFolder(); + } + + // we have to get the new MSG_FolderInfo * for the + // l10n'd folder name + if (XmToggleButtonGetState(m_server_toggle)) { + + MSG_FolderInfo *server=m_serverDropdown->getSelectedFolder(); + + // get the URL for the server + URL_Struct *folderUrl=MSG_ConstructUrlForFolder(NULL, server); + int urllen=XP_STRLEN(folderUrl->address) + 1 + + XP_STRLEN(m_l10n_name); + char *url = new char[urllen]; + + // construct the new URL + XP_STRCPY(url, folderUrl->address); + XP_STRCAT(url, "/"); + XP_STRCAT(url, m_l10n_name); + + // get the MSG_FolderInfo for the folder + MSG_FolderInfo *folder= + (MSG_FolderInfo *)m_master->FindMailFolder(url, TRUE); + XP_FREE(url); + NET_FreeURLStruct(folderUrl); + + return folder; + } + return NULL; +} + +void XFE_PrefsMailFolderDialog::setFolder(MSG_FolderInfo *folder, int l10n_name) +{ + + MSG_FolderLine folderLine; + m_retVal=folder; + + MSG_FolderInfo *server=GetHostFolderInfo(folder); + if (!server) server=MSG_GetLocalMailTree(m_master); + + MSG_GetFolderLineById(m_master,folder,&folderLine); + if (l10n_name) { + if (m_l10n_name) XP_FREE(m_l10n_name); + m_l10n_name = XP_STRDUP(XP_GetString(l10n_name)); + } + if (!m_l10n_name) m_l10n_name = XP_STRDUP(XP_GetString(MK_MSG_SENT_L10N_NAME)); + + // set instructions + char *char_format = XP_GetString(XFE_CHOOSE_FOLDER_INSTRUCT); + char *char_string = PR_smprintf(char_format, m_l10n_name); + XmString xmstr = XmStringCreateLocalized(char_string); + XtVaSetValues(m_instruct_label, + XmNlabelString, xmstr, + NULL); + XmStringFree(xmstr); + XP_FREE(char_string); + + char_format = XP_GetString(XFE_FOLDER_ON_FORMAT); + char_string = PR_smprintf(char_format, m_l10n_name, ""); + xmstr = XmStringCreateLocalized(char_string); + XtVaSetValues(m_server_toggle, + XmNlabelString, xmstr, + NULL); + XmStringFree(xmstr); + XP_FREE(char_string); + + + m_serverDropdown->selectFolder(server); + m_folderDropdown->selectFolder(folder); + if (folder && !XP_STRCMP(m_l10n_name, folderLine.name)) { + XmToggleButtonSetState(m_server_toggle, True, True); + // because the first time niether is set, and thus + // the callback to un-set m_folder_toggle isn't called + m_folderDropdown->setSensitive(False); + } else { + XmToggleButtonSetState(m_folder_toggle, True, True); + // because the first time niether is set, and thus + // the callback to un-set m_server_toggle isn't called + m_serverDropdown->setSensitive(False); + } +} + +void XFE_PrefsMailFolderDialog::cb_folderClick(Widget, + XtPointer closure, + XtPointer callData) +{ + XFE_PrefsMailFolderDialog *theDialog=(XFE_PrefsMailFolderDialog *)closure; + XmToggleButtonCallbackStruct *cbs = + (XmToggleButtonCallbackStruct *)callData; + theDialog->folderToggle(cbs); +} + +void XFE_PrefsMailFolderDialog::cb_serverClick(Widget, + XtPointer closure, + XtPointer callData) +{ + XFE_PrefsMailFolderDialog *theDialog=(XFE_PrefsMailFolderDialog *)closure; + XmToggleButtonCallbackStruct *cbs = + (XmToggleButtonCallbackStruct *)callData; + theDialog->serverToggle(cbs); +} + + hide(); +void XFE_PrefsMailFolderDialog::folderToggle(XmToggleButtonCallbackStruct *cbs) +{ + XP_ASSERT(cbs); + m_folderDropdown->setSensitive(cbs->set); + if (cbs->set) + XmToggleButtonSetState(m_server_toggle, False, True); + +} + +void XFE_PrefsMailFolderDialog::serverToggle(XmToggleButtonCallbackStruct *cbs) +{ + XP_ASSERT(cbs); + m_serverDropdown->setSensitive(cbs->set); + if (cbs->set) + XmToggleButtonSetState(m_folder_toggle, False, True); +} + +MSG_FolderInfo *XFE_PrefsMailFolderDialog::prompt() +{ + m_doneWithLoop=False; + show(); + + while (!m_doneWithLoop) + fe_EventLoop(); + + hide(); + return m_retVal; +} diff --git a/mozilla/cmd/xfe/src/PrefsMailFolderDlg.h b/mozilla/cmd/xfe/src/PrefsMailFolderDlg.h new file mode 100644 index 00000000000..26611e6fc70 --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsMailFolderDlg.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 4 -*- + PrefsMailFolderDlg.h -- Generic dialog for choosing a mail folder + Copyright © 1998 Netscape Communications Corporation, all rights reserved. + Created: Alec Flett , 05-Mar-98 + */ + +#ifndef _xfe_prefsfolderdialog_h +#define _xfe_prefsfolderdialog_h +#include "msgmast.h" +#include "Dialog.h" +#include "FolderDropdown.h" + +class XFE_PrefsMailFolderDialog : public XFE_Dialog +{ + public: + XFE_PrefsMailFolderDialog(Widget parent, + MSG_Master *master, + MWContext *context); + + virtual ~XFE_PrefsMailFolderDialog(); + + MSG_FolderInfo *prompt(); + virtual void initPage(); // initialize dialog + void setFolder(MSG_FolderInfo *folder, int l10n_name=0); + // get the chosen mail folder + MSG_FolderInfo *getFolder(); + + MWContext *getContext(); + +private: + + static void cb_ok(Widget, XtPointer, XtPointer); + static void cb_cancel(Widget, XtPointer, XtPointer); + static void cb_newFolder(Widget, XtPointer, XtPointer); + static void cb_folderClick(Widget, XtPointer, XtPointer); + static void cb_serverClick(Widget, XtPointer, XtPointer); + void folderToggle(XmToggleButtonCallbackStruct *cbs); + void serverToggle(XmToggleButtonCallbackStruct *cbs); + void newFolder(); + + MWContext *m_context; + + Widget m_instruct_label; + Widget m_server_toggle; + Widget m_folder_toggle; + Widget m_new_folder_button; + XFE_FolderDropdown *m_serverDropdown; + XFE_FolderDropdown *m_folderDropdown; + + MSG_Master *m_master; + MSG_FolderInfo *m_folder, *m_server; + MSG_FolderLine *m_folderLine, *m_serverLine; + char *m_l10n_name; + + XP_Bool m_doneWithLoop; + MSG_FolderInfo *m_retVal; +}; + +#endif diff --git a/mozilla/cmd/xfe/src/PrefsPageAddress.cpp b/mozilla/cmd/xfe/src/PrefsPageAddress.cpp new file mode 100644 index 00000000000..4c12d0895d1 --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsPageAddress.cpp @@ -0,0 +1,440 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +/* + PrefsPageAddress.cpp - Mail/News Addressing Preference Pane + Created: Alec Flett + */ + +#include "felocale.h" +#include "prefapi.h" +#include "PrefsPageAddress.h" + +XFE_PrefsPageAddress::XFE_PrefsPageAddress(XFE_PrefsDialog *dialog) + : XFE_PrefsPage(dialog), + m_pLdapServers(0), + m_dirList(0), + m_dirCount(0), + m_dirSelected(0) +{ + + + // get ready to fill up LDAP Directory combo + // taken from WinFE + char location[256]; + int nLen = sizeof(location); + + m_pLdapServers=XP_ListNew(); + + PREF_GetDefaultCharPref("browser.addressbook_location", location, &nLen); + DIR_GetServerPreferences(&m_pLdapServers, location); + + int count=XP_ListCount(m_pLdapServers); + // put list into a format DtComboBox can understand (array of pointers) + m_dirList = (DIR_Server**)XP_CALLOC(count, sizeof(DIR_Server*)); + m_dirNames= (XmString *) XP_CALLOC(count, sizeof(XmString)); + XP_List *pList=m_pLdapServers; + DIR_Server *pServer; + + // build up the array of XmStrings, and save the name of the + // one used for selection + int i=0; + while (pServer = (DIR_Server *)XP_ListNextObject(pList)) { + // only use LDAP servers + if ((!pServer->description) || + (pServer->dirType != LDAPDirectory)) + continue; + + m_dirList[i]=pServer; + m_dirNames[i]=XmStringCreateLocalized(pServer->description); + if (DIR_TestFlag(pServer, DIR_AUTO_COMPLETE_ENABLED)) + m_dirSelected=m_dirNames[i]; + i++; + + } + + m_dirCount=i; + +} + +XFE_PrefsPageAddress::~XFE_PrefsPageAddress() +{ + + DIR_DeleteServerList(m_pLdapServers); + int i; + for (i=0; igetPrefsParent(), + XtNvisual, &v, + XtNcolormap, &cmap, + XtNdepth, &depth, + 0); + ac=0; + XtSetArg(av[ac], XtNvisual, v); ac++; + XtSetArg(av[ac], XtNcolormap, cmap); ac++; + XtSetArg(av[ac], XtNdepth, depth); ac++; + XtSetArg(av[ac], XmNarrowType, XmMOTIF); ac++; + + i=0; + Widget complete_label; + Widget conflict_label; + Widget only_label; + Widget conflict_radiobox; + + kids[i++] = complete_label = + XmCreateLabelGadget(message_form, "completeLabel", NULL, 0); + + kids[i++] = m_complete_ab_toggle = + XmCreateToggleButtonGadget(message_form, "completeABToggle", NULL, 0); + + kids[i++] = m_complete_dir_toggle = + XmCreateToggleButtonGadget(message_form, "completeDirToggle", NULL, 0); + + // av holds the visual/colormap/depth stuff + kids[i++] = m_complete_dir_combo = + DtCreateComboBox(message_form, "completeDirCombo", av, ac); + + kids[i++] = conflict_label = + XmCreateLabelGadget(message_form, "conflictLabel", NULL, 0); + + kids[i++] = conflict_radiobox = + XmCreateRadioBox(message_form, "conflictRadioBox", NULL, 0); + m_conflict_show_toggle = + XmCreateToggleButtonGadget(conflict_radiobox,"conflictShowToggle",NULL,0); + m_conflict_accept_toggle = + XmCreateToggleButtonGadget(conflict_radiobox,"conflictAcceptToggle",NULL,0); + + kids[i++] = only_label = + XmCreateLabelGadget(message_form, "onlyMatchLabel", NULL, 0); + + kids[i++] = m_only_ab_toggle = + XmCreateToggleButtonGadget(message_form, "onlyMatchToggle", NULL, 0); + + + + // place the widgets + XtVaSetValues(complete_label, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + XtVaSetValues(m_complete_ab_toggle, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, complete_label, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + XtVaSetValues(m_complete_dir_toggle, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_complete_ab_toggle, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + + XtVaSetValues(m_complete_dir_combo, + XmNitems, m_dirNames, + XmNitemCount, m_dirCount, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_complete_dir_toggle, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + XtVaSetValues(conflict_label, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_complete_dir_combo, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + XtVaSetValues(conflict_radiobox, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, conflict_label, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + + XtVaSetValues(only_label, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, conflict_radiobox, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + XtVaSetValues(m_only_ab_toggle, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, only_label, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + + XtManageChildren(kids,i); + XtManageChild(m_conflict_show_toggle); + XtManageChild(m_conflict_accept_toggle); + XtManageChild(message_frame); + XtManageChild(message_form); + XtManageChild(message_label); + + return message_frame; +} + +Widget XFE_PrefsPageAddress::createSortFrame(Widget parent, Widget attachTo) { + + Widget kids[3]; + int i; + Arg av[10]; + int ac; + + XP_ASSERT(parent); + + Widget sort_frame; + ac=0; + if (attachTo==NULL) { + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + } + else { + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, attachTo); ac++; + } + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_NONE); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + sort_frame = + XmCreateFrame(parent,"sortFrame",av,ac); + + Widget sort_form; + ac=0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + sort_form = + XmCreateForm(sort_frame,"sortForm",av,ac); + + Widget sort_label; + ac=0; + XtSetArg(av[ac], XmNchildType, XmFRAME_TITLE_CHILD); ac++; + sort_label = + XmCreateLabelGadget(sort_frame, "sortLabel",av,ac); + + ac=0; + i=0; + + Widget sort_radiobox; + + sort_radiobox = + XmCreateRadioBox(sort_form, "sortRadioBox", NULL, 0); + + kids[i++] = m_sort_first_toggle = + XmCreateToggleButton(sort_radiobox, "sortFirstToggle", NULL, 0); + kids[i++] = m_sort_last_toggle = + XmCreateToggleButton(sort_radiobox, "sortLastToggle", NULL, 0); + + XtVaSetValues(sort_radiobox, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + XtManageChild(sort_radiobox); + + XtManageChildren(kids,i); + XtManageChild(sort_form); + XtManageChild(sort_frame); + XtManageChild(sort_label); + + return sort_frame; +} + + + +void XFE_PrefsPageAddress::init() { + Bool boolval; + Bool locked; + + PREF_GetBoolPref("ldap_1.autoComplete.useAddressBooks", &boolval); + locked=PREF_PrefIsLocked("ldap_1.autoComplete.useAddressBooks"); + XtVaSetValues(m_complete_ab_toggle, + XmNset, boolval, + XmNsensitive, !locked, + NULL); + + PREF_GetBoolPref("ldap_1.autoComplete.useDirectory", &boolval); + locked=PREF_PrefIsLocked("ldap_1.autoComplete.useDirectory"); + XtVaSetValues(m_complete_dir_toggle, + XmNset, boolval, + XmNsensitive, !locked, + NULL); + + if (m_dirSelected) + DtComboBoxSelectItem(m_complete_dir_combo, m_dirSelected); + + PREF_GetBoolPref("ldap_1.autoComplete.showDialogForMultipleMatches", + &boolval); + locked= + PREF_PrefIsLocked("ldap_1.autoComplete.showDialogForMultipleMatches"); + XtVaSetValues(m_conflict_show_toggle, + XmNset, boolval, + XmNsensitive, !locked, + NULL); + XtVaSetValues(m_conflict_accept_toggle, + XmNset, !boolval, + XmNsensitive, !locked, + NULL); + + PREF_GetBoolPref("ldap_1.autoComplete.skipDirectoryIfLocalMatchFound", + &boolval); + locked= + PREF_PrefIsLocked("ldap_1.autoComplete.skipDirectoryIfLocalMatchFound"); + XtVaSetValues(m_only_ab_toggle, + XmNset, boolval, + XmNsensitive, !locked, + NULL); + + + PREF_GetBoolPref("mail.addr_book.lastnamefirst", &boolval); + locked=PREF_PrefIsLocked("mail.addr_book.lastnamefirst"); + XtVaSetValues(m_sort_first_toggle, + XmNset, !boolval, + XmNsensitive, !locked, + NULL); + XtVaSetValues(m_sort_last_toggle, + XmNset, boolval, + XmNsensitive, !locked, + NULL); + setInitialized(TRUE); +} + +void XFE_PrefsPageAddress::install() { + +} + +void XFE_PrefsPageAddress::save() { + Bool boolval; + int32 intval; + + boolval = XmToggleButtonGadgetGetState(m_complete_ab_toggle); + PREF_SetBoolPref("ldap_1.autoComplete.useAddressBooks", boolval); + boolval = XmToggleButtonGadgetGetState(m_complete_dir_toggle); + PREF_SetBoolPref("ldap_1.autoComplete.useDirectory", boolval); + + // find index of selected server + XtVaGetValues(m_complete_dir_combo, + XmNselectedPosition, &intval, + NULL); + + // get corresponding DIR_Server* + DIR_Server *pServer=m_dirList[intval]; + + DIR_SetAutoCompleteEnabled(m_pLdapServers, pServer, boolval); + DIR_SaveServerPreferences(m_pLdapServers); + + boolval = XmToggleButtonGadgetGetState(m_conflict_show_toggle); + PREF_SetBoolPref("ldap_1.autoComplete.showDialogForMultipleMatches", + boolval); + boolval = XmToggleButtonGadgetGetState(m_only_ab_toggle); + PREF_SetBoolPref("ldap_1.autoComplete.skipDirectoryIfLocalMatchFound", + boolval); + + boolval = XmToggleButtonGadgetGetState(m_sort_last_toggle); + PREF_SetBoolPref("mail.addr_book.lastnamefirst", boolval); + +} + +Boolean XFE_PrefsPageAddress::verify() { + + return True; +} + diff --git a/mozilla/cmd/xfe/src/PrefsPageAddress.h b/mozilla/cmd/xfe/src/PrefsPageAddress.h new file mode 100644 index 00000000000..63ade34c4db --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsPageAddress.h @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +/* + PrefsDialog.cpp -- class for XFE preferences dialogs. + Created: Alec Flett + */ + +#ifndef _xfe_prefspageaddress_h +#define _xfe_prefspageaddress_h + +#include +#include "PrefsDialog.h" + +class XFE_PrefsPageAddress : public XFE_PrefsPage +{ + public: + + XFE_PrefsPageAddress(XFE_PrefsDialog *dialog); + virtual ~XFE_PrefsPageAddress(); + + virtual void create(); + + virtual void init(); + virtual void install(); + + virtual void save(); + + virtual Boolean verify(); + + private: + + Widget createMessageFrame(Widget parent, Widget attachTo); + Widget createSortFrame(Widget parent, Widget attachTo); + Widget m_complete_ab_toggle; + Widget m_complete_dir_toggle; + Widget m_complete_dir_combo; + + Widget m_conflict_show_toggle; + Widget m_conflict_accept_toggle; + + Widget m_only_ab_toggle; + + Widget m_sort_first_toggle; + Widget m_sort_last_toggle; + + XP_List *m_pLdapServers; /* list of DIR_Servers */ + DIR_Server **m_dirList; /* array of DIR_Servers */ + XmString *m_dirNames; /* XmString names of DIR_Servers */ + int m_dirCount; + + XmString m_dirSelected; /* intitially selected directory */ +}; + +#endif diff --git a/mozilla/cmd/xfe/src/PrefsPageLIFiles.cpp b/mozilla/cmd/xfe/src/PrefsPageLIFiles.cpp new file mode 100644 index 00000000000..a529aeb0653 --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsPageLIFiles.cpp @@ -0,0 +1,203 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +/* + PrefsPageLIFiles.cpp -- LI File preferences. + Created: Daniel Malmer , 21-Apr-98. + */ + +#include "rosetta.h" +#include "felocale.h" +#include "prefapi.h" +#include "PrefsPageLIFiles.h" +#include "Xfe/Geometry.h" + +// XFE_PrefsPageLIFiles + +XFE_PrefsPageLIFiles::XFE_PrefsPageLIFiles(XFE_PrefsDialog* dialog) : XFE_PrefsPage(dialog) +{ +} + + +XFE_PrefsPageLIFiles::XFE_PrefsPageLIFiles(Widget w) : XFE_PrefsPage(w) +{ +} + + +XFE_PrefsPageLIFiles::~XFE_PrefsPageLIFiles() +{ +} + + +void +XFE_PrefsPageLIFiles::create() +{ + int ac; + Arg av[16]; + Widget frame_label; + Widget form; + Widget file_label; + Widget row_column; + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + m_wPage = XmCreateFrame(m_wPageForm, "liFiles", av, ac); + XtManageChild(m_wPage); + + ac = 0; + XtSetArg(av[ac], XmNchildType, XmFRAME_TITLE_CHILD); ac++; + frame_label = XmCreateLabelGadget(m_wPage, "frameLabel", av, ac); + XtManageChild(frame_label); + + ac = 0; + form = XmCreateForm(m_wPage, "form", av, ac); + XtManageChild(form); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + file_label = XmCreateLabelGadget(form, "fileLabel", av, ac); + XtManageChild(file_label); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, file_label); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNpacking, XmPACK_COLUMN); ac++; + XtSetArg(av[ac], XmNnumColumns, 2); ac++; + row_column = XmCreateRowColumn(form, "row_column", av, ac); + XtManageChild(row_column); + + ac = 0; + m_bookmark_toggle = XmCreateToggleButtonGadget(row_column, "bookmarkToggle", av, ac); + XtManageChild(m_bookmark_toggle); + + ac = 0; + m_cookies_toggle = XmCreateToggleButtonGadget(row_column, "cookiesToggle", av, ac); + XtManageChild(m_cookies_toggle); + + ac = 0; + m_filter_toggle = XmCreateToggleButtonGadget(row_column, "filterToggle", av, ac); + XtManageChild(m_filter_toggle); + + ac = 0; + m_addrbook_toggle = XmCreateToggleButtonGadget(row_column, "addrbookToggle", av, ac); + XtManageChild(m_addrbook_toggle); + + ac = 0; + m_navcenter_toggle = XmCreateToggleButtonGadget(row_column, "navcenterToggle", av, ac); + XtManageChild(m_navcenter_toggle); + + ac = 0; + m_prefs_toggle = XmCreateToggleButtonGadget(row_column, "prefsToggle", av, ac); + XtManageChild(m_prefs_toggle); + + ac = 0; + m_javasec_toggle = XmCreateToggleButtonGadget(row_column, "javasecToggle", av, ac); + XtManageChild(m_javasec_toggle); + + HG82167 + + setCreated(TRUE); +} + + +void +XFE_PrefsPageLIFiles::init() +{ + XP_Bool enabled = FALSE; + PREF_GetBoolPref("li.client.bookmarks", &enabled); + XtVaSetValues(m_bookmark_toggle, XmNset, enabled, + XmNsensitive, !PREF_PrefIsLocked("li.client.bookmarks"), NULL); + + PREF_GetBoolPref("li.client.cookies", &enabled); + XtVaSetValues(m_cookies_toggle, XmNset, enabled, + XmNsensitive, !PREF_PrefIsLocked("li.client.cookies"), NULL); + + PREF_GetBoolPref("li.client.filters", &enabled); + XtVaSetValues(m_filter_toggle, XmNset, enabled, + XmNsensitive, !PREF_PrefIsLocked("li.client.filters"), NULL); + + PREF_GetBoolPref("li.client.addressbook", &enabled); + XtVaSetValues(m_addrbook_toggle, XmNset, enabled, + XmNsensitive, !PREF_PrefIsLocked("li.client.addressbook"), NULL); + + PREF_GetBoolPref("li.client.navcntr", &enabled); + XtVaSetValues(m_navcenter_toggle, XmNset, enabled, + XmNsensitive, !PREF_PrefIsLocked("li.client.navcntr"), NULL); + + PREF_GetBoolPref("li.client.liprefs", &enabled); + XtVaSetValues(m_prefs_toggle, XmNset, enabled, + XmNsensitive, !PREF_PrefIsLocked("li.client.liprefs"), NULL); + + PREF_GetBoolPref("li.client.javasecurity", &enabled); + XtVaSetValues(m_javasec_toggle, XmNset, enabled, + XmNsensitive, !PREF_PrefIsLocked("li.client.javasecurity"), NULL); + + HG21761 +} + + +void +XFE_PrefsPageLIFiles::install() +{ +} + + +void +XFE_PrefsPageLIFiles::save() +{ + PREF_SetBoolPref("li.client.bookmarks", + XmToggleButtonGetState(m_bookmark_toggle)); + PREF_SetBoolPref("li.client.cookies", + XmToggleButtonGetState(m_cookies_toggle)); + PREF_SetBoolPref("li.client.filters", + XmToggleButtonGetState(m_filter_toggle)); + PREF_SetBoolPref("li.client.addressbook", + XmToggleButtonGetState(m_addrbook_toggle)); + PREF_SetBoolPref("li.client.navcntr", + XmToggleButtonGetState(m_navcenter_toggle)); + PREF_SetBoolPref("li.client.liprefs", + XmToggleButtonGetState(m_prefs_toggle)); + PREF_SetBoolPref("li.client.javasecurity", + XmToggleButtonGetState(m_javasec_toggle)); + HG10280 +} + + +Boolean +XFE_PrefsPageLIFiles::verify() +{ + return TRUE; +} + + +Widget +XFE_PrefsPageLIFiles::get_frame() +{ + return m_wPage; +} + + diff --git a/mozilla/cmd/xfe/src/PrefsPageLIFiles.h b/mozilla/cmd/xfe/src/PrefsPageLIFiles.h new file mode 100644 index 00000000000..67c7b2dcab4 --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsPageLIFiles.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +/* + PrefsPageLIFiles.cpp -- class for LI file preferences dialog. + Created: Daniel Malmer + */ + +#ifndef _xfe_prefspagelifiles_h +#define _xfe_prefspagelifiles_h + +#include "rosetta.h" +#include +#include "PrefsDialog.h" + +class XFE_PrefsPageLIFiles : public XFE_PrefsPage +{ + public: + + XFE_PrefsPageLIFiles(XFE_PrefsDialog *dialog); + XFE_PrefsPageLIFiles(Widget parent); + virtual ~XFE_PrefsPageLIFiles(); + + virtual void create(); + virtual void init(); + virtual void install(); + virtual void save(); + + virtual Boolean verify(); + + Widget get_frame(); + + private: + Widget m_bookmark_toggle; + Widget m_cookies_toggle; + Widget m_filter_toggle; + Widget m_addrbook_toggle; + Widget m_navcenter_toggle; + Widget m_prefs_toggle; + Widget m_javasec_toggle; + HG89217 +}; + +#endif diff --git a/mozilla/cmd/xfe/src/PrefsPageLIGeneral.cpp b/mozilla/cmd/xfe/src/PrefsPageLIGeneral.cpp new file mode 100644 index 00000000000..a8bfb1cd52d --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsPageLIGeneral.cpp @@ -0,0 +1,352 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + + + +/* + PrefsPageLIGeneral.cpp -- LI General preferences. + Created: Daniel Malmer , 21-Apr-98. + */ + + +#include "felocale.h" +#include "prefapi.h" +#include "secnav.h" +#include "PrefsPageLIGeneral.h" +#include "Xfe/Geometry.h" + +#include + + +XFE_PrefsPageLIGeneral::XFE_PrefsPageLIGeneral(XFE_PrefsDialog* dialog, DialogUsage usage) : XFE_PrefsPage(dialog) +{ + m_usage = usage; +} + + +XFE_PrefsPageLIGeneral::XFE_PrefsPageLIGeneral(Widget w, DialogUsage usage) : XFE_PrefsPage(w) +{ + m_usage = usage; +} + + +XFE_PrefsPageLIGeneral::~XFE_PrefsPageLIGeneral() +{ +} + + +// XFE_PrefsPageLIGeneral::create +// Contains general prefs and user info. +// General prefs consists of an 'enable' toggle, and a 'background sync' +// toggle, with a text field for the sync interval. +// User info consists of a text field for user name and password. +void +XFE_PrefsPageLIGeneral::create() +{ + int ac; + Arg av[16]; + Widget top_label; + Widget top_form; + Widget bottom_label; + Widget bottom_form; + Widget user_info_label; + Widget user_label; + Widget password_label; + Dimension label_height; + Dimension label_width; + int max_length; + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + m_wPage = XmCreateForm(m_wPageForm, "liGeneral", av, ac); + XtManageChild(m_wPage); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + m_top_frame = XmCreateFrame(m_wPage, "topFrame", av, ac); + XtManageChild(m_top_frame); + + ac = 0; + XtSetArg(av[ac], XmNchildType, XmFRAME_TITLE_CHILD); ac++; + top_label = XmCreateLabelGadget(m_top_frame, "topTitle", av, ac); + XtManageChild(top_label); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + top_form = XmCreateForm(m_top_frame, "topForm", av, ac); + XtManageChild(top_form); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + m_enable_toggle = XmCreateToggleButtonGadget(top_form, "enableToggle", av, ac); + XtManageChild(m_enable_toggle); + +#ifdef LI_BACKGROUND_SYNC_ENABLED + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, m_enable_toggle); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + m_sync_toggle = XmCreateToggleButtonGadget(top_form, "syncToggle", av, ac); + XtManageChild(m_sync_toggle); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, m_sync_toggle); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNleftWidget, m_sync_toggle); ac++; + XtSetArg(av[ac], XmNcolumns, 4); ac++; + m_sync_text = XmCreateTextField(top_form, "syncText", av, ac); + XtManageChild(m_sync_text); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, m_sync_toggle); ac++; + XtSetArg(av[ac], XmNtopWidget, m_sync_toggle); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNleftWidget, m_sync_text); ac++; + unit_label = XmCreateLabelGadget(top_form, "unitLabel", av, ac); + XtManageChild(unit_label); +#endif + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, m_top_frame); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + m_bottom_frame = XmCreateFrame(m_wPage, "bottomFrame", av, ac); + XtManageChild(m_bottom_frame); + + ac = 0; + XtSetArg(av[ac], XmNchildType, XmFRAME_TITLE_CHILD); ac++; + bottom_label = XmCreateLabelGadget(m_bottom_frame, "bottomTitle", av, ac); + XtManageChild(bottom_label); + +#ifdef LI_BACKGROUND_SYNC_ENABLED + label_height = XfeVaGetTallestWidget(m_sync_toggle, m_sync_text, unit_label, NULL); + XtVaSetValues(m_sync_toggle, XmNheight, label_height, NULL); + XtVaSetValues(m_sync_text, XmNheight, label_height, NULL); + XtVaSetValues(unit_label, XmNheight, label_height, NULL); +#endif + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + bottom_form = XmCreateForm(m_bottom_frame, "bottomForm", av, ac); + XtManageChild(bottom_form); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + user_info_label = XmCreateLabel(bottom_form, "userInfoLabel", av, ac); + XtManageChild(user_info_label); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, user_info_label); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + user_label = XmCreateLabel(bottom_form, "userLabel", av, ac); + XtManageChild(user_label); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, user_info_label); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNleftWidget, user_label); ac++; + XtSetArg(av[ac], XmNalignment, XmALIGNMENT_END); ac++; + m_user_text = XmCreateTextField(bottom_form, "userText", av, ac); + XtManageChild(m_user_text); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, user_label); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNalignment, XmALIGNMENT_END); ac++; + password_label = XmCreateLabel(bottom_form, "passwordLabel", av, ac); + if ( m_usage == login ) XtManageChild(password_label); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, m_user_text); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET); ac++; + XtSetArg(av[ac], XmNleftWidget, m_user_text); ac++; + m_password_text = XmCreateTextField(bottom_form, "passwordText", av, ac); + if ( m_usage == login ) XtManageChild(m_password_text); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, m_user_text); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET); ac++; + XtSetArg(av[ac], XmNleftWidget, m_user_text); ac++; + m_save_password_toggle = XmCreateToggleButtonGadget(bottom_form, "savePasswordToggle", av, ac); + if ( m_usage == pref ) XtManageChild(m_save_password_toggle); + + label_height = XfeVaGetTallestWidget(user_label, m_user_text, NULL); + XtVaSetValues(user_label, XmNheight, label_height, NULL); + XtVaSetValues(m_user_text, XmNheight, label_height, NULL); + + label_height = XfeVaGetTallestWidget(password_label, m_password_text, m_save_password_toggle, NULL); + XtVaSetValues(password_label, XmNheight, label_height, NULL); + XtVaSetValues(m_password_text, XmNheight, label_height, NULL); + XtVaSetValues(m_save_password_toggle, XmNheight, label_height, NULL); + + label_width = XfeVaGetWidestWidget(user_label, password_label, NULL); + XtVaSetValues(user_label, XmNwidth, label_width, NULL); + XtVaSetValues(password_label, XmNwidth, label_width, NULL); + +#ifdef LI_BACKGROUND_SYNC_ENABLED + /* TODO - add callback to validate sync_text */ + XtAddCallback(m_sync_toggle, XmNvalueChangedCallback, cb_toggle, this); +#endif + + XtVaGetValues(m_password_text, XmNmaxLength, &max_length, NULL); + fe_SetupPasswdText(m_password_text, max_length); + + setCreated(TRUE); +} + + +#ifdef LI_BACKGROUND_SYNC_ENABLED +void +XFE_PrefsPageLIGeneral::syncToggle(Widget w, XtPointer calldata) +{ + XmToggleButtonCallbackStruct* cb = (XmToggleButtonCallbackStruct*) calldata; + + if ( w == m_sync_toggle ) { + XtSetSensitive(m_sync_text, + !PREF_PrefIsLocked("li.sync.time") && cb->set); + } else { + abort(); + } +} + + +void +XFE_PrefsPageLIGeneral::cb_toggle(Widget w, XtPointer closure, XtPointer call_data) +{ + ((XFE_PrefsPageLIGeneral*)closure)->syncToggle(w, call_data); +} +#endif /* LI_BACKGROUND_SYNC_ENABLED */ + + +void +XFE_PrefsPageLIGeneral::init() +{ + XP_Bool li_enabled = FALSE; +#ifdef LI_BACKGROUND_SYNC_ENABLED + XP_Bool li_sync_enabled = FALSE; + int32 li_sync_time = 0; +#endif + char* li_login_name = NULL; + + PREF_GetBoolPref("li.enabled", &li_enabled); + PREF_CopyCharPref("li.login.name", &li_login_name); +#ifdef LI_BACKGROUND_SYNC_ENABLED + PREF_GetBoolPref("li.sync.enabled", &li_sync_enabled); + PREF_GetIntPref("li.sync.time", &li_sync_time); +#endif + + XtVaSetValues(m_enable_toggle, XmNset, li_enabled, + XmNsensitive, !PREF_PrefIsLocked("li.enabled"), NULL); +#ifdef LI_BACKGROUND_SYNC_ENABLED + XtVaSetValues(m_sync_toggle, XmNset, li_sync_enabled, + XmNsensitive, !PREF_PrefIsLocked("li.sync.enabled"), NULL); + XtVaSetValues(m_sync_text, XmNsensitive, + !PREF_PrefIsLocked("li.sync.time") && li_sync_enabled, NULL); + + /* TODO - convert li_sync_time to str */ + fe_SetTextField(m_sync_text, ""); +#endif /* LI_BACKGROUND_SYNC_ENABLED */ + fe_SetTextField(m_user_text, li_login_name); + fe_SetTextField(m_password_text, ""); + + XtSetSensitive(m_user_text, !PREF_PrefIsLocked("li.login.name")); + XtSetSensitive(m_password_text, !PREF_PrefIsLocked("li.login.password")); + + setInitialized(TRUE); +} + + +void +XFE_PrefsPageLIGeneral::install() +{ +} + + +void +XFE_PrefsPageLIGeneral::save() +{ + char* str; + char* munged_str; + + PREF_SetBoolPref("li.enabled", XmToggleButtonGetState(m_enable_toggle)); +#ifdef LI_BACKGROUND_SYNC_ENABLED + PREF_SetBoolPref("li.sync.enabled", XmToggleButtonGetState(m_sync_toggle)); + /* TODO - background sync time */ +#endif + str = fe_GetTextField(m_user_text); + PREF_SetCharPref("li.login.name", str); + if ( str ) XtFree(str); + + if ( XtIsManaged(m_password_text) ) { + str = fe_GetPasswdText(m_password_text); + munged_str = str ? SECNAV_MungeString(str) : (char *)NULL; + PREF_SetCharPref("li.login.password", munged_str); + XP_FREEIF(munged_str); + if ( str ) XtFree(str); + } + + if ( XtIsManaged(m_save_password_toggle) ) { + PREF_SetBoolPref("li.login.save_password", + XmToggleButtonGetState(m_save_password_toggle)); + } +} + + +Boolean +XFE_PrefsPageLIGeneral::verify() +{ + return TRUE; +} + + +Widget +XFE_PrefsPageLIGeneral::get_top_frame() +{ + return m_top_frame; +} + + +Widget +XFE_PrefsPageLIGeneral::get_bottom_frame() +{ + return m_bottom_frame; +} + + diff --git a/mozilla/cmd/xfe/src/PrefsPageLIGeneral.h b/mozilla/cmd/xfe/src/PrefsPageLIGeneral.h new file mode 100644 index 00000000000..60c7c3eb361 --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsPageLIGeneral.h @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +/* + PrefsPageLIGeneral.cpp -- class for LI general preferences dialog. + Created: Daniel Malmer + */ + +#ifndef _xfe_prefspageligeneral_h +#define _xfe_prefspageligeneral_h + +#include +#include "PrefsDialog.h" + +class XFE_PrefsPageLIGeneral : public XFE_PrefsPage +{ + public: + + enum DialogUsage {pref, login}; + + XFE_PrefsPageLIGeneral(XFE_PrefsDialog *dialog, DialogUsage usage = pref); + XFE_PrefsPageLIGeneral(Widget parent, DialogUsage usage = pref); + virtual ~XFE_PrefsPageLIGeneral(); + + virtual void create(); + virtual void init(); + virtual void install(); + virtual void save(); + + virtual Boolean verify(); + +#ifdef LI_BACKGROUND_SYNC_ENABLED + void syncToggle(Widget, XtPointer); + static void cb_toggle(Widget, XtPointer, XtPointer); +#endif + + Widget get_top_frame(); + Widget get_bottom_frame(); + + private: + DialogUsage m_usage; + + Widget m_top_frame; + Widget m_bottom_frame; + + Widget m_enable_toggle; + +#ifdef LI_BACKGROUND_SYNC_ENABLED + Widget m_sync_toggle; + Widget m_sync_text; +#endif + + Widget m_user_text; + Widget m_password_text; + Widget m_save_password_toggle; +}; + +#endif diff --git a/mozilla/cmd/xfe/src/PrefsPageLIServer.cpp b/mozilla/cmd/xfe/src/PrefsPageLIServer.cpp new file mode 100644 index 00000000000..80be489ded2 --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsPageLIServer.cpp @@ -0,0 +1,300 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +/* + PrefsPageLIServer.cpp -- LI server preferences. + Created: Daniel Malmer , 21-Apr-98. + */ + + +#include "felocale.h" +#include "prefapi.h" +#include "PrefsPageLIServer.h" +#include "Xfe/Geometry.h" + +#include + + +XFE_PrefsPageLIServer::XFE_PrefsPageLIServer(XFE_PrefsDialog* dialog) : XFE_PrefsPage(dialog) +{ +} + + +XFE_PrefsPageLIServer::XFE_PrefsPageLIServer(Widget w) : XFE_PrefsPage(w) +{ +} + + +XFE_PrefsPageLIServer::~XFE_PrefsPageLIServer() +{ +} + + +void +XFE_PrefsPageLIServer::create() +{ + int ac; + Arg av[16]; + Widget frame_label; + Widget form; + Widget server_label; + Widget ldap_addr_label; + Widget ldap_base_label; + Widget http_base_label; + Dimension spacing; + Dimension indicator_size; + Dimension label_height; + Dimension label_width; + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + m_wPage = XmCreateFrame(m_wPageForm, "liServer", av, ac); + XtManageChild(m_wPage); + + ac = 0; + XtSetArg(av[ac], XmNchildType, XmFRAME_TITLE_CHILD); ac++; + frame_label = XmCreateLabelGadget(m_wPage, "frameLabel", av, ac); + XtManageChild(frame_label); + + ac = 0; + form = XmCreateForm(m_wPage, "form", av, ac); + XtManageChild(form); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + server_label = XmCreateLabelGadget(form, "serverLabel", av, ac); + XtManageChild(server_label); + + ac = 0; + XtSetArg(av[ac], XmNindicatorType, XmONE_OF_MANY); ac++; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, server_label); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNtopOffset, 4); ac++; + XtSetArg(av[ac], XmNleftOffset, 16); ac++; + m_ldap_toggle = XmCreateToggleButtonGadget(form, "ldapToggle", av, ac); + XtManageChild(m_ldap_toggle); + + XtVaGetValues(m_ldap_toggle, XmNindicatorSize, &indicator_size, + XmNspacing, &spacing, NULL); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, m_ldap_toggle); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET); ac++; + XtSetArg(av[ac], XmNleftWidget, m_ldap_toggle); ac++; + XtSetArg(av[ac], XmNleftOffset, indicator_size + spacing); ac++; + XtSetArg(av[ac], XmNalignment, XmALIGNMENT_END); ac++; + ldap_addr_label = XmCreateLabelGadget(form, "ldapAddrLabel", av, ac); + XtManageChild(ldap_addr_label); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, m_ldap_toggle); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNleftWidget, ldap_addr_label); ac++; + m_ldap_addr_text = XmCreateTextField(form, "ldapAddrText", av, ac); + XtManageChild(m_ldap_addr_text); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, ldap_addr_label); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET); ac++; + XtSetArg(av[ac], XmNleftWidget, m_ldap_toggle); ac++; + XtSetArg(av[ac], XmNleftOffset, indicator_size + spacing); ac++; + XtSetArg(av[ac], XmNalignment, XmALIGNMENT_END); ac++; + ldap_base_label = XmCreateLabelGadget(form, "ldapBaseLabel", av, ac); + XtManageChild(ldap_base_label); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, m_ldap_addr_text); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNleftWidget, ldap_base_label); ac++; + m_ldap_base_text = XmCreateTextField(form, "ldapBaseText", av, ac); + XtManageChild(m_ldap_base_text); + + ac = 0; + XtSetArg(av[ac], XmNindicatorType, XmONE_OF_MANY); ac++; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, ldap_base_label); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET); ac++; + XtSetArg(av[ac], XmNleftWidget, m_ldap_toggle); ac++; + XtSetArg(av[ac], XmNtopOffset, 4); ac++; + m_http_toggle = XmCreateToggleButtonGadget(form, "httpToggle", av, ac); + XtManageChild(m_http_toggle); + + XtVaGetValues(m_http_toggle, XmNindicatorSize, &indicator_size, + XmNspacing, &spacing, NULL); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, m_http_toggle); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET); ac++; + XtSetArg(av[ac], XmNleftWidget, m_ldap_toggle); ac++; + XtSetArg(av[ac], XmNleftOffset, indicator_size + spacing); ac++; + XtSetArg(av[ac], XmNalignment, XmALIGNMENT_END); ac++; + http_base_label = XmCreateLabelGadget(form, "httpBaseLabel", av, ac); + XtManageChild(http_base_label); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, m_http_toggle); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNleftWidget, http_base_label); ac++; + m_http_base_text = XmCreateTextField(form, "httpBaseText", av, ac); + XtManageChild(m_http_base_text); + + label_height = XfeVaGetTallestWidget(ldap_addr_label, m_ldap_addr_text, NULL); + XtVaSetValues(ldap_addr_label, XmNheight, label_height, NULL); + XtVaSetValues(m_ldap_addr_text, XmNheight, label_height, NULL); + + label_height = XfeVaGetTallestWidget(ldap_base_label, m_ldap_base_text, NULL); + XtVaSetValues(ldap_base_label, XmNheight, label_height, NULL); + XtVaSetValues(m_ldap_base_text, XmNheight, label_height, NULL); + + label_height = XfeVaGetTallestWidget(http_base_label, m_http_base_text, NULL); + XtVaSetValues(http_base_label, XmNheight, label_height, NULL); + XtVaSetValues(m_http_base_text, XmNheight, label_height, NULL); + + label_width = XfeVaGetWidestWidget(ldap_addr_label, ldap_base_label, + http_base_label, NULL); + XtVaSetValues(ldap_addr_label, XmNwidth, label_width, NULL); + XtVaSetValues(ldap_base_label, XmNwidth, label_width, NULL); + XtVaSetValues(http_base_label, XmNwidth, label_width, NULL); + + XtAddCallback(m_ldap_toggle, XmNvalueChangedCallback, cb_toggle, this); + XtAddCallback(m_http_toggle, XmNvalueChangedCallback, cb_toggle, this); + + setCreated(TRUE); +} + + +void +XFE_PrefsPageLIServer::init() +{ + XP_Bool use_ldap; + char* li_protocol = NULL; + char* ldap_addr = NULL; + char* ldap_base = NULL; + char* http_base = NULL; + + PREF_CopyCharPref("li.protocol", &li_protocol); + PREF_CopyCharPref("li.server.ldap.url", &ldap_addr); + PREF_CopyCharPref("li.server.ldap.userbase", &ldap_base); + PREF_CopyCharPref("li.server.http.baseURL", &http_base); + + fe_SetTextField(m_ldap_addr_text, ldap_addr); + fe_SetTextField(m_ldap_base_text, ldap_base); + fe_SetTextField(m_http_base_text, http_base); + + /* ldap is the default, so use ldap if li_protocol == NULL */ + use_ldap = (!li_protocol || !XP_STRCMP(li_protocol, "ldap")); + + XtVaSetValues(m_ldap_toggle, XmNset, use_ldap, + XmNsensitive, !PREF_PrefIsLocked("li.protocol"), NULL); + XtVaSetValues(m_ldap_addr_text, XmNsensitive, + !PREF_PrefIsLocked("li.server.ldap.url") && use_ldap, NULL); + XtVaSetValues(m_ldap_base_text, XmNsensitive, + !PREF_PrefIsLocked("li.server.ldap.userbase") && use_ldap, NULL); + XtVaSetValues(m_http_toggle, XmNset, !use_ldap, + XmNsensitive, !PREF_PrefIsLocked("li.protocol"), NULL); + XtVaSetValues(m_http_base_text, XmNsensitive, + !PREF_PrefIsLocked("li.server.http.baseURL") && !use_ldap, NULL); + + setInitialized(TRUE); +} + + +void +XFE_PrefsPageLIServer::install() +{ +} + + +void +XFE_PrefsPageLIServer::save() +{ + char* str; + + PREF_SetCharPref("li.protocol", + XmToggleButtonGetState(m_ldap_toggle) ? "ldap" : "http"); + + str = fe_GetTextField(m_ldap_addr_text); + PREF_SetCharPref("li.server.ldap.url", str); + if ( str ) XtFree(str); + str = fe_GetTextField(m_ldap_base_text); + PREF_SetCharPref("li.server.ldap.userbase", str); + if ( str ) XtFree(str); + str = fe_GetTextField(m_http_base_text); + PREF_SetCharPref("li.server.http.baseURL", str); + if ( str ) XtFree(str); +} + + +void +XFE_PrefsPageLIServer::cb_toggle(Widget w, XtPointer closure, XtPointer data) +{ + ((XFE_PrefsPageLIServer*)closure)->toggleCallback(w, data); +} + + +void +XFE_PrefsPageLIServer::toggleCallback(Widget w, XtPointer call_data) +{ + XP_Bool use_ldap; + XmToggleButtonCallbackStruct* cb = (XmToggleButtonCallbackStruct*) call_data; + + if ( !cb->set ) { + XtVaSetValues(w, XmNset, True, NULL); + } else { + use_ldap = ( w == m_ldap_toggle ); + + XmToggleButtonSetState(m_ldap_toggle, use_ldap, False); + XmToggleButtonSetState(m_http_toggle, !use_ldap, False); + + XtSetSensitive(m_ldap_addr_text, + use_ldap && !PREF_PrefIsLocked("li.server.ldap.url")); + XtSetSensitive(m_ldap_base_text, + use_ldap && !PREF_PrefIsLocked("li.server.ldap.userbase")); + XtSetSensitive(m_http_base_text, + !use_ldap && !PREF_PrefIsLocked("li.server.http.baseURL")); + } +} + + +Boolean +XFE_PrefsPageLIServer::verify() +{ + return TRUE; +} + + +Widget +XFE_PrefsPageLIServer::get_frame() +{ + return m_wPage; +} + + diff --git a/mozilla/cmd/xfe/src/PrefsPageLIServer.h b/mozilla/cmd/xfe/src/PrefsPageLIServer.h new file mode 100644 index 00000000000..bc32c90cc30 --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsPageLIServer.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +/* + PrefsPageLIServer.cpp -- class for LI server preferences dialog. + Created: Daniel Malmer + */ + +#ifndef _xfe_prefspageliserver_h +#define _xfe_prefspageliserver_h + +#include +#include "PrefsDialog.h" +#include "PrefsData.h" + +class XFE_PrefsPageLIServer : public XFE_PrefsPage +{ + public: + + XFE_PrefsPageLIServer(XFE_PrefsDialog *dialog); + XFE_PrefsPageLIServer(Widget parent); + virtual ~XFE_PrefsPageLIServer(); + + virtual void create(); + virtual void init(); + virtual void install(); + virtual void save(); + + virtual Boolean verify(); + + Widget get_frame(); + + void toggleCallback(Widget, XtPointer); + static void cb_toggle(Widget, XtPointer, XtPointer); + + private: + Widget m_ldap_toggle; + Widget m_ldap_addr_text; + Widget m_ldap_base_text; + + Widget m_http_toggle; + Widget m_http_base_text; +}; + +#endif diff --git a/mozilla/cmd/xfe/src/PrefsPageMessages.cpp b/mozilla/cmd/xfe/src/PrefsPageMessages.cpp new file mode 100644 index 00000000000..8bc06529c56 --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsPageMessages.cpp @@ -0,0 +1,601 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1996 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +/* + PrefsPageMessages.cpp - Messages preference pane + Created: Alec Flett + */ + +#include "felocale.h" +#include "prefapi.h" +#include "PrefsPageMessages.h" +#include "xpgetstr.h" +#include +#include + +#define SPELLCHECK "mail.SpellCheckBeforeSend" +#define FORWARD "mail.forward_message_mode" + +#define AUTOQUOTE "mail.auto_quote" +#define AUTOQUOTE_STYLE "mailnews.reply_on_top" + +#define WRAP_LENGTH "mailnews.wraplength" +#define WRAP_LONG_LINES "mail.wrap_long_lines" + +#define EIGHTBIT_MIME "mail.strictly_mime" + + +// string resources +extern int XFE_AUTOQUOTE_STYLE_ABOVE; +extern int XFE_AUTOQUOTE_STYLE_BELOW; +extern int XFE_AUTOQUOTE_STYLE_SELECT; + +extern int XFE_FORWARD_INLINE; +extern int XFE_FORWARD_QUOTED; +extern int XFE_FORWARD_ATTACH; + +XFE_PrefsPageMessages::XFE_PrefsPageMessages(XFE_PrefsDialog *dialog) + : XFE_PrefsPage(dialog) +{ + m_autoquote_strings[AUTOQUOTE_PREF_ABOVE] = + XmStringCreateLocalized(XP_GetString(XFE_AUTOQUOTE_STYLE_ABOVE)); + m_autoquote_strings[AUTOQUOTE_PREF_BELOW] = + XmStringCreateLocalized(XP_GetString(XFE_AUTOQUOTE_STYLE_BELOW)); + m_autoquote_strings[AUTOQUOTE_PREF_SELECT] = + XmStringCreateLocalized(XP_GetString(XFE_AUTOQUOTE_STYLE_SELECT)); + m_autoquote_strings[AUTOQUOTE_ITEMS] = NULL; + + m_forward_strings[FORWARD_PREF_INLINE] = + XmStringCreateLocalized(XP_GetString(XFE_FORWARD_INLINE)); + m_forward_strings[FORWARD_PREF_QUOTED] = + XmStringCreateLocalized(XP_GetString(XFE_FORWARD_QUOTED)); + m_forward_strings[FORWARD_PREF_ATTACH] = + XmStringCreateLocalized(XP_GetString(XFE_FORWARD_ATTACH)); + m_forward_strings[FORWARD_ITEMS] = NULL; + +} + +XFE_PrefsPageMessages::~XFE_PrefsPageMessages() +{ + XmStringFree(m_autoquote_strings[AUTOQUOTE_PREF_ABOVE]); + XmStringFree(m_autoquote_strings[AUTOQUOTE_PREF_BELOW]); + XmStringFree(m_autoquote_strings[AUTOQUOTE_PREF_SELECT]); + + XmStringFree(m_forward_strings[FORWARD_PREF_QUOTED]); + XmStringFree(m_forward_strings[FORWARD_PREF_INLINE]); + XmStringFree(m_forward_strings[FORWARD_PREF_ATTACH]); +} + + +void XFE_PrefsPageMessages::create() +{ + Arg av[5]; + int ac; + + Widget form; + + ac=0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + + m_wPage = form = + XmCreateForm(m_wPageForm, "mailnewsMessages", av,ac); + XtManageChild(form); + + Widget reply_frame=createReplyFrame(form, NULL); + Widget spell_frame=createSpellFrame(form, reply_frame); + Widget wrap_frame =createWrapFrame (form, spell_frame); + Widget eightbit_frame=createEightBitFrame(form, wrap_frame); + + setCreated(TRUE); +} + +Widget XFE_PrefsPageMessages::createReplyFrame (Widget parent, + Widget attachTo) +{ + Widget kids[7]; + int i=0; + Arg av[10]; + int ac; + Widget frame; + ac=0; + if (attachTo==NULL) { + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + } + else { + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, attachTo); ac++; + } + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_NONE); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + frame = + XmCreateFrame(parent,"replyFrame",av,ac); + + Widget form; + ac=0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + form = + XmCreateForm(frame,"replyForm",av,ac); + + Widget label; + ac=0; + XtSetArg(av[ac], XmNchildType, XmFRAME_TITLE_CHILD); ac++; + label = + XmCreateLabelGadget(frame, "replyLabel",av,ac); + + // create the widgets + i=0; + + // we need this crap because XmLists are stupid + Visual *v=0; + Colormap cmap=0; + Cardinal depth=0; + XtVaGetValues(getPrefsDialog()->getPrefsParent(), + XtNvisual, &v, + XtNcolormap, &cmap, + XtNdepth, &depth, + 0); + ac=0; + XtSetArg(av[ac], XtNvisual, v); ac++; + XtSetArg(av[ac], XtNcolormap, cmap); ac++; + XtSetArg(av[ac], XtNdepth, depth); ac++; + XtSetArg(av[ac], XmNarrowType, XmMOTIF); ac++; + + Widget combo_label; + Widget forward_label; + + kids[i++] = forward_label = + XmCreateLabelGadget(form, "forwardLabel", NULL,0); + kids[i++] = m_forward_combo = + DtCreateComboBox(form, "forwardCombo", av, ac); + + + kids[i++] = m_autoquote_toggle = + XmCreateToggleButtonGadget(form, "autoquoteToggle", NULL, 0); + + kids[i++] = combo_label = + XmCreateLabelGadget(form, "autoquoteMyReplyLabel", NULL, 0); + + // av holds the visual/colormap/depth stuff + kids[i++] = m_autoquote_style_combo = + DtCreateComboBox(form,"autoquoteStyleCombo",av,ac); + + + // lay them out + int max_height1 = XfeVaGetTallestWidget(forward_label, + m_forward_combo, + NULL); + + int max_height3 = XfeVaGetTallestWidget(combo_label, + m_autoquote_style_combo, + NULL); + // 1st row + XtVaSetValues(forward_label, + XmNheight, max_height1, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + NULL); + + XtVaSetValues(m_forward_combo, + XmNheight, max_height1, + XmNitems, m_forward_strings, + XmNitemCount, FORWARD_ITEMS, + XmNvisibleItemCount, FORWARD_ITEMS, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, forward_label, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, forward_label, + NULL); + + // 2nd row + XtVaSetValues(m_autoquote_toggle, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, forward_label, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + + // 3rd row + XtVaSetValues(combo_label, + XmNheight, max_height3, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_autoquote_toggle, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + + XtVaSetValues(m_autoquote_style_combo, + XmNheight, max_height3, + XmNitems, m_autoquote_strings, + XmNitemCount, AUTOQUOTE_ITEMS, + XmNvisibleItemCount, AUTOQUOTE_ITEMS, + XmNheight, max_height3, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, combo_label, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, combo_label, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + + + XtManageChildren(kids,i); + XtManageChild(form); + XtManageChild(label); + XtManageChild(frame); + + return frame; +} + +Widget XFE_PrefsPageMessages::createSpellFrame(Widget parent, + Widget attachTo) +{ + Widget kids[2]; + int i=0; + Arg av[10]; + int ac; + + Widget frame; + ac=0; + if (attachTo==NULL) { + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + } + else { + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, attachTo); ac++; + } + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_NONE); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + frame = + XmCreateFrame(parent,"spellFrame",av,ac); + + Widget form; + ac=0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + form = + XmCreateForm(frame,"spellForm",av,ac); + + Widget label; + ac=0; + XtSetArg(av[ac], XmNchildType, XmFRAME_TITLE_CHILD); ac++; + label = + XmCreateLabelGadget(frame, "spellLabel",av,ac); + + kids[i++] = m_spellcheck_toggle = + XmCreateToggleButtonGadget(form, "spellToggle", NULL, 0); + + XtVaSetValues(m_spellcheck_toggle, + XmNleftAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + + XtManageChildren(kids,i); + XtManageChild(form); + XtManageChild(label); + XtManageChild(frame); + + return frame; +} + +Widget XFE_PrefsPageMessages::createWrapFrame (Widget parent, + Widget attachTo) +{ + Widget kids[10]; + int i=0; + Arg av[10]; + int ac; + + Widget frame; + ac=0; + if (attachTo==NULL) { + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + } + else { + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, attachTo); ac++; + } + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_NONE); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + frame = + XmCreateFrame(parent,"wrapFrame",av,ac); + + Widget form; + ac=0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + form = + XmCreateForm(frame,"wrapForm",av,ac); + + Widget label; + ac=0; + XtSetArg(av[ac], XmNchildType, XmFRAME_TITLE_CHILD); ac++; + label = + XmCreateLabelGadget(frame, "wrapLabel",av,ac); + + Widget wrap_before_label; + Widget wrap_after_label; + + kids[i++] = m_wrap_toggle = + XmCreateToggleButtonGadget(form, "wrapToggle", NULL, 0); + + kids[i++] = wrap_before_label = + XmCreateLabelGadget(form, "wrapLengthLabel", NULL, 0); + kids[i++] = m_wrap_length_text = + XmCreateTextField(form, "wrapLengthText", NULL, 0); + kids[i++] = wrap_after_label = + XmCreateLabelGadget(form, "wrapLengthAfterLabel", NULL, 0); + + int max_height2 = XfeVaGetTallestWidget(wrap_before_label, + m_wrap_length_text, + wrap_after_label, + NULL); + XtVaSetValues(m_wrap_toggle, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + + XtVaSetValues(wrap_before_label, + XmNheight, max_height2, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_wrap_toggle, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + + XtVaSetValues(m_wrap_length_text, + XmNheight, max_height2, + XmNcolumns, 3, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, XmATTACH_WIDGET, + XmNtopWidget, wrap_before_label, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, wrap_before_label, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + + XtVaSetValues(wrap_after_label, + XmNheight, max_height2, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, XmATTACH_WIDGET, + XmNtopWidget, m_wrap_length_text, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, m_wrap_length_text, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + + XtManageChildren(kids,i); + XtManageChild(form); + XtManageChild(label); + XtManageChild(frame); + + return frame; +} + +Widget XFE_PrefsPageMessages::createEightBitFrame (Widget parent, + Widget attachTo) +{ + Widget kids[3]; + int i=0; + Arg av[10]; + int ac; + + Widget frame; + ac=0; + if (attachTo==NULL) { + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + } + else { + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, attachTo); ac++; + } + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_NONE); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + frame = + XmCreateFrame(parent,"eightbitFrame",av,ac); + + Widget form; + ac=0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + form = + XmCreateRadioBox(frame,"eightbitForm",av,ac); + + Widget label; + ac=0; + XtSetArg(av[ac], XmNchildType, XmFRAME_TITLE_CHILD); ac++; + label = + XmCreateLabelGadget(frame, "eightbitLabel",av,ac); + + kids[i++] = m_eightbit_asis_toggle = + XmCreateToggleButtonGadget(form, "eightbitAsIsToggle", NULL, 0); + kids[i++] = m_eightbit_quoted_toggle = + XmCreateToggleButtonGadget(form, "eightbitQuotedToggle", NULL, 0); + + XtManageChildren(kids,i); + XtManageChild(form); + XtManageChild(label); + XtManageChild(frame); + return frame; +} + + +void XFE_PrefsPageMessages::init() +{ + XP_Bool boolval; + XP_Bool locked; + int32 intval; + char charval[10]; // this is only for the 2 or 3 character + // intvals - remember to enlarge if you + // need more + + PREF_GetIntPref(FORWARD, &intval); + locked=PREF_PrefIsLocked(FORWARD); + XtVaSetValues(m_forward_combo, + XmNsensitive, !locked, + NULL); + + XP_ASSERT(intvalpref value + PREF_SetIntPref(AUTOQUOTE_STYLE, intval); + + // should we spellcheck? + boolval=XmToggleButtonGadgetGetState(m_spellcheck_toggle); + PREF_SetBoolPref(SPELLCHECK, boolval); + + // should we wrap? + boolval=XmToggleButtonGadgetGetState(m_wrap_toggle); + PREF_SetBoolPref(WRAP_LONG_LINES, boolval); + + // where to wrap? + intval=getWrapLength(); + if (intval>0) PREF_SetIntPref(WRAP_LENGTH, intval); + + // MIME or as-is + boolval=XmToggleButtonGadgetGetState(m_eightbit_quoted_toggle); + PREF_SetBoolPref(EIGHTBIT_MIME, boolval); + +} + +Boolean XFE_PrefsPageMessages::verify() +{ + return True; +} + + +int32 +XFE_PrefsPageMessages::getWrapLength() +{ + char *charval=XmTextFieldGetString(m_wrap_length_text); + int32 intval=XP_ATOI(charval); + XtFree(charval); + return intval; +} diff --git a/mozilla/cmd/xfe/src/PrefsPageMessages.h b/mozilla/cmd/xfe/src/PrefsPageMessages.h new file mode 100644 index 00000000000..a63d6db9f52 --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsPageMessages.h @@ -0,0 +1,81 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +#ifndef _xfe_prefspagemessages_h +#define _xfe_prefspagemessages_h + +#include +#include "PrefsDialog.h" + +// positions of the autoquote styles inthe DtComboBox +// these must be increasing and added to the combobox int this order +#define AUTOQUOTE_PREF_BELOW 0 +#define AUTOQUOTE_PREF_ABOVE 1 +#define AUTOQUOTE_PREF_SELECT 2 +#define AUTOQUOTE_ITEMS 3 + +#define FORWARD_PREF_ATTACH 0 +#define FORWARD_PREF_QUOTED 1 +#define FORWARD_PREF_INLINE 2 +#define FORWARD_ITEMS 3 + +class XFE_PrefsPageMessages : public XFE_PrefsPage +{ + public: + + XFE_PrefsPageMessages(XFE_PrefsDialog *dialog); + virtual ~XFE_PrefsPageMessages(); + + virtual void create(); + + virtual void init(); + virtual void install(); + + virtual void save(); + + virtual Boolean verify(); + + int32 getWrapLength(); + + private: + + Widget createReplyFrame(Widget parent, Widget attachTo); + Widget createSpellFrame(Widget parent, Widget attachTo); + Widget createWrapFrame(Widget parent, Widget attachTo); + Widget createEightBitFrame(Widget parent, Widget attachTo); + + Widget m_forward_combo; + + Widget m_autoquote_toggle; + Widget m_autoquote_style_combo; + + Widget m_spellcheck_toggle; + + Widget m_wrap_toggle; + Widget m_wrap_length_text; + + Widget m_eightbit_asis_toggle; + Widget m_eightbit_quoted_toggle; + + XmString m_autoquote_strings[AUTOQUOTE_ITEMS+1]; + XmString m_forward_strings[FORWARD_ITEMS+1]; +}; + +#endif diff --git a/mozilla/cmd/xfe/src/PrefsPageNServer.cpp b/mozilla/cmd/xfe/src/PrefsPageNServer.cpp new file mode 100644 index 00000000000..600211bc87a --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsPageNServer.cpp @@ -0,0 +1,630 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1996 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +/* + PrefsPageNServer.cpp - News Server preference pane + Created: Alec Flett + */ + +#include "rosetta.h" +#include "felocale.h" +#include "prefapi.h" +#include "msgcom.h" +#include "xpgetstr.h" +#include "Xfe/Geometry.h" +#include "PrefsPageNServer.h" +#include "PrefsDialogNServer.h" + +extern int MK_MSG_REMOVE_HOST_CONFIRM; +extern int XFE_NEWSSERVER_DEFAULT; + +// #include "xfe.h" + +// these are the preferences I'm using: +#define NEWS_DIRECTORY "news.directory" +#define NEWS_MAX_ARTICLES "news.max_articles" +#define NEWS_NOTIFY "news.notify.on" + +XFE_PrefsPageNServer::XFE_PrefsPageNServer(XFE_PrefsDialog *dialog) + : XFE_PrefsPage(dialog), + m_newsHosts(0), + m_newsDialog(0) +{ +} +XFE_PrefsPageNServer::~XFE_PrefsPageNServer() +{ + if (m_newsDialog) delete m_newsDialog; +} + +void +XFE_PrefsPageNServer::create() +{ + Arg av[5]; + int ac; + + Widget form; + + ac=0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + + m_wPage = form = + XmCreateForm(m_wPageForm, "mailnewsNServer", av,ac); + XtManageChild(form); + + Widget server_frame = createServerListFrame(form, NULL); + /* Widget local_frame = */ createLocalFrame(form, server_frame); + + setCreated(TRUE); +} + +Widget +XFE_PrefsPageNServer::createServerListFrame(Widget parent, + Widget attachTo) +{ + Widget kids[7]; + int i=0; + Arg av[10]; + int ac; + Widget frame; + ac=0; + if (attachTo==NULL) { + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + } + else { + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, attachTo); ac++; + } + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_NONE); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + frame = + XmCreateFrame(parent,"serverFrame",av,ac); + + Widget form; + ac=0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNfractionBase, 3); ac++; + form = + XmCreateForm(frame,"serverForm",av,ac); + + Widget label; + ac=0; + XtSetArg(av[ac], XmNchildType, XmFRAME_TITLE_CHILD); ac++; + label = + XmCreateLabelGadget(frame, "serverLabel",av,ac); + + + // we need this crap because XmLists are stupid + Visual *v=0; + Colormap cmap=0; + Cardinal depth=0; + XtVaGetValues(getPrefsDialog()->getPrefsParent(), + XtNvisual, &v, + XtNcolormap, &cmap, + XtNdepth, &depth, + 0); + ac=0; + XtSetArg(av[ac], XtNvisual, v); ac++; + XtSetArg(av[ac], XtNcolormap, cmap); ac++; + XtSetArg(av[ac], XtNdepth, depth); ac++; + XtSetArg(av[ac], XmNarrowType, XmMOTIF); ac++; + + // create the widgets + i=0; + Widget add_button; + Widget edit_button; + Widget delete_button; + Widget default_button; + + m_server_list = + XmCreateScrolledList(form, "serverList", av, ac); + + kids[i++] = add_button = + XmCreatePushButtonGadget(form, "serverAddButton", NULL, 0); + kids[i++] = edit_button = + XmCreatePushButtonGadget(form, "serverEditButton", NULL, 0); + kids[i++] = delete_button = + XmCreatePushButtonGadget(form, "serverDeleteButton", NULL, 0); + kids[i++] = default_button = + XmCreatePushButtonGadget(form, "serverDefaultButton", NULL, 0); + + // place the widgets + XtVaSetValues(XtParent(m_server_list), + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_POSITION, + XmNrightPosition, 2, // 2/3 of the width + XmNbottomAttachment, XmATTACH_FORM, + NULL); + XtVaSetValues(add_button, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, XtParent(m_server_list), + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + XtVaSetValues(edit_button, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, add_button, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, XtParent(m_server_list), + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + XtVaSetValues(delete_button, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, edit_button, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, XtParent(m_server_list), + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + XtVaSetValues(default_button, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, delete_button, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, XtParent(m_server_list), + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + + XtAddCallback(add_button, XmNactivateCallback, cb_addServer, this); + XtAddCallback(edit_button, XmNactivateCallback, cb_editServer, this); + XtAddCallback(delete_button, XmNactivateCallback, cb_deleteServer, this); + XtAddCallback(default_button, XmNactivateCallback, cb_setDefault, this); + + XtManageChild(m_server_list); + XtManageChildren(kids,i); + XtManageChild(form); + XtManageChild(label); + XtManageChild(frame); + + // set default doesn't work yet. + XtSetSensitive(default_button, False); + + return frame; +} + + +Widget +XFE_PrefsPageNServer::createLocalFrame(Widget parent, + Widget attachTo) +{ + Widget kids[7]; + int i=0; + Arg av[10]; + int ac; + Widget frame; + ac=0; + if (attachTo==NULL) { + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + } + else { + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, attachTo); ac++; + } + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_NONE); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + frame = + XmCreateFrame(parent,"localFrame",av,ac); + + Widget form; + ac=0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + form = + XmCreateForm(frame,"localForm",av,ac); + + Widget label; + ac=0; + XtSetArg(av[ac], XmNchildType, XmFRAME_TITLE_CHILD); ac++; + label = + XmCreateLabelGadget(frame, "localLabel",av,ac); + + Widget dir_label; + Widget messages_label; + + + kids[i++] = dir_label = + XmCreateLabelGadget(form, "dirLabel", NULL,0); + kids[i++] = m_directory_text = + fe_CreateText(form, "dirText", NULL, 0); + kids[i++] = m_directory_button = + XmCreatePushButtonGadget(form,"chooseButton", NULL, 0); + kids[i++] = m_size_limit_toggle = + XmCreateToggleButtonGadget(form, "sizeLimitToggle", NULL, 0); + kids[i++] = m_size_limit_text = + fe_CreateText(form, "sizeLimitText", NULL, 0); + kids[i++] = messages_label = + XmCreateLabelGadget(form, "sizeLimit2", NULL, 0); + + int max_height1 = XfeVaGetTallestWidget(m_directory_text, + m_directory_button, + NULL); + + int max_height2 = XfeVaGetTallestWidget(m_size_limit_toggle, + m_size_limit_text, + messages_label, + NULL); + + XtVaSetValues(dir_label, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + XtVaSetValues(m_directory_text, + XmNheight, max_height1, + XmNcolumns, 40, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, dir_label, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + XtVaSetValues(m_directory_button, + XmNheight, max_height2, + XmNtopAttachment, XmATTACH_NONE, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, m_directory_text, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNbottomWidget, m_directory_text, + NULL); + + XtVaSetValues(m_size_limit_toggle, + XmNheight, max_height2, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, m_directory_text, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + XtVaSetValues(m_size_limit_text, + XmNheight, max_height2, + XmNcolumns, 4, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, m_size_limit_toggle, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, m_size_limit_toggle, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + XtVaSetValues(messages_label, + XmNheight, max_height2, + XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, + XmNtopWidget, m_size_limit_text, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, m_size_limit_text, + XmNrightAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_NONE, + NULL); + + XtAddCallback(m_directory_button,XmNactivateCallback,cb_chooseDir, this); + + XtManageChildren(kids,i); + XtManageChild(form); + XtManageChild(label); + XtManageChild(frame); + return frame; +} + +void +XFE_PrefsPageNServer::init() +{ + + + char *charval; + XP_Bool boolval; + XP_Bool locked; + int32 intval; + + // news directory + PREF_CopyCharPref(NEWS_DIRECTORY,&charval); + fe_SetTextField(m_directory_text, charval); + XP_FREE(charval); + locked=PREF_PrefIsLocked(NEWS_DIRECTORY); + XtVaSetValues(m_directory_text, + XmNsensitive, !locked, + NULL); + XtVaSetValues(m_directory_button, + XmNsensitive, !locked, + NULL); + + // max messages ask + PREF_GetBoolPref(NEWS_NOTIFY, &boolval); + XmToggleButtonSetState(m_size_limit_toggle, boolval, False); + locked = PREF_PrefIsLocked(NEWS_NOTIFY); + XtVaSetValues(m_size_limit_toggle, + XmNsensitive, !locked, + NULL); + + + // max messages + PREF_GetIntPref(NEWS_MAX_ARTICLES, &intval); + charval=PR_smprintf("%d",intval); + fe_SetTextField(m_size_limit_text, charval); + XP_FREE(charval); + locked = PREF_PrefIsLocked(NEWS_MAX_ARTICLES); + XtVaSetValues(m_size_limit_text, + XmNsensitive, !locked, + NULL); + + + refreshList(); + + m_newsDialog=new XFE_PrefsNServerDialog(getPrefsDialog()->getBaseWidget()); + m_newsDialog->create(); + + setInitialized(TRUE); +} + +void XFE_PrefsPageNServer::refreshList() +{ + // now fill up the server list + + MSG_Master *master=fe_getMNMaster(); + + int32 count; + XmString *xmstrHosts; + + count=MSG_GetNewsHosts(master, NULL, 0); + + m_newsHosts = new MSG_NewsHost*[count]; + xmstrHosts = new XmString[count]; + MSG_GetNewsHosts(master, m_newsHosts, count); + + MSG_NewsHost *defaultHost = MSG_GetDefaultNewsHost(master); + + int i; + for (i=0; igetServerName(); + int32 port = m_newsDialog->getPort(); + HG13181 + XP_Bool password = m_newsDialog->getPassword(); + + + XP_FREE(server_name); +} + +void +XFE_PrefsPageNServer::save() +{ + char *charval; + XP_Bool boolval; + int32 intval; + + charval = fe_GetTextField(m_directory_text); + if (charval) { + PREF_SetCharPref(NEWS_DIRECTORY, charval); + XP_FREE(charval); + } + + charval = fe_GetTextField(m_size_limit_text); + if (charval) { + intval = XP_ATOI(charval); + PREF_SetIntPref(NEWS_MAX_ARTICLES, intval); + XP_FREE(charval); + } + + boolval = XmToggleButtonGetState(m_size_limit_toggle); + PREF_SetBoolPref(NEWS_NOTIFY, boolval); + +} + +void +XFE_PrefsPageNServer::install() +{ + +} + +Boolean +XFE_PrefsPageNServer::verify() +{ + return TRUE; +} + +void +XFE_PrefsPageNServer::cb_addServer(Widget, XtPointer closure, XtPointer) +{ + ((XFE_PrefsPageNServer*)closure)->addServer(); +} + +void +XFE_PrefsPageNServer::cb_editServer(Widget, XtPointer closure, XtPointer) +{ + ((XFE_PrefsPageNServer*)closure)->editServer(); +} + +void +XFE_PrefsPageNServer::cb_deleteServer(Widget, XtPointer closure, XtPointer) +{ + ((XFE_PrefsPageNServer*)closure)->deleteServer(); +} + +void +XFE_PrefsPageNServer::cb_setDefault(Widget, XtPointer closure, XtPointer) +{ + ((XFE_PrefsPageNServer*)closure)->setDefault(); +} + +void +XFE_PrefsPageNServer::cb_serverSelected(Widget, XtPointer closure, XtPointer) +{ + ((XFE_PrefsPageNServer*)closure)->serverSelected(); +} + +void +XFE_PrefsPageNServer::cb_chooseDir(Widget, XtPointer closure, XtPointer) +{ + ((XFE_PrefsPageNServer*)closure)->chooseDir(); +} + + +void +XFE_PrefsPageNServer::addServer() +{ + XP_ASSERT(m_newsDialog); + if (!m_newsDialog) return; + + if (!m_newsDialog->prompt()) return; + + MSG_Master *master=fe_getMNMaster(); + + MSG_CreateNewsHost(master, + m_newsDialog->getServerName(), + HG14871, + m_newsDialog->getPort()); + refreshList(); +} + +void +XFE_PrefsPageNServer::editServer() +{ + + MSG_Host *newsHost = getSelected(); + if (!newsHost) return; + + const char *server_name = MSG_GetHostName(newsHost); + int32 port = MSG_GetHostPort(newsHost); + XP_Bool xxx = HG19861; + XP_Bool password = FALSE; // what's the API here? + + m_newsDialog->init(server_name, port, xxx, password); + if (m_newsDialog->prompt()) + saveServer(); + + refreshList(); +} + +void +XFE_PrefsPageNServer::deleteServer() +{ + MSG_Host *host = getSelected(); + if (!host) return; + + const char *confirm_message; + confirm_message = PR_smprintf(XP_GetString(MK_MSG_REMOVE_HOST_CONFIRM), + MSG_GetHostUIName(host)); + + if (!XFE_Confirm(getContext(), confirm_message)) + return; + + MSG_NewsHost *newsHost=MSG_GetNewsHostFromMSGHost(host); + XP_ASSERT(newsHost); + if (!newsHost) return; + + MSG_Master *master=fe_getMNMaster(); + + MSG_DeleteNewsHost(master, newsHost); + refreshList(); +} + +void +XFE_PrefsPageNServer::setDefault() +{ + MSG_Host *host = getSelected(); + if (!host) return; + +} + +void +XFE_PrefsPageNServer::serverSelected() +{ + +} + +void +XFE_PrefsPageNServer::chooseDir() +{ + +} + diff --git a/mozilla/cmd/xfe/src/PrefsPageNServer.h b/mozilla/cmd/xfe/src/PrefsPageNServer.h new file mode 100644 index 00000000000..63d26949967 --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsPageNServer.h @@ -0,0 +1,84 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +/* + PrefsDialog.cpp -- class for XFE preferences dialogs. + Created: Alec Flett + */ + + +#ifndef _xfe_prefspagenserver_h +#define _xfe_prefspagenserver_h + +#include +#include "PrefsDialog.h" +#include "PrefsDialogNServer.h" +#include "msgcom.h" + +class XFE_PrefsPageNServer : public XFE_PrefsPage +{ + public: + + XFE_PrefsPageNServer(XFE_PrefsDialog *dialog); + virtual ~XFE_PrefsPageNServer(); + + virtual void create(); + virtual void init(); + virtual void save(); + virtual void install(); + virtual Boolean verify(); + + + // widget callbacks + static void cb_addServer(Widget, XtPointer, XtPointer); + static void cb_editServer(Widget, XtPointer, XtPointer); + static void cb_deleteServer(Widget, XtPointer, XtPointer); + static void cb_setDefault(Widget, XtPointer, XtPointer); + static void cb_serverSelected(Widget, XtPointer, XtPointer); + static void cb_chooseDir(Widget, XtPointer, XtPointer); + + void addServer(); + void editServer(); + void deleteServer(); + void setDefault(); + void serverSelected(); + void chooseDir(); + + // utility functions + void refreshList(); + MSG_Host *getSelected(); + void saveServer(); + private: + + Widget createServerListFrame(Widget, Widget); + Widget createLocalFrame(Widget, Widget); + + Widget m_server_list; + Widget m_directory_text; + Widget m_directory_button; + Widget m_size_limit_toggle; + Widget m_size_limit_text; + + MSG_NewsHost **m_newsHosts; + XFE_PrefsNServerDialog *m_newsDialog; +}; + +#endif + diff --git a/mozilla/cmd/xfe/src/PrefsPageSmartUpdate.cpp b/mozilla/cmd/xfe/src/PrefsPageSmartUpdate.cpp new file mode 100644 index 00000000000..e2c6122e6eb --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsPageSmartUpdate.cpp @@ -0,0 +1,266 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +/* + PrefsPageSmartUpdate.cpp -- LI File preferences. + Created: Daniel Malmer , 21-Apr-98. + */ + + +#include "felocale.h" +#include "xpgetstr.h" +#include "prefapi.h" +#include "softupdt.h" +#include "PrefsPageSmartUpdate.h" +#include "Xfe/Geometry.h" +#include "NSReg.h" + +extern int MK_OUT_OF_MEMORY; +extern int SU_CONTINUE_UNINSTALL; +extern int SU_ERROR_UNINSTALL; + +// XFE_PrefsPageSmartUpdate + +XFE_PrefsPageSmartUpdate::XFE_PrefsPageSmartUpdate(XFE_PrefsDialog* dialog) : XFE_PrefsPage(dialog) +{ +} + + +XFE_PrefsPageSmartUpdate::~XFE_PrefsPageSmartUpdate() +{ +} + + +void +XFE_PrefsPageSmartUpdate::create() +{ + int ac; + Arg av[16]; + Widget top_frame; + Widget bottom_frame; + Widget column; + Widget form; + Widget uninstall_label; + Widget uninstall_button; + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + m_wPage = XmCreateForm(m_wPageForm, "smartUpdate", av, ac); + XtManageChild(m_wPage); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_NONE); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNtopOffset, 4); ac++; + top_frame = XmCreateFrame(m_wPage, "topFrame", av, ac); + XtManageChild(top_frame); + + ac = 0; + column = XmCreateRowColumn(top_frame, "column", av, ac); + XtManageChild(column); + + ac = 0; + m_enable_toggle = XmCreateToggleButtonGadget(column, "enableToggle", av, ac); + XtManageChild(m_enable_toggle); + + ac = 0; + m_confirm_toggle = XmCreateToggleButtonGadget(column, "confirmToggle", av, ac); + XtManageChild(m_confirm_toggle); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, top_frame); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNtopOffset, 4); ac++; + bottom_frame = XmCreateFrame(m_wPage, "bottomFrame", av, ac); + XtManageChild(bottom_frame); + + ac = 0; + form = XmCreateForm(bottom_frame, "form", av, ac); + XtManageChild(form); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_NONE); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_NONE); ac++; + uninstall_label = XmCreateLabelGadget(form, "uninstallLabel", av, ac); + XtManageChild(uninstall_label); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, uninstall_label); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_NONE); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_NONE); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + uninstall_button = XmCreatePushButton(form, "uninstallButton", av, ac); + XtManageChild(uninstall_button); + + XtAddCallback(uninstall_button, XmNactivateCallback, uninstall_cb, this); + + ac = 0; + XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNtopWidget, uninstall_label); ac++; + XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(av[ac], XmNrightAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(av[ac], XmNrightWidget, uninstall_button); ac++; + m_uninstall_list = XmCreateList(form, "uninstallList", av, ac); + XtManageChild(m_uninstall_list); + + /* Leave this unmanaged */ + ac = 0; + m_fake_list = XmCreateList(form, "fakeList", av, ac); + + setCreated(TRUE); +} + + +void +XFE_PrefsPageSmartUpdate::init() +{ + void* context = NULL; + char packageName[1024]; + char regPackageName[1024]; + XP_Bool enabled; + + PREF_GetBoolPref("autoupdate.enabled", &enabled); + XtVaSetValues(m_enable_toggle, XmNset, enabled, + XmNsensitive, !PREF_PrefIsLocked("autoupdate.enabled"), NULL); + + PREF_GetBoolPref("autoupdate.confirm_install", &enabled); + XtVaSetValues(m_confirm_toggle, XmNset, enabled, + XmNsensitive, !PREF_PrefIsLocked("autoupdate.confirm"), NULL); + + XmListDeleteAllItems(m_uninstall_list); + XmListDeleteAllItems(m_fake_list); + + while ( SU_EnumUninstall(&context, + packageName, sizeof(packageName), + regPackageName, sizeof(regPackageName)) == + REGERR_OK ) { + XmString xm_str; + + /* Don't let them uninstall Communicator! */ + if ( !XP_STRCMP(packageName, UNINSTALL_NAV_STR) ) continue; + + /* If no package name exists, use the registry name */ + xm_str = XmStringCreate(packageName[0] ? packageName : + regPackageName, XmFONTLIST_DEFAULT_TAG); + XmListAddItem(m_uninstall_list, xm_str, 0); + XmStringFree(xm_str); + + xm_str = XmStringCreate(regPackageName, XmFONTLIST_DEFAULT_TAG); + XmListAddItem(m_fake_list, xm_str, 0); + XmStringFree(xm_str); + } +} + + +void +XFE_PrefsPageSmartUpdate::install() +{ +} + + +void +XFE_PrefsPageSmartUpdate::save() +{ + PREF_SetBoolPref("autoupdate.enabled", + XmToggleButtonGetState(m_enable_toggle)); + PREF_SetBoolPref("autoupdate.confirm_install", + XmToggleButtonGetState(m_confirm_toggle)); +} + + +Boolean +XFE_PrefsPageSmartUpdate::verify() +{ + return TRUE; +} + + +void +XFE_PrefsPageSmartUpdate::uninstallCB(Widget /* w */, XtPointer /* calldata */) +{ + int* list = NULL; + int count = 0; + XmString* xm_pkg_names = NULL; + XmString* xm_registry_names = NULL; + char* pkg_name = NULL; + char* registry_name = NULL; + char* confirm_msg = NULL; + + XmListGetSelectedPos(m_uninstall_list, &list, &count); + + /* count should always be either '0' or '1' */ + if ( count == 1 ) { + + XtVaGetValues(m_uninstall_list, XmNitems, &xm_pkg_names, NULL); + XtVaGetValues(m_fake_list, XmNitems, &xm_registry_names, NULL); + + XmStringGetLtoR(xm_pkg_names[(*list)-1], + XmFONTLIST_DEFAULT_TAG, &pkg_name); + XmStringGetLtoR(xm_registry_names[(*list)-1], + XmFONTLIST_DEFAULT_TAG, ®istry_name); + + if ( !pkg_name || !*pkg_name || !registry_name || !*registry_name ) { + FE_Alert(getContext(), XP_GetString(MK_OUT_OF_MEMORY)); + return; + } + + confirm_msg = PR_smprintf(XP_GetString(SU_CONTINUE_UNINSTALL), + pkg_name); + + if ( confirm_msg == NULL ) { + FE_Alert(getContext(), XP_GetString(MK_OUT_OF_MEMORY)); + return; + } + + if ( FE_Confirm(getContext(), confirm_msg) ) { + if ( SU_Uninstall(registry_name) < 0 ) { + FE_Alert(getContext(), XP_GetString(SU_ERROR_UNINSTALL)); + } else { + XmListDeletePositions(m_uninstall_list, list, count); + XmListDeletePositions(m_fake_list, list, count); + } + } + + if ( pkg_name ) XtFree(pkg_name); + if ( registry_name ) XtFree(registry_name); + XP_FREEIF(confirm_msg); + } +} + + +void +XFE_PrefsPageSmartUpdate::uninstall_cb(Widget w, XtPointer closure, XtPointer calldata) +{ + ((XFE_PrefsPageSmartUpdate*) closure)->uninstallCB(w, calldata); +} + + diff --git a/mozilla/cmd/xfe/src/PrefsPageSmartUpdate.h b/mozilla/cmd/xfe/src/PrefsPageSmartUpdate.h new file mode 100644 index 00000000000..e2c4b068bae --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsPageSmartUpdate.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +/* + PrefsPageSmartUpdate.cpp -- class for Smart Update preferences dialog. + Created: Daniel Malmer + */ + +#ifndef _xfe_prefspagesmartupdate_h +#define _xfe_prefspagesmartupdate_h + +#include +#include "PrefsDialog.h" + +class XFE_PrefsPageSmartUpdate : public XFE_PrefsPage +{ + public: + + XFE_PrefsPageSmartUpdate(XFE_PrefsDialog *dialog); + virtual ~XFE_PrefsPageSmartUpdate(); + + virtual void create(); + virtual void init(); + virtual void install(); + virtual void save(); + + virtual Boolean verify(); + + void uninstallCB(Widget, XtPointer); + static void uninstall_cb(Widget, XtPointer, XtPointer); + + private: + void populateList(); + Widget m_enable_toggle; + Widget m_confirm_toggle; + Widget m_uninstall_list; + Widget m_fake_list; +}; + +#endif diff --git a/mozilla/cmd/xfe/src/PrefsPages.h b/mozilla/cmd/xfe/src/PrefsPages.h new file mode 100644 index 00000000000..5e520a1ad58 --- /dev/null +++ b/mozilla/cmd/xfe/src/PrefsPages.h @@ -0,0 +1,34 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +#ifndef _xfe_prefspages_h +#define _xfe_prefspages_h + +#ifndef MOZ_LITE +#include "PrefsPageAddress.h" +#include "PrefsPageMessages.h" +#include "PrefsPageNServer.h" +#endif +#ifdef MOZ_LI +#include "PrefsPageLIGeneral.h" +#include "PrefsPageLIServer.h" +#include "PrefsPageLIFiles.h" +#endif +#include "PrefsPageSmartUpdate.h" +#endif diff --git a/mozilla/cmd/xfe/src/Progress.cpp b/mozilla/cmd/xfe/src/Progress.cpp new file mode 100644 index 00000000000..4147adcecf8 --- /dev/null +++ b/mozilla/cmd/xfe/src/Progress.cpp @@ -0,0 +1,390 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +////////////////////////////////////////////////////////////////////// +// Progress.cpp +// By Daniel Malmer +// +// Implementation of the XP progress dialog. +// Basically a wrapper around a DownloadFrame. +////////////////////////////////////////////////////////////////////// + +#include "Progress.h" +#include "progress.h" +#include "Xfe/ProgressBar.h" +#include "Dashboard.h" +#include "xpassert.h" + +#if 0 +#include "ViewGlue.h" +#endif /* + +////////////////////////////////////////////////////////////////////// +// C++ implementation of C API +////////////////////////////////////////////////////////////////////// + +/* + * XFE_Progress + */ +XFE_Progress::XFE_Progress(Widget parent) : XFE_DownloadFrame(parent, NULL) +{ + Widget label; + + label = XtNameToWidget(getBaseWidget(), "*downloadURLLabel"); + if ( label ) fe_SetString(label, XmNlabelString, ""); + + label = XtNameToWidget(getBaseWidget(), "*downloadFileLabel"); + if ( label ) fe_SetString(label, XmNlabelString, ""); +#if 1 + /* 116773: Reopen ThreadFrame after "OK" IMAP Upgrade dialog + * leads to core dump + */ + notifyInterested(XFE_Component::logoStartAnimation, NULL); +#endif +} + + +/* + * ~XFE_Progress + */ +XFE_Progress::~XFE_Progress() +{ +} + + +/* + * XFE_Progress::Create + */ +pw_ptr +XFE_Progress::Create(MWContext* /*parent*/, PW_WindowType /*type*/) +{ + return (pw_ptr) this; +} + + +/* + * XFE_Progress::Destroy + */ +void +XFE_Progress::Destroy(void) +{ +#if 1 + /* 116773: Reopen ThreadFrame after "OK" IMAP Upgrade dialog + * leads to core dump + */ + delete_response(); +#endif +} + + +/* + * XFE_Progress::SetCancelCallback + */ +void +XFE_Progress::SetCancelCallback(PW_CancelCallback cancelcb, void* cancelClosure) +{ + m_cancel_cb = cancelcb; + m_cancel_closure = cancelClosure; +} + + +/* + * XFE_Progress::isCommandEnabled + */ +XP_Bool +XFE_Progress::isCommandEnabled(CommandType cmd, + void */*calldata*/, XFE_CommandInfo*) +{ + return ( cmd == xfeCmdStopLoading ); +} + + +/* + * XFE_Progress::handlesCommand + */ +XP_Bool +XFE_Progress::handlesCommand(CommandType cmd, + void */*calldata*/, XFE_CommandInfo*) +{ + return ( cmd == xfeCmdStopLoading ); +} + + +/* + * XFE_Progress::doCommand + */ +void +XFE_Progress::doCommand(CommandType cmd, + void */*calldata*/, XFE_CommandInfo*) +{ + if ( cmd == xfeCmdStopLoading && m_cancel_cb ) { + m_cancel_cb(m_cancel_closure); + } +#if 0 + /* 130116: core in canceling AB import progress dlg + */ + delete_response(); +#endif +} + + +/* + * XFE_Progress::SetWindowTitle + */ +void +XFE_Progress::SetWindowTitle(const char *title) +{ + XP_ASSERT(title); + + XFE_SetDocTitle(fe_WidgetToMWContext(getBaseWidget()), (char*) title); +} + + +/* + * XFE_Progress::SetLine1 + */ +void +XFE_Progress::SetLine1(const char* text) +{ + setAddress((char*) text); +} + + +/* + * XFE_Progress::SetLine2 + */ +void +XFE_Progress::SetLine2(const char* text) +{ + setDestination((char*) text); +} + + +/* + * XFE_Progress::SetProgressText + */ +void +XFE_Progress::SetProgressText(const char* text) +{ + m_dashboard->setStatusText(text); +} + + +/* + * XFE_Progress::SetProgressRange + */ +void +XFE_Progress::SetProgressRange(int32 minimum, int32 maximum) +{ + m_progress_bar_min = minimum; + m_progress_bar_max = maximum; +} + + +/* + * XFE_Progress::SetProgressValue + */ +void +XFE_Progress::SetProgressValue(int32 value) +{ + int32 pct; + + XP_ASSERT(value >= m_progress_bar_min); + XP_ASSERT(value <= m_progress_bar_max); + + pct = (int32) (100 * + (double) value / (double) ( m_progress_bar_max - m_progress_bar_min )); + + XFE_SetProgressBarPercent(getContext(), pct); +} + +/* + * this is a slight hack so that the CONTEXT_DATA macro + * still works even with this type of context + */ +void +XFE_Progress::AssociateWindowWithContext(MWContext *context) +{ + XP_ASSERT(context); + if (!context) return; + + XP_ASSERT(context->type==MWContextProgressModule); + if (context->type!=MWContextProgressModule) return; + + +#if 1 + /* 116773: Reopen ThreadFrame after "OK" IMAP Upgrade dialog + * leads to core dump + */ + fe_ContextData *fec = XP_NEW_ZAP (fe_ContextData); + XP_ASSERT(fec); + CONTEXT_DATA(context) = fec; + CONTEXT_WIDGET(context) = getBaseWidget(); + // ViewGlue_addMapping(this, (void *)context); +#else + fe_ContextData *fec; + fec = (fe_ContextData *)XP_CALLOC(sizeof(fe_ContextData), 1); + + CONTEXT_DATA(context) = fec; + CONTEXT_WIDGET(context) = getBaseWidget(); +#endif +} + +/********************************************************************** + these are the C-visible functions from progress.h +**********************************************************************/ + +/* + * pw_Create + */ +pw_ptr +pw_Create(MWContext* context, PW_WindowType type) +{ + XFE_Progress* pw; + + pw = new XFE_Progress(context ? CONTEXT_WIDGET(context) : FE_GetToplevelWidget()); + + return pw->Create(context, type); +} + + +/* + * pw_SetCancelCallback + */ +void +pw_SetCancelCallback(pw_ptr pw, PW_CancelCallback cancelcb, void* cancelClosure) +{ + if (pw) + ((XFE_Progress *)pw)->SetCancelCallback(cancelcb, cancelClosure); +} + + +/* + * pw_Show + */ +void +pw_Show(pw_ptr pw) +{ + if (pw) + ((XFE_Progress *)pw)->show(); +} + + +/* + * pw_Hide + */ +void +pw_Hide(pw_ptr pw) +{ + if (pw) { + ((XFE_Progress *)pw)->hide(); +#if 1 + /* 116773: Reopen ThreadFrame after "OK" IMAP Upgrade dialog + * leads to core dump + */ + ((XFE_Progress *)pw)->notifyInterested(XFE_Component::logoStopAnimation, NULL); +#endif + } +} + + +/* + * pw_Destroy + */ +void +pw_Destroy(pw_ptr pw) +{ + if (pw) + ((XFE_Progress *)pw)->Destroy(); +} + + +/* + * pw_SetWindowTitle + */ +void +pw_SetWindowTitle(pw_ptr pw, const char *title) +{ + if (pw) + ((XFE_Progress *)pw)->SetWindowTitle(title); +} + + +/* + * pw_SetLine1 + */ +void +pw_SetLine1(pw_ptr pw, const char *text) +{ + if (pw) + ((XFE_Progress *)pw)->SetLine1(text); +} + + +/* + * pw_SetLine2 + */ +void +pw_SetLine2(pw_ptr pw, const char *text) +{ + if (pw) + ((XFE_Progress *)pw)->SetLine2(text); +} + + +/* + * pw_SetProgressText + */ +void +pw_SetProgressText(pw_ptr pw, const char * text) +{ + if (pw) + ((XFE_Progress *)pw)->SetProgressText(text); +} + + +/* + * pw_SetProgressRange + */ +void +pw_SetProgressRange(pw_ptr pw, int32 minimum, int32 maximum) +{ + if (pw) + ((XFE_Progress *)pw)->SetProgressRange(minimum, maximum); +} + + +/* + * pw_SetProgressValue + */ +void +pw_SetProgressValue(pw_ptr pw, int32 value) +{ + if (pw) + ((XFE_Progress *)pw)->SetProgressValue(value); +} + +void +fe_pw_AssociateWindowWithContext(MWContext *context, + pw_ptr pw) +{ + if (pw) + ((XFE_Progress *)pw)->AssociateWindowWithContext(context); +} + diff --git a/mozilla/cmd/xfe/src/Progress.h b/mozilla/cmd/xfe/src/Progress.h new file mode 100644 index 00000000000..ed3e2d50c0f --- /dev/null +++ b/mozilla/cmd/xfe/src/Progress.h @@ -0,0 +1,70 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* + Progress.h -- class definitions for FE IMAP upgrade dialogs + Created: Daniel Malmer , 5-Apr-98. + */ + +#ifndef _xfe_progress_h +#define _xfe_progress_h + +#include "DownloadFrame.h" +#include "pw_public.h" + +// work around clash with XFE_Progress in xfe.h +#define XFE_Progress XFE_Progress1 + +class XFE_Progress : public XFE_DownloadFrame +{ + private: + int32 m_progress_bar_min; + int32 m_progress_bar_max; + PW_CancelCallback m_cancel_cb; + void* m_cancel_closure; + + public: + XFE_Progress(Widget parent); + virtual ~XFE_Progress(); + + virtual XP_Bool isCommandEnabled(CommandType cmd, void *calldata = NULL, + XFE_CommandInfo* = NULL); + virtual void doCommand(CommandType cmd, void *calldata = NULL, + XFE_CommandInfo* = NULL); + virtual XP_Bool handlesCommand(CommandType cmd, void *calldata = NULL, + XFE_CommandInfo* = NULL); + + // C++ implementation of C API + pw_ptr Create(MWContext* parent, PW_WindowType type); + void SetCancelCallback(PW_CancelCallback cancelcb, void* cancelClosure); + void Destroy(void); + + void SetWindowTitle(const char* title); + void SetLine1(const char* text); + void SetLine2(const char* text); + + void SetProgressText(const char* text); + void SetProgressRange(int32 minimum, int32 maximum); + void SetProgressValue(int32 value); + void AssociateWindowWithContext(MWContext *context); +}; + +#endif + + diff --git a/mozilla/cmd/xfe/src/ProgressFrame.cpp b/mozilla/cmd/xfe/src/ProgressFrame.cpp new file mode 100644 index 00000000000..e2ca09cb8b9 --- /dev/null +++ b/mozilla/cmd/xfe/src/ProgressFrame.cpp @@ -0,0 +1,370 @@ +////////////////////////////////////////////////////////////////////// +// ProgressFrame.cpp +// By Daniel Malmer +// +// Implementation of the XP progress dialog. +// Basically a wrapper around a DownloadFrame. +////////////////////////////////////////////////////////////////////// + +#include "ProgressFrame.h" +#include "pw_public.h" +#include "Xfe/ProgressBar.h" +#include "Dashboard.h" +#include "xpassert.h" + +#if 0 +#include "ViewGlue.h" +#endif /* + +////////////////////////////////////////////////////////////////////// +// C++ implementation of C API +////////////////////////////////////////////////////////////////////// + +/* + * XFE_ProgressFrame + */ +XFE_ProgressFrame::XFE_ProgressFrame(Widget parent) : XFE_DownloadFrame(parent, NULL) +{ + Widget label; + + label = XtNameToWidget(getBaseWidget(), "*downloadURLLabel"); + if ( label ) fe_SetString(label, XmNlabelString, ""); + + label = XtNameToWidget(getBaseWidget(), "*downloadFileLabel"); + if ( label ) fe_SetString(label, XmNlabelString, ""); +#if 1 + /* 116773: Reopen ThreadFrame after "OK" IMAP Upgrade dialog + * leads to core dump + */ + notifyInterested(XFE_Component::logoStartAnimation, NULL); +#endif +} + + +/* + * ~XFE_ProgressFrame + */ +XFE_ProgressFrame::~XFE_ProgressFrame() +{ +} + + +/* + * XFE_ProgressFrame::Create + */ +pw_ptr +XFE_ProgressFrame::Create(MWContext* /*parent*/, PW_WindowType /*type*/) +{ + return (pw_ptr) this; +} + + +/* + * XFE_ProgressFrame::Destroy + */ +void +XFE_ProgressFrame::Destroy(void) +{ +#if 1 + /* 116773: Reopen ThreadFrame after "OK" IMAP Upgrade dialog + * leads to core dump + */ + delete_response(); +#endif +} + + +/* + * XFE_ProgressFrame::SetCancelCallback + */ +void +XFE_ProgressFrame::SetCancelCallback(PW_CancelCallback cancelcb, void* cancelClosure) +{ + m_cancel_cb = cancelcb; + m_cancel_closure = cancelClosure; +} + + +/* + * XFE_ProgressFrame::isCommandEnabled + */ +XP_Bool +XFE_ProgressFrame::isCommandEnabled(CommandType cmd, + void */*calldata*/, XFE_CommandInfo*) +{ + return ( cmd == xfeCmdStopLoading ); +} + + +/* + * XFE_ProgressFrame::handlesCommand + */ +XP_Bool +XFE_ProgressFrame::handlesCommand(CommandType cmd, + void */*calldata*/, XFE_CommandInfo*) +{ + return ( cmd == xfeCmdStopLoading ); +} + + +/* + * XFE_ProgressFrame::doCommand + */ +void +XFE_ProgressFrame::doCommand(CommandType cmd, + void */*calldata*/, XFE_CommandInfo*) +{ + if ( cmd == xfeCmdStopLoading && m_cancel_cb ) { + m_cancel_cb(m_cancel_closure); + } + + delete_response(); +} + + +/* + * XFE_ProgressFrame::SetWindowTitle + */ +void +XFE_ProgressFrame::SetWindowTitle(const char *title) +{ + XP_ASSERT(title); + + XFE_SetDocTitle(fe_WidgetToMWContext(getBaseWidget()), (char*) title); +} + + +/* + * XFE_ProgressFrame::SetLine1 + */ +void +XFE_ProgressFrame::SetLine1(const char* text) +{ + setAddress((char*) text); +} + + +/* + * XFE_ProgressFrame::SetLine2 + */ +void +XFE_ProgressFrame::SetLine2(const char* text) +{ + setDestination((char*) text); +} + + +/* + * XFE_ProgressFrame::SetProgressText + */ +void +XFE_ProgressFrame::SetProgressText(const char* text) +{ + m_dashboard->setStatusText(text); +} + + +/* + * XFE_ProgressFrame::SetProgressRange + */ +void +XFE_ProgressFrame::SetProgressRange(int32 minimum, int32 maximum) +{ + m_progress_bar_min = minimum; + m_progress_bar_max = maximum; +} + + +/* + * XFE_ProgressFrame::SetProgressValue + */ +void +XFE_ProgressFrame::SetProgressValue(int32 value) +{ + int32 pct; + + XP_ASSERT(value >= m_progress_bar_min); + XP_ASSERT(value <= m_progress_bar_max); + + pct = (int32) (100 * + (double) value / (double) ( m_progress_bar_max - m_progress_bar_min )); + + XFE_SetProgressBarPercent(getContext(), pct); +} + +/* + * this is a slight hack so that the CONTEXT_DATA macro + * still works even with this type of context + */ +void +XFE_ProgressFrame::AssociateWindowWithContext(MWContext *context) +{ + XP_ASSERT(context); + if (!context) return; + + XP_ASSERT(context->type==MWContextProgressModule); + if (context->type!=MWContextProgressModule) return; + + +#if 1 + /* 116773: Reopen ThreadFrame after "OK" IMAP Upgrade dialog + * leads to core dump + */ + fe_ContextData *fec = XP_NEW_ZAP (fe_ContextData); + XP_ASSERT(fec); + CONTEXT_DATA(context) = fec; + CONTEXT_WIDGET(context) = getBaseWidget(); + // ViewGlue_addMapping(this, (void *)context); +#else + fe_ContextData *fec; + fec = (fe_ContextData *)XP_CALLOC(sizeof(fe_ContextData), 1); + + CONTEXT_DATA(context) = fec; + CONTEXT_WIDGET(context) = getBaseWidget(); +#endif +} + +/********************************************************************** + these are the C-visible functions from progress.h +**********************************************************************/ + +/* + * pw_Create + */ +pw_ptr +pw_Create(MWContext* context, PW_WindowType type) +{ + XFE_ProgressFrame* pw; + + if ( context == NULL ) return NULL; + + pw = new XFE_ProgressFrame(CONTEXT_WIDGET(context)); + + return pw->Create(context, type); +} + + +/* + * pw_SetCancelCallback + */ +void +pw_SetCancelCallback(pw_ptr pw, PW_CancelCallback cancelcb, void* cancelClosure) +{ + if (pw) + ((XFE_ProgressFrame *)pw)->SetCancelCallback(cancelcb, cancelClosure); +} + + +/* + * pw_Show + */ +void +pw_Show(pw_ptr pw) +{ + if (pw) + ((XFE_ProgressFrame *)pw)->show(); +} + + +/* + * pw_Hide + */ +void +pw_Hide(pw_ptr pw) +{ + if (pw) { + ((XFE_ProgressFrame *)pw)->hide(); +#if 1 + /* 116773: Reopen ThreadFrame after "OK" IMAP Upgrade dialog + * leads to core dump + */ + ((XFE_ProgressFrame *)pw)->notifyInterested(XFE_Component::logoStopAnimation, NULL); +#endif + } +} + + +/* + * pw_Destroy + */ +void +pw_Destroy(pw_ptr pw) +{ + if (pw) + ((XFE_ProgressFrame *)pw)->Destroy(); +} + + +/* + * pw_SetWindowTitle + */ +void +pw_SetWindowTitle(pw_ptr pw, const char *title) +{ + if (pw) + ((XFE_ProgressFrame *)pw)->SetWindowTitle(title); +} + + +/* + * pw_SetLine1 + */ +void +pw_SetLine1(pw_ptr pw, const char *text) +{ + if (pw) + ((XFE_ProgressFrame *)pw)->SetLine1(text); +} + + +/* + * pw_SetLine2 + */ +void +pw_SetLine2(pw_ptr pw, const char *text) +{ + if (pw) + ((XFE_ProgressFrame *)pw)->SetLine2(text); +} + + +/* + * pw_SetProgressText + */ +void +pw_SetProgressText(pw_ptr pw, const char * text) +{ + if (pw) + ((XFE_ProgressFrame *)pw)->SetProgressText(text); +} + + +/* + * pw_SetProgressRange + */ +void +pw_SetProgressRange(pw_ptr pw, int32 minimum, int32 maximum) +{ + if (pw) + ((XFE_ProgressFrame *)pw)->SetProgressRange(minimum, maximum); +} + + +/* + * pw_SetProgressValue + */ +void +pw_SetProgressValue(pw_ptr pw, int32 value) +{ + if (pw) + ((XFE_ProgressFrame *)pw)->SetProgressValue(value); +} + +void +fe_pw_AssociateWindowWithContext(MWContext *context, + pw_ptr pw) +{ + if (pw) + ((XFE_ProgressFrame *)pw)->AssociateWindowWithContext(context); +} + diff --git a/mozilla/cmd/xfe/src/ProgressFrame.h b/mozilla/cmd/xfe/src/ProgressFrame.h new file mode 100644 index 00000000000..29fb265ec5b --- /dev/null +++ b/mozilla/cmd/xfe/src/ProgressFrame.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4 -*- + ProgressFrame.h -- class definitions for FE IMAP upgrade dialogs + Copyright © 1998 Netscape Communications Corporation, all rights reserved. + Created: Daniel Malmer , 5-Apr-98. + */ + +#ifndef _xfe_progress_frame_h +#define _xfe_progress_frame_h + +#include "DownloadFrame.h" +#include "pw_public.h" + +class XFE_ProgressFrame : public XFE_DownloadFrame +{ + private: + int32 m_progress_bar_min; + int32 m_progress_bar_max; + PW_CancelCallback m_cancel_cb; + void* m_cancel_closure; + + public: + XFE_ProgressFrame(Widget parent); + virtual ~XFE_ProgressFrame(); + + virtual XP_Bool isCommandEnabled(CommandType cmd, void *calldata = NULL, + XFE_CommandInfo* = NULL); + virtual void doCommand(CommandType cmd, void *calldata = NULL, + XFE_CommandInfo* = NULL); + virtual XP_Bool handlesCommand(CommandType cmd, void *calldata = NULL, + XFE_CommandInfo* = NULL); + + // C++ implementation of C API + pw_ptr Create(MWContext* parent, PW_WindowType type); + void SetCancelCallback(PW_CancelCallback cancelcb, void* cancelClosure); + void Destroy(void); + + void SetWindowTitle(const char* title); + void SetLine1(const char* text); + void SetLine2(const char* text); + + void SetProgressText(const char* text); + void SetProgressRange(int32 minimum, int32 maximum); + void SetProgressValue(int32 value); + void AssociateWindowWithContext(MWContext *context); +}; + +#endif + + diff --git a/mozilla/cmd/xfe/src/RelatedLinksMenu.cpp b/mozilla/cmd/xfe/src/RelatedLinksMenu.cpp new file mode 100644 index 00000000000..e4565b70abb --- /dev/null +++ b/mozilla/cmd/xfe/src/RelatedLinksMenu.cpp @@ -0,0 +1,535 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/*---*- Mode: C++; tab-width: 4 -*--------------------------------------*/ +/* */ +/* */ +/*----------------------------------------------------------------------*/ +/* */ +/* Name: FrameListMenu.cpp */ +/* Description: XFE_FrameListMenu component implementation. */ +/* */ +/* Created: Ramiro Estrugo */ +/* Date: Fri Jan 30 01:13:11 PST 1998 */ +/* */ +/*----------------------------------------------------------------------*/ + +#include "RelatedLinksMenu.h" +#include "BookmarkBase.h" // For pixmaps +#include "BrowserFrame.h" // For fe_showBrowser() +#include "MozillaApp.h" +#include "View.h" +#include "felocale.h" +#include "intl_csi.h" + +#if DO_IT_WITH_FANCY_PIXMAPS +#include +#else +#include +#endif + +#include + +#include "xpgetstr.h" // for XP_GetString() +#include "../../lib/xp/rl.h" /* For related links */ + +#define MAX_MENU_ITEM_LENGTH 60 +#define MAX_NUM_RELATED_LINKS 50 + +extern int XFE_UNTITLED; + +////////////////////////////////////////////////////////////////////////// +// +// Used to pass data to the callbacks +// +////////////////////////////////////////////////////////////////////////// +typedef struct +{ + XFE_RelatedLinksMenu * object; // Object + RL_Item rl_item; // Related link item + XP_Bool new_window; // Open a new window ? +} _RL_ItemCallbackStruct; +////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////// +XFE_RelatedLinksMenu::XFE_RelatedLinksMenu(XFE_Frame * frame, + Widget cascade) : + _frame(frame), + _cascade(cascade), + _rl_window(NULL) +{ + // Obtain the submenu + XtVaGetValues(_cascade,XmNsubMenuId,&_submenu,NULL); + + XP_ASSERT( _submenu != NULL ); + XP_ASSERT( XfeIsCascade(_cascade) ); + + XtAddCallback(_cascade, + XmNdestroyCallback, + &XFE_RelatedLinksMenu::cascadeDestroyCB, + (XtPointer) this); + + XtAddCallback(_cascade, + XmNcascadingCallback, + &XFE_RelatedLinksMenu::cascadingCB, + (XtPointer) this); + + // Make the delay 100 + XtVaSetValues(_cascade,XmNmappingDelay,100,NULL); + + _rl_window = RL_MakeRLWindow(); +} +////////////////////////////////////////////////////////////////////////// +XFE_RelatedLinksMenu::~XFE_RelatedLinksMenu() +{ + if (_rl_window) + { + RL_DestroyRLWindow(_rl_window); + } +} +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::generate(Widget cascade, + XtPointer /* data */, + XFE_Frame * frame) +{ + XFE_RelatedLinksMenu * obj; + + obj = new XFE_RelatedLinksMenu(frame,cascade); + + // Store the BookmarkMenu instance in the quickfile button + // XmNinstancePointer. This overrides the XmNinstancePointer + // installed by the XFE_Button class + XtVaSetValues(cascade,XmNinstancePointer,(XtPointer) obj,NULL); +} +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::setRelatedLinksAddress(char * address) +{ + XP_ASSERT( address != NULL ); + + if (!address) + { + return; + } + + RL_SetRLWindowURL(_rl_window,address); + +// #ifdef DEBUG_ramiro +// printf("RL_SetRLWindowURL(%p,%s)\n",_rl_window,address); +// #endif +} +////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////// +// +// Cascade callbacks +// +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::cascadeDestroyCB(Widget,XtPointer clientData,XtPointer) +{ + XFE_RelatedLinksMenu * obj = (XFE_RelatedLinksMenu*)clientData; + + XP_ASSERT( obj != NULL ); + + delete obj; +} +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::cascadingCB(Widget,XtPointer clientData,XtPointer) +{ + XFE_RelatedLinksMenu *obj = (XFE_RelatedLinksMenu*)clientData; + + XP_ASSERT( obj != NULL ); + + obj->cascading(); +} +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::cascading() +{ + destroyItems(); + + XP_ASSERT( _frame != NULL ); + + addItems(_submenu,_rl_window); +} +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::destroyItems() +{ + XP_ASSERT( XfeIsAlive(_cascade) ); + + XfeCascadeDestroyChildren(_cascade); +} +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::addItems(Widget submenu,RL_Window rl_window) +{ + XP_ASSERT( XfeIsAlive(submenu) ); + XP_ASSERT( XmIsRowColumn(submenu) ); + XP_ASSERT( rl_window != NULL ); + + if (!rl_window) + { + return; + } + + RL_Item rl_item; + int i = 0; + + for(rl_item = RL_WindowItems(rl_window), i = 0; + rl_item != NULL; + rl_item = RL_NextItem(rl_item), i++) + { + XP_ASSERT( rl_item != NULL ); + + if (rl_item && (i < MAX_NUM_RELATED_LINKS)) + { + addItem(submenu,rl_item); + } + } +} +////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////// +// +// Menu item callbacks +// +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::itemActivateCB(Widget,XtPointer clientData,XtPointer) +{ + _RL_ItemCallbackStruct * data = (_RL_ItemCallbackStruct *) clientData; + + XP_ASSERT( data != NULL ); + XP_ASSERT( data->object != NULL ); + + data->object->itemActivate(data->rl_item,data->new_window); +} +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::itemArmCB(Widget,XtPointer clientData,XtPointer) +{ + _RL_ItemCallbackStruct * data = (_RL_ItemCallbackStruct *) clientData; + + XP_ASSERT( data != NULL ); + XP_ASSERT( data->object != NULL ); + + data->object->itemArm(data->rl_item); +} +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::itemDisarmCB(Widget,XtPointer clientData,XtPointer) +{ + _RL_ItemCallbackStruct * data = (_RL_ItemCallbackStruct *) clientData; + + XP_ASSERT( data != NULL ); + XP_ASSERT( data->object != NULL ); + + data->object->itemDisarm(); +} +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::itemDestroyCB(Widget,XtPointer clientData,XtPointer) +{ + _RL_ItemCallbackStruct * data = (_RL_ItemCallbackStruct *) clientData; + + XP_ASSERT( data != NULL ); + + XP_FREE(data); +} +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::itemActivate(RL_Item rl_item,XP_Bool new_window) +{ + XP_ASSERT( _frame != NULL ); + XP_ASSERT( rl_item != NULL ); + + if (!rl_item) + { + return; + } + + char * address = RL_ItemUrl(rl_item); + + if (!address) + { + return; + } + + URL_Struct * url = NET_CreateURLStruct(address,NET_DONT_RELOAD); + + if (new_window) + { + fe_showBrowser(FE_GetToplevelWidget(),NULL,NULL,url); + } + else + { + if (_frame->handlesCommand(xfeCmdOpenUrl,(void *) url)) + { + _frame->doCommand(xfeCmdOpenUrl,(void *) url); + } + } +} +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::itemArm(RL_Item rl_item) +{ + XP_ASSERT( _frame != NULL ); + XP_ASSERT( rl_item != NULL ); + + if (!rl_item) + { + return; + } + + char * address = RL_ItemUrl(rl_item); + + if (!address) + { + return; + } + + _frame->notifyInterested(XFE_View::statusNeedsUpdatingMidTruncated, + (void*) address); +} +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::itemDisarm() +{ + XP_ASSERT( _frame != NULL ); + + _frame->notifyInterested(XFE_View::statusNeedsUpdatingMidTruncated, + (void*) ""); +} +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::addItem(Widget submenu,RL_Item rl_item) +{ + XP_ASSERT( XfeIsAlive(submenu) ); + XP_ASSERT( XmIsRowColumn(submenu) ); + XP_ASSERT( rl_item != NULL ); + + // Make sure the title is valid + if (!rl_item) + { + return; + } + + + RL_ItemType item_type = RL_GetItemType(rl_item); + +// #ifdef DEBUG_ramiro +// printf("XFE_RelatedLinksMenu::addItem(%s,%s,%d)\n", +// RL_ItemName(rl_item) ? RL_ItemName(rl_item) : "NULL", +// RL_ItemUrl(rl_item) ? RL_ItemUrl(rl_item) : "NULL", +// item_type); +// #endif + + if (item_type == RL_CONTAINER) + { + addContainer(submenu,rl_item); + } + else if(item_type == RL_SEPARATOR) + { + addSeparator(submenu,rl_item); + } + else if(item_type == RL_LINK) + { + addLink(submenu,rl_item,False); + } + else if(item_type == RL_LINK_NEW_WINDOW) + { + addLink(submenu,rl_item,True); + } + else + { + XP_ASSERT( 0 ); + } +} +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::addSeparator( Widget submenu,RL_Item /* rl_item */) +{ + XP_ASSERT( XfeIsAlive(submenu) ); + XP_ASSERT( XmIsRowColumn(submenu) ); + + Widget separator; + + separator = XmCreateSeparator(submenu,"separator",NULL,0); + + XtVaSetValues(separator,XmNshadowThickness,2,NULL); + + XtManageChild(separator); +} +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::addContainer(Widget submenu,RL_Item rl_item) +{ + XP_ASSERT( XfeIsAlive(submenu) ); + XP_ASSERT( XmIsRowColumn(submenu) ); + XP_ASSERT( rl_item != NULL ); + + if (!rl_item) + { + return; + } + + RL_Item rl_child = NULL; + int i = 0; + Widget child_cascade = NULL; + Widget child_submenu = NULL; + + XfeCreateCascadeAndSubmenu(submenu, + "relatedCascade", + "relatedSubmenu", + xmCascadeButtonWidgetClass, + &child_cascade, + &child_submenu); + + for(rl_child = RL_ItemChild(rl_item), i = 0; + rl_child != NULL; + rl_child = RL_NextItem(rl_child), i++) + { + XP_ASSERT( rl_child != NULL ); + + if (rl_child && (i < MAX_NUM_RELATED_LINKS)) + { + addItem(child_submenu,rl_child); + } + } +} +////////////////////////////////////////////////////////////////////////// +void +XFE_RelatedLinksMenu::addLink(Widget submenu, + RL_Item rl_item, + XP_Bool new_window) +{ + XP_ASSERT( XfeIsAlive(submenu) ); + XP_ASSERT( XmIsRowColumn(submenu) ); + + char * item_name = RL_ConvertItemName(rl_item, fe_LocaleCharSetID); + char * item_address = RL_ItemUrl(rl_item); + + // If theres no name and item_address, whats the point ? + if (!item_name && !item_address) + { + return; + } + + // Make sure the item_name is something + if (!item_name) + { + item_name = item_address; + } + + _RL_ItemCallbackStruct * data; + + // Create a new bookmark data structure for the callbacks + data = XP_NEW_ZAP(_RL_ItemCallbackStruct); + + data->object = this; + data->rl_item = rl_item; + data->new_window = new_window; + +#if DO_IT_WITH_FANCY_PIXMAPS + // Create the new item + Widget item = XtVaCreateManagedWidget(xfeCmdOpenTargetUrl, + xfeBmButtonWidgetClass, + submenu, + NULL); + + // Set the pixmaps if needed + if (fe_globalPrefs.toolbar_style != BROWSER_TOOLBAR_TEXT_ONLY) + { + Pixmap bookmarkPixmap = XFE_BookmarkBase::getBookmarkPixmap(); + Pixmap bookmarkMask = XFE_BookmarkBase::getBookmarkMask(); + + if (XfePixmapGood(bookmarkPixmap) && XfePixmapGood(bookmarkMask)) + { + XtVaSetValues(item, + XmNlabelPixmap, bookmarkPixmap, + XmNlabelPixmapMask, bookmarkMask, + NULL); + } + } +#else + // Create the new item + Widget item = XtVaCreateManagedWidget(xfeCmdOpenTargetUrl, + xmPushButtonWidgetClass, + submenu, + NULL); +#endif + + XtRealizeWidget(item); + + // Add callbacks to item + XtAddCallback(item, + XmNactivateCallback, + &XFE_RelatedLinksMenu::itemActivateCB, + (XtPointer) data); + + XtAddCallback(item, + XmNarmCallback, + &XFE_RelatedLinksMenu::itemArmCB, + (XtPointer) data); + + XtAddCallback(item, + XmNdisarmCallback, + &XFE_RelatedLinksMenu::itemDisarmCB, + (XtPointer) data); + + // Format the title + char * buffer = (String) XtNewString(item_name); + + XP_ASSERT( buffer != NULL ); + + // Truncate the title and install it on the item + if (buffer) + { +// MWContext * context = _frame->getContext(); +// INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(context); + XmFontList font_list; + + INTL_MidTruncateString(fe_LocaleCharSetID, + buffer, + buffer, + MAX_MENU_ITEM_LENGTH); + + + XmString label = fe_ConvertToXmString((unsigned char *) buffer, + fe_LocaleCharSetID, + NULL, + XmFONT_IS_FONT, + &font_list); + + if (label) + { + XtVaSetValues(item,XmNlabelString,label,NULL); + + XmStringFree(label); + } + + // Free the allocated memory + XtFree(buffer); + } +} +////////////////////////////////////////////////////////////////////////// diff --git a/mozilla/cmd/xfe/src/RelatedLinksMenu.h b/mozilla/cmd/xfe/src/RelatedLinksMenu.h new file mode 100644 index 00000000000..ff25a3c8229 --- /dev/null +++ b/mozilla/cmd/xfe/src/RelatedLinksMenu.h @@ -0,0 +1,97 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +/*----------------------------------------------------------------------*/ +/* */ +/* Name: RelatedLinksMenu.h */ +/* Description: XFE_RelatedLinksMenu component header file. */ +/* */ +/* Created: Ramiro Estrugo */ +/* Date: Fri Jan 30 01:13:11 PST 1998 */ +/* */ +/*----------------------------------------------------------------------*/ + +//#include "NotificationCenter.h" +#include "Frame.h" +#include "ntypes.h" +#include "../../lib/xp/rl.h" /* For related links */ + +#ifndef _xfe_related_links_menu_h_ +#define _xfe_related_links_menu_h_ + +// This class can be used with a DYNA_CASCADEBUTTON or +// DYNA_MENUITEMS. + +class XFE_RelatedLinksMenu// : public XFE_NotificationCenter +{ +public: + + virtual ~XFE_RelatedLinksMenu(); + + // this function occupies the generateCallrelated slot in a menuspec. + static void generate(Widget cascade, + XtPointer data, + XFE_Frame * frame); + + void setRelatedLinksAddress (char * address); + +private: + + XFE_RelatedLinksMenu(XFE_Frame * frame, + Widget cascade); + + // the toplevel component -- the thing we dispatch our events to. + XFE_Frame * _frame; + + // the cascade button we're tied to. + Widget _cascade; + + // the row column we're in. + Widget _submenu; + + RL_Window _rl_window; + + // Cascade callrelateds + static void cascadingCB (Widget, XtPointer, XtPointer); + static void cascadeDestroyCB (Widget, XtPointer, XtPointer); + + // Menu item callrelateds + static void itemActivateCB (Widget, XtPointer, XtPointer); + static void itemArmCB (Widget, XtPointer, XtPointer); + static void itemDisarmCB (Widget, XtPointer, XtPointer); + static void itemDestroyCB (Widget, XtPointer, XtPointer); + + void cascading (); + + void itemActivate (RL_Item rl_item,XP_Bool new_window); + void itemArm (RL_Item rl_item); + void itemDisarm (); + + void destroyItems (); + + void addItems (Widget submenu,RL_Window rl_window); + + void addItem (Widget submenu,RL_Item rl_item); + void addContainer (Widget submenu,RL_Item rl_item); + void addSeparator (Widget submenu,RL_Item rl_item); + void addLink (Widget submenu,RL_Item rl_item,XP_Bool new_window); +}; + +#endif /* _xfe_related_links_menu_h_ */ diff --git a/mozilla/cmd/xfe/src/SubUpgradeDialog.cpp b/mozilla/cmd/xfe/src/SubUpgradeDialog.cpp new file mode 100644 index 00000000000..6fe7c4b8a4f --- /dev/null +++ b/mozilla/cmd/xfe/src/SubUpgradeDialog.cpp @@ -0,0 +1,146 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +#include "felocale.h" +#include "xfe.h" +#include "xpgetstr.h" +#include "SubUpgradeDialog.h" + +extern "C" int XFE_SUBUPGRADE_AUTOSUBSCRIBE; + +extern "C" MSG_IMAPUpgradeType +fe_promptIMAPSubscriptionUpgrade(MWContext *context, + const char *hostName) +{ + MSG_IMAPUpgradeType retval; + XFE_SubUpgradeDialog *sud = new XFE_SubUpgradeDialog(context,hostName); + + retval=sud->prompt(); + delete sud; + + return retval; +} + +XFE_SubUpgradeDialog::XFE_SubUpgradeDialog(MWContext *context, + const char *hostName) + :XFE_Dialog(CONTEXT_WIDGET(context), + "SubUpgradeDialog",TRUE, TRUE, FALSE, FALSE, FALSE), // ok and cancel + m_retVal(MSG_IMAPUpgradeDont), + m_doneWithLoop(0), + m_hostname(hostName) +{ + create(); + init(); +} +XFE_SubUpgradeDialog::~XFE_SubUpgradeDialog() { + +} + +void XFE_SubUpgradeDialog::create() +{ + + Widget form; + Widget paragraph_label; + + Widget selection_radiobox; + + form=XmCreateForm(m_chrome, "form", NULL,0); + + paragraph_label=XmCreateLabelGadget(form, "paragraphLabel", NULL,0); + selection_radiobox=XmCreateRadioBox(form, "selectionBox", NULL,0); + + m_automatic_toggle=XmCreateToggleButtonGadget(selection_radiobox,"automaticToggle",NULL,0); + m_custom_toggle=XmCreateToggleButtonGadget(selection_radiobox,"customToggle",NULL,0); + + char *automatic_toggle= + PR_smprintf(XP_GetString(XFE_SUBUPGRADE_AUTOSUBSCRIBE), m_hostname); + XmString automatic_toggle_string = + XmStringCreateLocalized(automatic_toggle); + + XtVaSetValues(m_automatic_toggle, + XmNlabelString, automatic_toggle_string, + NULL); + XP_FREE(automatic_toggle); + XmStringFree(automatic_toggle_string); + + XtVaSetValues(paragraph_label, + XmNtopAttachment, XmATTACH_FORM, + XmNalignment, XmALIGNMENT_BEGINNING, + NULL); + + XtVaSetValues(selection_radiobox, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, paragraph_label, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + + XtManageChild(m_custom_toggle); + XtManageChild(m_automatic_toggle); + XtManageChild(selection_radiobox); + XtManageChild(paragraph_label); + XtManageChild(form); + + XtAddCallback(m_chrome, XmNokCallback, cb_ok, this); + XtAddCallback(m_chrome, XmNcancelCallback, cb_cancel, this); + +} + +void XFE_SubUpgradeDialog::init() +{ + XmToggleButtonGadgetSetState(m_automatic_toggle, True, True); +} + +void XFE_SubUpgradeDialog::cb_ok(Widget, XtPointer closure, XtPointer) +{ + ((XFE_SubUpgradeDialog*)closure)->ok(); +} + + + +void XFE_SubUpgradeDialog::cb_cancel(Widget, XtPointer closure, XtPointer) +{ + ((XFE_SubUpgradeDialog*)closure)->cancel(); +} + +void XFE_SubUpgradeDialog::ok() { + if (XmToggleButtonGadgetGetState(m_automatic_toggle)) + m_retVal=MSG_IMAPUpgradeAutomatic; + else if (XmToggleButtonGadgetGetState(m_custom_toggle)) + m_retVal=MSG_IMAPUpgradeCustom; + + m_doneWithLoop=True; + +} + +void XFE_SubUpgradeDialog::cancel() { + m_doneWithLoop=True; +} + +MSG_IMAPUpgradeType XFE_SubUpgradeDialog::prompt() { + m_doneWithLoop=False; + show(); + while (!m_doneWithLoop) + fe_EventLoop(); + + hide(); + return m_retVal; +} diff --git a/mozilla/cmd/xfe/src/SubUpgradeDialog.h b/mozilla/cmd/xfe/src/SubUpgradeDialog.h new file mode 100644 index 00000000000..4038d4a15a4 --- /dev/null +++ b/mozilla/cmd/xfe/src/SubUpgradeDialog.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +#ifndef _xfe_subupgradedialog_h +#define _xfe_subupgradedialog_h + +#include "Dialog.h" +#include "msgcom.h" +#include "Xm/Xm.h" + +class XFE_SubUpgradeDialog: public XFE_Dialog +{ + public: + XFE_SubUpgradeDialog(MWContext *context, + const char *hostName); + + virtual ~XFE_SubUpgradeDialog(); + + MSG_IMAPUpgradeType prompt(); + + void create(); + void init(); + + + private: + void ok(); + void cancel(); + static void cb_ok(Widget, XtPointer, XtPointer); + static void cb_cancel(Widget, XtPointer, XtPointer); + + Widget m_automatic_toggle; + Widget m_custom_toggle; + + MSG_IMAPUpgradeType m_retVal; + + XP_Bool m_doneWithLoop; + const char *m_hostname; +}; + +#endif diff --git a/mozilla/cmd/xfe/src/TabView.cpp b/mozilla/cmd/xfe/src/TabView.cpp new file mode 100644 index 00000000000..f01a9fc9ffb --- /dev/null +++ b/mozilla/cmd/xfe/src/TabView.cpp @@ -0,0 +1,79 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1996 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* + XmLTabView.cpp -- class definition for XmLTabView + Created: Tao Cheng , 12-nov-96 + */ + +#include "TabView.h" +#include +#include "xpgetstr.h" + + +XFE_XmLTabView::XFE_XmLTabView(XFE_Component *top, + XFE_View *view, /* the parent view */ + int tab_string_id): XFE_View(top, view) +{ + Widget folder; + WidgetList tabs; + int ntabs; + + m_labelWidth = 0; + + // Create tab + XmString xmstr; + xmstr = XmStringCreate(XP_GetString(tab_string_id ), + XmFONTLIST_DEFAULT_TAG); + + // Get the folder + folder = view->getBaseWidget(); + + // Add the tab + Widget form = XmLFolderAddTabForm(folder, xmstr); + + XmStringFree(xmstr); + + // Get the tab list + XtVaGetValues(folder, XmNtabCount, &ntabs, NULL); + XtVaGetValues(folder, XmNtabWidgetList, &tabs, NULL); + + // Save the tab + m_tab = tabs[ntabs - 1]; + + setBaseWidget(form); +} + +XFE_XmLTabView::~XFE_XmLTabView() +{ +} + +void +XFE_XmLTabView::hide() +{ + XtUnmanageChild(getBaseWidget()); + XtUnmanageChild(m_tab); +} + +void +XFE_XmLTabView::show() +{ + XtManageChild(m_tab); + XtManageChild(getBaseWidget()); +} diff --git a/mozilla/cmd/xfe/src/TabView.h b/mozilla/cmd/xfe/src/TabView.h new file mode 100644 index 00000000000..b1a78641744 --- /dev/null +++ b/mozilla/cmd/xfe/src/TabView.h @@ -0,0 +1,69 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1996 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- + XmlTabView.h -- class definition for XFE_XmlTabView + Created: Tao Cheng , 12-nov-96 + */ + +#ifndef _xfe_xmltabview_h +#define _xfe_xmltabview_h + +#include "View.h" + +// This is a general wrapper of XmLFloder s tab form + +class XFE_XmLTabView: public XFE_View { +public: + XFE_XmLTabView(XFE_Component *top, /* the parent folderDialog */ + XFE_View *view, /* the parent view */ + int tab_string_id); + + virtual ~XFE_XmLTabView(); + + virtual void setDlgValues() {}; + virtual void getDlgValues() {}; + virtual void apply(){}; + virtual void hide(); + virtual void show(); + +protected: + Dimension m_labelWidth; + +private: + /* m_widget is the tab form + */ + Widget m_tab; +}; /* XFE_XmLTabView */ + +/* Offsets between widgets when hardwired is needed + */ +const int labelWidth = 125; +const int labelHeight = 30; +const int textFWidth = 175; +const int textFHeight = 30; + +const int separatorHeight = 10; + +const int majorVSpac = 6; +const int minorVSpac = 3; +const int majorHSpac = 6; +const int minorHSpac = 3; + +#endif /* _xfe_xmltabview_h */ diff --git a/mozilla/cmd/xfe/src/TaskBarManager.cpp b/mozilla/cmd/xfe/src/TaskBarManager.cpp new file mode 100644 index 00000000000..e813c9f87cc --- /dev/null +++ b/mozilla/cmd/xfe/src/TaskBarManager.cpp @@ -0,0 +1,211 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +#include "TaskBarManager.h" + +#include "TaskBar.h" + +#include // For MWM_DECOR_BORDER +#include // For XtMoveWidget + +// Was fe_TaskBarManager_Create() +XFE_TaskBarManager::XFE_TaskBarManager(MWContext *context, Pixel bg) +{ + // We need to add notification to set this + m_biffState = MSG_BIFF_Unknown; + // A preference will eventually determine this state + // as well as the position + m_isFloat = False; + m_taskBarList = XP_ListNew(); + + Widget shell = createFloatPalette(context); + + m_floatTaskBar = new XFE_TaskBar(shell, NULL, TRUE); + + if ( m_isFloat ) + XtPopup (shell, XtGrabNone); +} + +// Was fe_TaskBarManager_Register() +// Used to create icons in here for some reason +void +XFE_TaskBarManager::Register(XFE_TaskBar *taskb) +{ + if (!taskb ) return; + + XP_ListAddObject(m_taskBarList, taskb); +} + +void +XFE_TaskBarManager::Unregister(XFE_TaskBar *taskb) +{ + if (!taskb) return; + + XP_ListRemoveObject(m_taskBarList, taskb); +} + +void +XFE_TaskBarManager::DoDock() +{ + // Instead of doing these searching gymnastics, + // should we just make a member to hold onto the shell? -slamm + Widget shell = m_floatForm; + while (shell && !XtIsShell(shell)) { + shell = XtParent(m_floatForm); + } + if ( shell ) XtPopdown(shell); + + int count = XP_ListCount(m_taskBarList); + + XFE_TaskBar *taskp; + for ( int i = 1; i <= count; i++ ) + { + taskp = (XFE_TaskBar *) + XP_ListGetObjectNum(m_taskBarList, i); + + taskp->Dock(); + } + m_isFloat = FALSE; +} + +void +XFE_TaskBarManager::DoFloat(Widget w) +{ + Position x,y; + Position rootX, rootY; + Dimension width, height; + + XtVaGetValues(w, XmNx, &x, XmNy, &y, + XmNheight, &height, XmNwidth, &width, + 0); + + XtTranslateCoords(w, x, y, &rootX, &rootY); + + int count = XP_ListCount(m_taskBarList); + + XFE_TaskBar *taskp = 0; + for ( int i = 1; i <= count; i++ ) + { + taskp = (XFE_TaskBar*)XP_ListGetObjectNum(m_taskBarList, i); + taskp->Undock(); + } + + Widget shell = m_floatForm; + while (shell && !XtIsShell(shell)) shell = XtParent(shell); + XtMoveWidget(shell, rootX, rootY + height); + if ( shell ) + XtPopup (shell, XtGrabNone); + + m_isFloat = TRUE; +} + +void +XFE_TaskBarManager::RaiseFloat() +{ + Widget shell; + + shell = m_floatForm; + while (shell && !XtIsShell(shell)) shell = XtParent(shell); + if ( shell ) + XtPopup (shell, XtGrabNone); +} + +void +XFE_TaskBarManager::UpdateBiffState(MSG_BIFF_STATE biffState) +{ + m_biffState = biffState; + + // Update them icons; + + int count = XP_ListCount(m_taskBarList); + + XFE_TaskBar *taskp; + for ( int i = 1; i <= count; i++ ) + { + taskp = (XFE_TaskBar *) + XP_ListGetObjectNum(m_taskBarList, i); + + taskp->UpdateBiffState(biffState); + } + m_floatTaskBar->UpdateBiffState(biffState); +} + +// Was fe_TaskBarManager_CreateFloatPalette +Widget +XFE_TaskBarManager::createFloatPalette(MWContext *context) +{ + Widget mainw = CONTEXT_WIDGET(context); + Widget toplevel = XtParent(mainw); + Widget shell; + Arg av[20]; + int ac = 0; + Visual *v = 0; + Colormap cmap = 0; + Cardinal depth = 0; + Widget controlW; + Widget rowcol; + Widget form; + + XtVaGetValues(mainw, XtNvisual, &v, + XtNcolormap, &cmap, XtNdepth, &depth, 0 ); + + ac = 0; + XtSetArg(av[ac], XmNvisual, v); ac++; + XtSetArg(av[ac], XmNdepth, depth); ac++; + XtSetArg(av[ac], XmNcolormap, cmap); ac++; + XtSetArg(av[ac], XmNautoUnmanage, False); ac++; + XtSetArg(av[ac], XmNdeleteResponse, XmDESTROY); ac++; + XtSetArg(av[ac], XmNallowShellResize, True); ac++; + XtSetArg(av[ac], XmNwidth, 160); ac++; + XtSetArg(av[ac], XmNheight, 36); ac++; + XtSetArg(av[ac], XmNmwmDecorations, + MWM_DECOR_BORDER|MWM_DECOR_TITLE ); ac++; + XtSetArg(av[ac], XmNwindowGroup, XtWindow(mainw)); ac++; + shell = XtCreatePopupShell("FloatIconBox", topLevelShellWidgetClass, + toplevel, av, ac); + + XtAddCallback(shell, XmNdestroyCallback, + XFE_TaskBarManager::destroy_cb, this); + + XtRealizeWidget(shell); + + return shell; +} + +// Was fe_TaskBarManager_DestroyCB +// static callback +void +XFE_TaskBarManager::destroy_cb(Widget w, XtPointer clientData, + XtPointer CallData) +{ + MWContext *context = 0; + struct fe_MWContext_cons *cons; + + XFE_TaskBarManager *taskBMgr = (XFE_TaskBarManager *)CallData; + + /* Use the first available browser context as the context */ + cons = fe_all_MWContexts; + if ( cons ) + { + context = cons->context; + taskBMgr->createFloatPalette(context); + taskBMgr->DoDock(); + } +} + diff --git a/mozilla/cmd/xfe/src/TaskBarManager.h b/mozilla/cmd/xfe/src/TaskBarManager.h new file mode 100644 index 00000000000..d99ded65c2f --- /dev/null +++ b/mozilla/cmd/xfe/src/TaskBarManager.h @@ -0,0 +1,71 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1996 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- + TaskBar.h -- class definition for taskbar. + Created: Stephen Lamm , 19-Nov-96. + */ + +#ifndef _xfe_taskbar_manager_h +#define _xfe_taskbar_manager_h + +#include "xp_core.h" +#include "Component.h" + +#include "ntypes.h" // For MWContext. +#include "msgcom.h" // Need for BIFF stuff. + +#include + +class XFE_TaskBar; + +class XFE_TaskBarManager : public XFE_Component +{ +public: + XFE_TaskBarManager(MWContext *context, Pixel bg); + + void Register(XFE_TaskBar *taskBar); + void Unregister(XFE_TaskBar *taskBar); + + void DoDock(); + void DoFloat(Widget w); + void RaiseFloat(); + + XP_Bool IsTaskBarFloating() { return m_isFloat; } + + MSG_BIFF_STATE GetBiffState() { return m_biffState; } + + // use NotificationCenter to call updateBiffState() ?? + // (through a new static callback) + void UpdateBiffState(MSG_BIFF_STATE biffState); + +private: + MSG_BIFF_STATE m_biffState; + Boolean m_isFloat; + XP_List *m_taskBarList; + Widget m_floatForm; + XFE_TaskBar *m_floatTaskBar; + + Widget createFloatPalette(MWContext *context); + + static void destroy_cb(Widget, XtPointer, XtPointer); +}; + + +#endif /* _xfe_taskbar_manager__h */ diff --git a/mozilla/cmd/xfe/src/ToolbarButton.cpp b/mozilla/cmd/xfe/src/ToolbarButton.cpp new file mode 100644 index 00000000000..60e52cffea8 --- /dev/null +++ b/mozilla/cmd/xfe/src/ToolbarButton.cpp @@ -0,0 +1,67 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1996 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- + ToolbarButton.cpp -- implementation file for buttons that can appear in + the toolbar + Created: Chris Toshok , 13-Aug-96. + */ + +#include "ToolbarButton.h" +#include +#include "xp_mcom.h" + +ToolbarButton::ToolbarButton(Toolbar *toolbar, + XFEView *respView, + const char *button_name, + XFECommandType button_command, + fe_icon *button_icon) : XFEComponent() +{ + Widget button; + + parentToolbar = toolbar; + name = XP_STRDUP(name); + command = button_command; + icon = button_icon; + v = respView; + + // this isn't really right... + button = XtVaCreateManagedWidget(button_name, + xmPushButtonWidgetClass, + toolbar->baseWidget(), + NULL); + + setBaseWidget(button); + + XtAddCallback(button, XmNactivateCallback, activateCallback, this); +} + +void +ToolbarButton::activateCallback(Widget, XtPointer clientData, XtPointer) +{ + ToolbarButton *obj = (ToolbarButton*)clientData; + + obj->activate(); +} + +void +ToolbarButton::activate() +{ + v->doCommand(command); +} diff --git a/mozilla/cmd/xfe/src/ToolbarButton.h b/mozilla/cmd/xfe/src/ToolbarButton.h new file mode 100644 index 00000000000..1d782796470 --- /dev/null +++ b/mozilla/cmd/xfe/src/ToolbarButton.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1996 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- + ToolbarButton.h -- class definition for the toolbar buttons + Created: Chris Toshok , 13-Aug-96. + */ + +#ifndef _xfe_toolbarbutton_h +#define _xfe_toolbarbutton_h + +#include "xp_core.h" + +#include "XFEComponent.h" +#include "XFEView.h" +#include "Command.h" +#include "Toolbar.h" + +#include "mozilla.h" /* for MWContext!!! */ +#include "icons.h" + +class ToolbarButton : public XFEComponent +{ +private: + Toolbar *parentToolbar; + XFECommandType command; + const char *name; + fe_icon *icon; + XFEView *v; + + void activate(); + + static void activateCallback(Widget, XtPointer, XtPointer); + +public: + ToolbarButton(Toolbar *toolbar, + XFEView *respView, + const char *button_name, + XFECommandType button_command, + fe_icon *button_icon); + + Toolbar *getToolbar() { return parentToolbar; } +}; + +#endif /* _xfe_toolbar_h */ diff --git a/mozilla/cmd/xfe/src/ViewDashBDlg.cpp b/mozilla/cmd/xfe/src/ViewDashBDlg.cpp new file mode 100644 index 00000000000..f3a5ad55bfb --- /dev/null +++ b/mozilla/cmd/xfe/src/ViewDashBDlg.cpp @@ -0,0 +1,215 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- + ViewDashBDlg.cpp -- View dialog with a dashboard. + Created: Tao Cheng , 27-apr-98 + */ + + +#include "ViewDashBDlg.h" +#include + +XFE_ViewDashBDlg::XFE_ViewDashBDlg(Widget parent, + char *name, + MWContext *context, + Boolean ok, + Boolean cancel, + Boolean help, + Boolean apply, + Boolean modal) + : XFE_ViewDialog(NULL, parent, name, + context, + FALSE, // ok + FALSE, // cancel + FALSE, // help + FALSE, // apply + FALSE, // separator + FALSE, // modal + create_chrome_widget(parent, name, + TRUE, + TRUE, + FALSE, + FALSE, + FALSE, + modal)) +{ + m_okBtn = 0; + + m_dashboard = new XFE_Dashboard(this, + m_chrome, + NULL, // XFE_Frame + False /* taskbar */); + + m_dashboard->setShowStatusBar(True); + m_dashboard->setShowProgressBar(True); + + // create the button area + m_buttonArea = createButtonArea(m_chrome, ok, cancel, help, apply); + + XtVaSetValues(m_dashboard->getBaseWidget(), + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + + XtVaSetValues(m_buttonArea, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNtopAttachment, XmATTACH_NONE, + XmNbottomAttachment, XmATTACH_WIDGET, + XmNbottomWidget, m_dashboard->getBaseWidget(), + XmNmarginWidth, 0, + XmNmarginHeight, 0, + NULL); + +} + +void +XFE_ViewDashBDlg::attachView() +{ + // subclass create the view area + XP_ASSERT(m_aboveButtonArea); + XtVaSetValues(m_aboveButtonArea, + XmNleftAttachment, XmATTACH_FORM, + XmNleftOffset, 3, + XmNrightAttachment, XmATTACH_FORM, + XmNrightOffset, 3, + XmNtopAttachment, XmATTACH_FORM, + XmNtopOffset, 3, + XmNbottomAttachment, XmATTACH_WIDGET, + XmNbottomWidget, m_buttonArea, + NULL); + + XtManageChild(m_buttonArea); + m_dashboard->show(); +} + +XFE_ViewDashBDlg::~XFE_ViewDashBDlg() +{ +#if 0 + /* Bug 140476: core dump in LDAP search + * dashboard shall not be destroyed here + */ + if (m_dashboard) { + Widget w = m_dashboard->getBaseWidget(); + if (w && XfeIsAlive(w)) + XtDestroyWidget(w); + m_dashboard = 0; + }/* if */ +#endif +} + +Widget +XFE_ViewDashBDlg::createButtonArea(Widget parent, + Boolean ok, + Boolean cancel, + Boolean help, + Boolean /*apply*/) +{ + Widget msgb; + Widget button; + + msgb = XmCreateMessageBox(parent, "messagebox", NULL, 0); + + /* We have to do this explicitly because of AIX versions come + with these buttons by default */ + fe_UnmanageChild_safe(XmMessageBoxGetChild(msgb, XmDIALOG_SEPARATOR)); + if (!ok) + fe_UnmanageChild_safe(XmMessageBoxGetChild(msgb, XmDIALOG_OK_BUTTON)); + else + { + m_okBtn = XmMessageBoxGetChild(msgb, XmDIALOG_OK_BUTTON); + + if (m_okBtn) + { + XtManageChild(m_okBtn); + XtAddCallback(msgb, XmNokCallback, ok_cb, this); + } + } + if (!cancel) + fe_UnmanageChild_safe(XmMessageBoxGetChild(msgb, XmDIALOG_CANCEL_BUTTON)); + else + { + button = XmMessageBoxGetChild(msgb, XmDIALOG_CANCEL_BUTTON); + + if (button) + { + XtManageChild(button); + XtAddCallback(msgb, XmNcancelCallback, cancel_cb, this); + } + } + + if (!help) + fe_UnmanageChild_safe(XmMessageBoxGetChild(msgb, XmDIALOG_HELP_BUTTON)); + else + { + button = XmMessageBoxGetChild(msgb, XmDIALOG_HELP_BUTTON); + + if (button) + { + XtManageChild(button); + XtAddCallback(msgb, XmNhelpCallback, help_cb, this); + } + } + + return msgb; +} + +Widget +XFE_ViewDashBDlg::create_chrome_widget(Widget parent, + char *name, + Boolean /* ok */, + Boolean /* cancel */, + Boolean /* help */, + Boolean /* apply */, + Boolean /* separator */, + Boolean modal) +{ + Visual *v = 0; + Colormap cmap = 0; + Cardinal depth = 0; + Arg av[20]; + int ac; + Widget chrome; + + XtVaGetValues (parent, + XtNvisual, &v, + XtNcolormap, &cmap, + XtNdepth, &depth, + 0); + + ac = 0; + XtSetArg (av[ac], XmNvisual, v); ac++; + XtSetArg (av[ac], XmNdepth, depth); ac++; + XtSetArg (av[ac], XmNcolormap, cmap); ac++; + XtSetArg (av[ac], XmNallowShellResize, TRUE); ac++; + XtSetArg (av[ac], XmNtransientFor, parent); ac++; + if (modal) { + XtSetArg (av[ac], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ac++; + } + XtSetArg (av[ac], XmNdeleteResponse, XmDESTROY); ac++; + XtSetArg (av[ac], XmNautoUnmanage, False); ac++; + + chrome = XmCreateFormDialog(parent, name, av, ac); + + return chrome; +} + diff --git a/mozilla/cmd/xfe/src/ViewDashBDlg.h b/mozilla/cmd/xfe/src/ViewDashBDlg.h new file mode 100644 index 00000000000..b6aaad70295 --- /dev/null +++ b/mozilla/cmd/xfe/src/ViewDashBDlg.h @@ -0,0 +1,72 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- + ViewDashBDlg.h -- View dialog with a dashboard. + Created: Tao Cheng , 27-apr-98. + */ + + + +#ifndef _VIEWDASHBDLG_H_ +#define _VIEWDASHBDLG_H_ + +#include "ViewDialog.h" +#include "Dashboard.h" + +class XFE_ViewDashBDlg : public XFE_ViewDialog +{ +public: + XFE_ViewDashBDlg(Widget parent, + char *name, + MWContext *context, + Boolean ok = True, + Boolean cancel = True, + Boolean help = False, + Boolean apply = False, + Boolean modal = False); + + virtual ~XFE_ViewDashBDlg(); + + +protected: + static Widget create_chrome_widget(Widget parent, + char *name, + Boolean ok, + Boolean cancel, + Boolean help, + Boolean apply, + Boolean separator, + Boolean modal); + + Widget createButtonArea(Widget parent, + Boolean ok, Boolean cancel, + Boolean help, Boolean apply); + + virtual void attachView(); + + XFE_Dashboard *m_dashboard; + Widget m_aboveButtonArea; + Widget m_okBtn; + +private: + Widget m_buttonArea; +}; + +#endif /* _VIEWDASHBDLG_H_ */ diff --git a/mozilla/cmd/xfe/src/XmLFolderDialog.cpp b/mozilla/cmd/xfe/src/XmLFolderDialog.cpp new file mode 100644 index 00000000000..85c84f7cb9e --- /dev/null +++ b/mozilla/cmd/xfe/src/XmLFolderDialog.cpp @@ -0,0 +1,138 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1996 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- + XmLFolderDialog.cpp -- class definition for XmLFolderDialog + Created: Tao Cheng , 12-nov-96 + */ + +#include "XmLFolderDialog.h" +#include "XmLFolderView.h" + +// +// This is the dialog it self +// +XFE_XmLFolderDialog::XFE_XmLFolderDialog(XFE_View *view, + Widget parent, + char *name, + MWContext *context, + Boolean ok, + Boolean cancel, + Boolean help, + Boolean apply, + Boolean separator, + Boolean modal): + XFE_ViewDialog(view, + parent, + name, + context, + ok, + cancel, + help, + apply, + separator, + modal) +{ + + /* 1. Dialog frame is created in XFE_ViewDialog::XFE_ViewDialog + */ + m_clientData = 0; + + /* 2. Create a folder view. m_chrome is the dialog widget + */ + XFE_XmLFolderView *folderview = + new XFE_XmLFolderView(this /* topComponent */, m_chrome, + 0, 0); + + folderview->show(); + + // + // we don't want a default value, since return does other stuff for + // us. + XtVaSetValues(m_chrome, /* the dialog */ + XmNdefaultButton, NULL, + NULL); + + /* set and show view + */ + setView(folderview); + + +} + +XFE_XmLFolderDialog::~XFE_XmLFolderDialog() +{ + /* 100892: Caldera: address book problem causes continuous loop + * reset flag to leave fe_eventloop() + */ + if (m_clientData) + *m_clientData = (ANS_t) eCANCEL; +} + +void XFE_XmLFolderDialog::setDlgValues() +{ + if (m_view) + ((XFE_XmLFolderView *) m_view)->setDlgValues(); +#if defined(DEBUG_tao_) + else + printf("\n***[XFE_XmLFolderDialog::setDlgValues] m_view is not set!"); +#endif +} + +void XFE_XmLFolderDialog::getDlgValues() +{ + if (m_view) + ((XFE_XmLFolderView *) m_view)->getDlgValues(); +#if defined(DEBUG_tao_) + else + printf("\n***[XFE_XmLFolderDialog::setDlgValues] m_view is not set!"); +#endif +} + +void XFE_XmLFolderDialog::cancel() +{ + // default setting + if (m_clientData) + *m_clientData = (ANS_t) eCANCEL; + else + hide(); +} + +void XFE_XmLFolderDialog::apply() +{ + + // default setting + if (m_clientData) + *m_clientData = (ANS_t) eAPPLY; + + if (m_view) + ((XFE_XmLFolderView *) m_view)->apply(); +} + +void XFE_XmLFolderDialog::ok() +{ + // default setting + if (m_clientData) + *m_clientData = (ANS_t) eAPPLY; + + apply(); + + if (!m_clientData && m_okToDestroy) + hide(); +} diff --git a/mozilla/cmd/xfe/src/XmLFolderDialog.h b/mozilla/cmd/xfe/src/XmLFolderDialog.h new file mode 100644 index 00000000000..47b6e573654 --- /dev/null +++ b/mozilla/cmd/xfe/src/XmLFolderDialog.h @@ -0,0 +1,68 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1996 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- + XmLFolderDialog.h -- class definition for XmLFolderDialog + Created: Tao Cheng , 12-nov-96 + */ + +#ifndef _xfe_xmlfolderdialog_h +#define _xfe_xmlfolderdialog_h + +#include "ViewDialog.h" + +// This is a general wrapper of dialog with XmLFloder widget +class XFE_XmLFolderDialog: public XFE_ViewDialog { +public: + XFE_XmLFolderDialog(XFE_View *view, /* the parent view */ + Widget parent, + char *name, + MWContext *context, + Boolean ok = TRUE, + Boolean cancel = TRUE, + Boolean help = TRUE, + Boolean apply = TRUE, + Boolean separator = TRUE, + Boolean modal = TRUE); + + virtual ~XFE_XmLFolderDialog(); + + virtual void setDlgValues(); + virtual void getDlgValues(); + + typedef enum { + eWAITING = 0, eCANCEL, eAPPLY, eOTHERS + } ANS_t; + + // + void setClientData(ANS_t *data) {m_clientData = data;} + +protected: + virtual void cancel(); + virtual void apply(); + virtual void ok(); + + ANS_t *m_clientData; + +private: + + +}; /* XFE_XmLFolderDialog */ + +#endif /* _xfe_xmlfolderdialog_h */ diff --git a/mozilla/cmd/xfe/src/XmLFolderView.cpp b/mozilla/cmd/xfe/src/XmLFolderView.cpp new file mode 100644 index 00000000000..6876668568c --- /dev/null +++ b/mozilla/cmd/xfe/src/XmLFolderView.cpp @@ -0,0 +1,141 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1996 + * Netscape Communications Corporation. All Rights Reserved. + */ + + +/* -*- Mode: C++; tab-width: 4 -*- + XmLFolderView.cpp -- class definition for XmLFolderView + Created: Tao Cheng , 12-nov-96 + */ + +#include "XmLFolderView.h" +#include "TabView.h" +#include + +XFE_XmLFolderView::XFE_XmLFolderView(XFE_Component *top, + Widget parent, /* the dialog */ + int *tabNameId, int nTabs): + XFE_MNView(top, NULL, NULL, NULL) +{ + /* 1. Create the folder + */ + + Widget folder = XtCreateWidget("xmlFolder", + xmlFolderWidgetClass, + parent, NULL, 0); + XtAddCallback(folder, + XmNactivateCallback, XFE_XmLFolderView::tabActivateCB, this); + + /* set base widget + */ + setBaseWidget(folder); + + /* 2. Create tab view + */ + if (tabNameId) + for (int i=0; i < nTabs; i++) { + XFE_XmLTabView *tab = new XFE_XmLTabView(top, this, + tabNameId[i]); + addView(tab); + tab->show(); + }/* for i */ +#if defined(DEBUG_tao_) + else { + printf("\n Warning: [XFE_XmLFolderView::XFE_XmLFolderView]tabNameId is NULL!!"); + }/* else */ +#endif + + /* set pos 0 as the default active tab + */ + tabActivate(0); +} + + +XFE_XmLFolderView::~XFE_XmLFolderView() +{ +} + +void +XFE_XmLFolderView::tabActivate(int pos) +{ + getDlgValues(); + /* set active view + */ + if (m_subviews && m_subviews[pos]) { + /* if need to handle ex-active tab + */ + + /* reset activeTab + */ + m_activeTab = (XFE_XmLTabView *)m_subviews[pos]; + m_activeTab->setDlgValues(); + }/* if */ +} + +void +XFE_XmLFolderView::tabActivateCB(Widget /*w*/, + XtPointer clientData, + XtPointer callData) +{ + XFE_XmLFolderView *obj = (XFE_XmLFolderView *)clientData; + XmLFolderCallbackStruct *cbs = (XmLFolderCallbackStruct*)callData; + + obj->tabActivate(cbs->pos); +} + +XFE_XmLTabView* XFE_XmLFolderView::addTab(int tabNameId) +{ + XFE_XmLTabView *tab = new XFE_XmLTabView(getToplevel(), this, + tabNameId); + addView(tab); + tab->show(); + return tab; +}/* XFE_XmLFolderView::addTab */ + +void XFE_XmLFolderView::addTab(XFE_XmLTabView* tab, XP_Bool show) +{ + addView(tab); + if (show) tab->show(); +}/* XFE_XmLFolderView::addTab */ + +void XFE_XmLFolderView::setDlgValues() +{ + if (m_subviews && m_numsubviews) { + for (int i=0; i < m_numsubviews; i++) { + ((XFE_XmLTabView *) m_subviews[i])->setDlgValues(); + }/* for i */ + }/* if */ +}/* XFE_XmLFolderView::setDlgValues() */ + +void XFE_XmLFolderView::getDlgValues() +{ + if (m_subviews && m_numsubviews) { + for (int i=0; i < m_numsubviews; i++) { + ((XFE_XmLTabView *) m_subviews[i])->getDlgValues(); + }/* for i */ + }/* if */ +}/* XFE_XmLFolderView::getDlgValues() */ + +void XFE_XmLFolderView::apply() +{ + if (m_subviews && m_numsubviews) { + for (int i=0; i < m_numsubviews; i++) { + ((XFE_XmLTabView *) m_subviews[i])->apply(); + }/* for i */ + }/* if */ +}/* XFE_XmLFolderView::apply() */ diff --git a/mozilla/cmd/xfe/src/XmLFolderView.h b/mozilla/cmd/xfe/src/XmLFolderView.h new file mode 100644 index 00000000000..b33b952bc01 --- /dev/null +++ b/mozilla/cmd/xfe/src/XmLFolderView.h @@ -0,0 +1,64 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1996 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- + XmlFolderView.h -- class definition for XFE_XmlFolderView + Created: Tao Cheng , 12-nov-96 + */ + +#ifndef _xfe_xmlfolderview_h +#define _xfe_xmlfolderview_h + +#include "MNView.h" + +class XFE_XmLTabView; + +// This is a general wrapper of XmLFloder widget +class XFE_XmLFolderView: public XFE_MNView { + +public: + XFE_XmLFolderView(XFE_Component *top, /* the parent folderDialog */ + Widget parent, + int *tabNameId, + int nTabs); + virtual ~XFE_XmLFolderView(); + + // + XFE_XmLTabView* addTab(int tabNameId); + void addTab(XFE_XmLTabView* tab, XP_Bool show=TRUE); + + virtual void setDlgValues(); + virtual void getDlgValues(); + virtual void apply(); + +protected: + + // invoked when you switch tabs + virtual void tabActivate(int pos); + static void tabActivateCB(Widget, XtPointer, XtPointer); + +private: + /* m_widget is the folder widget + */ + /* m_subviews contains the sub folder view: XFE_FolderView + */ + XFE_XmLTabView *m_activeTab; +}; /* XFE_XmLFolderView */ + +#endif /* _xfe_xmlfolderview_h */ diff --git a/mozilla/cmd/xfe/src/date_term.h b/mozilla/cmd/xfe/src/date_term.h new file mode 100644 index 00000000000..51c3be4d84a --- /dev/null +++ b/mozilla/cmd/xfe/src/date_term.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +/* -*- Mode: C++; tab-width: 4 -*- + date_term.h -- the common headers for mail filter and messsage search + date terms + Created: Seth Spitzer , 06-15-1998 + */ + +#ifndef _xfe_date_term_h_ +#define _xfe_date_term_h_ + +extern int XFE_SEARCH_INVALID_DATE; + +/* sspitzer: + * we aren't using these yet. But we will eventually. +extern int XFE_SEARCH_INVALID_MONTH; +extern int XFE_SEARCH_INVALID_DAY; +extern int XFE_SEARCH_INVALID_YEAR; + */ + +/* sspitzer: + * part of fix for bug: #138740 + * + * MAX_BYTES_NEEDED_FOR_XP_SEARCH_DATE_FORMAT needs to be >= the + * maximum size strftime(x,y,XP_GetString(XFE_SEARCH_DATE_FORMAT), z) + * could return. for "%m/%d/%Y", example: "01/01/1998" + * which needs 11 bytes, strlen("01/01/1998") + one for '\0' + * + * so if you change XFE_SEARCH_DATE_FORMAT (in ns/cmd/xfe/xfe_err.h) + * you better change MAX_BYTES_NEEDED_FOR_XP_SEARCH_DATE_FORMAT + */ +#define MAX_BYTES_NEEDED_FOR_XP_SEARCH_DATE_FORMAT 40 + +#endif /* _xfe_date_term_h_ */ diff --git a/mozilla/cmd/xfe/src/main.cpp b/mozilla/cmd/xfe/src/main.cpp new file mode 100644 index 00000000000..7e90bbe06bb --- /dev/null +++ b/mozilla/cmd/xfe/src/main.cpp @@ -0,0 +1,24 @@ + +#include "FolderFrame.h" +#include "MsgFrame.h" +#include "ThreadFrame.h" +#include "AddrBookFrame.h" + +Widget toplevel; +XtAppContext theApp; + +int +main(int argc, + char **argv) +{ + toplevel = XtAppInitialize(&theApp, "Mozilla", NULL, 0, &argc, argv, NULL, NULL, 0); + + fe_showFolders(toplevel); + fe_showMsg(toplevel); + fe_showThread(toplevel); + fe_showAddrBook(toplevel); + + XtAppMainLoop(theApp); + + return 0; // keep cc happy +} diff --git a/mozilla/cmd/xfe/xfe_err.h b/mozilla/cmd/xfe/xfe_err.h index c9209e726ba..11c805d0e0a 100644 --- a/mozilla/cmd/xfe/xfe_err.h +++ b/mozilla/cmd/xfe/xfe_err.h @@ -1563,6 +1563,11 @@ ResDef(XFE_NEWSSERVER_DEFAULT, XFE_ERR_OFFSET+631, " (default)") ResDef(XFE_FORWARD_INLINE, XFE_ERR_OFFSET+632, "Inline") ResDef(XFE_FORWARD_QUOTED, XFE_ERR_OFFSET+633, "Quoted") ResDef(XFE_FORWARD_ATTACH, XFE_ERR_OFFSET+634, "As Attachment") + +ResDef(XFE_SUBUPGRADE_AUTOSUBSCRIBE, XFE_ERR_OFFSET+635, "Automatically subscribe to all my folders on \"%s\"") + +ResDef(XFE_MOVEMAIL_TAB, XFE_ERR_OFFSET + 636, "MoveMail") + END_STR(mcom_cmd_xfe_xfe_err_h_strings) #endif /* __XFE_XFE_ERR_H_ */ diff --git a/mozilla/config/mac/DefinesMozilla.h b/mozilla/config/mac/DefinesMozilla.h index 32d56e27a8d..c959b8f8247 100644 --- a/mozilla/config/mac/DefinesMozilla.h +++ b/mozilla/config/mac/DefinesMozilla.h @@ -60,6 +60,3 @@ #define ZIP_NAME "java"##VERSION_NUMBER */ -// 97/05/05 jrm -- use phil's new search scope api -#define B3_SEARCH_API 1 - diff --git a/mozilla/include/abcom.h b/mozilla/include/abcom.h index cfd9794acc0..b8959616611 100644 --- a/mozilla/include/abcom.h +++ b/mozilla/include/abcom.h @@ -46,6 +46,9 @@ XP_BEGIN_PROTOS /* #define FE_IMPLEMENTS_VISIBLE_NC */ /* as each platform uses the new API, we should set this flag */ /* #define MOZ_NEWADDR */ /* FE address book developers should uncomment this flag. Leave it commented for the tree */ +/* XXX - It dun't work without it */ +#define MOZ_NEWADDR 1 + const ABID AB_ABIDUNKNOWN = 0; /* Find a better home for this somewhere! */ /* new errors added by mscott for the 2 pane AB UI. I will eventually name this enumerated type AB_Error */ diff --git a/mozilla/include/msg_srch.h b/mozilla/include/msg_srch.h index 999b389a585..cd7dc155b0d 100644 --- a/mozilla/include/msg_srch.h +++ b/mozilla/include/msg_srch.h @@ -28,7 +28,6 @@ #include "dirprefs.h" /* for DIR_AttributeId */ #define FE_IMPLEMENTS_BOOLEAN_OR -#define B3_SEARCH_API typedef enum { diff --git a/mozilla/include/net.h b/mozilla/include/net.h index f068033b2d5..e1cca48297b 100644 --- a/mozilla/include/net.h +++ b/mozilla/include/net.h @@ -1085,6 +1085,11 @@ extern Bool NET_IsURLInMemCache(URL_Struct *URL_s); */ extern XP_Bool NET_IsLocalFileURL(char *address); + +/* read the Cache File allocation table. + */ +extern void NET_ReadCacheFAT(char * cachefatfile, XP_Bool stat_files); + /* unload the disk cache FAT list to disk * * set final_call to true if this is the last call to diff --git a/mozilla/include/netcburl.h b/mozilla/include/netcburl.h index e69de29bb2d..45c3dd47ea5 100644 --- a/mozilla/include/netcburl.h +++ b/mozilla/include/netcburl.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* + This file defines the way of getting a URL that you can put into generated + HTML. If the user accesses that URL, then the callback function you + provided will get called with the data you provided. + + Currently, this is all implemented in lib/libnet/mkcburl.c. +*/ + +/* make sure we only include this once */ +#ifndef _NET_CBURL_H_ +#define _NET_CBURL_H_ + +XP_BEGIN_PROTOS + +typedef void (*NET_CallbackURLFunc)(void* closure, const char* url); + + +/* Creates a callback URL. Returns a string which is a valid URL that you + can emit into an HTML stream. (Returns NULL on out-of-memory error.) + The returned string must be freed using XP_FREE(). If the returned URL + is ever processed, then the given function will be called with the given + closure. The actual URL string is also passed, so that the function + can process any form data and the like. */ + +char* +NET_CallbackURLCreate(NET_CallbackURLFunc func, void* closure); + + + +/* Informs the callback system that the given function and closure are no + longer valid. This frees all storage associated with the above call, + and must be called at some point or a memory leak will result. */ + +int +NET_CallbackURLFree(NET_CallbackURLFunc func, void* closure); + +XP_END_PROTOS + +#endif /* _NET_CBURL_H_ */ diff --git a/mozilla/js/ref/macbuild/JavaSession/JavaSession.cpp b/mozilla/js/ref/macbuild/JavaSession/JavaSession.cpp new file mode 100644 index 00000000000..08bec3e5b8a --- /dev/null +++ b/mozilla/js/ref/macbuild/JavaSession/JavaSession.cpp @@ -0,0 +1,154 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* + JavaSession.cpp + + Uses MRJ to open a Java VM. + + by Patrick C. Beard. + */ + +#include "JavaSession.h" + +#include +#include +#include + +#include + +extern "C" { + +static void java_stdout(JMSessionRef session, const void *message, UInt32 messageLengthInBytes); +static void java_stderr(JMSessionRef session, const void *message, UInt32 messageLengthInBytes); +static SInt32 java_stdin(JMSessionRef session, void *buffer, SInt32 maxBufferLength); +static Boolean java_exit(JMSessionRef session, int value); +static Boolean java_authenticate(JMSessionRef session, const char *url, const char *realm, + char userName[255], char password[255]); +static void java_lowmem(JMSessionRef session); + +} + +static JMSessionCallbacks callbacks = { + kJMVersion, /* should be set to kJMVersion */ + &java_stdout, /* JM will route "stdout" to this function. */ + &java_stderr, /* JM will route "stderr" to this function. */ + &java_stdin, /* read from console - can be nil for default behavior (no console IO) */ + &java_exit, /* handle System.exit(int) requests */ + &java_authenticate, /* present basic authentication dialog */ + &java_lowmem /* Low Memory notification Proc */ +}; + +JavaSession::JavaSession() : mSession(NULL) +{ + OSStatus status = ::JMOpenSession(&mSession, /* eDisableJITC */ eJManager2Defaults, eCheckRemoteCode, + &callbacks, kTextEncodingMacRoman, NULL); + checkStatus(status); +} + +JavaSession::~JavaSession() +{ + if (mSession != NULL) { + OSStatus status = ::JMCloseSession(mSession); + checkStatus(status); + } +} + +JNIEnv* JavaSession::getEnv() +{ + return ::JMGetCurrentEnv(mSession); +} + +static StringPtr c2p(const char* str, StringPtr pstr) +{ + unsigned char len = strlen(str); + memcpy(pstr + 1, str, len); + *pstr = len; + return pstr; +} + +jclass JavaSession::getClass(const char* className) +{ + JNIEnv* env = getEnv(); + jclass result = env->FindClass(className); + if (result == NULL) throw OSStatusException(fnfErr); +/* if (result == NULL) { + Str255 pClassName; + c2p(className, pClassName); + Handle classHandle = ::GetNamedResource(ClassResType, pClassName); + if (classHandle != NULL) { + ::HLock(classHandle); + result = env->DefineClass(className, NULL, (signed char*)*classHandle, ::GetHandleSize(classHandle)); + ::ReleaseResource(classHandle); + } + } */ + return result; +} + +/** + * Adds a Mac-style path name to the MRJ class path. + */ +void JavaSession::addClassPath(const char* jarFilePath) +{ + Str255 pJarFilePath; + FSSpec jarFileSpec; + OSStatus status = FSMakeFSSpec( 0, 0, // use "current working directory" + c2p(jarFilePath, pJarFilePath), + &jarFileSpec); + checkStatus(status); + status = JMAddToClassPath(mSession, &jarFileSpec); + checkStatus(status); +} + +// OBLIGATORY JMSession callbacks. + +static void java_stdout(JMSessionRef session, const void *message, UInt32 messageLengthInBytes) +{ + char* msg = (char*)message; + while (messageLengthInBytes--) { + char c = *msg++; + if (c == '\r') + c = '\n'; + fputc(c, stdout); + } +} + +static void java_stderr(JMSessionRef session, const void *message, UInt32 messageLengthInBytes) +{ + char* msg = (char*)message; + while (messageLengthInBytes--) { + char c = *msg++; + if (c == '\r') + c = '\n'; + fputc(c, stderr); + } +} + +static SInt32 java_stdin(JMSessionRef session, void *buffer, SInt32 maxBufferLength) { return -1; } + +static Boolean java_exit(JMSessionRef session, int value) { return false; } + +static Boolean java_authenticate(JMSessionRef session, const char *url, const char *realm, + char userName[255], char password[255]) +{ + return true; +} + +static void java_lowmem(JMSessionRef session) +{ +} diff --git a/mozilla/js/ref/macbuild/JavaSession/JavaSession.h b/mozilla/js/ref/macbuild/JavaSession/JavaSession.h new file mode 100644 index 00000000000..4ad066d8683 --- /dev/null +++ b/mozilla/js/ref/macbuild/JavaSession/JavaSession.h @@ -0,0 +1,46 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* + JavaSession.h + + Uses MRJ to open a Java VM. + + by Patrick C. Beard + */ + +#pragma once + +#include +#include + +#include "OSStatusException.h" + +class JavaSession { +public: + JavaSession(); + ~JavaSession(); + + JNIEnv* getEnv(); + jclass getClass(const char* className); + + void addClassPath(const char* jarFilePath); + +private: + JMSessionRef mSession; +}; diff --git a/mozilla/js/ref/macbuild/JavaSession/OSStatusException.h b/mozilla/js/ref/macbuild/JavaSession/OSStatusException.h new file mode 100644 index 00000000000..3a7c37abf27 --- /dev/null +++ b/mozilla/js/ref/macbuild/JavaSession/OSStatusException.h @@ -0,0 +1,46 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* + OSStatusException.h + + MacOS OSStatus exception. + + by Patrick C. Beard. + */ + +#pragma once + +#ifndef __TYPES__ +#include +#endif + +class OSStatusException { +public: + OSStatusException(OSStatus status) : mStatus(status) {} + OSStatus getStatus() { return mStatus; } + +private: + OSStatus mStatus; +}; + +inline void checkStatus(OSStatus status) +{ + if (status != noErr) + throw OSStatusException(status); +} diff --git a/mozilla/lib/libmsg/msgimap.cpp b/mozilla/lib/libmsg/msgimap.cpp index db2a0ccb48c..b053eb930f6 100644 --- a/mozilla/lib/libmsg/msgimap.cpp +++ b/mozilla/lib/libmsg/msgimap.cpp @@ -51,6 +51,7 @@ #include "msgundmg.h" #include "prlong.h" #include "msgbiff.h" +#include "netutils.h" extern "C" { diff --git a/mozilla/lib/libmsg/msgmdn.cpp b/mozilla/lib/libmsg/msgmdn.cpp index 8fb343850af..a4680427f2d 100644 --- a/mozilla/lib/libmsg/msgmdn.cpp +++ b/mozilla/lib/libmsg/msgmdn.cpp @@ -31,6 +31,9 @@ #include "msgurlq.h" #include "gui.h" #include "msgprefs.h" +#include "mkutils.h" +#include "mktcp.h" +#include "netutils.h" extern "C" { diff --git a/mozilla/lib/libmsg/msgprefs.cpp b/mozilla/lib/libmsg/msgprefs.cpp index 6b6a30bd661..a4619c2e3b1 100644 --- a/mozilla/lib/libmsg/msgprefs.cpp +++ b/mozilla/lib/libmsg/msgprefs.cpp @@ -30,7 +30,12 @@ #include "pmsgfilt.h" #include "msgimap.h" #include "imaphost.h" -#include "mkreg.h" + +extern "C" +{ + #include "mkreg.h" +} + #include "ptrarray.h" #ifdef XP_MAC diff --git a/mozilla/lib/libmsg/msgsend.cpp b/mozilla/lib/libmsg/msgsend.cpp index 1d325dc847b..f4e8a51f750 100644 --- a/mozilla/lib/libmsg/msgsend.cpp +++ b/mozilla/lib/libmsg/msgsend.cpp @@ -39,7 +39,7 @@ #include "msgprefs.h" #include "msgmast.h" #include "msgcpane.h" -#ifdef MOZ_SECURITY +#ifndef NO_SECURITY #include HG02902 #endif #include "grpinfo.h" @@ -1416,10 +1416,12 @@ MSG_RegisterConverters (void) NULL, MIME_VCardConverter); /* Decoders from mimejul.c for text/calendar */ +#ifdef MOZ_CALENDAR NET_RegisterContentTypeConverter (TEXT_CALENDAR, FO_PRESENT, NULL, MIME_JulianConverter); NET_RegisterContentTypeConverter (TEXT_CALENDAR, FO_PRINT, NULL, MIME_JulianConverter); +#endif } diff --git a/mozilla/modules/calendar/src/libcal/Makefile b/mozilla/modules/calendar/src/libcal/Makefile index 67596f73191..6dba172877a 100644 --- a/mozilla/modules/calendar/src/libcal/Makefile +++ b/mozilla/modules/calendar/src/libcal/Makefile @@ -103,7 +103,9 @@ NSTOM_DEPTH = $(NS_DEPTH)/../mozilla INCLUDES += -I./ical -I./itip -I./ui -I./util -I./htmlform/include \ -I$(NSTOM_DEPTH)/dist/public/util \ -I$(NSTOM_DEPTH)/dist/public/img \ - -I$(NSTOM_DEPTH)/dist/public/layer + -I$(NSTOM_DEPTH)/dist/public/layer \ + -I$(NSTOM_DEPTH)/libnls/headers/ + diff --git a/mozilla/modules/calendar/src/libcal/itip/txnobj.h b/mozilla/modules/calendar/src/libcal/itip/txnobj.h index f5b666e7f3c..de0a2755f91 100644 --- a/mozilla/modules/calendar/src/libcal/itip/txnobj.h +++ b/mozilla/modules/calendar/src/libcal/itip/txnobj.h @@ -217,8 +217,7 @@ public: -------------------------------- propList is list of properties to return in events stream is CAPIStream to read/write data*/ - /* - + /*TODO: newer CAPI interface*/ /* virtual CAPIStatus handleCAPI(CAPISession * s, CAPIHandle ** ppH, diff --git a/mozilla/modules/security/freenav/rosetta.h b/mozilla/modules/security/freenav/rosetta.h index 6f0ff43dead..18161e882c1 100644 --- a/mozilla/modules/security/freenav/rosetta.h +++ b/mozilla/modules/security/freenav/rosetta.h @@ -15,6 +15,9 @@ * Reserved. */ +#ifndef _ROSETTA_H_ +#define _ROSETTA_H_ + #define HG87729 #define HG92192 FALSE #define HG21872 @@ -227,15 +230,15 @@ #define HG87237 #define HG52423 #define HG76373 TRUE -#define HG72388 FALSE; -#define HG52223 FALSE; +#define HG72388 TRUE; +#define HG52223 TRUE; #define HG53535 #define HG52528 FALSE; #define HG73277 FALSE #define HG73738 #define HG62522 #define HG62363 -#define HG24242 +#define HG24242 this_entry->protocol = HTTP_TYPE_URL; #define HG42469 #define HG87376 #define HG76363 @@ -331,13 +334,13 @@ #define HG20476 #define HG87358 #define HG93765 -#define HG92743 +#define HG92743 Bool dummy, #define HG83665 -#define HG98376 +#define HG98376 FALSE, #define HG92362 #define HG26300 -#define HG27229 "mkfile.h" -#define HG27230 +#define HG27229 "rosetta.h" +#define HG27230 FALSE, #define HG27326 "mkutils.h" #define HG32828 #define HG26363 "msgnet.h" @@ -362,13 +365,13 @@ #define HG23833 #define HG28330 #define HG10299 "gui.h" -#define HG10300 +#define HG10300 FALSE, #define HG82332 #define HG32949 #define HG38737 #define HG29239 #define HG28287 -#define HG22999 +#define HG22999 FALSE, #define HG93230 #define HG29399 #define HG20900 @@ -380,14 +383,16 @@ #define HG23298 #define HG29898 #define HG22201 -#define HG38738 -#define HG02873 +#define HG38738 FALSE, +#define HG02873 FALSE, #define HG92892 #define HG92871 #define HG82772 #define HG03903 #define HG82773 FALSE #define HG22087 +#define HG22088 +#define HG22089 FALSE #define HG21092 #define HG09309 #define HG29802 0 @@ -399,7 +404,7 @@ #define HG29398 #define HG83273 #define HG09438 "mkpop3.h" -#define HG09439 +#define HG09439 FALSE, #define HG23535 "mktcp.h" #define HG27655 #define HG83763 @@ -427,6 +432,7 @@ #define HG75442 #define HG59731 #define HG56898 +#define HG78111 FALSE /* WINDOWS FRONT END */ @@ -488,6 +494,7 @@ #define HG79266 #define HG72611 #define HG72625 +#define HG47991 #define HG27367 #define HG72671 #define HG72729 ss = XP_GetString(XFE_SECURITY_DISABLED); @@ -609,31 +616,63 @@ #define HG21182 #define HG02030 #define HG93649 - - - - - - - - - - - - - - - - - - - - - - - - - +#define HG18966 +#define HG27311 +#define HG89217 +#define HG82167 +#define HG21761 +#define HG10280 +#define HG19271 +#define HG10977 +#define HG19711 +#define HG19189 +#define HG71676 +#define HG71851 +#define HG78266 +#define HG18177 +#define HG18760 +#define HG71661 +#define HG90177 +#define HG79299 +#define HG21897 m_port_text +#define HG17661 m_port_text +#define HG18261 +#define HG19877 +#define HG19871 +#define HG19773 +#define HG18162 +#define HG13181 +#define HG14871 FALSE +#define HG19861 FALSE +#define HG21511 +#define HG19721 +#define HG19511 +#define HG17271 +#define HG18671 +#define HG19616 +#define HG19879 +#define HG17922 +#define HG98219 SetDlgItemInt(IDC_EDIT_PORT_NO, LDAP_PORT); +#define HG92710 +#define HG27626 +#define HG28981 +#define HG72186 +#define HG92672 +#define HG90271 +#define HG92177 +#define HG98216 +#define HG91761 SetDlgItemInt(IDC_EDIT_PORT_NO, LDAP_PORT); + +#define HG51387 +#define HG51388 +#define HG51389 + +#define HG43287 +#define HG43288 + + + +#endif /* _ROSETTA_H_ */ diff --git a/mozilla/network/cache/extcache.c b/mozilla/network/cache/extcache.c index bf00a1c7399..50a6d2f1000 100644 --- a/mozilla/network/cache/extcache.c +++ b/mozilla/network/cache/extcache.c @@ -33,6 +33,8 @@ * Modifications/Addition: Gagan Saksena, 97 */ +#include "rosetta.h" + #ifndef EXT_DB_ROUTINES #include "secnav.h" #include "sechash.h" @@ -188,7 +190,7 @@ total_size += old_obj->string ? PL_strlen(old_obj->string)+1 : 0 ADD_STRING_SIZE(charset); ADD_STRING_SIZE(filename); total_size += sizeof(uint32); /* size of secinfo */ - total_size += SECNAV_SSLSocketStatusLength(old_obj->sec_info); + total_size += HG73653(old_obj->sec_info); ADD_STRING_SIZE(page_services_url); #undef ADD_STRING_SIZE @@ -220,8 +222,8 @@ total_size += old_obj->string ? PL_strlen(old_obj->string)+1 : 0 * char * filename; * int32 filename_len; * - * int32 security_on; - * unsigned char * sec_info; + * int32 HG52242; + * unsigned char * HG42320; * * int32 method; * @@ -291,6 +293,7 @@ total_size += old_obj->string ? PL_strlen(old_obj->string)+1 : 0 COPY_INT32((void *)cur_ptr, &total_size); cur_ptr = cur_ptr + sizeof(int32); + HG73209 /* put the version number of the structure * format that we are using * By using a version string when writting @@ -315,12 +318,13 @@ total_size += old_obj->string ? PL_strlen(old_obj->string)+1 : 0 STUFF_BOOL(is_relative_path); - STUFF_NUMBER(security_on); + HG98379 #ifndef EXT_DB_ROUTINES + HG42539 /* save the security info */ if ( old_obj->sec_info ) { - len = SECNAV_SSLSocketStatusLength(old_obj->sec_info); + len = HG65293(old_obj->sec_info); COPY_INT32((void *)cur_ptr, &len); cur_ptr = cur_ptr + sizeof(int32); @@ -484,6 +488,7 @@ net_DBDataToCacheStruct(DBT * db_obj) return(NULL); } + HG32839 RETRIEVE_TIMET(last_modified); RETRIEVE_TIMET(last_accessed); RETRIEVE_TIMET(expires); @@ -497,26 +502,7 @@ net_DBDataToCacheStruct(DBT * db_obj) RETRIEVE_BOOL(is_relative_path); - RETRIEVE_NUMBER(security_on); - - /* security info */ - if(cur_ptr > max_ptr) - return(rv); - COPY_INT32(&len, cur_ptr); - cur_ptr += sizeof(int32); - - if ( len == 0 ) { - rv->sec_info = NULL; - } else { - rv->sec_info = PR_Malloc(len); - if ( rv->sec_info == NULL ) { - return(rv); - } - - memcpy(rv->sec_info, cur_ptr, len); - cur_ptr += len; - } - + HG72761 RETRIEVE_NUMBER(method); @@ -636,8 +622,7 @@ net_GenCacheDBKey(char *address, char *post_data, int32 post_data_size) size += str_len; /* for address string */ size += sizeof(int32); /* for size of post_data */ - if(post_data_size) - size += MD5_HASH_SIZE; + HG42490 rv->size = size; rv->data = PR_Malloc(size); @@ -653,6 +638,7 @@ net_GenCacheDBKey(char *address, char *post_data, int32 post_data_size) /* put in the checksum size */ COPY_INT32(data_ptr, &size); data_ptr = data_ptr + sizeof(int32); + HG83777 /* put in the size of the address string */ COPY_INT32(data_ptr, &str_len); @@ -667,20 +653,7 @@ net_GenCacheDBKey(char *address, char *post_data, int32 post_data_size) *hash = '#'; /* put in the size of the post data */ - if(post_data_size) - { - int32 size_of_md5 = MD5_HASH_SIZE; - unsigned char post_data_hash[MD5_HASH_SIZE]; - - MD5_HashBuf(post_data_hash, (unsigned char*)post_data, post_data_size); - - COPY_INT32(data_ptr, &size_of_md5); - data_ptr = data_ptr + sizeof(int32); - - /* put in the post data if there is any */ - memcpy(data_ptr, post_data_hash, sizeof(post_data_hash)); - } - else + HG73699 { COPY_INT32(data_ptr, &post_data_size); data_ptr = data_ptr + sizeof(int32); @@ -703,6 +676,7 @@ net_GetAddressFromCacheKey(DBT *key) /* check for minimum size */ if(key->size < 10) return(NULL); + HG72873 /* validate size checksum */ data = (char *)key->data; diff --git a/mozilla/network/cache/mkcache.c b/mozilla/network/cache/mkcache.c index c984da988bb..3f19565d156 100644 --- a/mozilla/network/cache/mkcache.c +++ b/mozilla/network/cache/mkcache.c @@ -27,6 +27,7 @@ /* dbm code */ #include "mkcache.h" +#include "rosetta.h" #include "extcache.h" #include "mkgeturl.h" #include "netutils.h" @@ -36,7 +37,7 @@ #include "xp_mcom.h" #include "client.h" #include "mkstream.h" -#include "secnav.h" +#include HG65288 #include "mcom_db.h" #include "prclist.h" #include "prmem.h" @@ -72,7 +73,7 @@ extern int MK_CACHE_LAST_MODIFIED; extern int MK_CACHE_EXPIRES; extern int MK_CACHE_LAST_ACCESSED; extern int MK_CACHE_CHARSET; -extern int MK_CACHE_SECURE; +HG73891 extern int MK_CACHE_USES_RELATIVE_PATH; extern int MK_CACHE_FROM_NETSITE_SERVER; @@ -105,8 +106,7 @@ PRIVATE uint32 net_MaxMemoryCacheSize=0; /* trace variable for cache testing */ MODULE_PRIVATE PRBool NET_CacheTraceOn = PR_FALSE; - -PRIVATE PRBool net_dont_disk_cache_ssl = PR_FALSE; +HG76298 PRIVATE DB * cache_database = 0; /* PRIVATE XXX Mac CodeWarrior bug */ PRCList active_cache_data_objects = PR_INIT_STATIC_CLIST(&active_cache_data_objects); @@ -121,16 +121,7 @@ typedef struct _CacheDataObject { PRIVATE void net_RemoveAllDiskCacheObjects(void); -/* pass in TRUE to disable disk caching - * of SSL documents. - * pass in FALSE to enable disk cacheing - * of SSL documents - */ -PUBLIC void -NET_DontDiskCacheSSL(XP_Bool set) -{ - net_dont_disk_cache_ssl = set; -} +HG87325 PUBLIC PRBool NET_IsCacheTraceOn(void) @@ -222,7 +213,7 @@ net_GetDiskCacheSize(void) { net_NumberInDiskCache = 0; } - + HG63453 } @@ -314,6 +305,7 @@ net_OpenCacheFatDB(void) cache_database = NULL; } + HG98471 if(cache_database) { if(-1 == (*cache_database->sync)(cache_database, 0)) @@ -454,7 +446,7 @@ net_StoreDiskCacheSize(void) data.size = PL_strlen(DISK_CACHE_NAME)+1; data.data = DISK_CACHE_NAME; - + HG52897 (*cache_database->put)(cache_database, net_DiskCacheNameKey, &data, 0); } @@ -637,7 +629,7 @@ net_CacheStore(net_CacheObject * obj, /* create new dbt data object */ data = net_CacheStructToDBData(obj); - + HG73870 if(!key || !data) { TRACEMSG(("Failed to get key or data: malloc error probably")); @@ -884,6 +876,7 @@ NET_RemoveDiskCacheObjects(uint32 remove_num) DBT data; DBT key; + HG74380 /* * This optimization is not valid for the case of URLs that normally * would not be cached because their size exceeds the cache size, but @@ -1606,23 +1599,24 @@ NET_CacheConverter (FO_Present_Types format_out, * the cache database is open */ + HG52980 TRACEMSG(("cache: want_to_cache is %s", want_to_cache ? "TRUE" : "FALSE")); if(want_to_cache - && (!net_dont_disk_cache_ssl - || PL_strncasecmp(URL_s->address, "https:", 6)) + && (HG73896 + PL_strncasecmp(URL_s->address, "https:", 6)) && !URL_s->dont_cache && (net_MaxDiskCacheSize > 0 || URL_s->must_cache) && net_OpenCacheFatDB() /* make sure database is open */ && PL_strncasecmp(URL_s->address, "news:", 5) /* not news: */ - && PL_strncasecmp(URL_s->address, "snews:", 6) /* not snews: */ + HG73684 && PL_strncasecmp(URL_s->address, "mailbox://", 10)) /* not IMAP */ do_disk_cache = TRUE; /* Special case for StreamAsFile plugins: */ if (URL_s->must_cache /* this only set by plugin code ? */ && ((PL_strncasecmp(URL_s->address, "news:", 5)==0) /* is news */ - || (PL_strncasecmp(URL_s->address, "https:", 6)==0) /* is secure */ + HG83903 || !PL_strncasecmp(URL_s->address, "mailbox://", 10))) /* is imap */ do_disk_cache = TRUE; @@ -1667,10 +1661,7 @@ NET_CacheConverter (FO_Present_Types format_out, StrAllocCopy(cache_object->charset, URL_s->charset); StrAllocCopy(cache_object->content_encoding, URL_s->content_encoding); - /* copy security info */ - cache_object->security_on = URL_s->security_on; - cache_object->sec_info = SECNAV_CopySSLSocketStatus(URL_s->sec_info); - + HG32138 StrAllocCopy(cache_object->page_services_url, URL_s->page_services_url); /* we always use relative paths in the main disk cache @@ -1709,7 +1700,7 @@ NET_CacheConverter (FO_Present_Types format_out, else if ((URL_s->address) && (!PL_strncasecmp(URL_s->address, "mailbox:", 8) || !PL_strncasecmp(URL_s->address, "news:", 5) - || !PL_strncasecmp(URL_s->address, "snews:", 6)) + HG65294) && (URL_s->content_type) && (*(URL_s->content_type))) { /* @@ -2622,9 +2613,7 @@ NET_FindURLInCache(URL_Struct * URL_s, MWContext *ctxt) URL_s->last_modified = found_cache_obj->last_modified; URL_s->is_netsite = found_cache_obj->is_netsite; - /* copy security info */ - URL_s->security_on = found_cache_obj->security_on; - URL_s->sec_info = SECNAV_CopySSLSocketStatus(found_cache_obj->sec_info); + HG83267 TRACEMSG(("Cached copy is valid. returning method")); @@ -3284,11 +3273,7 @@ PUT_PART(buffer); } TABLE_BOTTOM; - TABLE_TOP( XP_GetString(MK_CACHE_SECURE) ); - sprintf(buffer, "%s", cache_obj->security_on ? - "TRUE" : "FALSE"); - PUT_PART(buffer); - TABLE_BOTTOM; + HG63287 TABLE_TOP( XP_GetString(MK_CACHE_USES_RELATIVE_PATH) ); sprintf(buffer, "%s", cache_obj->is_relative_path ? diff --git a/mozilla/network/cache/mkmemcac.c b/mozilla/network/cache/mkmemcac.c index 7b901f085d3..c90bed43862 100644 --- a/mozilla/network/cache/mkmemcac.c +++ b/mozilla/network/cache/mkmemcac.c @@ -23,6 +23,7 @@ */ /* Please leave outside of ifdef for windows precompiled headers */ +#include "rosetta.h" #include "xp.h" #ifdef MOZILLA_CLIENT @@ -996,10 +997,7 @@ NET_MemCacheConverter (FO_Present_Types format_out, goto malloc_failure; /* skip memory cacheing */ } - /* copy security info */ - memory_copy->cache_obj.security_on = URL_s->security_on; - memory_copy->cache_obj.sec_info = - SECNAV_CopySSLSocketStatus(URL_s->sec_info); + HG83363 /* build the stream object */ stream = PR_NEW(NET_StreamClass); @@ -1235,11 +1233,7 @@ NET_FindURLInMemCache(URL_Struct * URL_s, MWContext *ctxt) URL_s->last_modified = found_cache_obj->cache_obj.last_modified; URL_s->is_netsite = found_cache_obj->cache_obj.is_netsite; - /* copy security info */ - URL_s->security_on = found_cache_obj->cache_obj.security_on; - URL_s->sec_info = - SECNAV_CopySSLSocketStatus(found_cache_obj->cache_obj.sec_info); - + HG26557 net_cache_adding_object++; /* semaphore */ /* reorder objects so that the list is in last accessed order */ XP_ListRemoveObject(net_MemoryCacheList, found_cache_obj); @@ -1881,10 +1875,7 @@ PUT_PART(buffer); } TABLE_BOTTOM; - TABLE_TOP("Secure:"); - sprintf(buffer, "%s", cache_obj->security_on ? "TRUE" : "FALSE"); - PUT_PART(buffer); - TABLE_BOTTOM; + HG27328 PL_strcpy(buffer, "\n

\n"); PUT_PART(buffer); diff --git a/mozilla/network/client/Makefile b/mozilla/network/client/Makefile index 68d464abf4e..0dfe7aecf78 100644 --- a/mozilla/network/client/Makefile +++ b/mozilla/network/client/Makefile @@ -30,6 +30,6 @@ endif EXPORTS= cnetinit.h REQUIRES = netcache network img layer fileurl httpurl ftpurl abouturl \ - gophurl jsurl remoturl dataurl util + gophurl jsurl remoturl dataurl util security include $(DEPTH)/config/rules.mk diff --git a/mozilla/network/client/makefile.win b/mozilla/network/client/makefile.win index 3aed106133a..918077a02c0 100644 --- a/mozilla/network/client/makefile.win +++ b/mozilla/network/client/makefile.win @@ -60,7 +60,7 @@ INCLUDES = $(LOCAL_INCLUDES) EXTRA_LIBS= -REQUIRES= network +REQUIRES= network security # use LINCS on win32 for now since REQUIRES seems to be broken #!if "$(MOZ_BITS)" != "16" @@ -76,7 +76,8 @@ LINCS= \ -I$(XPDIST)\public\jsurl \ -I$(XPDIST)\public\certurl \ -I$(XPDIST)\public\remoturl \ - -I$(XPDIST)\public\marimurl + -I$(XPDIST)\public\marimurl \ + -I$(XPDIST)\public\security #!endif !endif diff --git a/mozilla/network/cnvts/cvjscfg.c b/mozilla/network/cnvts/cvjscfg.c index 46ef07eb440..99600424119 100644 --- a/mozilla/network/cnvts/cvjscfg.c +++ b/mozilla/network/cnvts/cvjscfg.c @@ -19,6 +19,254 @@ #include "cvjscfg.h" #include "cvsimple.h" #include "prefapi.h" +#include "mkutils.h" +#include "netutils.h" +#include "xpgetstr.h" +#include "jsapi.h" +#include "fe_proto.h" + +static XP_Bool m_GettingConfigFile = FALSE; +static XP_Bool m_FindProxyInJSC = FALSE; +static XP_Bool m_TimesUp = FALSE; + +extern int XP_CONFIG_BLAST_WARNING; +extern int XP_RECEIVING_PROXY_AUTOCFG; +extern int XP_AUTOADMIN_MISSING; +extern int XP_BAD_AUTOCONFIG_NO_FAILOVER; +extern int XP_GLOBAL_EVEN_SAVED_IS_BAD; +extern int XP_CONF_LOAD_FAILED_USE_PREV; + +extern void NET_DisableGetURL(void); + +PRIVATE int jsc_try_failover(MWContext*); +PRIVATE XP_Bool jsc_check_for_find_proxy(void); +extern XP_Bool NET_InitPacfContext(void); + +PRIVATE void AutoAdminRefreshCallback(void *closure) +{ + NET_DownloadAutoAdminCfgFile(); +} + + +/* + * pref_DownloadDone + * Sets 'm_GettingConfigFile' to FALSE, and registers a timeout for + * jsc file polling. + */ +PRIVATE void +pref_DownloadDone(URL_Struct* URL_s, int status, MWContext* window_id) +{ + int32 minutes = 0; + + /* Might have been cancelled by a timeout */ + if ( m_GettingConfigFile == FALSE ) { + return; + } + + m_GettingConfigFile = FALSE; + + if (URL_s->server_status == 0) { + jsc_try_failover(window_id); + m_FindProxyInJSC = jsc_check_for_find_proxy(); + } + + PREF_GetConfigInt("autoadmin.refresh_interval", &minutes); + if ( minutes > 0 ) { + FE_SetTimeout((TimeoutCallbackFunction) AutoAdminRefreshCallback, + NULL, minutes * 60 * 1000); + } +} + + +/* + * AutoAdminTimeoutCallback + */ +PRIVATE void +AutoAdminTimeoutCallback(void *closure) +{ + m_TimesUp = TRUE; +} + + +/* + * PREF_DownloadConfigFile + * Get the global config file and execute it. + * Called at startup, and at the interval specified by + * "autoadmin.refresh_interval". + * The first time we get this file, we need to loop over FEU_StayingAlive(). + * After that, the main event loop will be happening, so we don't loop + * ourselves, or we'd freeze up the browser. + */ +PUBLIC void +NET_DownloadAutoAdminCfgFile() +{ + static XP_Bool first_time = TRUE; + XP_Bool append_email; + char* email_addr; + char* url = NULL; + MWContext* context; + +#ifdef MOZ_OFFLINE + if ( NET_IsOffline() ) return; +#endif + + if(first_time) + context = FE_GetInitContext(); + else + context = XP_FindSomeContext(); + + if ( first_time && (NET_InitPacfContext() == FALSE) ) return; + + PREF_CopyConfigString("autoadmin.global_config_url", &url); + + if (( url == NULL ) || (!(*url))) { + return; + } + + if ( !PREF_IsAutoAdminEnabled() ) { + XP_Bool failover = FALSE; + PREF_GetConfigBool("autoadmin.failover_to_cached", &failover); + if ( failover == FALSE ) { + NET_DisableGetURL(); + } + FE_Alert(context, XP_GetString(XP_AUTOADMIN_MISSING)); + return; + } + + PREF_GetConfigBool("autoadmin.append_emailaddr", &append_email); + if ( append_email ) { + StrAllocCat(url, "?"); + PREF_CopyCharPref("mail.identity.useremail", &email_addr); + if ( email_addr ) { + StrAllocCat(url, email_addr); + XP_FREE(email_addr); + } + } + + if ( url && *url ) { + URL_Struct* url_s = NET_CreateURLStruct(url, NET_SUPER_RELOAD); + + NET_GetURL(url_s, FO_JAVASCRIPT_CONFIG, context, pref_DownloadDone); + + m_GettingConfigFile = TRUE; + + if ( first_time ) { + int32 seconds; + first_time = FALSE; + /* Timeout so we don't hang here */ + PREF_GetConfigInt("autoadmin.timeout", &seconds); + if ( seconds > 0 ) { + FE_SetTimeout((TimeoutCallbackFunction) AutoAdminTimeoutCallback, + NULL, seconds * 1000); + } + while ( m_GettingConfigFile && !m_TimesUp ) { + FEU_StayingAlive(); + } + if ( m_TimesUp ) { + /* Give up, cause jsc download to fail */ + pref_DownloadDone(url_s, 0, context); + } + } + } +} + + +/* + * jsc_save + */ +PRIVATE void +jsc_save_config(char* bytes, int32 num_bytes) +{ + XP_File fp; + + if ( bytes == NULL || num_bytes <= 0 ) return; + + if( (fp = XP_FileOpen("", xpJSConfig, XP_FILE_WRITE)) == NULL ) return; + + XP_FileWrite(bytes, num_bytes, fp); + XP_FileClose(fp); +} + + +/* + * jsc_try_failover + */ +PRIVATE int +jsc_try_failover(MWContext* w) +{ + XP_Bool failover; + char* bytes = NULL; + int32 num_bytes; + XP_StatStruct st; + XP_File fp = NULL; + + PREF_GetConfigBool("autoadmin.failover_to_cached", &failover); + + if ( failover == FALSE ) { + FE_Alert(w, XP_GetString(XP_BAD_AUTOCONFIG_NO_FAILOVER)); + NET_DisableGetURL(); + return -1; + } + + if ( !FE_Confirm(w, XP_GetString(XP_CONF_LOAD_FAILED_USE_PREV)) ) return -1; + + if (XP_Stat("", &st, xpJSConfig) == -1) goto failed; + + if ( (fp = XP_FileOpen("", xpJSConfig, XP_FILE_READ)) == NULL ) goto failed; + + num_bytes = st.st_size; + + if ( num_bytes <= 0 ) goto failed; + + bytes = (char *)XP_ALLOC(num_bytes + 1); + + if ( bytes == NULL ) goto failed; + + if ( (num_bytes = XP_FileRead(bytes, num_bytes, fp)) <= 0 ) goto failed; + + bytes[num_bytes] = '\0'; + +#ifdef NEW_PREF_ARCH + if ( !PREF_EvaluateConfigScript(bytes, num_bytes, NULL, TRUE, TRUE, FALSE) )goto failed; +#else + if ( !PREF_EvaluateConfigScript(bytes, num_bytes, NULL, TRUE, TRUE) )goto failed; +#endif + + XP_FileClose(fp); + XP_FREEIF(bytes); + + return 0; + +failed: + FE_Alert(w, XP_GetString(XP_GLOBAL_EVEN_SAVED_IS_BAD)); + if ( fp != NULL ) XP_FileClose(fp); + XP_FREEIF(bytes); + return -1; +} + + +/* + * jsc_check_for_find_proxy + */ +PRIVATE XP_Bool +jsc_check_for_find_proxy(void) +{ + JSObject* globalConfig; + JSContext* configContext; + char buf[1024]; + JSBool ok; + jsval rv; + + PREF_GetConfigContext(&configContext); + PREF_GetGlobalConfigObject(&globalConfig); + + XP_SPRINTF(buf, "typeof(ProxyConfig.FindProxyForURL) == \"function\""); + ok = JS_EvaluateScript(configContext, globalConfig, buf, strlen(buf), 0, 0, + &rv); + + return (ok && JSVAL_IS_BOOLEAN(rv) && JSVAL_TO_BOOLEAN(rv) == JS_TRUE); +} + /* * jsc_complete @@ -33,7 +281,11 @@ jsc_complete(void* bytes, int32 num_bytes) #endif if ( bytes ) { +#ifdef NEW_PREF_ARCH + PREF_EvaluateConfigScript(bytes, num_bytes, NULL, TRUE, TRUE, FALSE); +#else PREF_EvaluateConfigScript(bytes, num_bytes, NULL, TRUE, TRUE); +#endif } else { /* If failover is ok, read the local cached config */ } @@ -48,7 +300,23 @@ jsc_complete(void* bytes, int32 num_bytes) MODULE_PRIVATE NET_StreamClass* NET_JavascriptConfig(int fmt, void* data_obj, URL_Struct* URL_s, MWContext* w) { + if ( URL_s == NULL || URL_s->address == NULL ) { + return NULL; + } + + NET_Progress(w, XP_GetString(XP_RECEIVING_PROXY_AUTOCFG)); + return NET_SimpleStream(fmt, (void*) jsc_complete, URL_s, w); } +/* + * NET_FindProxyInJSC + */ +MODULE_PRIVATE XP_Bool +NET_FindProxyInJSC(void) +{ + return m_FindProxyInJSC; +} + + diff --git a/mozilla/network/cnvts/cvpics.h b/mozilla/network/cnvts/cvpics.h index cd2648c3090..4b56212e955 100644 --- a/mozilla/network/cnvts/cvpics.h +++ b/mozilla/network/cnvts/cvpics.h @@ -1,3 +1,20 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ #ifndef CVPICS_H #define CVPICS_H diff --git a/mozilla/network/main/Makefile b/mozilla/network/main/Makefile index 48a830451e0..0b08000d295 100644 --- a/mozilla/network/main/Makefile +++ b/mozilla/network/main/Makefile @@ -17,6 +17,8 @@ DEPTH = ../.. +include $(DEPTH)/config/config.mk + MODULE = network LIBRARY_NAME = network @@ -51,11 +53,11 @@ CSRCS += \ endif EXPORTS= mkstream.h mkparse.h mkfsort.h mksort.h mkgeturl.h \ - mkselect.h mktcp.h netutils.h mkpadpac.h mkautocf.h mkhelp.h \ + netstream.h mkselect.h mktcp.h netutils.h mkpadpac.h mkautocf.h mkhelp.h \ mkutils.h mktrace.h -REQUIRES = jtools nspr2 dbm util js parse lay style pref htmldlgs java \ +REQUIRES = ldapurl imap4url jtools progress nspr2 dbm util js parse lay style pref htmldlgs java \ libfont netcache httpurl cnetinit security img layer netcnvts network \ mimetype ldap net diff --git a/mozilla/network/main/makefile.win b/mozilla/network/main/makefile.win index dc0d68b141d..49239980d3e 100644 --- a/mozilla/network/main/makefile.win +++ b/mozilla/network/main/makefile.win @@ -97,7 +97,7 @@ LIBRARY_NAME=network EXTRA_LIBS= -REQUIRES= foo parse jtools java zlib nspr dbm util network js security mimetype ldap net +REQUIRES= foo parse jtools java zlib nspr dbm util network js security mimetype ldap imap4url ldapurl net progress EXPORTS= mkstream.h \ mkparse.h \ mkfsort.h \ @@ -106,6 +106,7 @@ EXPORTS= mkstream.h \ mkselect.h \ mktcp.h \ netutils.h \ + netstream.h \ mkpadpac.h \ mkautocf.h \ mkutils.h \ @@ -139,6 +140,9 @@ LINCS=-I$(PUBLIC)\jtools \ -I$(PUBLIC)\network \ -I$(PUBLIC)\ldap \ -I$(PUBLIC)\net \ + -I$(PUBLIC)\progress \ + -I$(PUBLIC)\ldapurl \ + -I$(PUBLIC)\imap4url \ -I$(PUBLIC)\security #!endif diff --git a/mozilla/network/main/mkabook.cpp b/mozilla/network/main/mkabook.cpp index 7e738a96e0f..e27118ccebd 100644 --- a/mozilla/network/main/mkabook.cpp +++ b/mozilla/network/main/mkabook.cpp @@ -22,51 +22,847 @@ // // -#include "xp.h" #include "mkutils.h" +#include "xp.h" +#include "xp_str.h" + #include "mkgeturl.h" #include "mkabook.h" #include "addrbook.h" -// -// Callbacks from NET_GetURL -// +#include "abcom.h" +#include "xpgetstr.h" +#include "pw_public.h" -extern "C" int32 net_AddressBookLoad (ActiveEntry *ce) +extern "C" { - char * url = ce->URL_s->address; - char * path = NET_ParseURL(url, GET_PATH_PART); - char * search = NET_ParseURL(url, GET_SEARCH_PART); - if (!PL_strncasecmp(path,"add",3)) { - if (!PL_strncasecmp (search, "?vcard=", 7)) { - ABook* addressbook = FE_GetAddressBook(NULL); - if (addressbook) - AB_ImportFromVcardURL(addressbook, ce->window_id, NET_UnEscape(search+7)); + extern int MK_OUT_OF_MEMORY; + extern int MK_MALFORMED_URL_ERROR; + extern int MK_ADDR_IMPORT_CARDS; + extern int MK_ADDR_IMPORT_MAILINGLISTS; + extern int MK_ADDR_EXPORT_CARDS; + extern int MK_MSG_ADDRESS_BOOK; + extern int MK_ADDR_EXPORT_TITLE; + extern int MK_ADDR_IMPORT_TITLE; +} + +typedef enum { + abImportVCard, + abImport, + abExport, + abCopy /* copying, deleting, moving address book entries */ +} net_abUrlType; + +class NET_AddressBookConnData +{ +public: + NET_AddressBookConnData (ActiveEntry * ce) + { + m_commandData = NULL; + m_addressBook = NULL; + m_urlType = abImportVCard; + m_started = FALSE; + m_ce = ce; + m_container = NULL; + + // Netlib magic so we get a timeslice without waiting for a read-ready socket. + // It would be nice to ditch this, but the LDAP SDK manages the sockets. + if (m_ce) + { + m_ce->socket = NULL; + NET_SetCallNetlibAllTheTime(m_ce->window_id, "mkabook"); } } - return -1; + virtual ~NET_AddressBookConnData () + { + if (m_container) + { + AB_ReleaseContainer(m_container); + m_container = NULL; + } + + // Netlib magic so we won't get called anymore for this URL + // It would be nice to ditch this, but the LDAP SDK manages the sockets. + NET_ClearCallNetlibAllTheTime(m_ce->window_id, "mkabook"); + m_ce = NULL; + } + +#ifdef MOZ_NEWADDR + virtual int Interrupt() = 0; + virtual int Process() = 0; + virtual int Load() = 0; +#else + virtual int Interrupt() {return 0;} + virtual int Process() { return 0;} + virtual int Load() {return 0;} +#endif + +#ifdef MOZ_NEWADDR + virtual int Initialize(const char * url) {return 0;} +#else + XP_Bool Initialize(const char *url) { return FALSE;} + +#endif + + + void *m_cookie; + char *m_commandData; + ABook *m_addressBook; // only used by old address book APIs....remove when everyone is using the new APIs... + ActiveEntry *m_ce; + AB_ContainerInfo * m_container; + + net_abUrlType m_urlType; + XP_Bool m_started; +}; + +/********************************************************************************************** + The connection data class for importing a vcard URL into an address book. This operation is + actually a synchronous so we shouldn't have to fill in implementations for interrupt... +***********************************************************************************************/ +class net_AddressBookAddVCard : public NET_AddressBookConnData +{ +public: + net_AddressBookAddVCard(ActiveEntry * ce); + virtual ~net_AddressBookAddVCard(); + + virtual int Load(); + virtual int Process(); + virtual int Initialize(const char * url); + virtual int Interrupt(); + +protected: + int LoadContainerToAddTo(); +}; + +net_AddressBookAddVCard::net_AddressBookAddVCard(ActiveEntry *ce) : NET_AddressBookConnData(ce) +{ + m_urlType = abImportVCard; } +net_AddressBookAddVCard::~net_AddressBookAddVCard() +{} + +int net_AddressBookAddVCard::LoadContainerToAddTo() +{ + // acquire list of personal address books, then grab the first one... + XP_List * containers = AB_AcquireAddressBookContainers(m_ce->window_id); + if (containers) + { + // extract the first one + int index = 1; + m_container = NULL; + while (!m_container && index <= XP_ListCount(containers)) + m_container = (AB_ContainerInfo *) XP_ListGetObjectNum (containers, index++); + if (m_container) + AB_AcquireContainer(m_container); // acquire our own ref count + + AB_ReleaseContainersList(containers); // release ref count on all the ctrs + } + + return 0; +} + +int net_AddressBookAddVCard::Load() +{ + // load m_container...right now this is just the first address book available... + return LoadContainerToAddTo(); +} + +int net_AddressBookAddVCard::Process() +{ + // since this is a synchronous operation, do the import... + int status = 0; + if (m_container) + status = AB_ImportVCards(m_container, m_commandData); + + return MK_CONNECTED; +} + +int net_AddressBookAddVCard::Interrupt() +{ + XP_ASSERT(FALSE); // this should not happen because vcard happens synch.... + return 0; +} + +int net_AddressBookAddVCard::Initialize(const char * url) +{ + char *search = NET_ParseURL (url, GET_SEARCH_PART); + m_urlType = abImportVCard; + if (search) + { + m_commandData = XP_STRDUP (search + 7); + NET_UnEscape (m_commandData); + } + XP_FREEIF(search); + return 0; +} + +/********************************************************************************************** + All of our asynch operations in the new address book (export and import right now), use + progress windows. We use this class to pull out the progress window commonality between + export and import. net_AddressBookAsynchData handles creation and destruction of the + progress panes. We use virtual methods to determine the appropriate title and description + text for the progress windows +************************************************************************************************/ + +// call back function for canceling asynch operations... +static void CancelAsynch(void *closure) +{ + MWContext *progressContext = (MWContext *)closure; + if (progressContext) + XP_InterruptContext(progressContext); +} + +class net_AddressBookAsynchData : public NET_AddressBookConnData +{ +public: + net_AddressBookAsynchData(ActiveEntry * ce); + virtual ~net_AddressBookAsynchData(); + + // all address book connect data classes support Load, Process, Initialize and Interrupt + // as these 4 methods are called by the NET_ functions in mkabook.h + virtual int Load(); // creates progress windows... + + virtual int Process() = 0; + virtual int Initialize(const char * url) = 0; + virtual int Interrupt() = 0; + + // string functions used to build the progress window + virtual char * GetPWTitle() { return NULL;} + virtual char * GetPWLine1Text() { return NULL;} // caller must free this string... + virtual char * GetPWLine2Text() { return NULL;} // caller must free this string.. + +protected: + // Some asynch urls use progress windows.... + MWContext *m_progressContext; + pw_ptr m_progressWindow; +}; + +net_AddressBookAsynchData::net_AddressBookAsynchData(ActiveEntry * ce) : NET_AddressBookConnData(ce) +{ + m_progressContext = NULL; + m_progressWindow = NULL; +} + +net_AddressBookAsynchData::~net_AddressBookAsynchData() +{ + if (m_progressWindow) + { + PW_Hide(m_progressWindow); + PW_Destroy(m_progressWindow); + } + + if (m_progressContext) + PW_DestroyProgressContext(m_progressContext); +} + +int net_AddressBookAsynchData::Load() +{ + int status = 0; + // get m_container from the url struct... + if (m_ce && m_ce->URL_s) + m_container = (AB_ContainerInfo *)m_ce->URL_s->owner_data; // it was already acquired when it was inserted into the url_struct... + + if (m_container) + { + m_progressContext = PW_CreateProgressContext(); + if (m_progressContext) + { + m_progressWindow = PW_Create(NULL, pwStandard); + if (m_progressWindow) + { + PW_AssociateWindowWithContext(m_progressContext, m_progressWindow); + char * title = GetPWTitle(); + char * line1Text = GetPWLine1Text(); + char * line2Text = GetPWLine2Text(); + + // it is okay to set a NULL title or line... + PW_SetWindowTitle(m_progressWindow, title); // resource string + PW_SetLine1(m_progressWindow, line1Text);// do we want anything here? + PW_SetLine2(m_progressWindow, line2Text); // mscott: again...add a real resource string for this + PW_SetProgressRange(m_progressWindow,0,100); + PW_SetCancelCallback(m_progressWindow, CancelAsynch, m_ce->window_id); + PW_Show(m_progressWindow); + + // free any allocated strings + XP_FREEIF(line1Text); + XP_FREEIF(line2Text); + XP_FREEIF(title); + } + else + PW_DestroyProgressContext(m_progressContext); + } // if context + } // if container + + return status; +} + +/********************************************************************************************** + The connection data class for copying/moving entries between address books. This operation is + asynch. We assume that the AB_AddressCopyInfo struct for the transfer is stored in the owner_data + field of the url_struct. In addition, status information will be shown through the current event's + context. +***********************************************************************************************/ +class net_AddressBookCopy : public net_AddressBookAsynchData +{ +public: + net_AddressBookCopy(ActiveEntry * ce); + virtual ~net_AddressBookCopy(); + + virtual int Load(); + virtual int Process(); + virtual int Initialize(const char * url); + virtual int Interrupt(); + +protected: + // any state we may ned to store... + AB_AddressBookCopyInfo * m_abCopyInfo; +}; + +net_AddressBookCopy::net_AddressBookCopy(ActiveEntry * ce) : net_AddressBookAsynchData(ce) +{ + if (ce && ce->URL_s) + m_abCopyInfo = (AB_AddressBookCopyInfo *) ce->URL_s->owner_data; + else + m_abCopyInfo = NULL; +} + +net_AddressBookCopy::~net_AddressBookCopy() +{ + if (m_abCopyInfo) + XP_ASSERT(FALSE); // struct should have been released by finish or interrupt calls... +} + +int net_AddressBookCopy::Load() // over ride this from base class so we don't create the progress window... +{ + return 0; +} + +int net_AddressBookCopy::Initialize(const char * /* url */) +{ + // we don't have any command data for address book copy urls.... + m_urlType = abCopy; + return 0; +} + +int net_AddressBookCopy::Process() +{ + int status = 0; + XP_Bool copyFinished = FALSE; + if (m_abCopyInfo) + { + if (!m_started) // if we haven't begun the copy...use the begin APIs... + status = AB_BeginEntryCopy (m_abCopyInfo->srcContainer, m_ce->window_id, m_abCopyInfo, &m_cookie, ©Finished); + else // otherwise do more... + status = AB_MoreEntryCopy (m_abCopyInfo->srcContainer, m_ce->window_id, m_abCopyInfo, &m_cookie, ©Finished); + + if (status != AB_SUCCESS) // then mark ourselves as finished! + { + XP_ASSERT(FALSE); // what condition failed to cause us to be here?? + copyFinished = TRUE; + } + + // all progress is handled by the src container + + if (copyFinished) // if we are finished, + { + AB_FinishEntryCopy(m_abCopyInfo->srcContainer, m_ce->window_id, m_abCopyInfo, &m_cookie); // clean up everything.. + m_abCopyInfo = NULL; + status = MK_CONNECTED; + } + else + status = MK_WAITING_FOR_CONNECTION; + } + else + status = MK_CONNECTED; // this copy is done...we didn't have a copy info struct!!!! + + return status; +} + +int net_AddressBookCopy::Interrupt() +{ + // we are shutting down... + if (m_abCopyInfo) + AB_InterruptEntryCopy(m_abCopyInfo->srcContainer, m_ce->window_id, m_abCopyInfo, &m_cookie); + + m_abCopyInfo = NULL; + return 0; +} + +/********************************************************************************************** + The connection data class for import. Importing in the new address book is asynch. The + file to import is contained in the URL. However, the particular address book to import into + is stored in the owner_data field of the URL struct which is in the active entry. When the + URL_struct is created, the container was reference counted. So when you are done importing, + or if importing is canceled, we are now responsible for releasing the reference count + on the container. + + m_cookie is actually a struct which represents the import state for the container. When the + import is finished or interrupted, the container will free this cookie struct... +************************************************************************************************/ + + +class net_AddressBookImport : public net_AddressBookAsynchData +{ +public: + net_AddressBookImport(ActiveEntry * ce); + virtual ~net_AddressBookImport(); + + // leave it to the subclass to implement these.. + virtual int Process(); + virtual int Initialize(const char * url); + virtual int Interrupt(); + + int UpdateProgress(); + + // string functions used to build the progress window + virtual char * GetPWTitle(); + virtual char * GetPWLine1Text(); // caller must free this string... + virtual char * GetPWLine2Text(); // caller must free this string.. + +protected: + // import in the new address book is asynch..we need a progress window to show it off.... + uint32 m_passCount; // importing LDIF requires 2 passes....we want to keep track of which pass we are on for progress purposes... +}; + +net_AddressBookImport::net_AddressBookImport(ActiveEntry * ce) : net_AddressBookAsynchData(ce) +{ + m_passCount = 1; // always start with first pass... +} + +net_AddressBookImport::~net_AddressBookImport() +{ + if (m_cookie) + XP_ASSERT(FALSE); // should have been freed and cleared by container import code.... + + // container is released by base class... +} + +int net_AddressBookImport::Initialize(const char * url) +{ + char * search = NET_ParseURL(url, GET_SEARCH_PART); + + m_urlType = abImport; + m_commandData = XP_STRDUP(search+6); + NET_UnEscape(m_commandData); + return 0; +} + +char * net_AddressBookImport::GetPWTitle() +{ + return XP_STRDUP(XP_GetString(MK_ADDR_IMPORT_TITLE)); +} + +char * net_AddressBookImport::GetPWLine1Text() +{ + return NULL; // no line 1 text for import pane +} + +char * net_AddressBookImport::GetPWLine2Text() +{ + AB_ContainerAttribValue * attrib = NULL; + AB_GetContainerAttribute(m_container, attribName, &attrib); + + char * lineText = NULL; + if (m_passCount <= 1) + { + if (attrib->u.string) + lineText = PR_smprintf (XP_GetString(MK_ADDR_IMPORT_CARDS), attrib->u.string); + else + lineText = PR_smprintf (XP_GetString(MK_ADDR_IMPORT_CARDS),XP_GetString(MK_MSG_ADDRESS_BOOK)); + } + else + { + if (attrib->u.string) + lineText = PR_smprintf (XP_GetString(MK_ADDR_IMPORT_MAILINGLISTS), attrib->u.string); + else + lineText = PR_smprintf (XP_GetString(MK_ADDR_IMPORT_MAILINGLISTS), XP_GetString(MK_MSG_ADDRESS_BOOK)); + } + + AB_FreeContainerAttribValue(attrib); + + return lineText; +} + + +int net_AddressBookImport::Process() +{ + int status = 0; + XP_Bool importFinished; + if (!m_started) // if we haven't begun the import...use the begin APIs... + status = AB_ImportBegin (m_container, NULL, m_commandData /* file name */, &m_cookie /* import state */, &importFinished); + else // otherwise do more... + status = AB_ImportMore (m_container, &m_cookie, &importFinished); + + UpdateProgress(); + + // are we done? + if (importFinished) // if we are finished, + { + AB_ImportFinish(m_container, &m_cookie); + status = MK_CONNECTED; + } + else + status = MK_WAITING_FOR_CONNECTION; + + return status; +} + +int net_AddressBookImport::UpdateProgress () +{ + // now update progress bar... + uint32 position = 0; // position in the import file + uint32 fileLength = 0; + uint32 passCount = m_passCount; + int status = AB_ImportProgress(m_container, m_cookie, &position, &fileLength, &passCount); + if (passCount != m_passCount) + { + m_passCount = passCount; + char * text = GetPWLine1Text(); + PW_SetLine1(m_progressWindow, text); // mscott: again...add a real resource string for this + XP_FREEIF(text); + } + + if (fileLength) + { + int32 percent = (position * 100) / fileLength; + PW_SetProgressValue(m_progressWindow, percent); + } + return 0; +} + +int net_AddressBookImport::Interrupt() +{ + // we are shutting down... + AB_ImportInterrupt(m_container, &m_cookie); // destroys the container... + m_cookie = NULL; + // destruction will clean up the progress pane stuff... + return 0; +} + + +/********************************************************************************************** + The connection data class for export. Exporting in the new address book is asynch. The + file to export to is contained in the URL. However, the particular address book to export from + is stored in the owner_data field of the URL struct which is in the active entry. When the + URL_struct is created, the container was reference counted. So when you are done exporting, + or if export is canceled, we are responsible for releasing the reference count + on the container. + + m_cookie is actually a struct which represents the export state for the container. When the + export is finished or interrupted, the container will free this cookie struct... +************************************************************************************************/ + + +class net_AddressBookExport : public net_AddressBookAsynchData +{ +public: + net_AddressBookExport(ActiveEntry * ce); + virtual ~net_AddressBookExport(); + + virtual int Process(); + virtual int Initialize(const char * url); + virtual int Interrupt(); + + int UpdateProgress(); + + // string functions used to build the progress window + virtual char * GetPWTitle(); + virtual char * GetPWLine1Text(); // caller must free this string... + virtual char * GetPWLine2Text(); // caller must free this string.. + +protected: +}; + +net_AddressBookExport::net_AddressBookExport(ActiveEntry * ce) : net_AddressBookAsynchData(ce) +{} + +net_AddressBookExport::~net_AddressBookExport() +{ + if (m_cookie) + XP_ASSERT(FALSE); // should have been freed and cleared by container import code.... + + // container is released by base class... + // progress window is destroyed by base class.. +} + +int net_AddressBookExport::Initialize(const char * url) +{ + char * search = NET_ParseURL(url, GET_SEARCH_PART); + m_urlType = abExport; + m_commandData = XP_STRDUP(search+6); // + 6 to skip over the "?file=" part + NET_UnEscape(m_commandData); + return 0; +} + +char * net_AddressBookExport::GetPWTitle() +{ + return XP_STRDUP(XP_GetString(MK_ADDR_EXPORT_TITLE)); +} + +char * net_AddressBookExport::GetPWLine1Text() +{ + return NULL; // no line 1 text for import pane +} + +char * net_AddressBookExport::GetPWLine2Text() +{ + AB_ContainerAttribValue * attrib = NULL; + AB_GetContainerAttribute(m_container, attribName, &attrib); + + char * lineText = NULL; + if (attrib->u.string) + lineText = PR_smprintf (XP_GetString(MK_ADDR_EXPORT_CARDS), attrib->u.string); + else + lineText = PR_smprintf (XP_GetString(MK_ADDR_EXPORT_CARDS),XP_GetString(MK_MSG_ADDRESS_BOOK)); + AB_FreeContainerAttribValue(attrib); + + return lineText; +} + +int net_AddressBookExport::Process() +{ + int status = 0; + XP_Bool exportFinished; + if (!m_started) // if we haven't begun the import...use the begin APIs... + status = AB_ExportBegin (m_container, NULL, m_commandData /* file name */, &m_cookie /* import state */, &exportFinished); + else // otherwise do more... + status = AB_ExportMore (m_container, &m_cookie, &exportFinished); + + UpdateProgress(); + + // are we done? + if (exportFinished) // if we are finished, + { + AB_ExportFinish(m_container, &m_cookie); + status = MK_CONNECTED; + } + else + status = MK_WAITING_FOR_CONNECTION; + + return status; +} + +int net_AddressBookExport::UpdateProgress () +{ + // now update progress bar... + uint32 numberExported = 0; + uint32 totalEntries = 0; + + int status = AB_ExportProgress(m_container, m_cookie, &numberExported, &totalEntries); + + if (totalEntries) + { + int32 percent = (numberExported * 100) / totalEntries; + PW_SetProgressValue(m_progressWindow, percent); + } + return 0; +} + +int net_AddressBookExport::Interrupt() +{ + // we are shutting down... + AB_ExportInterrupt(m_container, &m_cookie); // destroys the container... + m_cookie = NULL; + // destruction will clean up the progress pane stuff... + return 0; +} + +// +// Callbacks from NET_GetURL +// +#ifdef MOZ_NEWADDR extern "C" int32 net_ProcessAddressBook (ActiveEntry *ce) { - PR_ASSERT(0); - return -1; + int status = 0; + NET_AddressBookConnData *cd = (NET_AddressBookConnData*) ce->con_data; + if (cd) + { + status = cd->Process(); // should return MK_WAITING_FOR_CONNECTION + cd->m_started = TRUE; + if (status < 0 || status == MK_CONNECTED) + { + delete cd; + ce->con_data = NULL; + } + } + + if(status == MK_CONNECTED) // i don't understand why mkgeturl won't end the url on MK_CONNECTED, but it doesn't... + status = -1; + + ce->status = status; + return ce->status; } +extern "C" int32 net_AddressBookLoad (ActiveEntry *ce) +{ + ce->status = -1; + int status = ce->status; + // examine URL to determine what type of connection data we need to create... + char * url = ce->URL_s->address; + char *path = NET_ParseURL (url, GET_PATH_PART); + char * search = NET_ParseURL(url, GET_SEARCH_PART); + NET_AddressBookConnData *cd = NULL; + switch (path[0]) + { + case 'a': + case 'A': + if (!XP_STRNCASECMP(path,"add",3)) + { + if (!XP_STRNCASECMP (search, "?vcard=", 7)) + cd = new net_AddressBookAddVCard(ce); + if (!cd) + status = MK_OUT_OF_MEMORY; + } + else + XP_ASSERT(FALSE); + break; + case 'i': + case 'I': + if (!XP_STRNCASECMP (path, "import", 6)) + { + if (!XP_STRNCASECMP (search, "?file=", 6)) + { + cd = new net_AddressBookImport(ce); + if (!cd) + status = MK_OUT_OF_MEMORY; + } //if ?file=... + } + break; + case 'e': + case 'E': + if (!XP_STRNCASECMP (path, "export", 6)) + { + if (!XP_STRNCASECMP (search, "?file=", 6)) + { + cd = new net_AddressBookExport(ce); + if (!cd) + status = MK_OUT_OF_MEMORY; + } //if ?file=... + } + break; + case 'c': + case 'C': + if (!XP_STRNCASECMP(path, "copy", 4)) + { + // we have no search part on copy urls.... + cd = new net_AddressBookCopy(ce); + if (!cd) + status = MK_OUT_OF_MEMORY; + } + break; + default: + status = MK_MALFORMED_URL_ERROR; + XP_ASSERT(FALSE); + + } + + ce->status = status; // catch any errors like MK_OUT_OF_MEMORY... + if (cd) + { + cd->Initialize(url); + cd->Load(); + ce->con_data = cd; // store the connection data + ce->status = net_ProcessAddressBook (ce); + } + + XP_FREEIF(path); + XP_FREEIF(search); + return ce->status; +} extern "C" int32 net_InterruptAddressBook (ActiveEntry * ce) { - PR_ASSERT(0); + ce->status = MK_INTERRUPTED; + NET_AddressBookConnData *cd = (NET_AddressBookConnData*) ce->con_data; + + if (cd) + { + cd->Interrupt(); + delete cd; + } + + return ce->status; +} +extern "C" void net_CleanupAddressBook(void) +{ + // I'm not sure what cleanup stuff I have to do here... + // leaving it empty for now... +} + +#else +extern "C" int32 net_ProcessAddressBook (ActiveEntry *ce) +{ + NET_AddressBookConnData *cd = (NET_AddressBookConnData*) ce->con_data; + switch (cd->m_urlType) + { + case abImportVCard: + AB_ImportFromVcardURL(cd->m_addressBook, ce->window_id, cd->m_commandData); //mscott + ce->status = -1; //phil/mscott should work, but seems not to. MK_CONNECTED; + break; + case abImport: + if (!cd->m_started) + ce->status = MK_CONNECTED; //mscott AB_ImportBegin (cd->m_addressBook, ce->window_id, cd->m_commandData, &cd->m_cookie); + else + ce->status = MK_CONNECTED; //mscott AB_ImportMore (cd->m_addressBook, ce->window_id, cd->m_cookie); + break; + } + + cd->m_started = TRUE; + + // Either an error, or we're already done + if (ce->status != MK_WAITING_FOR_CONNECTION) + delete cd; + + return ce->status; return -1; } +extern "C" int32 net_AddressBookLoad (ActiveEntry *ce) +{ + ce->status = -1; + + NET_AddressBookConnData *cd = new NET_AddressBookConnData(ce); + if (!cd) + return MK_OUT_OF_MEMORY; + + ce->con_data = cd; + + if (cd->Initialize (ce->URL_s->address)) + ce->status = net_ProcessAddressBook(ce); + + return ce->status; +} + extern "C" void net_CleanupAddressBook(void) +{} + +extern "C" int32 net_InterruptAddressBook (ActiveEntry * ce) { + ce->status = MK_INTERRUPTED; + NET_AddressBookConnData *cd = (NET_AddressBookConnData*) ce->con_data; + + switch (cd->m_urlType) + { + case abImportVCard: + XP_ASSERT(FALSE); // this shouldn't happen because vCard import happens synchronously + break; + case abImport: + //mscott AB_ImportInterrupt (cd->m_addressBook, ce->window_id, cd->m_cookie); + break; + default: + XP_ASSERT(FALSE); + } + + delete cd; + + return ce->status; } +#endif MODULE_PRIVATE void NET_InitAddressBookProtocol(void) @@ -80,4 +876,3 @@ NET_InitAddressBookProtocol(void) NET_RegisterProtocolImplementation(&abook_proto_impl, ADDRESS_BOOK_TYPE_URL); } - diff --git a/mozilla/network/main/mkautocf.c b/mozilla/network/main/mkautocf.c index 70b32cfe9fd..c5830e0c5c1 100644 --- a/mozilla/network/main/mkautocf.c +++ b/mozilla/network/main/mkautocf.c @@ -154,6 +154,7 @@ typedef enum { /* Declared in mkgeturl.c. NET_GetURL uses these variables to determine * whether or not the pac file has been loaded. */ +extern XP_Bool NET_FindProxyInJSC(void); extern XP_Bool NET_GlobalAcLoaded; extern PRBool NET_ProxyAcLoaded; @@ -768,22 +769,13 @@ PRIVATE unsigned int pacf_write_ready(NET_StreamClass *stream) { return MAX_WRITE_READY; } -PRIVATE void pacf_complete(NET_StreamClass *stream) { - PACF_Object *obj=stream->data_object; - jsval result; - XP_StatStruct st; - JSBool ok; +PUBLIC XP_Bool +NET_InitPacfContext(void) +{ + XP_Bool first_time = TRUE; -retry: - if (!obj->flag || obj->flag == 2) { - pacf_loading = FALSE; + if ( first_time == FALSE ) return TRUE; - - /* JONM -- Check me over please! - proxyConfig = - MOCHA_DefineNewObject(decoder->js_context, decoder->window_object, - "ProxyConfig", &pc_class, NULL, NULL, 0, - pc_props, pc_methods); */ PREF_GetConfigContext(&configContext); PREF_GetGlobalConfigObject(&globalConfig); @@ -796,13 +788,13 @@ retry: if (!JS_DefineProperties(configContext, proxyConfig, pc_props)) { - return; + return FALSE; } if (!JS_DefineFunctions(configContext, proxyConfig, pc_methods)) { - return; + return FALSE; } } @@ -812,10 +804,23 @@ retry: &pc_class, NULL, 0); - /*MOCHA_DefineNewObject(decoder->js_context, proxyConfig, "bindings", - &pc_class, NULL, NULL, 0, no_props, 0); */ - } + first_time = FALSE; + + return TRUE; +} + +PRIVATE void pacf_complete(NET_StreamClass *stream) { + PACF_Object *obj=stream->data_object; + jsval result; + XP_StatStruct st; + JSBool ok; + +retry: + if (!obj->flag || obj->flag == 2) { + pacf_loading = FALSE; + if ( !NET_InitPacfContext() ) return; + } if (!pacf_src_buf) { if ( pacf_do_failover == FALSE && !NET_UsingPadPac() ) { @@ -1107,11 +1112,11 @@ MODULE_PRIVATE char *pacf_find_proxies_for_url(MWContext *context, /* If proxy failover is not allowed, and we weren't * able to autoload the proxy, return a string that * pacf_get_proxy_addr will always fail with. */ - if ( !pacf_do_failover && !pacf_loading && !pacf_ok ) { + if ( !pacf_do_failover && !pacf_loading && !pacf_ok && !NET_FindProxyInJSC()) { return ""; } - if (!orig_url || !pacf_ok || pacf_loading || pacf_find_proxy_undefined) + if ( !NET_FindProxyInJSC() && (!orig_url || !pacf_ok || pacf_loading || pacf_find_proxy_undefined)) return NULL; if (!(bad_url = PL_strdup(orig_url))) @@ -1178,14 +1183,25 @@ MODULE_PRIVATE char *pacf_find_proxies_for_url(MWContext *context, if (p) *p = '\0'; } - sprintf(buf, "FindProxyForURL(\"%s\",\"%s\",\"%s\")", safe_url, host, + + if ( NET_FindProxyInJSC() ) { + XP_SPRINTF(buf, "ProxyConfig.FindProxyForURL(\"%s\",\"%s\",\"%s\")", safe_url, host, method ? method : "" ); + } else { + XP_SPRINTF(buf, "FindProxyForURL(\"%s\",\"%s\",\"%s\")", safe_url, host, + method ? method : "" ); + } if (!JS_AddRoot(configContext, &rv)) goto out; + if ( NET_FindProxyInJSC() ) { + ok = JS_EvaluateScript(configContext, globalConfig, + buf, strlen(buf), 0, 0, &rv); + } else { ok = JS_EvaluateScript(configContext, proxyConfig, buf, strlen(buf), 0, 0, &rv); + } if (ok) { if (JSVAL_IS_STRING(rv)) { diff --git a/mozilla/network/main/mkconect.c b/mozilla/network/main/mkconect.c index b4b687963b4..ee02a0047a1 100644 --- a/mozilla/network/main/mkconect.c +++ b/mozilla/network/main/mkconect.c @@ -122,7 +122,7 @@ typedef enum { struct _TCP_ConData { TCPStatesEnum next_state; /* states of the machine */ PRNetAddr net_addr; - XP_Bool use_security; + HG93765 time_t begin_time; }; @@ -893,6 +893,7 @@ net_start_first_connect(const char *host, FREE(buf); } + HG26300 /* set the begining time to be the current time. * the timeout value will be compared to this * later @@ -1026,7 +1027,7 @@ NET_BeginConnect (CONST char *url, char *prot_name, int def_port, PRFileDesc **sock, - Bool use_security, + HG92743 TCP_ConData **tcp_con_data, MWContext *window_id, char **error_msg, @@ -1253,7 +1254,7 @@ HG71089 { (*tcp_con_data)->next_state = NET_TCP_FINISH_CONNECT; /* save in case we need it */ - (*tcp_con_data)->use_security = use_security; + HG83665 } if(status < 0) @@ -1481,7 +1482,7 @@ error_out: prot_name, def_port, sock, - (*tcp_con_data)->use_security, + HG98376 tcp_con_data, window_id, error_msg, @@ -1492,6 +1493,7 @@ error_out: else FREE(host); + HG92362 if (error == PR_CONNECT_REFUSED_ERROR) { char * host = NET_ParseURL(url, GET_HOST_PART); diff --git a/mozilla/network/main/mkgeturl.c b/mozilla/network/main/mkgeturl.c index c1445c425e0..71c4d4ef1a1 100644 --- a/mozilla/network/main/mkgeturl.c +++ b/mozilla/network/main/mkgeturl.c @@ -23,6 +23,8 @@ * Designed and originally implemented by Lou Montulli '94 * Additions/Changes by Judson Valeski, Gagan Saksena 1997. */ +#include "rosetta.h" +#include "xp_error.h" #include "mkutils.h" #include "mkgeturl.h" #include "shist.h" @@ -222,9 +224,9 @@ extern int gethostname(char *, int); #ifndef XP_UNIX #ifdef NADA_VERSION -static char *https_security = "SECURITY_VERSION: -https"; +HG73787 #else -static char *https_security = "SECURITY_VERSION: +https"; +HG72987 #endif #endif @@ -252,10 +254,10 @@ PUBLIC char *XP_NEW_DOC_NAME = NULL; /* "Untitled" */ /* forward decl's */ PRIVATE void add_slash_to_URL (URL_Struct *URL_s); PRIVATE Bool override_proxy (CONST char * URL); -PRIVATE int net_output_security_url(ActiveEntry * this_entry, MWContext *cx); +HG52422 + PRIVATE void net_FreeURLAllHeaders(URL_Struct * URL_s); PRIVATE void NET_InitAboutProtocol(void); -PRIVATE void NET_InitSecurityProtocol(void); PRIVATE NET_TimeBombActive = FALSE; @@ -275,7 +277,7 @@ typedef struct _WaitingURLStruct { PRIVATE char * MKftp_proxy=0; PRIVATE char * MKgopher_proxy=0; PRIVATE char * MKhttp_proxy=0; -PRIVATE char * MKhttps_proxy=0; +HG62630 PRIVATE char * MKwais_proxy=0; PRIVATE char * MKnews_proxy=0; PRIVATE char * MKno_proxy=0; @@ -300,6 +302,8 @@ MODULE_PRIVATE CacheUseEnum NET_CacheUseMethod=CU_CHECK_PER_SESSION; MODULE_PRIVATE time_t NET_StartupTime=0; +PRIVATE XP_Bool get_url_disabled = FALSE; + MODULE_PRIVATE PRBool NET_ProxyAcLoaded = PR_FALSE; MODULE_PRIVATE XP_Bool NET_GlobalAcLoaded = FALSE; MODULE_PRIVATE int NET_TotalNumberOfOpenConnections=0; @@ -322,6 +326,11 @@ PRIVATE void net_cleanup_reg_protocol_impls(void); PRIVATE void NET_InitTotallyRandomStuffPeopleAddedProtocols(void); +PUBLIC void +NET_DisableGetURL(void) +{ + get_url_disabled = TRUE; +} /* fix Mac warning for missing prototype */ PUBLIC void @@ -420,18 +429,7 @@ NET_UpdateManualProxyInfo(const char * prefChanged) { } if (proxy) FREE_AND_CLEAR(proxy); - if (bSetupAll || !PL_strcmp(prefChanged, "network.proxy.ssl") || - !PL_strcmp(prefChanged, "network.proxy.ssl_port")) { - PREF_CopyCharPref("network.proxy.ssl",&proxy); - if(proxy && *proxy) { - PREF_GetIntPref("network.proxy.ssl_port",&iPort); - sprintf(text,"%s:%d", proxy, iPort); - StrAllocCopy(MKhttps_proxy, text); - iPort=0; - } - else - FREE_AND_CLEAR(MKhttps_proxy); - } + HG24324 if (proxy) FREE_AND_CLEAR(proxy); if (bSetupAll || !PL_strcmp(prefChanged, "network.proxy.news") || @@ -551,7 +549,7 @@ NET_SelectProxyStyle(NET_ProxyStyle style) FREE_AND_CLEAR(MKftp_proxy); FREE_AND_CLEAR(MKgopher_proxy); FREE_AND_CLEAR(MKhttp_proxy); - FREE_AND_CLEAR(MKhttps_proxy); + HG63454 FREE_AND_CLEAR(MKwais_proxy); FREE_AND_CLEAR(MKnews_proxy); FREE_AND_CLEAR(MKno_proxy); @@ -609,11 +607,7 @@ NET_SetupPrefs(const char * prefChanged) NET_SetCacheUseMethod((CacheUseEnum)nDocReqFreq); } - if (bSetupAll || !PL_strcmp(prefChanged,"browser.cache.disk_cache_ssl")) { - XP_Bool prefBool; - PREF_GetBoolPref("browser.cache.disk_cache_ssl",&prefBool); - NET_DontDiskCacheSSL(!prefBool); - } + HG42422 #ifdef MOZ_MAIL_NEWS if (bSetupAll || !PL_strcmp(prefChanged,"mail.allow_at_sign_in_user_name")) { XP_Bool prefBool; @@ -650,6 +644,26 @@ MODULE_PRIVATE int PR_CALLBACK NET_PrefChangedFunc(const char *pref, void *data) return TRUE; } +/* finish the init of 'netlib'. inits cookies, cache, history + * + */ +PUBLIC void +NET_FinishInitNetLib() +{ + +#ifdef MOZILLA_CLIENT + NET_ReadCacheFAT("", TRUE); + + NET_ReadCookies(""); + +#endif /* MOZILLA_CLIENT */ + + NET_RegisterEnableUrlMatchCallback(); + + NET_SetupPrefs(NULL); /* setup initial proxy, socks, dnsExpiration and cache preference info */ + +} + /* initialize the netlibrary and set the socket buffer size */ PUBLIC int @@ -686,6 +700,7 @@ NET_InitNetLib(int socket_buffer_size, int max_number_of_connections) net_EntryList = XP_ListNew(); +#ifdef XP_MAC #ifdef MOZILLA_CLIENT NET_CacheInit(); @@ -693,6 +708,7 @@ NET_InitNetLib(int socket_buffer_size, int max_number_of_connections) #endif /* MOZILLA_CLIENT */ +#endif /* XP_MAC */ NET_TotalNumberOfProcessingURLs=0; /* reset */ @@ -709,7 +725,6 @@ NET_InitNetLib(int socket_buffer_size, int max_number_of_connections) #ifdef JAVA libnet_asyncIO = PR_NewNamedMonitor("libnet"); #endif - NET_SetupPrefs(NULL); /* setup initial proxy, socks, dnsExpiration and cache preference info */ PREF_RegisterCallback("network.proxy",NET_PrefChangedFunc,NULL); PREF_RegisterCallback("browser.cache",NET_PrefChangedFunc,NULL); PREF_RegisterCallback("network.hosts.socks_server",NET_PrefChangedFunc,NULL); @@ -719,7 +734,6 @@ NET_InitNetLib(int socket_buffer_size, int max_number_of_connections) /* and registers the callbacks */ /* inits the proxy autodiscovery vars and registers their callbacks. */ NET_RegisterPadPrefCallbacks(); - NET_RegisterEnableUrlMatchCallback(); PREF_RegisterCallback("mail.allow_at_sign_in_user_name", NET_PrefChangedFunc, NULL); /* initialize protocol modules @@ -749,6 +763,7 @@ NET_InitNetLib(int socket_buffer_size, int max_number_of_connections) NET_InitLDAPProtocol(); NET_InitCertLdapProtocol(); NET_InitAddressBookProtocol(); + NET_InitIMAP4Protocol(); #endif /* MOZ_MAIL_NEWS */ return(status); @@ -1058,7 +1073,7 @@ PRIVATE XP_Bool net_does_url_require_socket_limit(int urltype) { if( urltype == HTTP_TYPE_URL - || urltype == SECURE_HTTP_TYPE_URL + HG64398 || urltype == FTP_TYPE_URL || urltype == NEWS_TYPE_URL || urltype == GOPHER_TYPE_URL) @@ -1821,6 +1836,12 @@ NET_GetURL (URL_Struct *URL_s, Bool confirm; Bool load_background; char *confirmstring; + + if ( get_url_disabled ) { + /* So that external URL's can't be loaded */ + return -1; + } + TRACEMSG(("Entering NET_GetURL")); LIBNET_LOCK(); @@ -1918,7 +1939,7 @@ NET_GetURL (URL_Struct *URL_s, && (CLEAR_CACHE_BIT(output_format) == FO_PRESENT) && (!(type = NET_URL_Type(URL_s->address)) || type == HTTP_TYPE_URL - || type == SECURE_HTTP_TYPE_URL + HG77355 || type == GOPHER_TYPE_URL || type == FTP_TYPE_URL || type == WAIS_TYPE_URL @@ -1977,7 +1998,7 @@ NET_GetURL (URL_Struct *URL_s, type = NET_URL_Type(URL_s->address); if (URL_s->method == URL_HEAD_METHOD && - type != HTTP_TYPE_URL && type != SECURE_HTTP_TYPE_URL && type != FILE_TYPE_URL) { + type != HTTP_TYPE_URL HG42421 && type != FILE_TYPE_URL) { /* We can only do HEAD on http connections. */ net_CallExitRoutine(exit_routine, URL_s, @@ -2035,6 +2056,7 @@ NET_GetURL (URL_Struct *URL_s, StrAllocCopy(new_address, PL_strchr(URL_s->address, ':')+1); FREE(URL_s->address); URL_s->address = new_address; + URL_s->allow_content_change = TRUE; type = NET_URL_Type(URL_s->address); @@ -2086,6 +2108,9 @@ NET_GetURL (URL_Struct *URL_s, #ifdef MOZILLA_CLIENT if( +#if defined(MOZ_LITE) && defined(XP_UNIX) + (type == NEWS_TYPE_URL && FE_AlternateNewsReader(URL_s->address)) || +#endif #if defined(XP_WIN) || defined(XP_MAC) || defined(XP_OS2) FE_UseExternalProtocolModule(window_id, output_format, URL_s, exit_routine) || #endif @@ -2115,7 +2140,7 @@ NET_GetURL (URL_Struct *URL_s, && type != DATA_TYPE_URL && type != HTML_DIALOG_HANDLER_TYPE_URL && type != HTML_PANEL_HANDLER_TYPE_URL - && type != INTERNAL_SECLIB_TYPE_URL + HG87237 && !PL_strcasestr(URL_s->address, "mcom.com") && !PL_strcasestr(URL_s->address, "netscape.com")) @@ -2169,17 +2194,16 @@ NET_GetURL (URL_Struct *URL_s, || !URL_s->post_headers || !PL_strncmp("Content-type", URL_s->post_headers, 12)) { - if(h && h->security_on) + if(h HG52423) { /* if this is not a secure transaction */ - if(type != SECURE_HTTP_TYPE_URL + if(HG76373 && !(type == NEWS_TYPE_URL && toupper(*URL_s->address) == 'S') ) { if(URL_s->method == URL_POST_METHOD) { - continue_loading_url = (Bool)SECNAV_SecurityDialog(window_id, - SD_INSECURE_POST_FROM_SECURE_DOC); + continue_loading_url = HG72388 } else if(!URL_s->redirecting_url && type != MAILTO_TYPE_URL @@ -2188,8 +2212,7 @@ NET_GetURL (URL_Struct *URL_s, { /* don't put up in case of redirect or mocha */ - continue_loading_url = (Bool)SECNAV_SecurityDialog(window_id, - SD_LEAVING_SECURE_SPACE); + continue_loading_url = HG52223 } } } @@ -2199,16 +2222,14 @@ NET_GetURL (URL_Struct *URL_s, * except news and mail posts */ if(URL_s->method == URL_POST_METHOD - && type != SECURE_HTTP_TYPE_URL + HG53535 && type != INTERNAL_NEWS_TYPE_URL && type != NEWS_TYPE_URL && type != HTML_DIALOG_HANDLER_TYPE_URL && type != HTML_PANEL_HANDLER_TYPE_URL - && type != INTERNAL_SECLIB_TYPE_URL && type != MAILTO_TYPE_URL) { - continue_loading_url = (Bool)SECNAV_SecurityDialog(window_id, - SD_INSECURE_POST_FROM_INSECURE_DOC); + continue_loading_url = (Bool)SECNAV_SecurityDialog(window_id, SD_INSECURE_POST_FROM_INSECURE_DOC); } } @@ -2353,10 +2374,12 @@ NET_GetURL (URL_Struct *URL_s, } if(type == HTTP_TYPE_URL || type == FILE_TYPE_URL || - type == SECURE_HTTP_TYPE_URL || type == GOPHER_TYPE_URL + HG73277 || type == GOPHER_TYPE_URL #ifdef JAVA - /* Castanet URLs don't work when there's no Java */ +#if 0 +/* DHIREN */ /* Kludge because castanet URLs don't work when there's no Java */ || type == MARIMBA_TYPE_URL +#endif #endif ) add_slash_to_URL(URL_s); @@ -2386,6 +2409,17 @@ NET_GetURL (URL_Struct *URL_s, (URL_s->force_reload == NET_CACHE_ONLY_RELOAD)) URL_s->force_reload = NET_DONT_RELOAD; + /* Before looking in the cache, make sure we check the + * special cases to know if + * we're allowed to use modified content. + * Two cases so far: a view-source URL, and + * any non-internal URL. Non-internal URLs are + * the result of link clicks, drag-and-drop, etc. + */ + if ((CLEAR_CACHE_BIT(output_format) == FO_VIEW_SOURCE) || + (!URL_s->internal_url && + (CLEAR_CACHE_BIT(output_format) != FO_MAIL_TO))) + URL_s->allow_content_change = TRUE; /* check for the url in the cache */ cache_method = NET_FindURLInCache(URL_s, window_id); @@ -2519,7 +2553,7 @@ NET_GetURL (URL_Struct *URL_s, } else if(NET_CacheUseMethod == CU_CHECK_ALL_THE_TIME && CLEAR_CACHE_BIT(output_format) == FO_PRESENT - && (type == HTTP_TYPE_URL || type == SECURE_HTTP_TYPE_URL) + && (type == HTTP_TYPE_URL HG73738) && !URL_s->expires) { /* cache testing stuff */ @@ -2549,7 +2583,7 @@ NET_GetURL (URL_Struct *URL_s, } else if(!URL_s->last_modified && CLEAR_CACHE_BIT(output_format) == FO_PRESENT - && (type == HTTP_TYPE_URL || type == SECURE_HTTP_TYPE_URL) + && (type == HTTP_TYPE_URL HG62522) #ifdef MOZ_OFFLINE && !NET_IsOffline() #endif /* MOZ_OFFLINE */ @@ -2735,16 +2769,16 @@ redo_load_switch: /* come here on file/ftp retry */ pacf_status = TRUE; if ((NET_ProxyAcLoaded || NET_GlobalAcLoaded) && (this_entry->protocol == HTTP_TYPE_URL - || this_entry->protocol == SECURE_HTTP_TYPE_URL - || this_entry->protocol == GOPHER_TYPE_URL - || this_entry->protocol == FTP_TYPE_URL - || this_entry->protocol == WAIS_TYPE_URL - || this_entry->protocol == URN_TYPE_URL - || this_entry->protocol == NFS_TYPE_URL - || (this_entry->protocol == NEWS_TYPE_URL - && !PL_strncasecmp(URL_s->address, "snews:", 6) - ) + HG62363 + || this_entry->protocol == GOPHER_TYPE_URL + || this_entry->protocol == FTP_TYPE_URL + || this_entry->protocol == WAIS_TYPE_URL + || this_entry->protocol == URN_TYPE_URL + || this_entry->protocol == NFS_TYPE_URL + || (this_entry->protocol == NEWS_TYPE_URL + && !PL_strncasecmp(URL_s->address, "snews:", 6) ) + ) && ((this_entry->proxy_conf = pacf_find_proxies_for_url(window_id, URL_s)) != NULL) && (pacf_status = pacf_get_proxy_addr(window_id, @@ -2756,12 +2790,7 @@ redo_load_switch: /* come here on file/ftp retry */ ) { TRACEMSG(("PAC returned \"%s\" for \"%s\".", this_entry->proxy_conf, URL_s->address)); - /* Secure protocols need to be kept in their own protocol - * to make SSL tunneling happen. */ - if (this_entry->protocol != SECURE_HTTP_TYPE_URL - && this_entry->protocol != NEWS_TYPE_URL) { - this_entry->protocol = HTTP_TYPE_URL; - } + HG24242 /* Everything else except SNEWS gets loaded by HTTP loader, * including HTTPS. */ @@ -3231,7 +3260,7 @@ PUBLIC int NET_ProcessNet (PRFileDesc *ready_fd, int fd_type) } else if(tmpEntry->status < 0 && !tmpEntry->URL_s->use_local_copy - && PR_GetError() != SSL_ERROR_BAD_CERTIFICATE + HG42469 && (tmpEntry->status == MK_CONNECTION_REFUSED || tmpEntry->status == MK_CONNECTION_TIMED_OUT || tmpEntry->status == MK_UNABLE_TO_CREATE_SOCKET @@ -3357,7 +3386,7 @@ PUBLIC int NET_ProcessNet (PRFileDesc *ready_fd, int fd_type) * Also allows waiting urls into the active list. */ PRIVATE int -net_InterruptActiveStream (ActiveEntry *entry) +net_InterruptActiveStream (ActiveEntry *entry, Bool show_warning) { TRACEMSG(("Terminating transfer on port #%d, proto: %d", entry->socket, entry->protocol)); @@ -3459,7 +3488,7 @@ NET_InterruptStream (URL_Struct *nurl) /* assert (entryToKill);*/ /* Kill it & free it */ if (entryToKill) - status = net_InterruptActiveStream (entryToKill); + status = net_InterruptActiveStream (entryToKill, TRUE); else status = -1; @@ -3499,7 +3528,7 @@ NET_InterruptSocket (PRFileDesc *socket) } if (entryToKill) - status = net_InterruptActiveStream (entryToKill); + status = net_InterruptActiveStream (entryToKill, TRUE); else status = -1; @@ -3651,7 +3680,7 @@ net_InternalInterruptWindow(MWContext * window_id, Bool show_warning) else { FE_EnableClicking(window_id); - net_InterruptActiveStream (tmpEntry); + net_InterruptActiveStream (tmpEntry, show_warning); number_killed += 1; /* unset call_all_connections_complete here @@ -4180,10 +4209,10 @@ NET_FreeURLStruct (URL_Struct * URL_s) FREEIF(URL_s->window_chrome); FREEIF(URL_s->refresh_url); FREEIF(URL_s->wysiwyg_url); + FREEIF(URL_s->origin_url); FREEIF(URL_s->error_msg); - FREEIF(URL_s->sec_info); - FREEIF(URL_s->redirect_sec_info); + HG87376 /* Free all memory associated with header information */ net_FreeURLAllHeaders(URL_s); @@ -4249,6 +4278,10 @@ net_FreeURLAllHeaders (URL_Struct * URL_s) /* if there is no slash in a URL besides the two that specify a host * then add one to the end. + * + * this fails with file:// URLs of the form file:///Hard_disk + * because there are 3 leading slashes already. Fixed for Mac, to fix bug 113706 + * */ PRIVATE void add_slash_to_URL (URL_Struct *URL_s) { @@ -4257,6 +4290,14 @@ PRIVATE void add_slash_to_URL (URL_Struct *URL_s) /* make sure there is a hostname */ +#if defined(XP_MAC) /* || defined(XP_WIN) || defined(XP_UNIX) - add when comfortable.*/ + /* should probably be XP at some stage */ + if (PL_strncmp("file://", URL_s->address, 7) == 0) + { + slash = PL_strchr(colon + 4,'/'); + } + else +#endif if(*(colon+1) == '/' && *(colon+2) == '/') slash = PL_strchr(colon+3,'/'); else @@ -4282,79 +4323,7 @@ PRIVATE void add_slash_to_URL (URL_Struct *URL_s) } #ifdef MOZILLA_CLIENT -/* print out security URL - */ -PRIVATE int net_output_security_url(ActiveEntry * cur_entry, MWContext *cx) -{ - NET_StreamClass * stream; - char * content_type; - char * which = cur_entry->URL_s->address; - char * colon = PL_strchr (which, ':'); - - if (colon) - { - /* found the first colon; now find the question mark - (as in "about:security?certs"). */ - which = colon + 1; - colon = PL_strchr (which, '?'); - if (colon) - which = colon + 1; - else - which = which + PL_strlen (which); /* give it "" */ - } - - content_type = SECNAV_SecURLContentType(which); - if (!content_type) { - cur_entry->status = MK_MALFORMED_URL_ERROR; - - } else if (!PL_strcasecmp(content_type, "advisor")) { - cur_entry->status = SECNAV_SecHandleSecurityAdvisorURL(cx, which); - - } else { - int status; - - StrAllocCopy(cur_entry->URL_s->content_type, content_type); - - cur_entry->format_out = CLEAR_CACHE_BIT(cur_entry->format_out); - - stream = NET_StreamBuilder(cur_entry->format_out, - cur_entry->URL_s, cur_entry->window_id); - - if (!stream) - return(MK_UNABLE_TO_CONVERT); - - status = SECNAV_SecURLData(which, stream, cx); - - if (status >= 0) { - (*stream->complete) (stream); - } else { - (*stream->abort) (stream, status); - } - - cur_entry->status = status; - - FREE(stream); - } - - return(-1); -} - -PRIVATE int32 -net_SecurityURLLoad(ActiveEntry *ce) -{ - if(ce->URL_s) - StrAllocCopy(ce->URL_s->charset, INTL_ResourceCharSet()); - return net_output_security_url(ce, ce->window_id); -} - -PRIVATE int32 -net_SeclibURLLoad(ActiveEntry *ce) -{ - if(ce->URL_s) - StrAllocCopy(ce->URL_s->charset, INTL_ResourceCharSet()); - SECNAV_HandleInternalSecURL(ce->URL_s, ce->window_id); - return -1; -} +HG83667 PRIVATE int32 net_HTMLPanelLoad(ActiveEntry *ce) @@ -4434,8 +4403,7 @@ net_reg_random_protocol(NET_ProtoInitFunc *LoadRoutine, int type) PRIVATE void NET_InitTotallyRandomStuffPeopleAddedProtocols(void) { - net_reg_random_protocol(net_SecurityURLLoad, SECURITY_TYPE_URL); - net_reg_random_protocol(net_SeclibURLLoad, INTERNAL_SECLIB_TYPE_URL); + HG00484 net_reg_random_protocol(net_HTMLPanelLoad, HTML_PANEL_HANDLER_TYPE_URL); net_reg_random_protocol(net_HTMLDialogLoad, HTML_DIALOG_HANDLER_TYPE_URL); net_reg_random_protocol(net_WysiwygLoad, WYSIWYG_TYPE_URL); @@ -4591,12 +4559,7 @@ NET_SetProxyServer(NET_ProxyType type, const char * org_host_port) else FREE_AND_CLEAR(MKhttp_proxy); break; - case HTTPS_PROXY: - if(host_port) - StrAllocCopy(MKhttps_proxy, host_port); - else - FREE_AND_CLEAR(MKhttps_proxy); - break; + HG88000 case NEWS_PROXY: if(host_port) StrAllocCopy(MKnews_proxy, host_port); @@ -4651,9 +4614,7 @@ NET_FindProxyHostForUrl(int url_type, char *url_address) return(MKhttp_proxy); break; - case SECURE_HTTP_TYPE_URL: - return(MKhttps_proxy); - break; + HG50027 case NEWS_TYPE_URL: case INTERNAL_NEWS_TYPE_URL: @@ -4786,8 +4747,7 @@ int32 net_MailtoLoad (ActiveEntry * cur_entry) char *other_random_headers = 0; /* unused (for now) */ char *priority = 0; char *newshost = 0; /* internal only */ - XP_Bool encrypt_p = FALSE; - XP_Bool sign_p = FALSE; /* internal only */ + HG72762 char *newspost_url = 0; XP_Bool force_plain_text = FALSE; to = NET_ParseURL (CE_URL_S->address, GET_PATH_PART); @@ -4853,12 +4813,7 @@ int32 net_MailtoLoad (ActiveEntry * cur_entry) } } break; - case 'E': case 'e': - if (!PL_strcasecmp (token, "encrypt") || - !PL_strcasecmp (token, "encrypted")) - encrypt_p = (!PL_strcasecmp(value, "true") || - !PL_strcasecmp(value, "yes")); - break; + HG16262 case 'F': case 'f': if (!PL_strcasecmp (token, "followup-to")) StrAllocCopy (followup_to, value); @@ -4896,11 +4851,7 @@ int32 net_MailtoLoad (ActiveEntry * cur_entry) case 'S': case 's': if(!PL_strcasecmp (token, "subject")) StrAllocCopy (subject, value); - else if ((!PL_strcasecmp (token, "sign") || - !PL_strcasecmp (token, "signed")) && - CE_URL_S->internal_url) - sign_p = (!PL_strcasecmp(value, "true") || - !PL_strcasecmp(value, "yes")); + HG72661 break; case 'P': case 'p': if (!PL_strcasecmp (token, "priority")) @@ -4949,11 +4900,7 @@ int32 net_MailtoLoad (ActiveEntry * cur_entry) { char *prefix = "news://"; char *slash = PL_strrchr (newshost, '/'); - if (slash && !PL_strcasecmp (slash, "/secure")) - { - *slash = 0; - prefix = "snews://"; - } + HG32828 newspost_url = (char *) PR_Malloc (PL_strlen (prefix) + PL_strlen (newshost) + 10); if (newspost_url) @@ -4965,16 +4912,11 @@ int32 net_MailtoLoad (ActiveEntry * cur_entry) } else { - XP_Bool newsServerIsSecure = FALSE; - PREF_GetBoolPref("news.server_is_secure", &newsServerIsSecure); - - if (newsServerIsSecure) - newspost_url = PL_strdup("snews:"); - else + HG13227 newspost_url = PL_strdup ("news:"); } -#if defined(XP_WIN) /* other FE's add here when Front end code added */ +#if defined(XP_WIN) || defined(XP_UNIX) /* other FE's add here when Front end code added */ FE_AlternateCompose( from, reply_to, to, cc, bcc, fcc, newsgroups, followup_to, organization, diff --git a/mozilla/network/main/mkmessag.c b/mozilla/network/main/mkmessag.c index 676f2e3f0da..5404c82fd8f 100644 --- a/mozilla/network/main/mkmessag.c +++ b/mozilla/network/main/mkmessag.c @@ -16,6 +16,7 @@ * Reserved. */ +#include "rosetta.h" #include "mkutils.h" #include "mktcp.h" #include "gui.h" @@ -52,7 +53,7 @@ NET_ExplainErrorDetails (int code, ...) if (IS_SSL_ERROR(code) || IS_SEC_ERROR(code)) { const char *s = XP_GetString(code); - msg = (s ? PL_strdup(s) : 0); + msg = (s ? XP_STRDUP(s) : 0); } if (!msg) @@ -142,7 +143,7 @@ NET_ExplainErrorDetails (int code, ...) case MK_MSG_ERROR_WRITING_MAIL_FOLDER: case MK_MSG_SEARCH_FAILED: case MK_MSG_FOLDER_BUSY: - msg = PL_strdup(XP_GetString(code)); + msg = XP_STRDUP(XP_GetString(code)); break; case MK_TCP_READ_ERROR: @@ -156,7 +157,7 @@ NET_ExplainErrorDetails (int code, ...) /* * For SSL/SEC errors, use the message without a wrapper. */ - msg = PL_strdup(XP_GetString(sub_error)); + msg = XP_STRDUP(XP_GetString(sub_error)); } else if (code == MK_UNABLE_TO_CONNECT && (sub_error == XP_ERRNO_EINVAL || sub_error == XP_ERRNO_EADDRINUSE)) { @@ -164,7 +165,7 @@ NET_ExplainErrorDetails (int code, ...) * With unable-to-connect errors, some errno values/strings * are not more helpful, so just use a plain message for these. */ - msg = PL_strdup(XP_GetString(MK_UNABLE_TO_CONNECT2)); + msg = XP_STRDUP(XP_GetString(MK_UNABLE_TO_CONNECT2)); } else { msg = PR_smprintf(XP_GetString(code), XP_GetString(sub_error)); } diff --git a/mozilla/network/main/mkparse.c b/mozilla/network/main/mkparse.c index 89811650a90..eb9ef6a7d03 100644 --- a/mozilla/network/main/mkparse.c +++ b/mozilla/network/main/mkparse.c @@ -681,10 +681,13 @@ NET_ParseDate(char *date_string) #endif } +/* Also skip '>' as part of host name */ + MODULE_PRIVATE char * NET_ParseURL (const char *url, int parts_requested) { - char *rv=0,*colon, *slash, *ques_mark, *hash_mark, *atSign, *host, *passwordColon; + char *rv=0,*colon, *slash, *ques_mark, *hash_mark; + char *atSign, *host, *passwordColon, *gtThan; assert(url); @@ -769,6 +772,11 @@ NET_ParseURL (const char *url, int parts_requested) if(ques_mark) *ques_mark = '\0'; + gtThan = PL_strchr(host, '>'); + + if (gtThan) + *gtThan = '\0'; + /* * Protect systems whose header files forgot to let the client know when * gethostbyname would trash their stack. @@ -805,6 +813,9 @@ NET_ParseURL (const char *url, int parts_requested) if(ques_mark) *ques_mark = '?'; + + if (gtThan) + *gtThan = '>'; } } } @@ -928,7 +939,9 @@ int netCharType[256] = 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */ 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */ 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */ - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */ + 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */ + /* bits for '@' changed from 7 to 0 so '@' can be escaped */ + /* in usernames and passwords in publishing. */ 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */ 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */ 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */ diff --git a/mozilla/network/main/mktcp.h b/mozilla/network/main/mktcp.h index eb3a78d0992..2153222ef07 100644 --- a/mozilla/network/main/mktcp.h +++ b/mozilla/network/main/mktcp.h @@ -18,6 +18,8 @@ #ifndef MKTCP_H #define MKTCP_H +#include "rosetta.h" + PR_BEGIN_EXTERN_C /* state machine data for host connect */ @@ -95,7 +97,7 @@ NET_BeginConnect (CONST char *url, char *protocol, int default_port, PRFileDesc **s, - Bool use_security, + HG92743 TCP_ConData **tcp_con_data, MWContext *window_id, char **error_msg, diff --git a/mozilla/network/main/mkutils.c b/mozilla/network/main/mkutils.c index 9b35b3c5c18..2b61a5b9e24 100644 --- a/mozilla/network/main/mkutils.c +++ b/mozilla/network/main/mkutils.c @@ -35,7 +35,7 @@ #include "msgcom.h" #include "mime.h" #include "secrng.h" -#include "ssl.h" +#include HG38763 #include "prefapi.h" #include "secnav.h" #include "preenc.h" @@ -822,7 +822,7 @@ HG29784 fprintf(stderr, "Fwrite wrote less than requested!\n"); #endif - /* safty for broken SSL_write */ + /* safety for broken write */ if(amt_wrt > amt_to_wrt) amt_wrt = amt_to_wrt; @@ -2122,25 +2122,9 @@ NET_IsFQDNMailAddress(const char * string) return(FALSE); } -static int use_ssl_for_imap4 = -1; /* -1 if uninitialized, 0 if FALSE, 1 if TRUE. */ +HG83778 -/* fix Mac warning about missing prototype */ -MODULE_PRIVATE int PR_CALLBACK net_use_ssl_for_imap4_changed_func(const char *pref, - void *data); -MODULE_PRIVATE int PR_CALLBACK net_use_ssl_for_imap4_changed_func(const char *pref, - void *data) -{ - int status = PREF_NOERROR; - - if (!PL_strcasecmp(pref,"mail.imap.server_ssl")) { - XP_Bool new_val; - - status = PREF_GetBoolPref("mail.imap.server_ssl", &new_val); - use_ssl_for_imap4 = (int)new_val; - } - return status; -} /* returns true if the URL is a secure URL address @@ -2152,8 +2136,7 @@ NET_IsURLSecure(char * address) TRACEMSG(("NET_IsURLSecure called, type: %d", type)); - if(type == SECURE_HTTP_TYPE_URL - || type == INTERNAL_IMAGE_TYPE_URL + if(HG83773 type == INTERNAL_IMAGE_TYPE_URL || type == SECURE_LDAP_TYPE_URL) return(TRUE); @@ -2174,22 +2157,7 @@ NET_IsURLSecure(char * address) * IMAP URLs begin with "mailbox://" unlike POP URLs which begin * with "mailbox:". */ - if(!PL_strncasecmp(address, "mailbox://", 10)) { - if (use_ssl_for_imap4 < 0) { /* If uninitialized. */ - XP_Bool new_val; - int status = PREF_GetBoolPref("mail.imap.server_ssl", &new_val); - - if (status == PREF_NOERROR) { - use_ssl_for_imap4 = (int)new_val; - PREF_RegisterCallback("mail.imap.server_ssl", - net_use_ssl_for_imap4_changed_func, NULL); - } - else { - return FALSE; - } - } - return (Bool)use_ssl_for_imap4; - } + HG35632 TRACEMSG(("NET_IsURLSecure: URL NOT SECURE")); @@ -2998,9 +2966,8 @@ NET_URL_Type (CONST char *URL) switch(*URL) { case 'a': case 'A': - if(!PL_strncasecmp(URL,"about:security", 14)) - return(SECURITY_TYPE_URL); - else if(!PL_strncasecmp(URL,"about:",6)) + HG83787 + if(!PL_strncasecmp(URL,"about:",6)) return(ABOUT_TYPE_URL); else if(!PL_strncasecmp(URL,"addbook:",8)) return(ADDRESS_BOOK_TYPE_URL); @@ -3036,8 +3003,7 @@ NET_URL_Type (CONST char *URL) case 'H': if(!PL_strncasecmp(URL,"http:",5)) return(HTTP_TYPE_URL); - else if(!PL_strncasecmp(URL,"https:",6)) - return(SECURE_HTTP_TYPE_URL); + HG73678 break; case 'i': case 'I': @@ -3057,10 +3023,7 @@ NET_URL_Type (CONST char *URL) return(HTML_DIALOG_HANDLER_TYPE_URL); else if(!PL_strncasecmp(URL,"internal-panel-handler",22)) return(HTML_PANEL_HANDLER_TYPE_URL); - else if(!PL_strncasecmp(URL,"internal-security-",18)) - return(INTERNAL_SECLIB_TYPE_URL); - else if(!PL_strncasecmp(URL,"internal-certldap",17)) - return(INTERNAL_CERTLDAP_TYPE_URL); + HG84378 else if(!PL_strncasecmp(URL,"IMAP:",5)) return(IMAP_TYPE_URL); break; @@ -3075,8 +3038,7 @@ NET_URL_Type (CONST char *URL) return(MOCHA_TYPE_URL); else if (!PL_strncasecmp(URL, "ldap:",5)) return(LDAP_TYPE_URL); - else if (!PL_strncasecmp(URL, "ldaps:",6)) - return(SECURE_LDAP_TYPE_URL); + HG84772 break; case 'm': case 'M': @@ -3524,3 +3486,5 @@ NET_GetURLQuick (URL_Struct * URL_s, return status; } } + + diff --git a/mozilla/network/protocol/Makefile b/mozilla/network/protocol/Makefile index b8ca67af59e..d598bbf28f6 100644 --- a/mozilla/network/protocol/Makefile +++ b/mozilla/network/protocol/Makefile @@ -19,12 +19,14 @@ DEPTH = ../.. +include $(DEPTH)/config/config.mk + DIRS = about \ file \ ftp \ gopher \ - http \ - remote + remote \ + $(NULL) ifndef MODULAR_NETLIB DIRS += \ @@ -34,5 +36,23 @@ DIRS += \ $(NULL) endif +ifdef MOZ_LDAP +DIRS += ldap \ + $(NULL) +endif + +ifdef MOZ_MAIL_NEWS +DIRS += \ + mailbox \ + smtp \ + nntp \ + pop3 \ + imap4 \ + certld \ + $(NULL) +endif + +DIRS += http + include $(DEPTH)/config/rules.mk diff --git a/mozilla/network/protocol/about/mkabout.c b/mozilla/network/protocol/about/mkabout.c index 9a56c1ca2c4..ace9d2bd4c3 100644 --- a/mozilla/network/protocol/about/mkabout.c +++ b/mozilla/network/protocol/about/mkabout.c @@ -1,3 +1,4 @@ +#include "rosetta.h" #include "xp.h" #include "net.h" #include "netutils.h" @@ -23,6 +24,9 @@ #include "abouturl.h" +/* XXX ugh. FE_GetNetDir returns XP_STRDUP'd result, must be XP_FREE'd */ +#include "xp_mem.h" + extern int XP_NETSITE_ ; extern int XP_LOCATION_ ; extern int XP_FILE_MIME_TYPE_ ; @@ -44,8 +48,7 @@ extern int XP_MAC_CREATOR_ ; extern int XP_CHARSET_ ; extern int XP_STATUS_UNKNOWN ; extern int XP_MSG_UNKNOWN ; -extern int XP_SECURITY_ ; -extern int XP_CERTIFICATE_ ; +HG42784 extern int XP_UNTITLED_DOCUMENT ; extern int XP_HAS_THE_FOLLOWING_STRUCT ; extern int XP_DOCUMENT_INFO ; @@ -189,25 +192,8 @@ net_OutputURLDocInfo(MWContext *ctxt, char *which, char **data, int32 *length) ADD_CELL(XP_GetString(XP_CHARSET_), XP_GetString(XP_MSG_UNKNOWN)); } - if(URL_s->cache_file || URL_s->memory_copy) - sec_msg = SECNAV_PrettySecurityStatus(URL_s->security_on, - URL_s->sec_info); - else - sec_msg = PL_strdup(XP_GetString(XP_STATUS_UNKNOWN)); + HG76363 - if(sec_msg) - { - ADD_CELL(XP_GetString(XP_SECURITY_), sec_msg); - PR_Free(sec_msg); - } - - sec_msg = SECNAV_SSLSocketCertString(URL_s->sec_info); - - if(sec_msg) - { - ADD_CELL(XP_GetString(XP_CERTIFICATE_), sec_msg); - PR_Free(sec_msg); - } StrAllocCat(output, ""); if(URL_s->content_type @@ -302,9 +288,9 @@ net_gen_pics_document(ActiveEntry *cur_entry) \ " - rv = PR_smprintf(PICS_HTML, help_dir); + rv = PR_smprintf(PICS_HTML, help_dir); - PR_Free(help_dir); + XP_FREE(help_dir); } @@ -739,6 +725,7 @@ PRIVATE Bool net_about_kludge(URL_Struct *URL_s) !PL_strcmp((char*)user, "\207\170\210\214\200\205") || /* paquin */ !PL_strcmp((char*)user, "\207\170\214\203\173") || /* pauld */ !PL_strcmp((char*)user, "\207\172\177\174\205") || /* pchen */ + !PL_strcmp((char*)user, "\207\177\200\203") || /* phil */ !PL_strcmp((char*)user, "\207\200\174\211\211\174") || /* pierre */ !PL_strcmp((char*)user, "\207\200\176\203\174\213") || /* piglet */ !PL_strcmp((char*)user, "\207\200\205\202\174\211\213\206\205") || /* pinkerton */ @@ -750,6 +737,7 @@ PRIVATE Bool net_about_kludge(URL_Struct *URL_s) !PL_strcmp((char*)user, "\211\201\172") || /* rjc */ !PL_strcmp((char*)user, "\211\204\216") || /* rmw */ !PL_strcmp((char*)user, "\211\170\204\211\170\201") || /* ramraj */ + !PL_strcmp((char*)user, "\211\200\172\170\211\173\206\171") || /* ricardob */ !PL_strcmp((char*)user, "\211\206\171\204") || /* robm */ !PL_strcmp((char*)user, "\211\206\174\171\174\211") || /* roeber */ !PL_strcmp((char*)user, "\211\207\206\213\213\212") || /* rpotts */ diff --git a/mozilla/network/protocol/callback/mkcburl.c b/mozilla/network/protocol/callback/mkcburl.c index e69de29bb2d..826647be2f5 100644 --- a/mozilla/network/protocol/callback/mkcburl.c +++ b/mozilla/network/protocol/callback/mkcburl.c @@ -0,0 +1,125 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +#include "xp.h" +#include "mkgeturl.h" +#include "netcburl.h" +#include "secrng.h" /* For RNG_GenerateGlobalRandomBytes */ + +typedef struct _NET_CallbackURLData { + int id; + char* match; + NET_CallbackURLFunc func; + void* closure; + struct _NET_CallbackURLData* next; +} NET_CallbackURLData; + +static NET_CallbackURLData* First = NULL; +static int Counter = 1; + + +char* +NET_CallbackURLCreate(NET_CallbackURLFunc func, void* closure) { + unsigned char rand_buf[13]; + char* result = NULL; + NET_CallbackURLData* tmp; + NET_CallbackURLFree(func, closure); + tmp = XP_NEW(NET_CallbackURLData); + if (!tmp) return NULL; + tmp->id = Counter++; + RNG_GenerateGlobalRandomBytes((void *) rand_buf, 12); + tmp->match = + PR_smprintf("%02X%02X%02X%02X" + "%02X%02X%02X%02X" + "%02X%02X%02X%02X", + rand_buf[0], rand_buf[1], rand_buf[2], rand_buf[3], + rand_buf[4], rand_buf[5], rand_buf[6], rand_buf[7], + rand_buf[8], rand_buf[9], rand_buf[10], rand_buf[11]); + if (tmp->match) { + result = PR_smprintf("internal-callback-handler:%d/%s", + tmp->id, tmp->match); + } + if (result == NULL) { + if (tmp->match) XP_FREE(tmp->match); + XP_FREE(tmp); + return NULL; + } + tmp->next = First; + First = tmp; + tmp->func = func; + tmp->closure = closure; + return result; +} + + +int +NET_CallbackURLFree(NET_CallbackURLFunc func, void* closure) { + NET_CallbackURLData** tmp; + NET_CallbackURLData* t; + for (tmp = &First ; *tmp ; tmp = &((*tmp)->next)) { + t = *tmp; + if (t->func == func && t->closure == closure) { + *tmp = t->next; + XP_FREE(t->match); + XP_FREE(t); + return 0; + } + } + return -1; +} + + + +int NET_LoadCallbackURL (ActiveEntry* ce) +{ + char* url = ce->URL_s->address; + char* path; + char* match; + int id; + NET_CallbackURLData* tmp; + + path = NET_ParseURL(url, GET_PATH_PART); + if (path == NULL) return -1; + id = XP_ATOI(path); + match = XP_STRCHR(path, '/'); + if (match) match++; + else match = ""; + + for (tmp = First ; tmp ; tmp = tmp->next) { + if (id == tmp->id && XP_STRCMP(match, tmp->match) == 0) { + (*tmp->func)(tmp->closure, url); + break; + } + } + XP_FREE(path); + return -1; +} + + +int NET_ProcessCallbackURL(ActiveEntry* ce) +{ + return -1; /* Should never get here */ +} + + +int NET_InterruptCallbackURL(ActiveEntry* ce) +{ + return -1; /* Should never get here */ +} + diff --git a/mozilla/network/protocol/certld/Makefile b/mozilla/network/protocol/certld/Makefile index 3fc78f14cb6..f1fbab0c356 100644 --- a/mozilla/network/protocol/certld/Makefile +++ b/mozilla/network/protocol/certld/Makefile @@ -21,12 +21,12 @@ MODULE = crtldurl LIBRARY_NAME = crtldurl CSRCS = \ - mkcretld.c \ + mkcertld.c \ $(NULL) -EXPORTS= crtldurl.h +EXPORTS= mkcertld.h -REQUIRES = network nspr2 dbm util pref js java fileurl \ +REQUIRES = ldap network nspr2 dbm util pref js java fileurl \ security layer img httpurl include $(DEPTH)/config/rules.mk diff --git a/mozilla/network/protocol/certld/makefile.win b/mozilla/network/protocol/certld/makefile.win index 87599908d33..c745a40ba0c 100644 --- a/mozilla/network/protocol/certld/makefile.win +++ b/mozilla/network/protocol/certld/makefile.win @@ -58,8 +58,8 @@ INCLUDES = $(LOCAL_INCLUDES) EXTRA_LIBS= -REQUIRES= network -EXPORTS= certurl.h +REQUIRES= network ldap +EXPORTS= mkcertld.h # use LINCS on win32 for now since REQUIRES seems to be broken #!if "$(MOZ_BITS)" != "16" @@ -70,6 +70,7 @@ LINCS= \ -I$(XPDIST)\public\pref \ -I$(XPDIST)\public\js \ -I$(XPDIST)\public\security \ + -I$(XPDIST)\public\ldap \ -I$(XPDIST)\public\network #!endif diff --git a/mozilla/network/protocol/dataurl/makefile.win b/mozilla/network/protocol/dataurl/makefile.win index 851d4ea4b22..b366b95b383 100644 --- a/mozilla/network/protocol/dataurl/makefile.win +++ b/mozilla/network/protocol/dataurl/makefile.win @@ -58,7 +58,7 @@ INCLUDES = $(LOCAL_INCLUDES) EXTRA_LIBS= -REQUIRES= network +REQUIRES= network security EXPORTS= dataurl.h # use LINCS on win32 for now since REQUIRES seems to be broken @@ -70,6 +70,7 @@ LINCS= \ -I$(XPDIST)\public\pref \ -I$(XPDIST)\public\js \ -I$(XPDIST)\public\netcnvts \ + -I$(XPDIST)\public\security \ -I$(XPDIST)\public\network #!endif diff --git a/mozilla/network/protocol/file/makefile.win b/mozilla/network/protocol/file/makefile.win index aa76cca5e80..17dc1d8a024 100644 --- a/mozilla/network/protocol/file/makefile.win +++ b/mozilla/network/protocol/file/makefile.win @@ -58,7 +58,7 @@ INCLUDES = $(LOCAL_INCLUDES) EXTRA_LIBS= -REQUIRES= network +REQUIRES= network security EXPORTS= fileurl.h # use LINCS on win32 for now since REQUIRES seems to be broken @@ -72,6 +72,7 @@ LINCS= \ -I$(PUBLIC)\jtools \ -I$(PUBLIC)\network \ -I$(PUBLIC)\mimetype \ + -I$(PUBLIC)\security \ $(NULL) #!endif diff --git a/mozilla/network/protocol/file/mkfile.c b/mozilla/network/protocol/file/mkfile.c index de936fb6d77..ab2e8698199 100644 --- a/mozilla/network/protocol/file/mkfile.c +++ b/mozilla/network/protocol/file/mkfile.c @@ -741,7 +741,7 @@ net_setup_file_stream (ActiveEntry * cur_entry) PR_ASSERT(0); #endif /* MOZILLA_CLIENT */ } - else if (!PL_strncmp(CE_URL_S->address, "Mailbox://", 10)) + else if (!PL_strncmp(CE_URL_S->address, "IMAP://", 7)) { #ifdef MOZILLA_CLIENT #ifdef MOZ_MAIL_NEWS @@ -938,6 +938,7 @@ net_read_directory_chunk (ActiveEntry * cur_entry) PL_strcpy(full_path, CD_FILENAME); PL_strcat(full_path, "/"); PL_strcat(full_path, dir_entry->d_name); + memset(&stat_entry, 0, sizeof(XP_StatStruct)); /* paranoia */ if(XP_Stat(full_path, &stat_entry, xpURL) != -1) { @@ -945,6 +946,9 @@ net_read_directory_chunk (ActiveEntry * cur_entry) if(S_ISDIR(stat_entry.st_mode)) { +#ifdef XP_MAC + stat_entry.st_size = 0; /* Mac stat gives spurious size data for folders */ +#endif file_entry->special_type = NET_DIRECTORY; StrAllocCat(file_entry->filename, "/"); } @@ -1004,7 +1008,7 @@ net_return_local_file_part_from_url(char *address) return NULL; /* imap urls are never local */ - if (!PL_strncasecmp(address,"mailbox://",10)) + if (!PL_strncasecmp(address,"IMAP://",7)) return NULL; /* mailbox url's are always local, but don't always point to a file */ @@ -1578,7 +1582,11 @@ PRIVATE void net_IdxConvComplete(NET_StreamClass *inputStream) * * note: add 4 to the date to get rid of the day of the week */ - if(file_entry->size) + if(file_entry->size +#ifdef XP_MAC + || file_entry->special_type == NET_FILE_TYPE /* show size for zero-length files */ +#endif + ) { PR_snprintf(&out_buf[PL_strlen(out_buf)], sizeof(out_buf) - PL_strlen(out_buf), diff --git a/mozilla/network/protocol/ftp/mkftp.c b/mozilla/network/protocol/ftp/mkftp.c index 2b7cb01e4a6..fbaf0ea1162 100644 --- a/mozilla/network/protocol/ftp/mkftp.c +++ b/mozilla/network/protocol/ftp/mkftp.c @@ -50,7 +50,7 @@ #include "mkfsort.h" #include "merrors.h" #include "shist.h" -#include "ssl.h" +#include HG27229 #include "prtime.h" #include "pwcacapi.h" #include "secnav.h" @@ -4112,7 +4112,7 @@ net_ProcessFTP(ActiveEntry * ce) "FTP", FTP_PORT, &cd->cc->csock, - FALSE, + HG27230 &cd->tcp_con_data, ce->window_id, &ce->URL_s->error_msg, @@ -4284,7 +4284,7 @@ net_ProcessFTP(ActiveEntry * ce) "FTP Data Connection", FTP_PORT, &cd->dsock, - FALSE, + HG27230 &cd->tcp_con_data, ce->window_id, &ce->URL_s->error_msg, diff --git a/mozilla/network/protocol/gopher/mkgopher.c b/mozilla/network/protocol/gopher/mkgopher.c index 509a756f831..540fdce2464 100644 --- a/mozilla/network/protocol/gopher/mkgopher.c +++ b/mozilla/network/protocol/gopher/mkgopher.c @@ -19,6 +19,7 @@ * Designed and Implemented by Lou Montulli circa '94 */ +#include "rosetta.h" #include "xp.h" #include "plstr.h" #include "prmem.h" @@ -1001,7 +1002,7 @@ net_ProcessGopher(ActiveEntry * cur_entry) "Gopher", 70, &CE_SOCK, - FALSE, + HG10300 &CD_TCP_CON_DATA, CE_WINDOW_ID, &CE_URL_S->error_msg, diff --git a/mozilla/network/protocol/http/Makefile b/mozilla/network/protocol/http/Makefile index 61e00679a18..8727f089cce 100644 --- a/mozilla/network/protocol/http/Makefile +++ b/mozilla/network/protocol/http/Makefile @@ -25,7 +25,7 @@ CSRCS = \ mkaccess.c \ $(NULL) -EXPORTS= httpurl.h httpauth.h cookies.h jscookie.h +EXPORTS= mkaccess.h httpurl.h httpauth.h cookies.h jscookie.h REQUIRES = netcache network nspr2 dbm util pref js java fileurl \ security layer img httpurl mimetype htmldlgs diff --git a/mozilla/network/protocol/http/makefile.win b/mozilla/network/protocol/http/makefile.win index 0ed14b5e168..51b0297d122 100644 --- a/mozilla/network/protocol/http/makefile.win +++ b/mozilla/network/protocol/http/makefile.win @@ -60,7 +60,7 @@ INCLUDES = $(LOCAL_INCLUDES) EXTRA_LIBS= REQUIRES= network -EXPORTS= httpurl.h cookies.h httpauth.h jscookie.h +EXPORTS= mkaccess.h httpurl.h cookies.h httpauth.h jscookie.h # use LINCS on win32 for now since REQUIRES seems to be broken #!if "$(MOZ_BITS)" != "16" diff --git a/mozilla/network/protocol/http/mkaccess.c b/mozilla/network/protocol/http/mkaccess.c index 17ee4e7a2a9..5e5031dec89 100644 --- a/mozilla/network/protocol/http/mkaccess.c +++ b/mozilla/network/protocol/http/mkaccess.c @@ -24,6 +24,7 @@ * This file implements HTTP access authorization * and HTTP cookies */ +#include "rosetta.h" #include "xp.h" #include "netutils.h" #include "mkselect.h" @@ -104,6 +105,8 @@ extern int MK_ACCESS_END_OF_SESSION; * on separate lists, but both lists consist of net_AuthStruct's. */ +HG73943 + PRIVATE XP_List * net_auth_list = NULL; PRIVATE XP_List * net_proxy_auth_list = NULL; @@ -131,17 +134,20 @@ typedef enum _net_AuthType { AUTH_INVALID = 0, AUTH_BASIC = 1 #ifdef SIMPLE_MD5 - , AUTH_SIMPLEMD5 = 2 /* Much better than "Basic" */ + HG72621 #endif /* SIMPLE_MD5 */ ,AUTH_FORTEZZA = 3 } net_AuthType; /* - * This struct describes both Basic and SimpleMD5 authentication stuff, + * This struct describes both Basic authentication stuff, * for both HTTP servers and proxies. * */ +HG10828 + + typedef struct _net_AuthStruct { net_AuthType auth_type; char * path; /* For normal authentication only */ @@ -151,14 +157,10 @@ typedef struct _net_AuthStruct { char * auth_string; /* Storage for latest Authorization str */ char * realm; /* For all auth schemes */ #ifdef SIMPLE_MD5 - char * domain; /* SimpleMD5 only */ - char * nonce; /* SimpleMD5 only */ - char * opaque; /* SimpleMD5 only */ - PRBool oldNonce; /* SimpleMD5 only */ - int oldNonce_retries; + HG82727 #endif char * challenge; - char * certChain; + HG26250 char * signature; char * clientRan; PRBool oldChallenge; @@ -194,6 +196,7 @@ net_CheckForAuthorization(char * address, Bool exact_match) return(auth_s); } } + HG25262 return(NULL); } @@ -211,6 +214,7 @@ NET_AuthorizationRequired(char * address) rv = net_CheckForAuthorization(address, FALSE); + HG17993 if(last_slash) *last_slash = '/'; @@ -280,7 +284,7 @@ net_free_auth_struct(net_AuthStruct *auth) PR_Free(auth->realm); /*FORTEZZA related stuff */ PR_FREEIF(auth->challenge); - PR_FREEIF(auth->certChain); + HG26763 PR_FREEIF(auth->signature); PR_FREEIF(auth->clientRan); /*End FORTEZZA related stuff*/ @@ -340,6 +344,8 @@ separate_http_key(char *key, char **address, char **realm) { char *tab; + HG72294 + *address = NULL; *realm = NULL; @@ -435,6 +441,7 @@ net_initialize_http_access(void) PC_RegisterDataInterpretFunc(HTTP_PW_MODULE_NAME, net_http_password_data_interp); + HG21522 } /* returns false if the user wishes to cancel authorization @@ -481,6 +488,7 @@ NET_AskForAuthString(MWContext *context, * authentication */ authenticate_header_value = XP_StripLine(authenticate); + HG03937 #define COMPUSERVE_HEADER_NAME "Remote-Passphrase" @@ -503,6 +511,7 @@ NET_AskForAuthString(MWContext *context, HTTP_BASIC_AUTH_TOKEN, sizeof(HTTP_BASIC_AUTH_TOKEN) - 1)) { + HG21632 /* unsupported auth type */ return(NET_AUTH_FAILED_DISPLAY_DOCUMENT); } @@ -585,6 +594,7 @@ NET_AskForAuthString(MWContext *context, * In this case we want to just retry the connection * since it will probably succede now. */ + HG22220 PR_FREEIF(host); PR_FREEIF(new_address); PR_FREEIF(username); @@ -802,6 +812,7 @@ NET_AskForAuthString(MWContext *context, return(NET_RETRY_WITH_AUTH); } + HG38932 NET_UUEncode((unsigned char *)u_pass_string, (unsigned char*) auth_string, len); PR_Free(u_pass_string); @@ -890,7 +901,7 @@ typedef struct _net_CookieStruct { char * cookie; time_t expires; time_t last_accessed; - Bool secure; /* only send for https connections */ + HG26237 Bool is_domain; /* is it a domain instead of an absolute host? */ } net_CookieStruct; @@ -1273,6 +1284,7 @@ NET_SetCookieBehaviorPref(NET_CookieBehaviorEnum x) { net_CookieBehavior = x; + HG83330 if(net_CookieBehavior == NET_DontUse) XP_FileRemove("", xpHTTPCookie); #if defined(CookieManagement) @@ -1407,7 +1419,7 @@ NET_GetCookie(MWContext * context, char * address) XP_List * list_ptr; net_CookieStruct * cookie_s; Bool first=TRUE; - Bool secure_path=FALSE; + HG26748 time_t cur_time = time(NULL); int host_length; int domain_length; @@ -1420,8 +1432,7 @@ NET_GetCookie(MWContext * context, char * address) if(NET_GetCookieBehaviorPref() == NET_DontUse) return NULL; - if(!PL_strncasecmp(address, "https", 5)) - secure_path = TRUE; + HG98476 /* search for all cookies */ @@ -1452,6 +1463,7 @@ NET_GetCookie(MWContext * context, char * address) &host[host_length - domain_length], domain_length)) { + HG20476 /* no match. FAIL */ continue; @@ -1476,8 +1488,7 @@ NET_GetCookie(MWContext * context, char * address) /* if the cookie is secure and the path isn't * dont send it */ - if(cookie_s->secure && !secure_path) - continue; /* back to top of while */ + HG83764 /* check for expired cookies */ @@ -1639,7 +1650,7 @@ net_IntSetCookieString(MWContext * context, char *cur_host = NET_ParseURL(cur_url, GET_HOST_PART); char *semi_colon, *ptr, *equal; const char *script_name; - PRBool set_secure=FALSE, is_domain=FALSE, ask=FALSE, accept=FALSE; + PRBool HG83744 is_domain=FALSE, ask=FALSE, accept=FALSE; MWContextType type; if(!context) { @@ -1666,6 +1677,7 @@ net_IntSetCookieString(MWContext * context, return; } + HG87358 /* terminate at any carriage return or linefeed */ for(ptr=set_cookie_header; *ptr; ptr++) if(*ptr == LF || *ptr == CR) { @@ -1686,8 +1698,7 @@ net_IntSetCookieString(MWContext * context, /* there must be some attributes. (hopefully) */ - if(PL_strcasestr(semi_colon, "secure")) - set_secure = TRUE; + HG83476 /* look for the path attribute */ @@ -1871,7 +1882,7 @@ net_IntSetCookieString(MWContext * context, cd->cookie_from_header = cookie_from_header; cd->expires = expires; cd->url = cur_url; - cd->secure = set_secure; + HG84777 cd->domain = is_domain; cd->prompt = NET_GetCookieWarningPref(); cd->preference = NET_GetCookieBehaviorPref(); @@ -1906,8 +1917,7 @@ net_IntSetCookieString(MWContext * context, if( cd->domain != is_domain ) /* lets hope the luser remembered to change the domain field */ is_domain = cd->domain; - if( cd->secure != set_secure ) - set_secure = cd->secure; + HG27398 } switch( result ) { case JSCF_reject: @@ -2094,7 +2104,7 @@ net_IntSetCookieString(MWContext * context, prev_cookie->path = path_from_header; prev_cookie->host = host_from_header; prev_cookie->name = name_from_header; - prev_cookie->secure = set_secure; + HG83263 prev_cookie->is_domain = is_domain; prev_cookie->last_accessed = time(NULL); } else { @@ -2121,7 +2131,7 @@ net_IntSetCookieString(MWContext * context, prev_cookie->path = path_from_header; prev_cookie->host = host_from_header; prev_cookie->expires = expires; - prev_cookie->secure = set_secure; + HG22730 prev_cookie->is_domain = is_domain; prev_cookie->last_accessed = time(NULL); @@ -2547,9 +2557,7 @@ NET_SaveCookies(char * filename) XP_FileWrite(cookie_s->path, -1, fp); XP_FileWrite("\t", 1, fp); - if(cookie_s->secure) - XP_FileWrite("TRUE", -1, fp); - else + HG74640 XP_FileWrite("FALSE", -1, fp); XP_FileWrite("\t", 1, fp); @@ -2610,7 +2618,7 @@ NET_ReadCookies(char * filename) size_t new_len; XP_File fp; char buffer[LINE_BUFFER_SIZE]; - char *host, *is_domain, *path, *secure, *expires, *name, *cookie; + char *host, *is_domain, *path, *xxx, *expires, *name, *cookie; Bool added_to_list; #if defined(CookieManagement) @@ -2625,11 +2633,11 @@ NET_ReadCookies(char * filename) /* format is: * - * host \t is_domain \t path \t secure \t expires \t name \t cookie + * host \t is_domain \t path \t xxx \t expires \t name \t cookie * * if this format isn't respected we move onto the next line in the file. * is_domain is TRUE or FALSE -- defaulting to FALSE - * secure is TRUE or FALSE -- should default to TRUE + * xxx is TRUE or FALSE -- should default to TRUE * expires is a time_t integer * cookie can have tabs */ @@ -2654,13 +2662,13 @@ NET_ReadCookies(char * filename) if(*path == CR || *path == LF || *path == 0) continue; - if( !(secure = PL_strchr(path, '\t')) ) + if( !(xxx = PL_strchr(path, '\t')) ) continue; - *secure++ = '\0'; - if(*secure == CR || *secure == LF || *secure == 0) + *xxx++ = '\0'; + if(*xxx == CR || *xxx == LF || *xxx == 0) continue; - if( !(expires = PL_strchr(secure, '\t')) ) + if( !(expires = PL_strchr(xxx, '\t')) ) continue; *expires++ = '\0'; if(*expires == CR || *expires == LF || *expires == 0) @@ -2699,10 +2707,9 @@ NET_ReadCookies(char * filename) StrAllocCopy(new_cookie->path, path); StrAllocCopy(new_cookie->host, host); new_cookie->expires = atol(expires); - if(!PL_strcmp(secure, "FALSE")) - new_cookie->secure = FALSE; - else - new_cookie->secure = TRUE; + + HG87365 + if(!PL_strcmp(is_domain, "TRUE")) new_cookie->is_domain = TRUE; else @@ -2752,9 +2759,10 @@ NET_ReadCookies(char * filename) * Figure out the authentication scheme used; currently supported: * * * Basic - * * SimpleMD5 * */ + HG73632 + PRIVATE net_AuthType net_auth_type(char *name) { @@ -2764,8 +2772,7 @@ net_auth_type(char *name) if (!PL_strncasecmp(name, "basic", 5)) return AUTH_BASIC; #ifdef SIMPLE_MD5 - else if (!PL_strncasecmp(name, "simplemd5", 9)) - return AUTH_SIMPLEMD5; + HG29383 #endif /*FORTEZZA checks*/ else if (!PL_strncasecmp(name, "fortezzaproxy", 13)) @@ -2909,7 +2916,7 @@ net_parse_authenticate_line(char *auth, net_AuthStruct *ret) /*Another FORTEZZA addition*/ if (!ret->oldChallenge) ret->oldChallenge_retries = 0; - /*End FPRTEZZA addition */ + /*End FORTEZZA addition */ return ret; } @@ -3045,22 +3052,7 @@ char *net_generate_auth_string(URL_Struct *url_s, #endif /* SIMPLE_MD5 */ /* Handle the FORTEZZA case */ case AUTH_FORTEZZA: - if (auth_s->signature && auth_s->challenge && - auth_s->certChain && auth_s->clientRan) { - int len; - - PR_FREEIF(auth_s->auth_string); - auth_s->auth_string = NULL; - - len = PL_strlen(auth_s->signature) + PL_strlen(auth_s->challenge) - + PL_strlen(auth_s->certChain) + PL_strlen(auth_s->clientRan) + 100; - auth_s->auth_string = (char *)PR_Malloc(len); - if (auth_s->auth_string) { - sprintf(auth_s->auth_string,"signature=\"%s\" challenge=\"%s\" " - "clientRan=\"%s\" certChain=\"%s\"",auth_s->signature, - auth_s->challenge, auth_s->clientRan, auth_s->certChain); - } - } + HG26251 break; /* Done Handling the FORTEZZA case */ } @@ -3193,11 +3185,7 @@ NET_AskForProxyAuth(MWContext * context, if (prev->auth_type == AUTH_FORTEZZA) { SECStatus rv; - rv = SECNAV_ComputeFortezzaProxyChallengeResponse(context, - prev->challenge, - &prev->signature, - &prev->clientRan, - &prev->certChain); + rv = HG26252 if ( rv != SECSuccess ) { return(FALSE); } @@ -3465,7 +3453,7 @@ net_AboutCookiesDialogDone(XPDialogState* state, char** argv, int argc, ((MWContext *)(state->arg), cookie->path, cookie->host, cookie->name, cookie->cookie, - cookie->expires, cookie->secure, cookie->is_domain); + cookie->expires, HG78111, cookie->is_domain); } return(PR_TRUE); diff --git a/mozilla/network/protocol/http/mkhttp.c b/mozilla/network/protocol/http/mkhttp.c index 778a9a2fe17..a73cd79cfad 100644 --- a/mozilla/network/protocol/http/mkhttp.c +++ b/mozilla/network/protocol/http/mkhttp.c @@ -36,7 +36,7 @@ #include "shist.h" #include "glhist.h" #include "mkparse.h" -#include "mkstream.h" +#include "netstream.h" #include "mkformat.h" #include "mkaccess.h" #include "cookies.h" @@ -264,8 +264,10 @@ int ReturnErrorStatus (int status) status |= status; /* set a breakpoint HERE to find errors */ return status; } + #define STATUS(Status) ReturnErrorStatus (Status) + #define PUTBLOCK(b, l) (*cd->stream->put_block) \ (cd->stream, b, l) #define PUTSTRING(s) (*cd->stream->put_block) \ @@ -274,6 +276,8 @@ int ReturnErrorStatus (int status) (cd->stream) #define ABORT_STREAM(s) (*cd->stream->abort) \ (cd->stream, s) + + PUBLIC void NET_SetSendRefererHeader(Bool b) { @@ -452,36 +456,11 @@ PRIVATE int net_start_http_connect(ActiveEntry * ce) { HTTPConData * cd = (HTTPConData *)ce->con_data; - Bool use_security=FALSE; + HG29898 int def_port; def_port = DEF_HTTP_PORT; - if(ce->protocol == SECURE_HTTP_TYPE_URL) - { - - if(CD_PROXY_SERVER) - { - CD_USE_PROXY_TUNNEL = TRUE; - CD_PROXY_TUNNEL_SETUP_DONE = FALSE; - - TRACEMSG(("HTTP: Using proxy tunnel")); - - /* put in code to check for using a proxy tunnel - * if we need to use proxy tunnel set the options - * on the next two lines - * - * use_security = TRUE; - * def_port = DEF_HTTPS_PORT; - * - * @@@@@ - */ - } - else - { - use_security = TRUE; - def_port = DEF_HTTPS_PORT; - } - } + HG22201 /* if proxy_server is non NULL then use the string as a host:port * when a proxy server is used a connection is made to the proxy @@ -498,7 +477,7 @@ net_start_http_connect(ActiveEntry * ce) "HTTP", def_port, &cd->connection->sock, - use_security, + HG38738 &CD_TCP_CON_DATA, CE_WINDOW_ID, &CE_URL_S->error_msg, @@ -512,7 +491,7 @@ net_start_http_connect(ActiveEntry * ce) "HTTP", def_port, &cd->connection->sock, - use_security, + HG02873 &CD_TCP_CON_DATA, CE_WINDOW_ID, &CE_URL_S->error_msg, @@ -613,10 +592,7 @@ net_finish_http_connect(ActiveEntry * ce) int def_port; def_port = DEF_HTTP_PORT; - if(ce->protocol == SECURE_HTTP_TYPE_URL) - { - def_port = DEF_HTTPS_PORT; - } + HG92892 /* if proxy_server is non NULL then use the string as a host:port * when a proxy server is used a connection is made to the proxy @@ -2013,48 +1989,10 @@ net_parse_first_http_line (ActiveEntry *ce) } } - /* this is where kipp says that I can finally query - * for the security data. We can't do it after the - * connect since the handshake isn't done yet... - */ - /* clear existing data - */ - PR_FREEIF(CE_URL_S->sec_info); - ce->URL_s->sec_info = SECNAV_SSLSocketStatus(cd->connection->sock, - &ce->URL_s->security_on); + HG21899 #ifdef MOZILLA_CLIENT - if ( ce->URL_s->redirect_sec_info && - ( ! (CD_USE_PROXY_TUNNEL && !CD_PROXY_TUNNEL_SETUP_DONE) ) ) { - /* don't do the redirect check when talking to the proxy, - * wait for it to come through here a second time, when - * we are really using xxx to talk to the real server. - */ - - PRBool compare; - - compare = SECNAV_CompareCertsForRedirection(ce->URL_s->sec_info, - ce->URL_s->redirect_sec_info); - - /* now that we are done with the redirecting info destroy it */ - PR_Free(ce->URL_s->redirect_sec_info); - ce->URL_s->redirect_sec_info = NULL; - - if ( compare != PR_TRUE ) { - /* certs are different */ - - do_redirect = (Bool)SECNAV_SecurityDialog(CE_WINDOW_ID, - SD_REDIRECTION_TO_SECURE_SITE); - if ( !do_redirect ) { - /* stop connection here!! */ - CE_URL_S->error_msg = 0; /* XXX - is this right? */ - - /* return TCP error - */ - return MK_TCP_READ_ERROR; - } - } - } + HG19088 #endif /* MOZILLA_CLIENT */ /* CE_STATUS greater than 0 @@ -2326,17 +2264,15 @@ net_parse_first_http_line (ActiveEntry *ce) * don't do if this is coming from history * don't do this if about to redirect */ - if(CE_URL_S->security_on + if(HG82773 && (CE_FORMAT_OUT == FO_CACHE_AND_PRESENT || CE_FORMAT_OUT == FO_PRESENT) && !CE_URL_S->history_num) { History_entry * h = SHIST_GetCurrent(&CE_WINDOW_ID->hist); - if(!h || !h->security_on) - SECNAV_SecurityDialog(CE_WINDOW_ID, - SD_ENTERING_SECURE_SPACE); - } + HG03903 + } #endif /* MOZILLA_CLIENT */ CD_USE_COPY_FROM_CACHE = TRUE; @@ -2696,21 +2632,7 @@ net_setup_http_stream(ActiveEntry * ce) } /* End URL_s->dontAllowDiffHostRedirect */ - if(CE_FORMAT_OUT == FO_CACHE_AND_PRESENT) { - if(NET_IsURLSecure(CE_URL_S->address)) { /* current URL is secure*/ - if(NET_IsURLSecure(CE_URL_S->redirecting_url)) { - /* new URL is secure */ - /* save the cert of the guy doing the redirect */ - ce->URL_s->redirect_sec_info = - SECNAV_CopySSLSocketStatus(ce->URL_s->sec_info); - - } else { /* new URL is not secure */ - /* ask the user to confirm */ - do_redirect = (Bool)SECNAV_SecurityDialog(CE_WINDOW_ID, - SD_REDIRECTION_TO_INSECURE_DOC); - } - } - } + HG92871 /* OK, now we've got the redirection URL stored * in redirecting_url. @@ -2764,33 +2686,31 @@ net_setup_http_stream(ActiveEntry * ce) } #ifdef MOZILLA_CLIENT - /* check to see if we just now entered a secure space - * - * don't do if this is coming from history - * don't do this if about to redirect - */ - if(CE_URL_S->security_on - && (CE_FORMAT_OUT == FO_CACHE_AND_PRESENT || CE_FORMAT_OUT == FO_PRESENT) - && !CE_URL_S->history_num) - { + + + /* */ + + /* check to see if we just now entered a secure space */ + /* don't do if this is coming from history */ + /* don't do this if about to redirect */ + if( HG22087 + (CE_FORMAT_OUT == FO_CACHE_AND_PRESENT || CE_FORMAT_OUT == FO_PRESENT) + && !CE_URL_S->history_num) + { History_entry * h = SHIST_GetCurrent(&CE_WINDOW_ID->hist); XP_Bool warn = FALSE; - + if (h == NULL) { - /* Deal with frames. If the window doesn't have history, - * then it is a new window or a new frame cell. - */ - if ( ce->window_id->grid_parent != NULL ) { + /* Deal with frames. If the window doesn't have history, */ + /* then it is a new window or a new frame cell. */ + if ( ce->window_id->grid_parent != NULL ) { h = SHIST_GetCurrent(&ce->window_id->grid_parent->hist); - if ( !h->security_on ) { - /* parent frame is not secure */ - warn = TRUE; - } + HG22088 } else { /* no parent frame - this is a top level window */ warn = TRUE; } - } else if ( !h->security_on ) { + } else if (HG22089) { warn = TRUE; } if ( warn ) { @@ -2890,7 +2810,7 @@ net_setup_http_stream(ActiveEntry * ce) CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CONVERT); return STATUS(CE_STATUS); } -HG94794 + NET_SetReadSelect(CE_WINDOW_ID, cd->connection->sock); if(ce->URL_s->files_to_post) @@ -2960,7 +2880,7 @@ HG94794 /* @@@ bug, check return status and only send * up to the return value */ - (*CD_STREAM->is_write_ready)(CD_STREAM); + (*cd->stream->is_write_ready)(CD_STREAM); CE_STATUS = PUTBLOCK(CD_LINE_BUFFER, CD_LINE_BUFFER_SIZE); CE_BYTES_RECEIVED = CD_LINE_BUFFER_SIZE; FE_GraphProgress(CE_WINDOW_ID, @@ -3123,7 +3043,7 @@ net_pull_http_data(ActiveEntry * ce) /* check to see if the stream is ready for writing */ - write_ready = (*CD_STREAM->is_write_ready)(CD_STREAM); + write_ready = (*cd->stream->is_write_ready)(CD_STREAM); if(!write_ready) { @@ -3220,7 +3140,7 @@ net_HTTPLoad (ActiveEntry * ce) { /* get memory for Connection Data */ HTTPConData * cd = PR_NEW(HTTPConData); - XP_Bool url_is_secure = FALSE; + HG21092 char *use_host; ce->con_data = cd; @@ -3264,8 +3184,7 @@ net_HTTPLoad (ActiveEntry * ce) else { use_host = NET_ParseURL(ce->URL_s->address, GET_HOST_PART); - if(!PL_strncasecmp(ce->URL_s->address, "https:", 6)) - url_is_secure = TRUE; + HG09309 } if(!use_host) @@ -3339,8 +3258,7 @@ net_HTTPLoad (ActiveEntry * ce) * and the connection * is not busy at the moment then reuse this connection. */ - if(!PL_strcmp(tmp_con->hostname, use_host) - && url_is_secure == tmp_con->secure + if(HG29802 && !tmp_con->busy) { cd->connection = tmp_con; @@ -3400,7 +3318,7 @@ net_HTTPLoad (ActiveEntry * ce) StrAllocCopy(cd->connection->hostname, use_host); - cd->connection->secure = url_is_secure; + HG93882 cd->connection->prev_cache = FALSE; /* this wasn't from the cache */ @@ -3696,7 +3614,7 @@ HG51096 CE_BYTES_RECEIVED); PR_FREEIF(CD_LINE_BUFFER); - PR_FREEIF(CD_STREAM); /* don't forget the stream */ + PR_Free(CD_STREAM); /* don't forget the stream */ PR_FREEIF(CD_SERVER_HEADERS); PR_FREEIF(cd->orig_host); if(CD_TCP_CON_DATA) @@ -3755,7 +3673,7 @@ HG51096 cd->connection->sock = NULL; if(CD_STREAM) - (*CD_STREAM->abort) (CD_STREAM, CE_STATUS); + (*cd->stream->abort)(CD_STREAM, CE_STATUS); CD_SEND_HTTP1 = FALSE; /* go back and send an HTTP0 request */ CD_NEXT_STATE = HTTP_START_CONNECT; @@ -3874,8 +3792,7 @@ NET_InitHTTPProtocol(void) http_proto_impl.cleanup = net_CleanupHTTP; NET_RegisterProtocolImplementation(&http_proto_impl, HTTP_TYPE_URL); - NET_RegisterProtocolImplementation(&http_proto_impl, SECURE_HTTP_TYPE_URL); - + HG93898 } #ifdef PROFILE diff --git a/mozilla/network/protocol/imap4/Makefile b/mozilla/network/protocol/imap4/Makefile index e69de29bb2d..7b241ad2658 100644 --- a/mozilla/network/protocol/imap4/Makefile +++ b/mozilla/network/protocol/imap4/Makefile @@ -0,0 +1,43 @@ +#!gmake +# +# The contents of this file are subject to the Netscape Public License +# Version 1.0 (the "NPL"); you may not use this file except in +# compliance with the NPL. You may obtain a copy of the NPL at +# http://www.mozilla.org/NPL/ +# +# Software distributed under the NPL is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL +# for the specific language governing rights and limitations under the +# NPL. +# +# The Initial Developer of this code under the NPL is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All Rights +# Reserved. + +DEPTH = ../../.. + +MODULE = imap4url +LIBRARY_NAME = imap4url + +CSRCS = \ + imap4url.c \ + $(NULL) + +CPPSRCS = \ + imapbody.cpp \ + imapearl.cpp \ + imaphier.cpp \ + imappars.cpp \ + mkimap4.cpp \ + $(NULL) + +EXPORTS= imap4pvt.h imap4url.h imapbody.h imaphier.h mkimap4.h + +REQUIRES = util network nspr2 security \ + netcache network nspr2 dbm util pref js java fileurl \ + security layer img httpurl mimetype htmldlgs + +include $(DEPTH)/config/rules.mk + +INCLUDES += -I$(DEPTH)/lib/libmsg diff --git a/mozilla/network/protocol/imap4/imap4pvt.h b/mozilla/network/protocol/imap4/imap4pvt.h index e69de29bb2d..b0e82c18b1f 100644 --- a/mozilla/network/protocol/imap4/imap4pvt.h +++ b/mozilla/network/protocol/imap4/imap4pvt.h @@ -0,0 +1,1586 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +/* classes and functions used to implement the IMAP4 netlib module. +*/ + +/* 45678901234567890123456789012345678901234567890123456789012345678901234567890 +*/ +#ifndef IMAP4PVT_H +#define IMAP4PVT_H + +#include "rosetta.h" +#include "mkimap4.h" +#include "prthread.h" +#include "prtypes.h" +#include "prmon.h" +#include "xp_list.h" +#include "imap.h" +#include "imaphier.h" +#include "idarray.h" +#include "ptrarray.h" +#include "mktcp.h" + + +#define IMAP4_PORT 143 /* the iana port for imap4 */ +#define IMAP4_PORT_SSL_DEFAULT 993 /* use a separate port for imap4 over ssl */ + +/* amount to expand for imap entry flags when we need more */ +#define kFlagEntrySize 100 + +// convenient function for atol on int32's +int32 atoint32(char *ascii); + +/* + Using a separate thread to drive the IMAP4 server connection algorithm + is being introduced in 4.0. Since this adding another thread to an + otherwise single threaded program (navigator), then we need a mechanism + for the IMAP thread to use existing, non thread safe, code. + It is a basic rule that all pre-existing code should be executed in the + main 'mozilla' thread. + + TImapFEEventQueue and TImapFEEvent exist to accomplish this goal. + + Whenever the imap thread needs to do something like post an alert or + operate a thermo, then it will add an TImapFEEvent to a TImapFEEventQueue. + + The main (mozilla) thread, in NET_ProcessIMAP4, will periodically check the queue + and execute any events. + + The mozilla thread will also run events in NET_InterruptIMAP4. However, some events + should not be run when interrupted. This is mainly because of sharing memory + between threads through these events, but it can also be for reasons of context + as well. (For instace, running a certain event might not make sense if we get + interrupted). In general, if any event dispatcher creates an event and passes memory + to it which will be freed in the IMAP thread after that event has run, then it should + tell it not to execute when interrupted. This is because interruption tells the thread + to die, which notifies the Event Completion monitor. So, we think these events have + finished, and therefore free the memory; however, the next thing that NET_InterruptIMAP4 + does is try to run these events, which access freed memory, and then bad things happen... + +*/ +typedef void (FEEventFunctionPointer)(void*,void*); + +class TImapFEEvent { +public: + TImapFEEvent(FEEventFunctionPointer *function, void *arg1, void*arg2, XP_Bool executeWhenInterrupted); + virtual ~TImapFEEvent(); + + virtual XP_Bool ShouldExecuteWhenInterrupted() { return fExecuteWhenInterrupted; } + virtual void DoEvent(); + virtual FEEventFunctionPointer *GetEventFunction() { return fEventFunction; } + virtual void SetEventFunction(FEEventFunctionPointer *f) { fEventFunction = f; } + +private: + FEEventFunctionPointer *fEventFunction; + void *fArg1, *fArg2; + XP_Bool fExecuteWhenInterrupted; +}; + +// all of the member functions of TImapFEEventQueue are multithread safe +class TImapFEEventQueue { +public: + static TImapFEEventQueue *CreateFEEventQueue(); + virtual ~TImapFEEventQueue(); + + virtual void AdoptEventToEnd(TImapFEEvent *event); + virtual void AdoptEventToBeginning(TImapFEEvent *event); + virtual TImapFEEvent* OrphanFirstEvent(); + + virtual int NumberOfEventsInQueue(); +protected: + TImapFEEventQueue(); + +private: + XP_List *fEventList; + PRMonitor *fListMonitor; + int fNumberOfEvents; +}; + + +/* This class is used by the imap module to get info about a + url. This ensures that the knowledge about the URL syntax + is not spread across hundreds of strcmp calls. +*/ + +#define IMAP_URL_TOKEN_SEPARATOR ">" + +class TIMAPUrl { +public: + TIMAPUrl(const char *url_string, XP_Bool internal); + virtual ~TIMAPUrl(); + + XP_Bool ValidIMAPUrl(); + + enum EUrlIMAPstate { + kAuthenticatedStateURL, + kSelectedStateURL + }; + + EUrlIMAPstate GetUrlIMAPstate(); + + + enum EIMAPurlType { + // kAuthenticatedStateURL urls + kTest, + kSelectFolder, + kLiteSelectFolder, + kExpungeFolder, + kCreateFolder, + kDeleteFolder, + kRenameFolder, + kMoveFolderHierarchy, + kLsubFolders, + kGetMailAccountUrl, + kDiscoverChildrenUrl, + kDiscoverLevelChildrenUrl, + kDiscoverAllBoxesUrl, + kDiscoverAllAndSubscribedBoxesUrl, + kAppendMsgFromFile, + kSubscribe, + kUnsubscribe, + kRefreshACL, + kRefreshAllACLs, + kListFolder, + kUpgradeToSubscription, + kFolderStatus, + kRefreshFolderUrls, + + // kSelectedStateURL urls + kMsgFetch, + kMsgHeader, + kSearch, + kDeleteMsg, + kDeleteAllMsgs, + kAddMsgFlags, + kSubtractMsgFlags, + kSetMsgFlags, + kOnlineCopy, + kOnlineMove, + kOnlineToOfflineCopy, + kOnlineToOfflineMove, + kOfflineToOnlineMove, + kBiff + }; + + EIMAPurlType GetIMAPurlType(); + + const char *GetUrlHost() { return fHostSubString; } + + // these functions have Preconditions + char *CreateCanonicalSourceFolderPathString(); + char *CreateCanonicalDestinationFolderPathString(); + + char *CreateServerSourceFolderPathString(); + char *CreateServerDestinationFolderPathString(); + + char *CreateSearchCriteriaString(); + char *CreateListOfMessageIdsString(); + char *GetIMAPPartToFetch(); + imapMessageFlagsType GetMsgFlags(); // kAddMsgFlags, kSubtractMsgFlags, or kSetMsgFlags only + + XP_Bool MessageIdsAreUids(); + XP_Bool MimePartSelectorDetected() { return fMimePartSelectorDetected; } + + void SetOnlineSubDirSeparator(char onlineDirSeparator); + char GetOnlineSubDirSeparator(); + + char *AllocateServerPath(const char *canonicalPath); + char *AllocateCanonicalPath(const char *serverPath); + + int GetChildDiscoveryDepth() { return fDiscoveryDepth; } + XP_Bool GetShouldSubscribeToAll(); + +private: + // assigning and copying not allowed + TIMAPUrl(const TIMAPUrl& copy); + TIMAPUrl& operator=(const TIMAPUrl& copy); + + void Parse(); + void ParseSearchCriteriaString(); + void ParseListofMessageIds(); + void ParseUidChoice(); + void ParseMsgFlags(); + void ParseChildDiscoveryDepth(); + + void ParseFolderPath(char **resultingCanonicalPath); + + char *ReplaceCharsInCopiedString(const char *stringToCopy, char oldChar, char newChar); + + + char *fUrlString; + char *fHostSubString; + char *fUrlidSubString; + char *fSourceCanonicalFolderPathSubString; + char *fDestinationCanonicalFolderPathSubString; + char *fSearchCriteriaString; + char *fListOfMessageIds; + char *fTokenPlaceHolder; + char fOnlineSubDirSeparator; // not static anymore - one delimiter for each URL we run. + + imapMessageFlagsType fFlags; + + XP_Bool fIdsAreUids; + XP_Bool fMimePartSelectorDetected; + + EUrlIMAPstate fIMAPstate; + EIMAPurlType fUrlType; + + XP_Bool fValidURL; + XP_Bool fInternal; + int fDiscoveryDepth; +}; + + +class TSearchResultSequence { +public: + virtual ~TSearchResultSequence(); + static TSearchResultSequence *CreateSearchResultSequence(); + + virtual void AddSearchResultLine(const char *searchLine); + virtual void ResetSequence(); + + friend class TSearchResultIterator; +private: + TSearchResultSequence(); + XP_List *fListOfLines; +}; + +class TSearchResultIterator { +public: + TSearchResultIterator(TSearchResultSequence &sequence); + virtual ~TSearchResultIterator(); + + void ResetIterator(); + int32 GetNextMessageNumber(); // returns 0 at end of list +private: + TSearchResultSequence &fSequence; + XP_List *fCurrentLine; + char *fPositionInCurrentLine; +}; + + +class TIMAP4BlockingConnection; + + +class TImapFlagAndUidState { +public: + TImapFlagAndUidState(int numberOfMessages, XP_Bool bSupportUserFlag = FALSE); + TImapFlagAndUidState(const TImapFlagAndUidState& state, XP_Bool bSupportUserFlag = FALSE); + virtual ~TImapFlagAndUidState(); + + int GetNumberOfMessages(); + int GetNumberOfDeletedMessages(); + + imap_uid GetUidOfMessage(int zeroBasedIndex); + imapMessageFlagsType GetMessageFlags(int zeroBasedIndex); + imapMessageFlagsType GetMessageFlagsFromUID(imap_uid uid, XP_Bool *foundIt, int32 *ndx); + XP_Bool IsLastMessageUnseen(void); + void ExpungeByIndex(uint32 index); + // adds to sorted list. protects against duplicates and going past fNumberOfMessageSlotsAllocated + void AddUidFlagPair(imap_uid uid, imapMessageFlagsType flags); + + imap_uid GetHighestNonDeletedUID(); + void Reset(uint32 howManyLeft); +private: + + int32 fNumberOfMessagesAdded; + int32 fNumberOfMessageSlotsAllocated; + int32 fNumberDeleted; + IDArray fUids; + imapMessageFlagsType *fFlags; + XP_Bool fSupportUserFlag; +}; + + +class TIMAPGenericParser { + +public: + TIMAPGenericParser(); + virtual ~TIMAPGenericParser(); + + // Connected() && !SyntaxError() + // Add any specific stuff in the derived class + virtual XP_Bool LastCommandSuccessful(); + + XP_Bool SyntaxError(); + virtual XP_Bool ContinueParse(); + + // if we get disconnected, end the current url processing and report to the + // the user. + XP_Bool Connected(); + virtual void SetConnected(XP_Bool error); + + char *CreateSyntaxErrorLine(); + +protected: + + // This is a pure virtual member which must be overridden in the derived class + // for each different implementation of a TIMAPGenericParser. + // For instance, one implementation (the TImapServerState) might get the next line + // from an open socket, whereas another implementation might just get it from a buffer somewhere. + // This fills in nextLine with the buffer, and returns TRUE if everything is OK. + // Returns FALSE if there was some error encountered. In that case, we reset the parser. + virtual XP_Bool GetNextLineForParser(char **nextLine) = 0; + + virtual void HandleMemoryFailure(); + virtual void skip_to_CRLF(); + virtual void skip_to_close_paren(); + virtual char *CreateString(); + virtual char *CreateAstring(); + virtual char *CreateNilString(); + virtual char *CreateLiteral(); + virtual char *CreateAtom(); + virtual char *CreateQuoted(XP_Bool skipToEnd = TRUE); + virtual char *CreateParenGroup(); + virtual void SetSyntaxError(XP_Bool error); + virtual XP_Bool at_end_of_line(); + + char *GetNextToken(); + void AdvanceToNextLine(); + void AdvanceTokenizerStartingPoint (int32 bytesToAdvance); + void ResetLexAnalyzer(); + +protected: + // use with care + char *fNextToken; + char *fCurrentLine; + char *fLineOfTokens; + char *fStartOfLineOfTokens; + char *fCurrentTokenPlaceHolder; + +private: + XP_Bool fAtEndOfLine; + XP_Bool fTokenizerAdvanced; + + char *fSyntaxErrorLine; + XP_Bool fDisconnected; + XP_Bool fSyntaxError; + +}; + +class TIMAPNamespace; +class TIMAPNamespaceList; +class TIMAPBodyShell; + +class TImapServerState : public TIMAPGenericParser { +public: + TImapServerState(TIMAP4BlockingConnection &imapConnection); + virtual ~TImapServerState(); + + // Overridden from the base parser class + virtual XP_Bool LastCommandSuccessful(); + virtual void HandleMemoryFailure(); + + virtual void ParseIMAPServerResponse(const char *currentCommand); + virtual void InitializeState(); + XP_Bool CommandFailed(); + + enum eIMAPstate { + kNonAuthenticated, + kAuthenticated, + kFolderSelected, + kWaitingForMoreClientInput + } ; + + virtual eIMAPstate GetIMAPstate(); + virtual void SetIMAPstate(eIMAPstate state) { fIMAPstate = state; }; + + const char *GetSelectedMailboxName(); // can be NULL + + // if we get a PREAUTH greeting from the server, initialize the parser to begin in + // the kAuthenticated state + void PreauthSetAuthenticatedState(); + + // these functions represent the state of the currently selected + // folder + XP_Bool CurrentFolderReadOnly(); + int32 NumberOfMessages(); + int32 NumberOfRecentMessages(); + int32 NumberOfUnseenMessages(); + int32 FolderUID(); + uint32 CurrentResponseUID(); + uint32 HighestRecordedUID(); + int32 SizeOfMostRecentMessage(); + void SetTotalDownloadSize(int32 newSize) { fTotalDownloadSize = newSize; } + + TSearchResultIterator *CreateSearchResultIterator(); + void ResetSearchResultSequence() {fSearchResults->ResetSequence();} + + // create a struct mailbox_spec from our info, used in + // libmsg c interface + struct mailbox_spec *CreateCurrentMailboxSpec(const char *mailboxName = NULL); + + // zero stops a list recording of flags and causes the flags for + // each individual message to be sent back to libmsg + void ResetFlagInfo(int numberOfInterestingMessages); + + // set this to false if you don't want to alert the user to server + // error messages + void SetReportingErrors(XP_Bool reportThem) { fReportingErrors=reportThem;} + XP_Bool GetReportingErrors() { return fReportingErrors; } + + uint32 GetCapabilityFlag() { return fCapabilityFlag; } + void SetCapabilityFlag(uint32 capability) {fCapabilityFlag = capability;} + XP_Bool ServerHasIMAP4Rev1Capability() { return (fCapabilityFlag & kIMAP4rev1Capability); } + XP_Bool ServerHasACLCapability() { return (fCapabilityFlag & kACLCapability); } + XP_Bool ServerHasNamespaceCapability() { return (fCapabilityFlag & kNamespaceCapability); } + XP_Bool ServerIsNetscape3xServer() { return fServerIsNetscape3xServer; } + XP_Bool ServerHasServerInfo() {return (fCapabilityFlag & kXServerInfoCapability);} + void ResetCapabilityFlag() ; + + const char *GetMailAccountUrl() { return fMailAccountUrl; } + const char *GetXSenderInfo() { return fXSenderInfo; } + void FreeXSenderInfo() { FREEIF(fXSenderInfo); } + const char *GetManageListsUrl() { return fManageListsUrl; } + const char *GetManageFiltersUrl() {return fManageFiltersUrl;} + const char *GetManageFolderUrl() {return fFolderAdminUrl;} + + + TImapFlagAndUidState *GetCurrentFlagState() { return fFlagState; } + + // Call this when adding a pipelined command to the session + void IncrementNumberOfTaggedResponsesExpected(const char *newExpectedTag); + + // Interrupt a Fetch, without really Interrupting (through netlib) + XP_Bool GetLastFetchChunkReceived(); + void ClearLastFetchChunkReceived(); + virtual XP_Bool SupportsUserFlags() { return fSupportsUserDefinedFlags; }; + void SetFlagState(TImapFlagAndUidState *state); + + XP_Bool GetFillingInShell(); + void UseCachedShell(TIMAPBodyShell *cachedShell); + +protected: + virtual void flags(); + virtual void response_data(); + virtual void resp_text(); + virtual void resp_cond_state(); + virtual void text_mime2(); + virtual void text(); + virtual void resp_text_code(); + virtual void response_done(); + virtual void response_tagged(); + virtual void response_fatal(); + virtual void resp_cond_bye(); + virtual void mailbox_data(); + virtual void numeric_mailbox_data(); + virtual void capability_data(); + virtual void xserverinfo_data(); + virtual void xmailboxinfo_data(); + virtual void namespace_data(); + virtual void myrights_data(); + virtual void acl_data(); + virtual void bodystructure_data(); + virtual void mime_data(); + virtual void mime_part_data(); + virtual void mime_header_data(); + virtual void msg_fetch(); + virtual void msg_obsolete(); + virtual void msg_fetch_headers(const char *partNum); + virtual void msg_fetch_content(XP_Bool chunk, int32 origin, const char *content_type); + virtual XP_Bool msg_fetch_quoted(XP_Bool chunk, int32 origin); + virtual XP_Bool msg_fetch_literal(XP_Bool chunk, int32 origin); + virtual void mailbox_list(XP_Bool discoveredFromLsub); + virtual void mailbox(mailbox_spec *boxSpec); + + virtual XP_Bool IsNumericString(const char *string); + + virtual void ProcessOkCommand(const char *commandToken); + virtual void ProcessBadCommand(const char *commandToken); + virtual void PreProcessCommandToken(const char *commandToken, + const char *currentCommand); + virtual void PostProcessEndOfLine(); + + // Overridden from the TIMAPGenericParser, to retrieve the next line + // from the open socket. + virtual XP_Bool GetNextLineForParser(char **nextLine); + virtual void end_of_line(); + +private: + XP_Bool fProcessingTaggedResponse; + XP_Bool fCurrentCommandFailed; + XP_Bool fReportingErrors; + + + XP_Bool fCurrentFolderReadOnly; + + XP_Bool fCurrentLineContainedFlagInfo; + imapMessageFlagsType fSavedFlagInfo; + + + XP_Bool fSupportsUserDefinedFlags; + + int32 fFolderUIDValidity; + int32 fNumberOfUnseenMessages; + int32 fNumberOfExistingMessages; + int32 fNumberOfRecentMessages; + uint32 fCurrentResponseUID; + uint32 fHighestRecordedUID; + int32 fSizeOfMostRecentMessage; + int32 fTotalDownloadSize; + + int fNumberOfTaggedResponsesExpected; + + char *fCurrentCommandTag; + + char *fZeroLengthMessageUidString; + + char *fSelectedMailboxName; + + TSearchResultSequence *fSearchResults; + + TImapFlagAndUidState *fFlagState; // NOT owned by this object, it's a copy, do not destroy + + eIMAPstate fIMAPstate; + + uint32 fCapabilityFlag; + char *fMailAccountUrl; + char *fNetscapeServerVersionString; + char *fXSenderInfo; /* changed per message download */ + char *fLastAlert; /* used to avoid displaying the same alert over and over */ + char *fManageListsUrl; + char *fManageFiltersUrl; + char *fFolderAdminUrl; + + // used for index->uid mapping + XP_Bool fCurrentCommandIsSingleMessageFetch; + int32 fUidOfSingleMessageFetch; + int32 fFetchResponseIndex; + + // used for aborting a fetch stream when we're pseudo-Interrupted + XP_Bool fDownloadingHeaders; + int32 numberOfCharsInThisChunk; + int32 charsReadSoFar; + XP_Bool fLastChunk; + + // Is the server a Netscape 3.x Messaging Server? + XP_Bool fServerIsNetscape3xServer; + + // points to the current body shell, if any + TIMAPBodyShell *m_shell; + + // The connection object + TIMAP4BlockingConnection &fServerConnection; +}; + +/* + This virtual base class contains the interface and the code + that drives the algorithms for performing imap4 server + transactions. + + This base class contains no knowledge of 'how things were' before + 4.0. The knowledge of our URL language, how we do socket io, + and how we perform events in other threads is found in a concrete + subclass. +*/ + +#define kOutputBufferSize 2048 + +class TNavigatorImapConnection; + + +class TIMAP4BlockingConnection { +public: + enum eFetchFields { + kEveryThingRFC822, + kEveryThingRFC822Peek, + kHeadersRFC822andUid, + kUid, + kFlags, + kRFC822Size, + kRFC822HeadersOnly, + kMIMEPart, + kMIMEHeader + }; + + + virtual ~TIMAP4BlockingConnection(); + + // This should be used only for setting the connection status, i.e. MK_CONNECTED, MK_WAITING_FOR_CONNECTION, etc. + // This should not be used for getting/setting the status of the currently running ActiveEntry. + // (Use Get/SetCurrentEntryStatus()) for passing values back to libnet about the currently running ActiveEntry. + int GetConnectionStatus(); + void SetConnectionStatus(int status); + + // safe downcast, returns non-null if cast worked + virtual TNavigatorImapConnection *GetNavigatorConnection(); + + // Have we detected new mail ? + virtual XP_Bool NewMailDetected(void) { return FALSE; }; + virtual void ParseIMAPandCheckForNewMail(char *buff = NULL) = 0; + + // These 2 functions are thread safe. If BlockedForIO then it will + // become unblocked with a call to NotifyIOCompletionMonitor + void NotifyIOCompletionMonitor(); + XP_Bool BlockedForIO(); + + virtual void ProcessBIFFRequest(); + + // non-authenticated state commands + virtual void InsecureLogin(const char *userName, const char *password); + // Authenticate will happen later + + virtual void AuthLogin(const char *userName, const char *password); + + // authenticated state commands + virtual void SelectMailbox(const char *mailboxName); + // will do examine as needed + virtual void CreateMailbox(const char *mailboxName); + virtual void DeleteMailbox(const char *mailboxName); + virtual void RenameMailbox(const char *existingName, const char *newName); + + virtual void AppendMessage(const char *mailboxName, + const char *messageSizeString, + imapMessageFlagsType flags); + + virtual void Subscribe(const char *mailboxName); + virtual void Unsubscribe(const char *mailboxName); + + virtual void List(const char *mailboxPattern,XP_Bool pipeLined = FALSE, XP_Bool dontTimeStamp = FALSE); + virtual void Lsub(const char *mailboxPattern, XP_Bool pipelined = FALSE); + + // selected state commands + virtual void FetchMessage(const char *messageIds, + eFetchFields whatToFetch, + XP_Bool idAreUid, + uint32 startByte = 0, uint32 endByte = 0, + char *part = 0) = 0; + virtual void Bodystructure(const char *messageId, XP_Bool idIsUid) = 0; + + virtual void Close(); + virtual void Expunge(); + virtual void Check(); + + virtual void Search(const char *searchCriteria, XP_Bool useUID, + XP_Bool notifyHit = TRUE); + + virtual void Store(const char *messageList, + const char *messageData, + XP_Bool idsAreUid); + + virtual void Copy( const char *messageList, + const char *destinationMailbox, + XP_Bool idsAreUid); + + + virtual void Netscape(); + virtual void XServerInfo(); + virtual void XMailboxInfo(const char *mailboxName); + + + // any state commands + virtual void Logout(); + virtual void Noop(); + virtual void Capability(); + + virtual void Namespace(); + virtual void MailboxData(); + virtual void GetMyRightsForFolder(const char *mailboxName); + virtual void GetACLForFolder(const char *mailboxName); + + virtual void StatusForFolder(const char *mailboxName); + + virtual void AlertUserEvent_UsingId(uint32 msgId) = 0; + virtual void AlertUserEvent(char *message) = 0; + virtual void AlertUserEventFromServer (char *serverSaid) = 0; + virtual void ProgressUpdateEvent(char *message) = 0; + virtual void ProgressEventFunction_UsingId(uint32 msgId) = 0; + virtual void ProgressEventFunction_UsingIdWithString(uint32 msgId, const char * extraInfo) = 0; + virtual void PercentProgressUpdateEvent(char *message, int percent) = 0; + virtual void PastPasswordCheckEvent() = 0; + + // does a blocking read of the socket, returns a newly allocated + // line or nil and a status + virtual char *CreateNewLineFromSocket() = 0; + // does a write to the socket + virtual int WriteLineToSocket(char *line) = 0; + + // Use these functions to stream a message fetch to the current context + // Used initially by TImapServerState + virtual void BeginMessageDownLoad(uint32 total_size, + const char *content_type)=0; // some downloads are header only + virtual void HandleMessageDownLoadLine(const char *line, XP_Bool chunkEnd)=0; + virtual void NormalMessageEndDownload()=0; + virtual void AbortMessageDownLoad()=0; + virtual void AddXMozillaStatusLine(uint16 flags)=0; // for XSender auth info + + // used to notify the fe to upload the message to this + // connection's socket + virtual void UploadMessage() = 0; + + // notify the fe about a message's imap message flags + virtual void NotifyMessageFlags( imapMessageFlagsType flags, MessageKey key) = 0; + + // notify the fe about a search hit + virtual void NotifySearchHit(const char *hitLine) = 0; + + // a call back from TImapServerState to handle mailbox discovery + virtual void DiscoverMailboxSpec(mailbox_spec *adoptedBoxSpec) = 0; + + // notify the fe about the state of the selected mailbox + virtual void UpdatedMailboxSpec(mailbox_spec *adoptedBoxSpec) = 0; + virtual void UpdateMailboxStatus(mailbox_spec *adoptedBoxSpec) = 0; + + // notify the fe that the current online copy is done + virtual void OnlineCopyCompleted(ImapOnlineCopyState copyState) = 0; + + // notify the fe that a folder was deleted + virtual void FolderDeleted(const char *mailboxName) = 0; + + // notify the fe that a folder creation failed + virtual void FolderNotCreated(const char *mailboxName) = 0; + + // notify the fe that a folder was deleted + virtual void FolderRenamed(const char *oldName, + const char *newName) = 0; + + // notify the fe that we're done finding folders + virtual void MailboxDiscoveryFinished() = 0; + + // return the server response parser + virtual TImapServerState &GetServerStateParser(); + + + // This function will report the low mem conditions to the user, + // empty the fe event queue and set the connection status to negative + // so netlib will exit ProcessImap4. + virtual void HandleMemoryFailure() = 0; + + + // if you call TellThreadToDie(FALSE) then you are responsible for + // calling SetIsSafeToDie when you are ready for the imap thread to + // delete the connection. + virtual void TellThreadToDie(XP_Bool isSafeToDie = TRUE)=0; + virtual void SetIsSafeToDie()=0; + virtual XP_Bool DeathSignalReceived()=0; + + virtual void ShowProgress()=0; + virtual const char *GetHostName()=0; + + void SetDeleteIsMoveToTrash(XP_Bool deleteIsMoveToTrash) {fDeleteModelIsMoveToTrash = deleteIsMoveToTrash;} + + // used the convert to/from UTF7IMAP + virtual char* CreateUtf7ConvertedString(const char *sourceString, XP_Bool toUtf7Imap)=0; + + static int64 GetTimeStampOfNonPipelinedList() { return fgTimeStampOfNonPipelinedList; } +protected: + void WaitForIOCompletion(); + void WaitForFEEventCompletion(); + void WaitForTunnelCompletion(); + + // manage the IMAP server command tags + void IncrementCommandTagNumber(); + + char *CreateEscapedMailboxName(const char *rawName); + + + // establish connection, this will not alert the user if there is a problem + // affects GetConnectionStatus() + virtual void EstablishServerConnection() = 0; + + + // called from concrete subclass constructor + TIMAP4BlockingConnection(); + virtual XP_Bool ConstructionSuccess(); // called by subclass factory + + char *GetOutputBuffer(); + char *GetServerCommandTag(); + PRMonitor *GetDataMemberMonitor(); + + // returns TRUE if the messageIdString indicates that this is an operation + // on more than one message. + XP_Bool HandlingMultipleMessages(char *messageIdString); + + void TimeStampListNow(); + + XP_Bool SupportsUserDefinedFlags() { return fServerState.SupportsUserFlags(); }; + +protected: + static int64 fgTimeStampOfNonPipelinedList; + TImapServerState fServerState; + XP_Bool fBlockedForIO; + XP_Bool fCloseNeededBeforeSelect; + XP_Bool fDeleteModelIsMoveToTrash; + int fConnectionStatus; + PRMonitor *fDataMemberMonitor; + PRMonitor *fIOMonitor; + char fCurrentServerCommandTag[10]; // enough for a billion + int fCurrentServerCommandTagNumber; + char fOutputBuffer[kOutputBufferSize]; + XP_Bool fFromHeaderSeen; + XP_Bool fNotifyHit; +}; + + +#define kDownLoadCacheSize 1536 +struct msg_line_info; + +class TLineDownloadCache { +public: + TLineDownloadCache(); + virtual ~TLineDownloadCache(); + + uint32 CurrentUID(); + uint32 SpaceAvailable(); + XP_Bool CacheEmpty(); + + msg_line_info *GetCurrentLineInfo(); + + void ResetCache(); + void CacheLine(const char *line, uint32 uid); +private: + char fLineCache[kDownLoadCacheSize]; + uint32 fBytesUsed; + + msg_line_info *fLineInfo; + +}; + +class TIMAPSocketInfo; +class TIMAPBodyShellCache; + +// this is a linked list of host info's +class TIMAPHostInfo +{ +public: + + // Host List + static XP_Bool AddHostToList(const char *hostName); + static void ResetAll(); + + // Capabilities + static uint32 GetCapabilityForHost(const char *hostName); + static XP_Bool SetCapabilityForHost(const char *hostName, uint32 capability); + static XP_Bool GetHostHasAdminURL(const char *hostName); + static XP_Bool SetHostHasAdminURL(const char *hostName, XP_Bool hasAdminUrl); + // Subscription + static XP_Bool GetHostIsUsingSubscription(const char *hostName); + static XP_Bool SetHostIsUsingSubscription(const char *hostName, XP_Bool usingSubscription); + + // Passwords + static char *GetPasswordForHost(const char *hostName); + static XP_Bool SetPasswordForHost(const char *hostName, const char *password); + static XP_Bool GetPasswordVerifiedOnline(const char *hostName); + static XP_Bool SetPasswordVerifiedOnline(const char *hostName); + + // Folders + static XP_Bool SetHaveWeEverDiscoveredFoldersForHost(const char *hostName, XP_Bool discovered); + static XP_Bool GetHaveWeEverDiscoveredFoldersForHost(const char *hostName); + + // Trash Folder + static XP_Bool SetOnlineTrashFolderExistsForHost(const char *hostName, XP_Bool exists); + static XP_Bool GetOnlineTrashFolderExistsForHost(const char *hostName); + + // INBOX + static char *GetOnlineInboxPathForHost(const char *hostName); + static XP_Bool GetShouldAlwaysListInboxForHost(const char *hostName); + static XP_Bool SetShouldAlwaysListInboxForHost(const char *hostName, XP_Bool shouldList); + + // Namespaces + static TIMAPNamespace *GetNamespaceForMailboxForHost(const char *hostName, const char *mailbox_name); + static XP_Bool AddNewNamespaceForHost(const char *hostName, TIMAPNamespace *ns); + static XP_Bool ClearServerAdvertisedNamespacesForHost(const char *hostName); + static XP_Bool ClearPrefsNamespacesForHost(const char *hostName); + static TIMAPNamespace *GetDefaultNamespaceOfTypeForHost(const char *hostName, EIMAPNamespaceType type); + static XP_Bool SetNamespacesOverridableForHost(const char *hostName, XP_Bool overridable); + static XP_Bool GetNamespacesOverridableForHost(const char *hostName); + static int GetNumberOfNamespacesForHost(const char *hostName); + static TIMAPNamespace *GetNamespaceNumberForHost(const char *hostName, int n); + static XP_Bool CommitNamespacesForHost(const char *hostName, MSG_Master *master); + + + // Hierarchy Delimiters + static XP_Bool AddHierarchyDelimiter(const char *hostName, char delimiter); + static char *GetHierarchyDelimiterStringForHost(const char *hostName); + static XP_Bool SetNamespaceHierarchyDelimiterFromMailboxForHost(const char *hostName, const char *boxName, char delimiter); + + // Message Body Shells + static XP_Bool AddShellToCacheForHost(const char *hostName, TIMAPBodyShell *shell); + static TIMAPBodyShell *FindShellInCacheForHost(const char *hostName, const char *mailboxName, const char *UID); + + static PRMonitor *gCachedHostInfoMonitor; + static TIMAPHostInfo *fHostInfoList; +protected: + TIMAPHostInfo(const char *hostName); + static TIMAPHostInfo *FindHost(const char *hostName); + ~TIMAPHostInfo(); + char *fHostName; + char *fCachedPassword; + TIMAPHostInfo *fNextHost; + uint32 fCapabilityFlags; + char *fHierarchyDelimiters; // string of top-level hierarchy delimiters + XP_Bool fHaveWeEverDiscoveredFolders; + char *fCanonicalOnlineSubDir; + TIMAPNamespaceList *fNamespaceList; + XP_Bool fNamespacesOverridable; + XP_Bool fUsingSubscription; + XP_Bool fOnlineTrashFolderExists; + XP_Bool fShouldAlwaysListInbox; + XP_Bool fHaveAdminURL; + XP_Bool fPasswordVerifiedOnline; + TIMAPBodyShellCache *fShellCache; +}; + +class MSG_FolderInfo; +class TIMAPMessagePartIDArray; + +class TNavigatorImapConnection : public TIMAP4BlockingConnection { +public: + static TNavigatorImapConnection* + Create(const char *hostName); + + virtual ~TNavigatorImapConnection(); + + static SetupConnection(ActiveEntry * ce, MSG_Master *master, XP_Bool loadingURL); + + // Performs a PR_LOG on logData, formatting it to a standard IMAP i/o log format. + // This checks the state of the parser and tells us which connection we're using, the selected folder, etc. + // logSubName is a name like "BODYSHELL," "NETWORK," "URL," etc... basically, a way to subcategorize what + // type of log entry this is. + // extraInfo is any extra notes that should be added to this log entry. For instance, a "NETWORK" entry + // might also pass in "READ" or "WRITE" as extraInfo. extraInfo can be NULL, in which case we don't add anything. + // logData is the data to be logged. + + void Log(const char *logSubName, const char *extraInfo, const char *logData); + + + // checks for new mail and gets headers if nescesary + virtual XP_Bool CheckNewMail(void); + virtual void SelectMailbox(const char *mailboxName); + virtual void Configure(XP_Bool GetHeaders, int32 TooFastTime, int32 IdealTime, + int32 ChunkAddSize, int32 ChunkSize, int32 ChunkThreshold, + XP_Bool FetchByChunks, int32 MaxChunkSize); + + // dumb but usefull safe downcast, returns non-null if cast worked + virtual TNavigatorImapConnection *GetNavigatorConnection(); + + // gets/sets the status of the currently running ActiveEntry. + // This should not be used for setting the connection status, i.e. MK_CONNECTED, MK_WAITING_FOR_CONNECTION, etc. + int GetCurrentEntryStatus(); + void SetCurrentEntryStatus(int status); + + // call by imap thread to endlessly process active entries + virtual void StartProcessingActiveEntries(); + + // thread safe: Called by main thread to submit ActiveEntry + // for processing + // Will block if !BlockedWaitingForActiveEntry + virtual void SubmitActiveEntry(ActiveEntry * ce, XP_Bool newConnection); + + // returns true if another thread is blocked waiting + virtual XP_Bool BlockedWaitingForActiveEntry(); + + /* I don't know about this! + // thread safe call to add another ActiveEntry to the queue + virtual void AddActiveEntryJob(ActiveEntry *entry); + + // thread safe call that will + // 1. Stop processing the current entry if it shares + // the same context with the passed entry + // + // 2. Remove entries from the queue if they share the + // same context with the passed entry + virtual void AbortActiveEntry(ActiveEntry *entry); + */ + + + // Tunnels + virtual int32 OpenTunnel (int32 maxNumberOfBytesToRead); + XP_Bool GetIOTunnellingEnabled(); + int32 GetTunnellingThreshold(); + + + TImapFEEventQueue &GetFEEventQueue(); + void SetBlockingThread(PRThread *blockingThread); + + // if you call TellThreadToDie(FALSE) then you are responsible for + // calling SetIsSafeToDie when you are ready for the imap thread to + // delete the connection. + virtual void TellThreadToDie(XP_Bool isSafeToDie = TRUE); + virtual void SetIsSafeToDie(); + + virtual XP_Bool DeathSignalReceived(); + + // this function is thread safe + // this function is used by the fe functions to signal their completion + void NotifyEventCompletionMonitor(); + + // This function is threadsafe + // It is used by certain FE events to signal the completion of an IO tunnel. + void NotifyTunnelCompletionMonitor(); + + // this function is thread safe + // this function is used by the fe signal complete message upload + void NotifyMessageUploadMonitor(); + + // this function is thread safe + // notifies that the fe thread is done processing fe events + void SignalEventQueueEmpty(); + + // not thread safe. intended to be called from the fe event functions + ActiveEntry *GetActiveEntry(); + TCP_ConData **GetTCPConData(); + + + // these functions might seem silly but I don't want the IMAP thread + // to ever muck with the ActiveEntry structure fields, so the socket + // will be set when we begin the connection. These setters and getters + // are thread safe + void SetIOSocket(PRFileDesc *socket); + PRFileDesc * GetIOSocket(); + + void SetOutputStream(NET_StreamClass *outputStream); + NET_StreamClass *GetOutputStream(); + + virtual XP_Bool UseSSL() { return FALSE; }; + + virtual void FetchMessage(const char *messageIds, + eFetchFields whatToFetch, + XP_Bool idAreUid, + uint32 startByte = 0, uint32 endByte = 0, + char *part = 0); + virtual void FetchTryChunking(const char *messageIds, + eFetchFields whatToFetch, + XP_Bool idAreUid, + char *part, + uint32 downloadSize); + virtual void PipelinedFetchMessageParts(const char *uid, + TIMAPMessagePartIDArray *parts); + virtual void Bodystructure(const char *messageId, XP_Bool idIsUid); + + virtual void Logout(); + + virtual void AlertUserEvent_UsingId(uint32 msgId); + virtual void AlertUserEvent(char *message); + virtual void AlertUserEventFromServer(char *serverSaid); + virtual void ProgressUpdateEvent(char *message); + virtual void ProgressEventFunction_UsingId(uint32 msgId); + virtual void ProgressEventFunction_UsingIdWithString(uint32 msgId, const char* extraInfo); + virtual void PercentProgressUpdateEvent(char *message, int percent); + virtual void PastPasswordCheckEvent(); + + void WaitForFEEventCompletion(); + void WaitForTunnelCompletion(); + + // does a blocking read of the socket, returns a newly allocated + // line or nil and a status + virtual char *CreateNewLineFromSocket(); + + // does a write to the socket + virtual int WriteLineToSocket(char *line); + + // establish connection, this will not alert the user if there is a + // problem. affects GetConnectionStatus() + virtual void EstablishServerConnection(); + + // Use these functions to stream a message fetch to the current context + // Used initially by TImapServerState + virtual void BeginMessageDownLoad(uint32 total_size, // for user, headers and body + const char *content_type); // some downloads are header only + virtual void HandleMessageDownLoadLine(const char *line, XP_Bool chunkEnd); + virtual void NormalMessageEndDownload(); + virtual void AbortMessageDownLoad(); + virtual void PostLineDownLoadEvent(msg_line_info *downloadLineDontDelete); + virtual void AddXMozillaStatusLine(uint16 flags); // for XSender auth info + + // used to notify the fe to upload the message to this + // connection's socket + virtual void UploadMessage(); + + virtual void UploadMessageFromFile(const char *srcFilePath, + const char *mailboxName, + imapMessageFlagsType flags); + + // notify the fe about a message's imap message flags + virtual void NotifyMessageFlags( imapMessageFlagsType flags, MessageKey key); + + // notify the fe about a search hit + virtual void NotifySearchHit(const char *hitLine); + + // a call back from TImapServerState to handle mailbox discovery + virtual void DiscoverMailboxSpec(mailbox_spec *adoptedBoxSpec); + + // notify the fe about the state of the selected mailbox + virtual void UpdatedMailboxSpec(mailbox_spec *adoptedBoxSpec); + virtual void UpdateMailboxStatus(mailbox_spec *adoptedBoxSpec); + + // notify the fe that the current online copy is done + virtual void OnlineCopyCompleted(ImapOnlineCopyState copyState); + + // notify the fe that a folder was deleted + virtual void FolderDeleted(const char *mailboxName); + + // notify the fe that a folder was renamed + virtual void FolderRenamed(const char *oldName, + const char *newName); + + // notify the fe that a folder creation failed + virtual void FolderNotCreated(const char *mailboxName); + + // handle any post-mailbox-discovery tasks (Trash folder, etc.) + virtual void MailboxDiscoveryFinished(); + + // notify the fe that we're done finding folders + virtual void SetFolderDiscoveryFinished(); + + // This function will report the low mem conditions to the user, + // empty the fe event queue and set the connection status to negative + // so netlib will exit ProcessImap4. + // threadsafe + virtual void HandleMemoryFailure(); + + // These three functions are used to graph the progress of a + // message download + int32 GetMessageSizeForProgress(); + int32 GetBytesMovedSoFarForProgress(); + void SetBytesMovedSoFarForProgress(int32 bytesMoved); + + // Returns true if a msg move/copy is happening + XP_Bool CurrentConnectionIsMove(); + + // receive the list of msg ids for fetching msgs + void NotifyKeyList(uint32 *bodyKeys, uint32 bodyCount); // storage adopted + + // receive the size of the next message append + void NotifyAppendSize(uint32 msgSize, imapMessageFlagsType flags); + + virtual XP_Bool NewMailDetected(void); + virtual void ParseIMAPandCheckForNewMail(char *buff = NULL); + + // returns a newly allocated string + char *GetCurrentConnectionURL(); + + // These calls are not thread safe. Called from the mozilla + // thread to keep track of whether we have set call netlib all the time + inline void SetCallingNetlibAllTheTime(XP_Bool callingNetlib) + { fCallingNetlibAllTheTime = callingNetlib; }; + + inline XP_Bool GetCallingNetlibAllTheTime() { return fCallingNetlibAllTheTime; }; + + static void ResetCachedConnectionInfo(); + + // sets the folder info for the currently running URL + void SetFolderInfo(MSG_FolderInfo *folder, XP_Bool folderNeedsSubscribing, + XP_Bool folderNeedsACLRefreshed); + + virtual void ShowProgress(); + + virtual void SetMailboxDiscoveryStatus(EMailboxDiscoverStatus status); + virtual EMailboxDiscoverStatus GetMailboxDiscoveryStatus(); + //static XP_Bool HaveWeEverDiscoveredNewMailboxes() { return fHaveWeEverFoundImapMailboxes; } + + void BodyIdMonitor(XP_Bool enter); + + void SetNeedsGreeting(XP_Bool need) { fNeedGreeting = need; } + void SetNeedsNoop(XP_Bool need) {fNeedNoop = need;} + static void ImapStartup(); + static void ImapShutDown(); + + TIMAPUrl *GetCurrentUrl() {return fCurrentUrl;} + + // used the convert to/from UTF7IMAP + virtual char* CreateUtf7ConvertedString(const char *sourceString, XP_Bool toUtf7Imap); + + uint32 GetMessageSize(const char *messageId, XP_Bool idsAreUids); + + virtual const char *GetHostName() {return fHostName;} + void PseudoInterrupt(XP_Bool interrupt); + void SetSubscribeParameters(XP_Bool autoSubscribe, + XP_Bool autoUnsubscribe, XP_Bool autoSubscribeOnOpen, XP_Bool autoUnsubscribeFromNoSelect, + XP_Bool shouldUpgrade, XP_Bool upgradeLeaveAlone, XP_Bool upgradeSubscribeAll); + XP_Bool GetSubscribingNow(); + + // includes subscription management + XP_Bool CreateMailboxRespectingSubscriptions(const char *mailboxName); + XP_Bool DeleteMailboxRespectingSubscriptions(const char *mailboxName); + XP_Bool RenameMailboxRespectingSubscriptions(const char *existingName, const char *newName, XP_Bool reallyRename); + + // notifies libmsg that we have a new personal/default namespace that we're using + void CommitNamespacesForHostEvent(); + // notifies libmsg that we have new capability data for the current host + void CommitCapabilityForHostEvent(); + + // Adds a set of rights for a given user on a given mailbox on the current host. + // if userName is NULL, it means "me," or MYRIGHTS. + // rights is a single string of rights, as specified by RFC2086, the IMAP ACL extension. + void AddFolderRightsForUser(const char *mailboxName, const char *userName, const char *rights); + // Clears all rights for a given folder, for all users. + void ClearAllFolderRights(const char *mailboxName); + // Refreshes the icon / flags for the given folder + void RefreshFolderACLView(const char *mailboxName); + XP_Bool FolderNeedsACLInitialized(const char *folderName); + // Returns true if we know that the given mailbox on the current host is \NoSelect + XP_Bool MailboxIsNoSelectMailbox(const char *mailboxName); + virtual void StatusForFolder(const char *mailboxName); + XP_Bool InitializeFolderUrl(const char *folderName); + + // We really need to break out the thread synchronizer from the + // connection class... + XP_Bool GetShowAttachmentsInline(); + + XP_Bool GetPseudoInterrupted(); + + void ResetProgressInfo(); + void SetActive(XP_Bool active); + XP_Bool GetActive(); + + // Sets whether or not the content referenced by the current ActiveEntry has been modified. + // Used for MIME parts on demand. + void SetContentModified(XP_Bool modified); + XP_Bool GetShouldFetchAllParts(); + +protected: + TNavigatorImapConnection(const char *hostName); + + // Given the canonical pattern prefix, list the children of the pattern + void CanonicalChildList(const char *canonicalPrefix, XP_Bool pipeLined); + void NthLevelChildList(const char *canonicalPrefix, int depth); + void ChildDiscoverySucceeded(); + + // called within the IMAP thread + virtual XP_Bool TryToLogon(); + virtual void ProcessCurrentURL(); + + // called immediately after we have been authenticated, to do + // an assorted variety of housekeeping (NAMESPACE, etc...) + virtual void ProcessAfterAuthenticated(); + + virtual void WaitForNextActiveEntry(); + virtual void WaitForEventQueueEmptySignal(); + + virtual void WaitForPotentialListOfMsgsToFetch(uint32 **msgs, uint32 &count); + virtual void WaitForNextAppendMessageSize(); + + + virtual uint32 GetAppendSize(); + virtual imapMessageFlagsType GetAppendFlags(); + + virtual void WaitForMessageUploadToComplete(); + + XP_Bool DeleteSubFolders(const char *selectedMailbox); + XP_Bool RenameHierarchyByHand(const char *oldParentMailboxName, const char *newParentMailboxName); + + virtual XP_Bool ConstructionSuccess(); // called by subclass factory + + virtual void ProcessStoreFlags(const char *messageIds, + XP_Bool idsAreUids, + imapMessageFlagsType flags, + XP_Bool addFlags); + + + virtual void ProcessAuthenticatedStateURL(); + virtual void ProcessSelectedStateURL(); + virtual void HandleCurrentUrlError(); + virtual void ProcessMailboxUpdate(XP_Bool handlePossibleUndo, XP_Bool fromBiffUpdate = FALSE); + + virtual void HeaderFetchCompleted(); + + virtual void FolderHeaderDump(uint32 *hdrUids, uint32 hdrCount); + virtual void FolderMsgDump(uint32 *bodyUids, uint32 bodyCount, TIMAP4BlockingConnection::eFetchFields fields); + virtual void FolderMsgDumpRecurse(uint32 *bodyUids, uint32 bodyCount, TIMAP4BlockingConnection::eFetchFields fields); + + virtual void PeriodicBiff(); + virtual void SendSetBiffIndicatorEvent(MSG_BIFF_STATE newState); + + virtual void AutoSubscribeToMailboxIfNecessary(const char *mailboxName); + virtual void FindMailboxesIfNecessary(); + virtual void DiscoverMailboxList(); + virtual void DiscoverAllAndSubscribedBoxes(); + + int GetNumberOfListedMailboxesWithUnlistedChildren(); + void GetOldIMAPHostNameEvent(char **oldHostName); + void UpgradeToSubscriptionFinishedEvent(EIMAPSubscriptionUpgradeState); + XP_Bool PromptUserForSubscribeUpgradePath(); + virtual void SubscribeToAllFolders(); + virtual void UpgradeToSubscription(); + + virtual void RefreshACLForFolder(const char *mailboxName); + virtual void RefreshACLForFolderIfNecessary(const char *mailboxName); + virtual void RefreshAllACLs(); + + char *CreatePossibleTrashName(const char *prefix); + + uint32 CountMessagesInIdString(const char *idString); + + void AdjustChunkSize(void); + + char *GetArbitraryHeadersToDownload(); + +protected: + TImapFEEventQueue *fFEEventQueue; + ActiveEntry *fCurrentEntry; + int32 fInputBufferSize; + char *fInputSocketBuffer; + TImapFlagAndUidState *fFlagState; + time_t fStartTime; // time in secs we started to download last message + time_t fEndTime; // ending time + int32 fTooFastTime; // secs, buffer filled ? make it bigger + int32 fIdealTime; // secs, buffer filled, good. + int32 fChunkAddSize; // size to add to buffer when not big enough + int32 fChunkStartSize; // size of buffer to start with or to reset to in bad connections + int32 fMaxChunkSize; // max size buffer can grow + XP_Bool fNewMail; // is there new mail waiting for next biff ? + XP_Bool fTrackingTime; // are we supposed to track it ? + +private: + XP_Bool fFEeventCompleted; + XP_Bool fTunnelCompleted; + XP_Bool fEventQueueEmptySignalHappened; + XP_Bool fMessageUploadCompleted; + XP_Bool fFetchMsgListIsNew; + XP_Bool fMsgAppendSizeIsNew; + XP_Bool fNextEntryEventSignalHappened; + XP_Bool fThreadShouldDie; + XP_Bool fCallingNetlibAllTheTime; + XP_Bool fIsSafeToDie; + XP_Bool fNeedGreeting; + XP_Bool fNeedNoop; + XP_Bool fMailToFetch; + XP_Bool fGetHeaders; + XP_Bool fShouldUpgradeToSubscription; + XP_Bool fUpgradeShouldLeaveAlone; + XP_Bool fUpgradeShouldSubscribeAll; + + int fProgressStringId; + uint32 fProgressIndex; + uint32 fProgressCount; + int64 fLastProgressTime; + int fLastPercent; + int fLastProgressStringId; + + + uint32 *fFetchMsgIdList; + uint32 fFetchCount; + + TIMAPUrl *fCurrentUrl; + TCP_ConData *fTCPConData; + PRThread *fBlockingThread; + PRMonitor *fEventCompletionMonitor; + PRMonitor *fTunnelCompletionMonitor; + PRMonitor *fActiveEntryMonitor; + PRMonitor *fEventQueueEmptySignalMonitor; + PRMonitor *fMessageUploadMonitor; + PRMonitor *fMsgCopyDataMonitor; + PRMonitor *fThreadDeathMonitor; + PRMonitor *fPermissionToDieMonitor; + PRMonitor *fPseudoInterruptMonitor; + PRMonitor *fWaitForBodyIdsMonitor; + PRFileDesc *fIOSocket; + NET_StreamClass *fOutputStream; + + enum EMailboxHierarchyNameState { + kNoOperationInProgress, + kDiscoverBaseFolderInProgress, + kDiscoverTrashFolderInProgress, + kDeleteSubFoldersInProgress, + kListingForInfoOnly, + kListingForInfoAndDiscovery, + kDiscoveringNamespacesOnly + }; + + XP_Bool fOnlineBaseFolderExists; + + ImapHierarchyMover *fHierarchyMover; + const char *fRootToBeRenamed; + EMailboxHierarchyNameState fHierarchyNameState; + + XP_List *fDeletableChildren; + + int32 fBytesMovedSoFarForProgress; + + uint32 fAppendMessageSize; + imapMessageFlagsType fAppendMsgFlags; + + TLineDownloadCache fDownLoadLineCache; + + EMailboxDiscoverStatus fDiscoveryStatus; + + static PRMonitor *fFindingMailboxesMonitor; + static PRMonitor *fUpgradeToSubscriptionMonitor; + static XP_Bool fHaveWeEverCheckedForSubscriptionUpgrade; + static MSG_BIFF_STATE fCurrentBiffState; + + // chunking prefs + XP_Bool fFetchByChunks; + int32 fChunkSize; + int32 fChunkThreshold; + + XP_Bool fAutoSubscribe, fAutoUnsubscribe, fAutoSubscribeOnOpen, fAutoUnsubscribeFromNoSelect; + + XP_List *fListedMailboxList; + static XP_Bool IsConnectionActive(const char *hostName, const char *folderName); + +protected: + static XPPtrArray *connectionList; + TIMAPSocketInfo *fSocketInfo; + XP_Bool fPseudoInterrupted; + char *fHostName; + MSG_FolderInfo *fFolderInfo; + XP_Bool fFolderNeedsSubscribing, fFolderNeedsACLRefreshed; + XP_Bool fActive; + +}; + + + + +HG83666 + + +class TIMAPSocketInfo { + + // methods +public: + TIMAPSocketInfo(); + + PRFileDesc * GetIOSocket() { return fIOSocket; } + void SetIOSocket(PRFileDesc *sock) { fIOSocket = sock; } + + char** GetNewLineBuffer() { return &newLine; } + void SetNewLineBuffer(char *buffer) { newLine = buffer; } + + int GetReadStatus() { return readStatus; } + void SetReadStatus(int status) { readStatus = status; } + + int32* GetInputBufferSize() { return pInputBufferSize; } + void SetInputBufferSize(int32 *size) { pInputBufferSize = size; } + + char** GetInputSocketBuffer() { return pInputSocketBuffer; } + void SetInputSocketBuffer(char **buf) { pInputSocketBuffer = buf; } + + Bool GetPauseForRead() { return pauseForRead; } + void SetPauseForRead(Bool pause) { pauseForRead = pause; } + + // members +public: + PRFileDesc *fIOSocket; + char** pInputSocketBuffer; + int32* pInputBufferSize; + char* newLine; + Bool pauseForRead; + int readStatus; + int writeStatus; + char* writeLine; +}; + + +// This class is only used for passing data +// between the IMAP and mozilla thread +class TIMAPACLRightsInfo +{ +public: + char *hostName, *mailboxName, *userName, *rights; +}; + +// a simple reference counting class used to keep Biff from interrupting an INBOX connection +// the inbox usage count is incremented by one if TRUE is passed to the constructor and then +// decremented by the destructor +class TInboxReferenceCount { +public: + TInboxReferenceCount(XP_Bool bumpInboxCount); + ~TInboxReferenceCount(); + + int GetInboxUsageCount(); + + static void ImapStartup(); + static void ImapShutDown(); +private: + static PRMonitor *fgDataSafeMonitor; + static int fgInboxUsageCount; + XP_Bool fInboxCountWasBumped; +}; + + + +class TIMAPNamespace +{ + +public: + TIMAPNamespace(EIMAPNamespaceType type, const char *prefix, char delimiter, XP_Bool from_prefs); + + ~TIMAPNamespace(); + + EIMAPNamespaceType GetType() { return m_namespaceType; } + const char * GetPrefix() { return m_prefix; } + char GetDelimiter() { return m_delimiter; } + void SetDelimiter(char delimiter); + XP_Bool GetIsDelimiterFilledIn() { return m_delimiterFilledIn; } + XP_Bool GetIsNamespaceFromPrefs() { return m_fromPrefs; } + + // returns -1 if this box is not part of this namespace, + // or the length of the prefix if it is part of this namespace + int MailboxMatchesNamespace(const char *boxname); + +protected: + EIMAPNamespaceType m_namespaceType; + char *m_prefix; + char m_delimiter; + XP_Bool m_fromPrefs; + XP_Bool m_delimiterFilledIn; + +}; + + +// represents a list of namespaces for a given host +// Basically, a C++ wrapper around an XP_List +class TIMAPNamespaceList +{ +public: + ~TIMAPNamespaceList(); + + static TIMAPNamespaceList *CreateTIMAPNamespaceList(); + + void ClearNamespaces(XP_Bool deleteFromPrefsNamespaces, XP_Bool deleteServerAdvertisedNamespaces); + int GetNumberOfNamespaces(); + int GetNumberOfNamespaces(EIMAPNamespaceType); + TIMAPNamespace *GetNamespaceNumber(int nodeIndex); + TIMAPNamespace *GetNamespaceNumber(int nodeIndex, EIMAPNamespaceType); + + TIMAPNamespace *GetDefaultNamespaceOfType(EIMAPNamespaceType type); + int AddNewNamespace(TIMAPNamespace *ns); + TIMAPNamespace *GetNamespaceForMailbox(const char *boxname); + +protected: + TIMAPNamespaceList(); // use CreateTIMAPNamespaceList to create one + + XP_List *m_NamespaceList; + +}; + + +// This class is currently only used for the one-time upgrade +// process from a LIST view to the subscribe model. +// It basically contains the name of a mailbox and whether or not +// its children have been listed. +class TIMAPMailboxInfo +{ +public: + TIMAPMailboxInfo(const char *name); + virtual ~TIMAPMailboxInfo(); + void SetChildrenListed(XP_Bool childrenListed) { m_childrenListed = childrenListed; } + XP_Bool GetChildrenListed() { return m_childrenListed; } + const char *GetMailboxName() { return m_mailboxName; } + +protected: + XP_Bool m_childrenListed; + char *m_mailboxName; +}; + + + +#endif // IMAP4PVT_H diff --git a/mozilla/network/protocol/imap4/imap4url.c b/mozilla/network/protocol/imap4/imap4url.c index e69de29bb2d..e03f4acbd4b 100644 --- a/mozilla/network/protocol/imap4/imap4url.c +++ b/mozilla/network/protocol/imap4/imap4url.c @@ -0,0 +1,658 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#include "mkutils.h" + +#ifndef __imap__ +#include "imap.h" +#endif + +#ifndef _MCOM_H_ +#include "xp_mcom.h" +#endif + +/* 45678901234567890123456789012345678901234567890123456789012345678901234567890 +*/ + +char useme; + +static char *createStartOfIMAPurl(const char *imapHost, int additionalSize) +{ + static const char *formatString = "IMAP://%s?"; + + char *returnString = XP_ALLOC(XP_STRLEN(formatString) + XP_STRLEN(imapHost) + additionalSize); + if (returnString) + sprintf(returnString, formatString, imapHost); + + return returnString; +} + + +/* Selecting a mailbox */ +/* imap4://HOST>select>MAILBOXPATH */ +char *CreateImapMailboxSelectUrl(const char *imapHost, + const char *mailbox, + char hierarchySeparator, + const char *undoDeleteIdentifierList) +{ + static const char *formatString = "select>%c%s>%s"; + + /* 22 enough for huge index string */ + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + + XP_STRLEN(mailbox) + + (undoDeleteIdentifierList ? XP_STRLEN(undoDeleteIdentifierList) : 1) + + 22); + + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), + formatString, + hierarchySeparator, + mailbox, + undoDeleteIdentifierList ? undoDeleteIdentifierList : ""); + + return returnString; +} + +/* lite select, used to verify UIDVALIDITY while going on/offline */ +char *CreateImapMailboxLITESelectUrl(const char *imapHost, + const char *mailbox, + char hierarchySeparator) +{ + static const char *formatString = "liteselect>%c%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(mailbox)); + + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), + formatString, + hierarchySeparator, + mailbox); + + return returnString; +} + +/* expunge, used in traditional imap delete model */ +char *CreateImapMailboxExpungeUrl(const char *imapHost, + const char *mailbox, + char hierarchySeparator) +{ + static const char *formatString = "expunge>%c%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(mailbox)); + + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), + formatString, + hierarchySeparator, + mailbox); + + return returnString; +} + +/* Creating a mailbox */ +/* imap4://HOST>create>MAILBOXPATH */ +char *CreateImapMailboxCreateUrl(const char *imapHost, const char *mailbox,char hierarchySeparator) +{ + static const char *formatString = "create>%c%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(mailbox)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, hierarchySeparator, mailbox); + + return returnString; +} + +/* discover the mailboxes of this account */ +char *CreateImapAllMailboxDiscoveryUrl(const char *imapHost) +{ + static const char *formatString = "discoverallboxes"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString); + + return returnString; +} + + +/* discover the mailboxes of this account, and the subscribed mailboxes */ +char *CreateImapAllAndSubscribedMailboxDiscoveryUrl(const char *imapHost) +{ + static const char *formatString = "discoverallandsubscribedboxes"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString); + + return returnString; +} + +/* discover the children of this mailbox */ +char *CreateImapChildDiscoveryUrl(const char *imapHost, const char *mailbox,char hierarchySeparator) +{ + static const char *formatString = "discoverchildren>%c%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(mailbox)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, hierarchySeparator, mailbox); + + return returnString; +} +/* discover the n-th level deep children of this mailbox */ +char *CreateImapLevelChildDiscoveryUrl(const char *imapHost, const char *mailbox,char hierarchySeparator, int n) +{ + static const char *formatString = "discoverlevelchildren>%d>%c%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(mailbox)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, n, hierarchySeparator, mailbox); + + return returnString; +} + +/* deleting a mailbox */ +/* imap4://HOST>delete>MAILBOXPATH */ +char *CreateImapMailboxDeleteUrl(const char *imapHost, const char *mailbox, char hierarchySeparator) +{ + static const char *formatString = "delete>%c%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(mailbox)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, hierarchySeparator, mailbox); + + return returnString; +} + +/* renaming a mailbox */ +/* imap4://HOST>rename>OLDNAME>NEWNAME */ +char *CreateImapMailboxRenameLeafUrl(const char *imapHost, + const char *oldBoxPathName, + char hierarchySeparator, + const char *newBoxLeafName) +{ + static const char *formatString = "rename>%c%s>%c%s"; + + char *returnString = NULL; + + /* figure out the new mailbox name */ + char *slash; + char *newPath = XP_ALLOC(XP_STRLEN(oldBoxPathName) + XP_STRLEN(newBoxLeafName) + 1); + if (newPath) + { + XP_STRCPY (newPath, oldBoxPathName); + slash = XP_STRRCHR (newPath, '/'); + if (slash) + slash++; + else + slash = newPath; /* renaming a 1st level box */ + + XP_STRCPY (slash, newBoxLeafName); + + + returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(oldBoxPathName) + XP_STRLEN(newPath)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, hierarchySeparator, oldBoxPathName, hierarchySeparator, newPath); + + XP_FREE( newPath); + } + + return returnString; +} + +/* renaming a mailbox, moving hierarchy */ +/* imap4://HOST>movefolderhierarchy>OLDNAME>NEWNAME */ +/* oldBoxPathName is the old name of the child folder */ +/* destinationBoxPathName is the name of the new parent */ +char *CreateImapMailboxMoveFolderHierarchyUrl(const char *imapHost, + const char *oldBoxPathName, + char oldHierarchySeparator, + const char *newBoxPathName, + char newHierarchySeparator) +{ + static const char *formatString = "movefolderhierarchy>%c%s>%c%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(oldBoxPathName) + XP_STRLEN(newBoxPathName)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, oldHierarchySeparator, oldBoxPathName, newHierarchySeparator, newBoxPathName); + + return returnString; +} + +/* listing available mailboxes */ +/* imap4://HOST>list>referenceName>MAILBOXPATH */ +/* MAILBOXPATH can contain wildcard */ +/* **** jefft -- I am using this url to detect whether an mailbox + exists on the Imap sever + */ +char *CreateImapListUrl(const char *imapHost, + const char *mailbox, + const char hierarchySeparator) +{ + static const char *formatString = "list>%c%s"; + + char *returnString = createStartOfIMAPurl(imapHost, + XP_STRLEN(formatString) + + XP_STRLEN(mailbox) + 1); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, + hierarchySeparator, mailbox); + + return returnString; +} + +/* biff */ +char *CreateImapBiffUrl(const char *imapHost, + const char *mailbox, + char hierarchySeparator, + uint32 uidHighWater) +{ + static const char *formatString = "biff>%c%s>%ld"; + + /* 22 enough for huge uid string */ + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + + XP_STRLEN(mailbox) + 22); + + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, hierarchySeparator, mailbox, (long)uidHighWater); + + return returnString; +} + + +static const char *sequenceString = "SEQUENCE"; +static const char *uidString = "UID"; + +/* fetching RFC822 messages */ +/* imap4://HOST>fetch>>MAILBOXPATH>x */ +/* 'x' is the message UID or sequence number list */ +/* will set the 'SEEN' flag */ +char *CreateImapMessageFetchUrl(const char *imapHost, + const char *mailbox, + char hierarchySeparator, + const char *messageIdentifierList, + XP_Bool messageIdsAreUID) +{ + static const char *formatString = "fetch>%s>%c%s>%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(sequenceString) + XP_STRLEN(mailbox) + XP_STRLEN(messageIdentifierList)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, messageIdsAreUID ? uidString : sequenceString, hierarchySeparator, mailbox, messageIdentifierList); + + return returnString; +} + +/* fetching the headers of RFC822 messages */ +/* imap4://HOST>header>>MAILBOXPATH>x */ +/* 'x' is the message UID or sequence number list */ +/* will not affect the 'SEEN' flag */ +char *CreateImapMessageHeaderUrl(const char *imapHost, + const char *mailbox, + char hierarchySeparator, + const char *messageIdentifierList, + XP_Bool messageIdsAreUID) +{ + static const char *formatString = "header>%s>%c%s>%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(sequenceString) + XP_STRLEN(mailbox) + XP_STRLEN(messageIdentifierList)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, messageIdsAreUID ? uidString : sequenceString, hierarchySeparator, mailbox, messageIdentifierList); + + return returnString; +} + +/* search an online mailbox */ +/* imap4://HOST>search>>MAILBOXPATH>SEARCHSTRING */ +/* 'x' is the message sequence number list */ +char *CreateImapSearchUrl(const char *imapHost, + const char *mailbox, + char hierarchySeparator, + const char *searchString, + XP_Bool messageIdsAreUID) +{ + static const char *formatString = "search>%s>%c%s>%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(sequenceString) + XP_STRLEN(mailbox) + XP_STRLEN(searchString)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, messageIdsAreUID ? uidString : sequenceString, hierarchySeparator, mailbox, searchString); + + return returnString; +} + +/* delete messages */ +/* imap4://HOST>deletemsg>>MAILBOXPATH>x */ +/* 'x' is the message UID or sequence number list */ +char *CreateImapDeleteMessageUrl(const char *imapHost, + const char *mailbox, + char hierarchySeparator, + const char *messageIds, + XP_Bool idsAreUids) +{ + static const char *formatString = "deletemsg>%s>%c%s>%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(sequenceString) + XP_STRLEN(mailbox) + XP_STRLEN(messageIds)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, idsAreUids ? uidString : sequenceString, hierarchySeparator, mailbox, messageIds); + + return returnString; +} + +/* delete all messages */ +/* imap4://HOST>deleteallmsgs>MAILBOXPATH */ +char *CreateImapDeleteAllMessagesUrl(const char *imapHost, + const char *mailbox, + char hierarchySeparator) +{ + static const char *formatString = "deleteallmsgs>%c%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(mailbox)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, hierarchySeparator, mailbox); + + return returnString; +} + +/* store +flags url */ +/* imap4://HOST>store+flags>>MAILBOXPATH>x>f */ +/* 'x' is the message UID or sequence number list */ +/* 'f' is the byte of flags */ +char *CreateImapAddMessageFlagsUrl(const char *imapHost, + const char *mailbox, + char hierarchySeparator, + const char *messageIds, + imapMessageFlagsType flags, + XP_Bool idsAreUids) +{ + static const char *formatString = "addmsgflags>%s>%c%s>%s>%d"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(sequenceString) + XP_STRLEN(mailbox) + XP_STRLEN(messageIds) + 10); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, idsAreUids ? uidString : sequenceString, hierarchySeparator, mailbox, messageIds, (int) flags); + + return returnString; +} + +/* store -flags url */ +/* imap4://HOST>store-flags>>MAILBOXPATH>x>f */ +/* 'x' is the message UID or sequence number list */ +/* 'f' is the byte of flags */ +char *CreateImapSubtractMessageFlagsUrl(const char *imapHost, + const char *mailbox, + char hierarchySeparator, + const char *messageIds, + imapMessageFlagsType flags, + XP_Bool idsAreUids) +{ + static const char *formatString = "subtractmsgflags>%s>%c%s>%s>%d"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(sequenceString) + XP_STRLEN(mailbox) + XP_STRLEN(messageIds) + 10); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, idsAreUids ? uidString : sequenceString, hierarchySeparator, mailbox, messageIds, (int) flags); + + return returnString; +} + +/* set flags url, make the flags match */ +char *CreateImapSetMessageFlagsUrl(const char *imapHost, + const char *mailbox, + char hierarchySeparator, + const char *messageIds, + imapMessageFlagsType flags, + XP_Bool idsAreUids) +{ + static const char *formatString = "setmsgflags>%s>%c%s>%s>%d"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(sequenceString) + XP_STRLEN(mailbox) + XP_STRLEN(messageIds) + 10); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, idsAreUids ? uidString : sequenceString, hierarchySeparator, mailbox, messageIds, (int) flags); + + return returnString; +} + +/* copy messages from one online box to another */ +/* imap4://HOST>onlineCopy>>SOURCEMAILBOXPATH>x> + DESTINATIONMAILBOXPATH */ +/* 'x' is the message UID or sequence number list */ +char *CreateImapOnlineCopyUrl(const char *imapHost, + const char *sourceMailbox, + char sourceHierarchySeparator, + const char *messageIds, + const char *destinationMailbox, + char destinationHierarchySeparator, + XP_Bool idsAreUids, + XP_Bool isMove) +{ + static const char *formatString = "%s>%s>%c%s>%s>%c%s"; + static const char *moveString = "onlinemove"; + static const char *copyString = "onlinecopy"; + + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(moveString) + XP_STRLEN(sequenceString) + XP_STRLEN(sourceMailbox) + XP_STRLEN(messageIds) + XP_STRLEN(destinationMailbox)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, + isMove ? moveString : copyString, + idsAreUids ? uidString : sequenceString, + sourceHierarchySeparator, sourceMailbox, + messageIds, + destinationHierarchySeparator, destinationMailbox); + + + + return returnString; +} + +/* copy messages from one online box to another */ +/* imap4://HOST>onlineCopy>>SOURCEMAILBOXPATH>x> + DESTINATIONMAILBOXPATH */ +/* 'x' is the message UID or sequence number list */ +char *CreateImapOnToOfflineCopyUrl(const char *imapHost, + const char *sourceMailbox, + char sourceHierarchySeparator, + const char *messageIds, + const char *destinationMailbox, + XP_Bool idsAreUids, + XP_Bool isMove) +{ + static const char *formatString = "%s>%s>%c%s>%s>%c%s"; + static const char *moveString = "onlinetoofflinemove"; + static const char *copyString = "onlinetoofflinecopy"; + + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(moveString) + XP_STRLEN(sequenceString) + XP_STRLEN(sourceMailbox) + XP_STRLEN(messageIds) + XP_STRLEN(destinationMailbox)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, + isMove ? moveString : copyString, + idsAreUids ? uidString : sequenceString, + sourceHierarchySeparator, sourceMailbox, + messageIds, + kOnlineHierarchySeparatorUnknown, destinationMailbox); + + + + return returnString; +} + +/* copy messages from an offline box to an online box */ +/* imap4://HOST>offtoonCopy>SOURCEMAILBOXPATH>x> + DESTINATIONMAILBOXPATH */ +/* 'x' is the size of the message to upload */ +char *CreateImapOffToOnlineCopyUrl(const char *imapHost, + const char *destinationMailbox, + char destinationHierarchySeparator) +{ + static const char *formatString = "offlinetoonlinecopy>%c%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(destinationMailbox)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, destinationHierarchySeparator, destinationMailbox); + + return returnString; +} + +/* get mail account rul */ +/* imap4://HOST>NETSCAPE */ +char *CreateImapManageMailAccountUrl(const char *imapHost) +{ + static const char *formatString = "netscape"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + 1); + StrAllocCat(returnString, formatString);; + + return returnString; +} + +/* append message from file url */ +/* imap4://HOST>appendmsgfromfile>DESTINATIONMAILBOXPATH */ +char *CreateImapAppendMessageFromFileUrl(const char *imapHost, + const char *destinationMailboxPath, + const char hierarchySeparator, + XP_Bool isDraft) +{ + const char *formatString = isDraft ? "appenddraftfromfile>%c%s" : + "appendmsgfromfile>%c%s"; + char *returnString = + createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + + XP_STRLEN(destinationMailboxPath)); + + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, + hierarchySeparator, destinationMailboxPath); + + return returnString; +} + +/* Subscribe to a mailbox on the given IMAP host */ +char *CreateIMAPSubscribeMailboxURL(const char *imapHost, const char *mailboxName) +{ + /* we don't need the hierarchy delimiter, so just use slash ("/") */ + static const char *formatString = "subscribe>/%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(mailboxName)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, mailboxName); + + return returnString; + +} + +/* Unsubscribe from a mailbox on the given IMAP host */ +char *CreateIMAPUnsubscribeMailboxURL(const char *imapHost, const char *mailboxName) +{ + /* we don't need the hierarchy delimiter, so just use slash ("/") */ + static const char *formatString = "unsubscribe>/%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(mailboxName)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, mailboxName); + + return returnString; + +} + + +/* Refresh the ACL for a folder on the given IMAP host */ +char *CreateIMAPRefreshACLForFolderURL(const char *imapHost, const char *mailboxName) +{ + /* we don't need the hierarchy delimiter, so just use slash ("/") */ + static const char *formatString = "refreshacl>/%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(mailboxName)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, mailboxName); + + return returnString; + +} + +/* Refresh the ACL for all folders on the given IMAP host */ +char *CreateIMAPRefreshACLForAllFoldersURL(const char *imapHost) +{ + /* we don't need the hierarchy delimiter, so just use slash ("/") */ + static const char *formatString = "refreshallacls>/"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString); + + return returnString; + +} + +/* Auto-Upgrade to IMAP subscription */ +char *CreateIMAPUpgradeToSubscriptionURL(const char *imapHost, XP_Bool subscribeToAll) +{ + static char *formatString = "upgradetosubscription>/"; + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString)); + if (subscribeToAll) + formatString[XP_STRLEN(formatString)-1] = '.'; + + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString); + + return returnString; + +} + +/* do a status command on a folder on the given IMAP host */ +char *CreateIMAPStatusFolderURL(const char *imapHost, const char *mailboxName, char hierarchySeparator) +{ + static const char *formatString = "folderstatus>%c%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(mailboxName)); + + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), + formatString, + hierarchySeparator, + mailboxName); + + return returnString; + +} + +/* Refresh the admin url for a folder on the given IMAP host */ +char *CreateIMAPRefreshFolderURLs(const char *imapHost, const char *mailboxName) +{ + /* we don't need the hierarchy delimiter, so just use slash ("/") */ + static const char *formatString = "refreshfolderurls>/%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(mailboxName)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, mailboxName); + + return returnString; + +} + +/* Force the reload of all parts of the message given in url */ +char *IMAP_CreateReloadAllPartsUrl(const char *url) +{ + char *returnUrl = PR_smprintf("%s&allparts", url); + return returnUrl; +} + +/* Explicitly LIST a given mailbox, and refresh its flags in the folder list */ +char *CreateIMAPListFolderURL(const char *imapHost, const char *mailboxName) +{ + /* we don't need the hierarchy delimiter, so just use slash ("/") */ + static const char *formatString = "listfolder>/%s"; + + char *returnString = createStartOfIMAPurl(imapHost, XP_STRLEN(formatString) + XP_STRLEN(mailboxName)); + if (returnString) + sprintf(returnString + XP_STRLEN(returnString), formatString, mailboxName); + + return returnString; +} diff --git a/mozilla/network/protocol/imap4/imap4url.h b/mozilla/network/protocol/imap4/imap4url.h index e69de29bb2d..fa0d2148a98 100644 --- a/mozilla/network/protocol/imap4/imap4url.h +++ b/mozilla/network/protocol/imap4/imap4url.h @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +/* this file defines the syntax of the imap4 url's and offers functions + that create url strings. If the functions do not offer enough + functionality then let kevin know before you starting creating strings + from scratch. */ + +#ifndef IMAP4URL_H +#define IMAP4URL_H + +#include "xp_mcom.h" + +/* 45678901234567890123456789012345678901234567890123456789012345678901234567890 +*/ + + +XP_BEGIN_PROTOS + +/* need mailbox status urls to get the number of message and the + number of unread messages */ + +/* Creating a mailbox */ +/* imap4://HOST?create?MAILBOXPATH */ +char *CreateImapMailboxCreateUrl(const char *imapHost, const char *mailbox); + +/* deleting a mailbox */ +/* imap4://HOST?delete?MAILBOXPATH */ +char *CreateImapMailboxDeleteUrl(const char *imapHost, const char *mailbox); + +/* renaming a mailbox */ +/* imap4://HOST?rename?OLDNAME?NEWNAME */ +char *CreateImapMailboxRenameUrl(const char *imapHost, + const char *oldBoxName, + const char *newBoxName); + +/* listing available mailboxes */ +/* imap4://HOST?list */ +char *CreateImapListUrl(const char *imapHost); + + +/* fetching RFC822 messages */ +/* imap4://HOST?fetch??MAILBOXPATH?x */ +/* 'x' is the message UID or sequence number list */ +/* will set the 'SEEN' flag */ +char *CreateImapMessageFetchUrl(const char *imapHost, + const char *mailbox, + const char *messageIdentifierList, + XP_Bool messageIdsAreUID); + + +/* fetching the headers of RFC822 messages */ +/* imap4://HOST?header??MAILBOXPATH?x */ +/* 'x' is the message UID or sequence number list */ +/* will not affect the 'SEEN' flag */ +char *CreateImapMessageHeaderUrl(const char *imapHost, + const char *mailbox, + const char *messageIdentifierList, + XP_Bool messageIdsAreUID); + +/* dump headers url. Notify the front end when the mailbox is selected and + when each message line is dumped */ +char *CreateImapMessageHeaderDumpUrl(const char *imapHost, + const char *mailbox); + +/* search an online mailbox */ +/* imap4://HOST?search??MAILBOXPATH?SEARCHSTRING */ +/* 'x' is the message sequence number list */ +char *CreateImapSearchUrl(const char *imapHost, + const char *mailbox, + const char *searchString, + XP_Bool messageIdsAreUID); + +/* delete messages */ +/* imap4://HOST?deletemsg??MAILBOXPATH?x */ +/* 'x' is the message UID or sequence number list */ +char *CreateImapDeleteMessageUrl(const char *imapHost, + const char *mailbox, + const char *messageIds, + XP_Bool idsAreUids); + +/* mark messages as read */ +/* imap4://HOST?markread??MAILBOXPATH?x */ +/* 'x' is the message UID or sequence number list */ +char *CreateImapMarkMessageReadUrl(const char *imapHost, + const char *mailbox, + const char *messageIds, + XP_Bool idsAreUids); + +/* mark messages as unread */ +/* imap4://HOST?markunread??MAILBOXPATH?x */ +/* 'x' is the message UID or sequence number list */ +char *CreateImapMarkMessageUnReadUrl(const char *imapHost, + const char *mailbox, + const char *messageIds, + XP_Bool idsAreUids); + +/* copy messages from one online box to another */ +/* imap4://HOST?onlineCopy?? + SOURCEMAILBOXPATH?x?DESTINATIONMAILBOXPATH */ +/* 'x' is the message UID or sequence number list */ +char *CreateImapOnlineCopyUrl(const char *imapHost, + const char *sourceMailbox, + const char *messageIds, + const char *destinationMailbox, + XP_Bool idsAreUids); + +#if DOTHISSTUFFLATER +/* copy a message from an online box to an offline box */ +/* imap4://HOST?ontooffCopy?SOURCEMAILBOXPATH?number=x? + DESTINATIONMAILBOXPATH */ +/* 'x' is the message sequence number */ +char *CreateImapOnToOfflineCopyUrl(const char *imapHost, + const char *sourceOnlineMailbox, + int32 messageSequenceNumber, + const char *destinationOfflineMailbox); + +/* copy a message from an offline box to an online box */ +/* imap4://HOST?offtoonCopy?SOURCEMAILBOXPATH?number=x? + DESTINATIONMAILBOXPATH */ +/* 'x' is the message sequence number */ +char *CreateImapOffToOnlineCopyUrl(const char *imapHost, + const char *sourceOnlineMailbox, + int32 messageSequenceNumber, + const char *destinationOfflineMailbox); + +/* get mail account rul */ +/* imap4://HOST?NETSCAPE */ +char *CreateImapManageMailAccountUrl(const char *imapHost); + +/* append message from file url */ +/* imap4://HOST?appendmsgfromfile>DESTINATIONMAILBOXPATH */ +char *CreateImapAppendMessageFromFileUrl(const char *imapHost, + const char *destinationMailboxPath, + const char hierarchySeparator, + XP_Bool isDraft); + +#endif + +XP_END_PROTOS + +#endif /* IMAP4URL_H */ + diff --git a/mozilla/network/protocol/imap4/imapbody.cpp b/mozilla/network/protocol/imap4/imapbody.cpp index e69de29bb2d..5c9798990c1 100644 --- a/mozilla/network/protocol/imap4/imapbody.cpp +++ b/mozilla/network/protocol/imap4/imapbody.cpp @@ -0,0 +1,1615 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + + +// imapbody.cpp +// Implementation of the TIMAPBodyShell and associated classes +// These are used to parse IMAP BODYSTRUCTURE responses, and intelligently (?) +// figure out what parts we need to display inline. + +#include "mkutils.h" +#include "ptrarray.h" +#include "imap4pvt.h" +#include "imapbody.h" +#include "xpgetstr.h" +#include "xp_hash.h" +#include "mime.h" // for IMAP_EXTERNAL_CONTENT_HEADER +#include "prlog.h" +#include "mktcp.h" + +extern "C" +{ + extern int MK_IMAP_EMPTY_MIME_PART; + extern int MK_IMAP_X_MOZ_IMAP_PART_HEADER; +} + + + +/* + Create a TIMAPBodyShell from a full BODYSTRUCUTRE response from the parser. + + The body shell represents a single, top-level object, the message. The message body + might be treated as either a container or a leaf (just like any arbitrary part). + + Steps for creating a part: + 1. Pull out the paren grouping for the part + 2. Create a generic part object with that buffer + 3. The factory will return either a leaf or container, depending on what it really is. + 4. It is responsible for parsing its children, if there are any +*/ + + +///////////// TIMAPBodyShell //////////////////////////////////// + +TIMAPBodyShell::TIMAPBodyShell(TNavigatorImapConnection *connection, const char *buf, uint32 UID, const char *folderName) +{ + m_isValid = FALSE; + m_isBeingGenerated = FALSE; + m_cached = FALSE; + m_gotAttachmentPref = FALSE; + m_generatingWholeMessage = FALSE; + m_showAttachmentsInline = TRUE; + m_generatingPart = NULL; + m_connection = connection; + XP_ASSERT(m_connection); + if (!m_connection) + return; + m_prefetchQueue = new TIMAPMessagePartIDArray(); + if (!m_prefetchQueue) + return; + XP_ASSERT(buf); + if (!buf) + return; + m_UID = PR_smprintf("%ld",UID); + XP_ASSERT(m_UID); + if (!m_UID) + return; +#ifdef DEBUG_chrisf + XP_ASSERT(folderName); +#endif + if (!folderName) + return; + m_folderName = XP_STRDUP(folderName); + if (!m_folderName) + return; + // Turn the BODYSTRUCTURE response into a form that the TIMAPBodypartMessage can be constructed from. + char *doctoredBuf = PR_smprintf("(\"message\" \"rfc822\" NIL NIL NIL NIL 0 () %s 0)", buf); + if (!doctoredBuf) + return; + SetIsValid(TRUE); + m_message = new TIMAPBodypartMessage(this, NULL, doctoredBuf, NULL, TRUE); + XP_FREE(doctoredBuf); + if (!m_message || !m_message->GetIsValid()) + return; +} + +TIMAPBodyShell::~TIMAPBodyShell() +{ + delete m_message; + delete m_prefetchQueue; + FREEIF(m_UID); + FREEIF(m_folderName); +} + +void TIMAPBodyShell::SetIsValid(XP_Bool valid) +{ +// if (!valid) +// PR_LOG(IMAP, out, ("BODYSHELL: Shell is invalid.")); + m_isValid = valid; +} + + +XP_Bool TIMAPBodyShell::GetShowAttachmentsInline() +{ + if (!m_gotAttachmentPref) + { + m_showAttachmentsInline = m_connection->GetShowAttachmentsInline(); + m_gotAttachmentPref = TRUE; + } + + return m_showAttachmentsInline; +} + + +// Fills in buffer (and adopts storage) for header object +void TIMAPBodyShell::AdoptMessageHeaders(char *headers, const char *partNum) +{ + if (!GetIsValid()) + return; + + if (!partNum) + partNum = "0"; + + // we are going to say that a message header object only has + // part data, and no header data. + TIMAPBodypart *foundPart = m_message->FindPartWithNumber(partNum); + if (foundPart) + { + TIMAPBodypartMessage *messageObj = foundPart->GetTIMAPBodypartMessage(); + if (messageObj) + { + messageObj->AdoptMessageHeaders(headers); + if (!messageObj->GetIsValid()) + SetIsValid(FALSE); + } + else + { + // We were filling in message headers for a given part number. + // We looked up that part number, found an object, but it + // wasn't of type message/rfc822. + // Something's wrong. + XP_ASSERT(FALSE); + } + } + else + SetIsValid(FALSE); +} + +// Fills in buffer (and adopts storage) for MIME headers in appropriate object. +// If object can't be found, sets isValid to FALSE. +void TIMAPBodyShell::AdoptMimeHeader(const char *partNum, char *mimeHeader) +{ + if (!GetIsValid()) + return; + + XP_ASSERT(partNum); + + TIMAPBodypart *foundPart = m_message->FindPartWithNumber(partNum); + + if (foundPart) + { + foundPart->AdoptHeaderDataBuffer(mimeHeader); + if (!foundPart->GetIsValid()) + SetIsValid(FALSE); + } + else + { + SetIsValid(FALSE); + } +} + + +void TIMAPBodyShell::AddPrefetchToQueue(TIMAP4BlockingConnection::eFetchFields fields, const char *partNumber) +{ + TIMAPMessagePartID *newPart = new TIMAPMessagePartID(fields, partNumber); + if (newPart) + { + m_prefetchQueue->Add(newPart); + } + else + { + // HandleMemoryFailure(); + } +} + +// Flushes all of the prefetches that have been queued up in the prefetch queue, +// freeing them as we go +void TIMAPBodyShell::FlushPrefetchQueue() +{ + m_connection->PipelinedFetchMessageParts(GetUID(), m_prefetchQueue); + m_prefetchQueue->RemoveAndFreeAll(); +} + +// Requires that the shell is valid when called +// Performs a preflight check on all message parts to see if they are all +// inline. Returns TRUE if all parts are inline, FALSE otherwise. +XP_Bool TIMAPBodyShell::PreflightCheckAllInline() +{ + XP_Bool rv = m_message->PreflightCheckAllInline(); +// if (rv) +// PR_LOG(IMAP, out, ("BODYSHELL: All parts inline. Reverting to whole message download.")); + return rv; +} + +// When partNum is NULL, Generates a whole message and intelligently +// leaves out parts that are not inline. + +// When partNum is not NULL, Generates a MIME part that hasn't been downloaded yet +// Ok, here's how we're going to do this. Essentially, this +// will be the mirror image of the "normal" generation. +// All parts will be left out except a single part which is +// explicitly specified. All relevant headers will be included. +// Libmime will extract only the part of interest, so we don't +// have to worry about the other parts. This also has the +// advantage that it looks like it will be more workable for +// nested parts. For instance, if a user clicks on a link to +// a forwarded message, then that forwarded message may be +// generated along with any images that the forwarded message +// contains, for instance. + + +int32 TIMAPBodyShell::Generate(char *partNum) +{ + m_isBeingGenerated = TRUE; + m_generatingPart = partNum; + int32 contentLength = 0; + + if (!GetIsValid() || PreflightCheckAllInline()) + { + // We don't have a valid shell, or all parts are going to be inline anyway. Fall back to fetching the whole message. +#ifdef DEBUG_chrisf + XP_ASSERT(GetIsValid()); +#endif + m_generatingWholeMessage = TRUE; + uint32 messageSize = m_connection->GetMessageSize(GetUID(), TRUE); + m_connection->SetContentModified(FALSE); // So that when we cache it, we know we have the whole message + if (!DeathSignalReceived()) + m_connection->FetchTryChunking(GetUID(), TIMAP4BlockingConnection::kEveryThingRFC822, TRUE, NULL, messageSize); + contentLength = (int32) messageSize; // ugh + } + else + { + // We have a valid shell. + XP_Bool streamCreated = FALSE; + m_generatingWholeMessage = FALSE; + + ////// PASS 1 : PREFETCH /////// + // First, prefetch any additional headers/data that we need + if (!GetPseudoInterrupted()) + m_message->Generate(FALSE, TRUE); // This queues up everything we need to prefetch + // Now, run a single pipelined prefetch (neato!) + FlushPrefetchQueue(); + + ////// PASS 2 : COMPUTE STREAM SIZE /////// + // Next, figure out the size from the parts that we're going to fill in, + // plus all of the MIME headers, plus the message header itself + if (!GetPseudoInterrupted()) + contentLength = m_message->Generate(FALSE, FALSE); + + // Setup the stream + if (!GetPseudoInterrupted() && !DeathSignalReceived()) + { + m_connection->BeginMessageDownLoad(contentLength, MESSAGE_RFC822); + streamCreated = TRUE; + } + + ////// PASS 3 : GENERATE /////// + // Generate the message + if (!GetPseudoInterrupted() && !DeathSignalReceived()) + m_message->Generate(TRUE, FALSE); + + // Close the stream here - normal. If pseudointerrupted, the connection will abort the download stream + if (!GetPseudoInterrupted() && !DeathSignalReceived()) + m_connection->NormalMessageEndDownload(); + else if (streamCreated) + m_connection->AbortMessageDownLoad(); + + m_generatingPart = NULL; + + } + + m_isBeingGenerated = FALSE; + return contentLength; +} + +XP_Bool TIMAPBodyShell::GetPseudoInterrupted() +{ + XP_Bool rv = m_connection->GetPseudoInterrupted(); + return rv; +} + +XP_Bool TIMAPBodyShell::DeathSignalReceived() +{ + XP_Bool rv = m_connection->DeathSignalReceived(); + return rv; +} + + +////////////// Helpers ////////////////////////////////////////////////// + + +// Pass in a buffer starting at the first open paren +// Returns the location of the corresponding closing paren, +// or NULL if there is none in the buffer +static char *findEndParenInBuffer(char *buf) +{ + char *where = buf; + int numCloseParensNeeded = 1; + while (where && *where && (numCloseParensNeeded > 0)) + { + where++; + if (*where == '(') + numCloseParensNeeded++; + else if (*where == ')') + numCloseParensNeeded--; + } + return where; +} + + +///////////// TIMAPBodypart //////////////////////////////////// + + + +/* static */ +TIMAPBodypart *TIMAPBodypart::CreatePart(TIMAPBodyShell *shell, char *partNum, const char *buf, TIMAPBodypart *parentPart) +{ + // Check to see if this buffer is a leaf or container + // (Look at second character - if an open paren, then it is a container) + if (buf[0] != '(' || !buf[1]) + { + XP_ASSERT(FALSE); + return NULL; + } + + if (buf[1] == '(') + { + // If a container: + return new TIMAPBodypartMultipart(shell, partNum, buf, parentPart); + } + else + { + // If a leaf: + TIMAPBodypart *rv = new TIMAPBodypartLeaf(shell, partNum, buf, parentPart); + if (rv && rv->GetIsValid()) + { + const char *bodyType = rv->GetBodyType(); + const char *bodySubType = rv->GetBodySubType(); + if (!XP_STRCASECMP(bodyType, "message") && + !XP_STRCASECMP(bodySubType, "rfc822")) + { + // This is actually a part of type message/rfc822, + // probably a forwarded message. delete this and return + // the new type + char *keepPartNum = XP_STRDUP(partNum); // partNum will be deleted next... + delete rv; + return new TIMAPBodypartMessage(shell, keepPartNum, buf, parentPart, FALSE); + } + } + return rv; + } +} + + +TIMAPBodypart::TIMAPBodypart(TIMAPBodyShell *shell, char *partNumber, const char *buf, TIMAPBodypart *parentPart) : + TIMAPGenericParser() +{ + SetIsValid(TRUE); + m_parentPart = parentPart; + m_partNumberString = partNumber; // storage adopted + if (!shell) + { + SetIsValid(FALSE); + return; + } + if (buf) + m_responseBuffer = XP_STRDUP(buf); + else + m_responseBuffer = NULL; + m_shell = shell; + m_partData = NULL; + m_headerData = NULL; + m_boundaryData = NULL; // initialize from parsed BODYSTRUCTURE + m_contentLength = 0; + m_partLength = 0; + + m_contentType = NULL; + m_bodyType = NULL; + m_bodySubType = NULL; + m_bodyID = NULL; + m_bodyDescription = NULL; + m_bodyEncoding = NULL; +} + +TIMAPBodypart::~TIMAPBodypart() +{ + FREEIF(m_partNumberString); + FREEIF(m_responseBuffer); + FREEIF(m_contentType); + FREEIF(m_bodyType); + FREEIF(m_bodySubType); + FREEIF(m_bodyID); + FREEIF(m_bodyDescription); + FREEIF(m_bodyEncoding); + FREEIF(m_partData); + FREEIF(m_headerData); + FREEIF(m_boundaryData); +} + +void TIMAPBodypart::SetIsValid(XP_Bool valid) +{ + m_isValid = valid; + if (!m_isValid) + { + //PR_LOG(IMAP, out, ("BODYSHELL: Part is invalid. Part Number: %s Content-Type: %s", m_partNumberString, m_contentType)); + m_shell->SetIsValid(FALSE); + } +} + +XP_Bool TIMAPBodypart::GetNextLineForParser(char **nextLine) +{ + XP_Bool rv = TRUE; + *nextLine = m_responseBuffer; + if (!m_responseBuffer) + rv = FALSE; + m_responseBuffer = NULL; + return rv; +} + +XP_Bool TIMAPBodypart::ContinueParse() +{ + return GetIsValid() && TIMAPGenericParser::ContinueParse() && m_shell->GetIsValid(); +} + +// Adopts storage for part data buffer. If NULL, sets isValid to FALSE. +void TIMAPBodypart::AdoptPartDataBuffer(char *buf) +{ + m_partData = buf; + if (!m_partData) + { + SetIsValid(FALSE); + } +} + +// Adopts storage for header data buffer. If NULL, sets isValid to FALSE. +void TIMAPBodypart::AdoptHeaderDataBuffer(char *buf) +{ + m_headerData = buf; + if (!m_headerData) + { + SetIsValid(FALSE); + } +} + +// Finds the part with given part number +// Returns a TIMAPBodystructure of the matched part if it is this +// or one of its children. Returns NULL otherwise. +TIMAPBodypart *TIMAPBodypart::FindPartWithNumber(const char *partNum) +{ + // either brute force, or do it the smart way - look at the number. + // (the parts should be ordered, and hopefully indexed by their number) + + if (m_partNumberString && !XP_STRCASECMP(partNum, m_partNumberString)) + return this; + + //if (!m_partNumberString && !XP_STRCASECMP(partNum, "1")) + // return this; + + return NULL; +} + +/* +void TIMAPBodypart::PrefetchMIMEHeader() +{ + if (!m_headerData && !m_shell->DeathSignalReceived()) + { + m_shell->GetConnection()->FetchMessage(m_shell->GetUID(), TIMAP4BlockingConnection::kMIMEHeader, TRUE, 0, 0, m_partNumberString); + // m_headerLength will be filled in when it is adopted from the parser + } + if (!m_headerData) + { + SetIsValid(FALSE); + } +} +*/ + +void TIMAPBodypart::QueuePrefetchMIMEHeader() +{ + m_shell->AddPrefetchToQueue(TIMAP4BlockingConnection::kMIMEHeader, m_partNumberString); +} + +int32 TIMAPBodypart::GenerateMIMEHeader(XP_Bool stream, XP_Bool prefetch) +{ + if (prefetch && !m_headerData) + { + QueuePrefetchMIMEHeader(); + return 0; + } + else if (m_headerData) + { + int32 mimeHeaderLength = 0; + + if (!ShouldFetchInline()) + { + // if this part isn't inline, add the X-Mozilla-IMAP-Part header + char *xPartHeader = PR_smprintf("%s: %s", IMAP_EXTERNAL_CONTENT_HEADER, m_partNumberString); + if (xPartHeader) + { + if (stream) + { + m_shell->GetConnection()->Log("SHELL","GENERATE-XHeader",m_partNumberString); + m_shell->GetConnection()->HandleMessageDownLoadLine(xPartHeader, FALSE); + } + mimeHeaderLength += XP_STRLEN(xPartHeader); + XP_FREE(xPartHeader); + } + } + + mimeHeaderLength += XP_STRLEN(m_headerData); + if (stream) + { + m_shell->GetConnection()->Log("SHELL","GENERATE-MIMEHeader",m_partNumberString); + m_shell->GetConnection()->HandleMessageDownLoadLine(m_headerData, FALSE); // all one line? Can we do that? + } + + return mimeHeaderLength; + } + else + { + SetIsValid(FALSE); // prefetch didn't adopt a MIME header + return 0; + } +} + +int32 TIMAPBodypart::GeneratePart(XP_Bool stream, XP_Bool prefetch) +{ + if (prefetch) + return 0; // don't need to prefetch anything + + if (m_partData) // we have prefetched the part data + { + if (stream) + { + m_shell->GetConnection()->Log("SHELL","GENERATE-Part-Prefetched",m_partNumberString); + m_shell->GetConnection()->HandleMessageDownLoadLine(m_partData, FALSE); + } + return XP_STRLEN(m_partData); + } + else // we are fetching and streaming this part's body as we go + { + if (stream && !m_shell->DeathSignalReceived()) + { + m_shell->GetConnection()->Log("SHELL","GENERATE-Part-Inline",m_partNumberString); + m_shell->GetConnection()->FetchTryChunking(m_shell->GetUID(), TIMAP4BlockingConnection::kMIMEPart, TRUE, m_partNumberString, m_partLength); + } + return m_partLength; // the part length has been filled in from the BODYSTRUCTURE response + } +} + +int32 TIMAPBodypart::GenerateBoundary(XP_Bool stream, XP_Bool prefetch, XP_Bool lastBoundary) +{ + if (prefetch) + return 0; // don't need to prefetch anything + + if (m_boundaryData) + { + if (!lastBoundary) + { + if (stream) + { + m_shell->GetConnection()->Log("SHELL","GENERATE-Boundary",m_partNumberString); + m_shell->GetConnection()->HandleMessageDownLoadLine(m_boundaryData, FALSE); + } + return XP_STRLEN(m_boundaryData); + } + else // the last boundary + { + char *lastBoundaryData = PR_smprintf("%s--", m_boundaryData); + if (lastBoundaryData) + { + if (stream) + { + m_shell->GetConnection()->Log("SHELL","GENERATE-Boundary-Last",m_partNumberString); + m_shell->GetConnection()->HandleMessageDownLoadLine(lastBoundaryData, FALSE); + } + int32 rv = XP_STRLEN(lastBoundaryData); + XP_FREE(lastBoundaryData); + return rv; + } + else + { + //HandleMemoryFailure(); + return 0; + } + } + } + else + return 0; +} + +int32 TIMAPBodypart::GenerateEmptyFilling(XP_Bool stream, XP_Bool prefetch) +{ + if (prefetch) + return 0; // don't need to prefetch anything + + char *emptyString = XP_GetString(MK_IMAP_EMPTY_MIME_PART); + if (emptyString) + { + if (stream) + { + m_shell->GetConnection()->Log("SHELL","GENERATE-Filling",m_partNumberString); + m_shell->GetConnection()->HandleMessageDownLoadLine(emptyString, FALSE); + } + return XP_STRLEN(emptyString); + } + else + return 0; +} + + +// Returns TRUE if the prefs say that this content type should +// explicitly be kept in when filling in the shell +XP_Bool TIMAPBodypart::ShouldExplicitlyFetchInline() +{ + return FALSE; +} + + +// Returns TRUE if the prefs say that this content type should +// explicitly be left out when filling in the shell +XP_Bool TIMAPBodypart::ShouldExplicitlyNotFetchInline() +{ + return FALSE; +} + + +///////////// TIMAPBodypartLeaf ///////////////////////////// + + +TIMAPBodypartLeaf::TIMAPBodypartLeaf(TIMAPBodyShell *shell, char *partNum, const char *buf, TIMAPBodypart *parentPart) : +TIMAPBodypart(shell, partNum, buf, parentPart) +{ + SetIsValid(ParseIntoObjects()); +} + +TIMAPBodypartType TIMAPBodypartLeaf::GetType() +{ + return IMAP_BODY_LEAF; +} + +int32 TIMAPBodypartLeaf::Generate(XP_Bool stream, XP_Bool prefetch) +{ + int32 len = 0; + + if (GetIsValid()) + { + + if (stream && !prefetch) + m_shell->GetConnection()->Log("SHELL","GENERATE-Leaf",m_partNumberString); + + // Stream out the MIME part boundary + //GenerateBoundary(); + XP_ASSERT(m_parentPart); + //TIMAPBodypartMessage *parentMessage = m_parentPart ? m_parentPart->GetTIMAPBodypartMessage() : NULL; + + // Stream out the MIME header of this part, if this isn't the only body part of a message + //if (parentMessage ? !parentMessage->GetIsTopLevelMessage() : TRUE) + if ((m_parentPart->GetType() != IMAP_BODY_MESSAGE_RFC822) + && !m_shell->GetPseudoInterrupted()) + len += GenerateMIMEHeader(stream, prefetch); + + if (!m_shell->GetPseudoInterrupted()) + { + if (ShouldFetchInline()) + { + // Fetch and stream the content of this part + len += GeneratePart(stream, prefetch); + } + else + { + // fill in the filling within the empty part + len += GenerateEmptyFilling(stream, prefetch); + } + } + } + m_contentLength = len; + return m_contentLength; +} + + +// leaves parser at next token after basic fields are parsed. +// This can therefore be used for types derived from leaves, +// such as message/rfc822 and text/*. +XP_Bool TIMAPBodypartLeaf::ParseIntoObjects() +{ + // No children for a leaf - just parse this + + // Eat the buffer into the parser + fNextToken = GetNextToken(); + + // body type ("application", "text", "image", etc.) + if (ContinueParse()) + { + fNextToken++; // eat the first '(' + m_bodyType = CreateNilString(); + if (ContinueParse()) + fNextToken = GetNextToken(); + else + SetIsValid(FALSE); + } + else + SetIsValid(FALSE); + + // body subtype ("gif", "html", etc.) + if (ContinueParse()) + { + m_bodySubType = CreateNilString(); + if (ContinueParse()) + fNextToken = GetNextToken(); + else + SetIsValid(FALSE); + } + else + SetIsValid(FALSE); + + // body parameter parenthesized list + if (ContinueParse()) + { + if (!fNextToken) + SetIsValid(FALSE); + else if (fNextToken[0] == '(') + { + if (!fNextToken[1]) + SetIsValid(FALSE); + else + { + if (fNextToken[1] != ')') + { + fNextToken++; + skip_to_close_paren(); + } + else + { + fNextToken = GetNextToken(); + } + } + } + else if (!XP_STRCASECMP(fNextToken, "NIL")) + fNextToken = GetNextToken(); + } + else + SetIsValid(FALSE); + + // body id + if (ContinueParse()) + { + m_bodyID = CreateNilString(); + if (ContinueParse()) + fNextToken = GetNextToken(); + else + SetIsValid(FALSE); + } + else + SetIsValid(FALSE); + + // body description + if (ContinueParse()) + { + m_bodyDescription = CreateNilString(); + if (ContinueParse()) + fNextToken = GetNextToken(); + else + SetIsValid(FALSE); + } + else + SetIsValid(FALSE); + + // body encoding + if (ContinueParse()) + { + m_bodyEncoding = CreateNilString(); + if (ContinueParse()) + fNextToken = GetNextToken(); + else + SetIsValid(FALSE); + } + else + SetIsValid(FALSE); + + // body size + if (ContinueParse()) + { + char *bodySizeString = CreateAtom(); + if (bodySizeString) + { + // convert to an int32 here, set m_partLength + m_partLength = atoint32(bodySizeString); + XP_FREE(bodySizeString); + } + else + SetIsValid(FALSE); + + if (ContinueParse()) + fNextToken = GetNextToken(); + else + SetIsValid(FALSE); + } + else + SetIsValid(FALSE); + + + // that's it, we can just leave the parser the way it is since + // we'll never use it again. + + if (GetIsValid() && m_bodyType && m_bodySubType) + { + m_contentType = PR_smprintf("%s/%s",m_bodyType,m_bodySubType); + } + + return TRUE; +} + +// returns TRUE if this part should be fetched inline for generation. +XP_Bool TIMAPBodypartLeaf::ShouldFetchInline() +{ + char *generatingPart = m_shell->GetGeneratingPart(); + if (generatingPart) + { + // If we are generating a specific part + if (!XP_STRCMP(generatingPart, m_partNumberString)) + { + // This is the part we're generating + return TRUE; + } + else + { + // If this is the only body part of a message, and that + // message is the part being generated, then this leaf should + // be inline as well. + if ((m_parentPart->GetType() == IMAP_BODY_MESSAGE_RFC822) && + (!XP_STRCMP(m_parentPart->GetPartNumberString(), generatingPart))) + return TRUE; + + // Leave out all other leaves if this isn't the one + // we're generating. + // Maybe change later to check parents, etc. + return FALSE; + } + } + else + { + // We are generating the whole message, possibly (hopefully) + // leaving out non-inline parts + + if (ShouldExplicitlyFetchInline()) + return TRUE; + if (ShouldExplicitlyNotFetchInline()) + return FALSE; + + // If the parent is a message (this is the only body part of that + // message), and that message should be inline, then its body + // should inherit the inline characteristics of that message + if (m_parentPart->GetType() == IMAP_BODY_MESSAGE_RFC822) + return m_parentPart->ShouldFetchInline(); + + if (!m_shell->GetShowAttachmentsInline()) + { + // The first text part is still displayed inline, + // even if View Attachments As Links is on. + if (!XP_STRCMP(m_partNumberString, "1") && + !XP_STRCASECMP(m_bodyType, "text")) + return TRUE; // we're downloading it inline + else + return FALSE; // we can leave it on the server + } + if (!XP_STRCASECMP(m_bodyType, "APPLICATION") && // If it is of type "application" + XP_STRNCASECMP(m_bodySubType, "x-pkcs7", 7) // and it's not a signature (signatures are inline) +#ifdef XP_MAC + && XP_STRCASECMP(m_bodySubType, "applefile") // and it's not an appledouble resource fork on Mac (they're inline) +#endif + ) + return FALSE; // we can leave it on the server + return TRUE; // we're downloading it inline + } +} + +XP_Bool TIMAPBodypartLeaf::PreflightCheckAllInline() +{ + // only need to check this part, since it has no children. + return ShouldFetchInline(); +} + + +///////////// TIMAPBodypartMessage //////////////////////// + +TIMAPBodypartMessage::TIMAPBodypartMessage(TIMAPBodyShell *shell, char *partNum, const char *buf, TIMAPBodypart *parentPart, + XP_Bool topLevelMessage) : TIMAPBodypartLeaf(shell, partNum, buf, parentPart) +{ + m_topLevelMessage = topLevelMessage; + if (m_topLevelMessage) + { + m_partNumberString = PR_smprintf("0"); + if (!m_partNumberString) + { + SetIsValid(FALSE); + return; + } + } + m_body = NULL; + m_headers = new TIMAPMessageHeaders(shell, m_partNumberString, this); // We always have a Headers object + if (!m_headers || !m_headers->GetIsValid()) + { + SetIsValid(FALSE); + return; + } + SetIsValid(ParseIntoObjects()); +} + +TIMAPBodypartType TIMAPBodypartMessage::GetType() +{ + return IMAP_BODY_MESSAGE_RFC822; +} + +TIMAPBodypartMessage::~TIMAPBodypartMessage() +{ + delete m_headers; + delete m_body; +} + +int32 TIMAPBodypartMessage::Generate(XP_Bool stream, XP_Bool prefetch) +{ + if (!GetIsValid()) + return 0; + + m_contentLength = 0; + + if (stream && !prefetch) + m_shell->GetConnection()->Log("SHELL","GENERATE-MessageRFC822",m_partNumberString); + + if (!m_topLevelMessage && !m_shell->GetPseudoInterrupted()) // not the top-level message - we need the MIME header as well as the message header + { + m_contentLength += GenerateMIMEHeader(stream, prefetch); + } + + if (!m_shell->GetPseudoInterrupted()) + m_contentLength += m_headers->Generate(stream, prefetch); + if (!m_shell->GetPseudoInterrupted()) + m_contentLength += m_body->Generate(stream, prefetch); + + return m_contentLength; +} + + +XP_Bool TIMAPBodypartMessage::ParseIntoObjects() +{ + if (ContinueParse()) // basic fields parsed out ok + { + // three additional fields: envelope structure, bodystructure, and size in lines + if (*fNextToken == '(') + { + // ignore the envelope... maybe when we have time we can parse it for fields + // char *envelope = CreateParenGroup(); + fNextToken++; + skip_to_close_paren(); + } + else + SetIsValid(FALSE); + + if (ContinueParse()) + { + if (*fNextToken == '(') + { + // extract the bodystructure and create the body from it + char *parenGroup = CreateParenGroup(); + if (parenGroup) + { + char *bodyPartNum = NULL; + if (!m_topLevelMessage) + bodyPartNum = PR_smprintf("%s.1", m_partNumberString); + else + bodyPartNum = PR_smprintf("1"); + if (bodyPartNum && ContinueParse()) // storage adopted by child part + { + m_body = TIMAPBodypart::CreatePart(m_shell, bodyPartNum, parenGroup, this); + } + else + { + // HandleMemoryFailure(); + } + XP_FREE(parenGroup); + } + else + { + SetSyntaxError(TRUE); + SetIsValid(FALSE); + } + } + else + SetIsValid(FALSE); + } + else + SetIsValid(FALSE); + + // ignore size in lines + } + + if (!m_body || !m_body->GetIsValid()) + SetIsValid(FALSE); + + return GetIsValid(); +} + + +XP_Bool TIMAPBodypartMessage::ShouldFetchInline() +{ + if (m_topLevelMessage) // the main message should always be defined as "inline" + return TRUE; + + char *generatingPart = m_shell->GetGeneratingPart(); + if (generatingPart) + { + // If we are generating a specific part + // Always generate containers (just don't fill them in) + // because it is low cost (everything is cached) + // and it gives the message its full MIME structure, + // to avoid any potential mishap. + return TRUE; + } + else + { + // Generating whole message + + if (ShouldExplicitlyFetchInline()) + return TRUE; + if (ShouldExplicitlyNotFetchInline()) + return FALSE; + + + if (!m_shell->GetShowAttachmentsInline()) + return FALSE; + + // Message types are inline, by default. + return TRUE; + } +} + +XP_Bool TIMAPBodypartMessage::PreflightCheckAllInline() +{ + if (!ShouldFetchInline()) + return FALSE; + + return m_body->PreflightCheckAllInline(); +} + +// Fills in buffer (and adopts storage) for header object +void TIMAPBodypartMessage::AdoptMessageHeaders(char *headers) +{ + if (!GetIsValid()) + return; + + // we are going to say that the message headers only have + // part data, and no header data. + m_headers->AdoptPartDataBuffer(headers); + if (!m_headers->GetIsValid()) + SetIsValid(FALSE); +} + +// Finds the part with given part number +// Returns a TIMAPBodystructure of the matched part if it is this +// or one of its children. Returns NULL otherwise. +TIMAPBodypart *TIMAPBodypartMessage::FindPartWithNumber(const char *partNum) +{ + // either brute force, or do it the smart way - look at the number. + // (the parts should be ordered, and hopefully indexed by their number) + + if (!XP_STRCASECMP(partNum, m_partNumberString)) + return this; + + return m_body->FindPartWithNumber(partNum); +} + +///////////// TIMAPBodypartMultipart //////////////////////// + + +TIMAPBodypartMultipart::TIMAPBodypartMultipart(TIMAPBodyShell *shell, char *partNum, const char *buf, TIMAPBodypart *parentPart) : +TIMAPBodypart(shell, partNum, buf, parentPart) +{ + if (!m_parentPart || (m_parentPart->GetType() == IMAP_BODY_MESSAGE_RFC822)) + { + // the multipart (this) will inherit the part number of its parent + FREEIF(m_partNumberString); + if (!m_parentPart) + { + m_partNumberString = PR_smprintf("0"); + } + else + { + m_partNumberString = XP_STRDUP(m_parentPart->GetPartNumberString()); + } + } + m_partList = new XPPtrArray(); + if (!m_partList) + { + SetIsValid(FALSE); + return; + } + if (!m_parentPart) + { + SetIsValid(FALSE); + return; + } + SetIsValid(ParseIntoObjects()); +} + +TIMAPBodypartType TIMAPBodypartMultipart::GetType() +{ + return IMAP_BODY_MULTIPART; +} + +TIMAPBodypartMultipart::~TIMAPBodypartMultipart() +{ + TIMAPBodypart *bp = NULL; + for (int i = m_partList->GetSize() - 1; i >= 0; i--) + { + delete (TIMAPBodypart *)(m_partList->GetAt(i)); + } + delete m_partList; +} + +int32 TIMAPBodypartMultipart::Generate(XP_Bool stream, XP_Bool prefetch) +{ + int32 len = 0; + + if (GetIsValid()) + { + if (stream && !prefetch) + m_shell->GetConnection()->Log("SHELL","GENERATE-Multipart",m_partNumberString); + + // Stream out the MIME header of this part + + XP_Bool parentIsMessageType = GetParentPart() ? (GetParentPart()->GetType() == IMAP_BODY_MESSAGE_RFC822) : TRUE; + + // If this is multipart/signed, then we always want to generate the MIME headers of this multipart. + // Otherwise, we only want to do it if the parent is not of type "message" + XP_Bool needMIMEHeader = !parentIsMessageType; // !XP_STRCASECMP(m_bodySubType, "signed") ? TRUE : !parentIsMessageType; + if (needMIMEHeader && !m_shell->GetPseudoInterrupted()) // not a message body's type + { + len += GenerateMIMEHeader(stream, prefetch); + } + + if (ShouldFetchInline()) + { + for (int i = 0; i < m_partList->GetSize(); i++) + { + if (!m_shell->GetPseudoInterrupted()) + len += GenerateBoundary(stream, prefetch, FALSE); + if (!m_shell->GetPseudoInterrupted()) + len += ((TIMAPBodypart *)(m_partList->GetAt(i)))->Generate(stream, prefetch); + } + if (!m_shell->GetPseudoInterrupted()) + len += GenerateBoundary(stream, prefetch, TRUE); + } + else + { + // fill in the filling within the empty part + if (!m_shell->GetPseudoInterrupted()) + len += GenerateEmptyFilling(stream, prefetch); + } + } + m_contentLength = len; + return m_contentLength; +} + +XP_Bool TIMAPBodypartMultipart::ParseIntoObjects() +{ + char *where = m_responseBuffer+1; + int childCount = 0; + // Parse children + // Pull out all the children parts from buf, and send them on their way + while (where[0] == '(' && ContinueParse()) + { + char *endParen = findEndParenInBuffer(where); + if (endParen) + { + int32 len = 1 + endParen - where; + char *parenGroup = (char *)XP_ALLOC((len + 1)*sizeof(char)); + if (parenGroup) + { + XP_STRNCPY_SAFE(parenGroup, where, len + 1); + parenGroup[len] = 0; + childCount++; + char *childPartNum = NULL; + if (XP_STRCMP(m_partNumberString, "0")) // not top-level + childPartNum = PR_smprintf("%s.%d", m_partNumberString, childCount); + else // top-level + childPartNum = PR_smprintf("%d", childCount); + if (childPartNum) // storage adopted for childPartNum + { + TIMAPBodypart *child = TIMAPBodypart::CreatePart(m_shell, childPartNum, parenGroup, this); + if (child) + { + m_partList->Add(child); + } + else + { + SetIsValid(FALSE); + } + } + else + { + SetIsValid(FALSE); + } + XP_FREE(parenGroup); + + // move the next child down + char *newBuf = NULL; + if (*(endParen + 1) == ' ') // last child + newBuf = PR_smprintf("(%s", endParen + 2); + else + newBuf = PR_smprintf("(%s", endParen + 1); + FREEIF(m_responseBuffer); + m_responseBuffer = newBuf; + where = m_responseBuffer+1; + } + else + { + SetIsValid(FALSE); + } + } + else + { + SetIsValid(FALSE); + } + } + + if (GetIsValid()) + { + m_bodyType = XP_STRDUP("multipart"); + + // Parse what's left of this. + + // Eat the buffer into the parser + fNextToken = GetNextToken(); + if (ContinueParse()) + { + // multipart subtype (mixed, alternative, etc.) + fNextToken++; + m_bodySubType = CreateNilString(); + if (ContinueParse()) + fNextToken = GetNextToken(); + else + SetIsValid(FALSE); + } + + if (ContinueParse()) + { + // body parameter parenthesized list, including + // boundary + fNextToken++; + while (ContinueParse() && *fNextToken != ')') + { + char *attribute = CreateNilString(); + if (ContinueParse()) + fNextToken = GetNextToken(); + else + SetIsValid(FALSE); + + if (ContinueParse() && attribute && + !XP_STRCASECMP(attribute, "BOUNDARY")) + { + char *boundary = CreateNilString(); + if (boundary) + { + m_boundaryData = PR_smprintf("--%s", boundary); + XP_FREE(boundary); + } + + if (ContinueParse()) + fNextToken = GetNextToken(); + else + SetIsValid(FALSE); + XP_FREE(attribute); + } + else + { + FREEIF(attribute); + if (ContinueParse()) + { + // removed because parser was advancing one too many tokens + //fNextToken = GetNextToken(); + char *value = CreateNilString(); + FREEIF(value); + if (ContinueParse()) + fNextToken = GetNextToken(); + //if (ContinueParse()) + // fNextToken = GetNextToken(); + } + } + } + } + + // We might want the disposition, but I think we can leave it like this for now. + + m_contentType = PR_smprintf("%s/%s", m_bodyType, m_bodySubType); + } + + if (!m_boundaryData) + { + // Actually, we should probably generate a boundary here. + SetIsValid(FALSE); + } + + return GetIsValid(); +} + +XP_Bool TIMAPBodypartMultipart::ShouldFetchInline() +{ + char *generatingPart = m_shell->GetGeneratingPart(); + if (generatingPart) + { + // If we are generating a specific part + // Always generate containers (just don't fill them in) + // because it is low cost (everything is cached) + // and it gives the message its full MIME structure, + // to avoid any potential mishap. + return TRUE; + } + else + { + // Generating whole message + + if (ShouldExplicitlyFetchInline()) + return TRUE; + if (ShouldExplicitlyNotFetchInline()) + return FALSE; + + // If "Show Attachments as Links" is on, and + // the parent of this multipart is not a message, + // then it's not inline. + if (!m_shell->GetShowAttachmentsInline() && + (m_parentPart->GetType() != IMAP_BODY_MESSAGE_RFC822)) + return FALSE; + + // multiparts are always inline + // (their children might not be, though) + return TRUE; + } +} + +XP_Bool TIMAPBodypartMultipart::PreflightCheckAllInline() +{ + XP_Bool rv = ShouldFetchInline(); + + int i = 0; + while (rv && (i < m_partList->GetSize())) + { + rv = ((TIMAPBodypart *)(m_partList->GetAt(i)))->PreflightCheckAllInline(); + i++; + } + + return rv; +} + +TIMAPBodypart *TIMAPBodypartMultipart::FindPartWithNumber(const char *partNum) +{ + XP_ASSERT(partNum); + + // check this + if (!XP_STRCMP(partNum, m_partNumberString)) + return this; + + // check children + for (int i = m_partList->GetSize() - 1; i >= 0; i--) + { + TIMAPBodypart *foundPart = ((TIMAPBodypart *)(m_partList->GetAt(i)))->FindPartWithNumber(partNum); + if (foundPart) + return foundPart; + } + + // not this, or any of this's children + return NULL; +} + + + +///////////// TIMAPMessageHeaders //////////////////////////////////// + + + +TIMAPMessageHeaders::TIMAPMessageHeaders(TIMAPBodyShell *shell, char *partNum, TIMAPBodypart *parentPart) : +TIMAPBodypart(shell, partNum, NULL, parentPart) +{ + if (!partNum) + { + SetIsValid(FALSE); + return; + } + m_partNumberString = XP_STRDUP(partNum); + if (!m_partNumberString) + { + SetIsValid(FALSE); + return; + } + if (!m_parentPart || !m_parentPart->GetTIMAPBodypartMessage()) + { + // Message headers created without a valid Message parent + XP_ASSERT(FALSE); + SetIsValid(FALSE); + } +} + +TIMAPBodypartType TIMAPMessageHeaders::GetType() +{ + return IMAP_BODY_MESSAGE_HEADER; +} + +void TIMAPMessageHeaders::QueuePrefetchMessageHeaders() +{ + + if (!m_parentPart->GetTIMAPBodypartMessage()->GetIsTopLevelMessage()) // not top-level headers + m_shell->AddPrefetchToQueue(TIMAP4BlockingConnection::kRFC822HeadersOnly, m_partNumberString); + else + m_shell->AddPrefetchToQueue(TIMAP4BlockingConnection::kRFC822HeadersOnly, NULL); +} + +int32 TIMAPMessageHeaders::Generate(XP_Bool stream, XP_Bool prefetch) +{ + // prefetch the header + if (prefetch && !m_partData && !m_shell->DeathSignalReceived()) + { + QueuePrefetchMessageHeaders(); + } + + if (stream && !prefetch) + m_shell->GetConnection()->Log("SHELL","GENERATE-MessageHeaders",m_partNumberString); + + // stream out the part data + if (ShouldFetchInline()) + { + if (!m_shell->GetPseudoInterrupted()) + m_contentLength = GeneratePart(stream, prefetch); + } + else + { + m_contentLength = 0; // don't fill in any filling for the headers + } + return m_contentLength; +} + +XP_Bool TIMAPMessageHeaders::ParseIntoObjects() +{ + return TRUE; +} + +XP_Bool TIMAPMessageHeaders::ShouldFetchInline() +{ + return m_parentPart->ShouldFetchInline(); +} + + +///////////// TIMAPBodyShellCache //////////////////////////////////// + + +static int +imap_shell_cache_strcmp (const void *a, const void *b) +{ + return XP_STRCMP ((const char *) a, (const char *) b); +} + + +TIMAPBodyShellCache::TIMAPBodyShellCache() +{ + m_shellHash = XP_HashTableNew(20, XP_StringHash, imap_shell_cache_strcmp); + m_shellList = new XPPtrArray(); +} + +/* static */ TIMAPBodyShellCache *TIMAPBodyShellCache::Create() +{ + TIMAPBodyShellCache *cache = new TIMAPBodyShellCache(); + if (!cache || !cache->m_shellHash || !cache->m_shellList) + return NULL; + + return cache; +} + +TIMAPBodyShellCache::~TIMAPBodyShellCache() +{ + while (EjectEntry()) ; + XP_HashTableDestroy(m_shellHash); + delete m_shellList; +} + +// We'll use an LRU scheme here. +// We will add shells in numerical order, so the +// least recently used one will be in slot 0. +XP_Bool TIMAPBodyShellCache::EjectEntry() +{ + if (m_shellList->GetSize() < 1) + return FALSE; + + TIMAPBodyShell *removedShell = (TIMAPBodyShell *) (m_shellList->GetAt(0)); + m_shellList->RemoveAt(0); + XP_Remhash(m_shellHash, removedShell->GetUID()); + delete removedShell; + + return TRUE; +} + +XP_Bool TIMAPBodyShellCache::AddShellToCache(TIMAPBodyShell *shell) +{ + // If it's already in the cache, then just return. + // This has the side-effect of re-ordering the LRU list + // to put this at the top, which is good, because it's what we want. + if (FindShellForUID(shell->GetUID(), shell->GetFolderName())) + return TRUE; + + // OK, so it's not in the cache currently. + + // First, for safety sake, remove any entry with the given UID, + // just in case we have a collision between two messages in different + // folders with the same UID. + TIMAPBodyShell *foundShell = (TIMAPBodyShell *) XP_Gethash(m_shellHash, shell->GetUID(), NULL); + if (foundShell) + { + XP_Remhash(m_shellHash, foundShell); + m_shellList->Remove(foundShell); + } + + // Add the new one to the cache + m_shellList->Add(shell); + XP_Puthash(m_shellHash, shell->GetUID(), shell); + shell->SetIsCached(TRUE); + + // while we're not over our size limit, eject entries + XP_Bool rv = TRUE; + while (GetSize() > GetMaxSize()) + { + rv = EjectEntry(); + } + + return rv; + +} + +TIMAPBodyShell *TIMAPBodyShellCache::FindShellForUID(const char *UID, const char *mailboxName) +{ + if (!UID) + return NULL; + + TIMAPBodyShell *foundShell = (TIMAPBodyShell *) XP_Gethash(m_shellHash, UID, NULL); + + if (!foundShell) + return NULL; + + // mailbox names must match also. + if (XP_STRCMP(mailboxName, foundShell->GetFolderName())) + return NULL; + + // adjust the LRU stuff + m_shellList->Remove(foundShell); // oh well, I suppose this defeats the performance gain of the hash if it actually is found + m_shellList->Add(foundShell); // Adds to end + + return foundShell; +} + +TIMAPBodyShell *TIMAPBodyShellCache::FindShellForUID(uint32 UID, const char *mailboxName) +{ + char *uidString = PR_smprintf("%ld", UID); + TIMAPBodyShell *rv = FindShellForUID(uidString, mailboxName); + FREEIF(uidString); + return rv; +} + + +///////////// TIMAPMessagePartID //////////////////////////////////// + + +TIMAPMessagePartID::TIMAPMessagePartID(TIMAP4BlockingConnection::eFetchFields fields, const char *partNumberString) +{ + m_fields = fields; + m_partNumberString = partNumberString; +} + +TIMAPMessagePartIDArray::TIMAPMessagePartIDArray() +{ +} + +TIMAPMessagePartIDArray::~TIMAPMessagePartIDArray() +{ + RemoveAndFreeAll(); +} + +void TIMAPMessagePartIDArray::RemoveAndFreeAll() +{ + while (GetSize() > 0) + { + TIMAPMessagePartID *part = GetPart(0); + delete part; + RemoveAt(0); + } +} diff --git a/mozilla/network/protocol/imap4/imapbody.h b/mozilla/network/protocol/imap4/imapbody.h index e69de29bb2d..ad0dd8247d1 100644 --- a/mozilla/network/protocol/imap4/imapbody.h +++ b/mozilla/network/protocol/imap4/imapbody.h @@ -0,0 +1,364 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* +TIMAPBodyShell and associated classes +*/ + +#ifndef IMAPBODY_H +#define IMAPBODY_H + +#include "imap.h" +#include "xp_list.h" + + +typedef enum _TIMAPBodypartType { + IMAP_BODY_MESSAGE_RFC822, + IMAP_BODY_MESSAGE_HEADER, + IMAP_BODY_LEAF, + IMAP_BODY_MULTIPART +} TIMAPBodypartType; + +class TIMAPGenericParser; +class TNavigatorImapConnection; +class TIMAPBodyShell; +class XPPtrArray; +class TIMAPBodypartMessage; +typedef struct xp_HashTable *XP_HashTable; + + +class TIMAPBodypart : public TIMAPGenericParser +{ +public: + // Construction + static TIMAPBodypart *CreatePart(TIMAPBodyShell *shell, char *partNum, const char *buf, TIMAPBodypart *parentPart); + virtual XP_Bool GetIsValid() { return m_isValid; } + virtual void SetIsValid(XP_Bool valid); + virtual TIMAPBodypartType GetType() = 0; + + // Generation + virtual int32 Generate(XP_Bool /*stream*/, XP_Bool /* prefetch */) { return -1; } // Generates an HTML representation of this part. Returns content length generated, -1 if failed. + virtual void AdoptPartDataBuffer(char *buf); // Adopts storage for part data buffer. If NULL, sets isValid to FALSE. + virtual void AdoptHeaderDataBuffer(char *buf); // Adopts storage for header data buffer. If NULL, sets isValid to FALSE. + virtual XP_Bool ShouldFetchInline() { return TRUE; } // returns TRUE if this part should be fetched inline for generation. + virtual XP_Bool PreflightCheckAllInline() { return TRUE; } + + virtual XP_Bool ShouldExplicitlyFetchInline(); + virtual XP_Bool ShouldExplicitlyNotFetchInline(); + +protected: // If stream is FALSE, simply returns the content length that will be generated + virtual int32 GeneratePart(XP_Bool stream, XP_Bool prefetch); // the body of the part itself + virtual int32 GenerateMIMEHeader(XP_Bool stream, XP_Bool prefetch); // the MIME headers of the part + virtual int32 GenerateBoundary(XP_Bool stream, XP_Bool prefetch, XP_Bool lastBoundary); // Generates the MIME boundary wrapper for this part. + // lastBoundary indicates whether or not this should be the boundary for the + // final MIME part of the multipart message. + virtual int32 GenerateEmptyFilling(XP_Bool stream, XP_Bool prefetch); // Generates (possibly empty) filling for a part that won't be filled in inline. + + // Part Numbers / Hierarchy +public: + virtual int GetPartNumber() { return m_partNumber; } // Returns the part number on this hierarchy level + virtual char *GetPartNumberString() { return m_partNumberString; } + virtual TIMAPBodypart *FindPartWithNumber(const char *partNum); // Returns the part object with the given number + virtual TIMAPBodypart *GetParentPart() { return m_parentPart; } // Returns the parent of this part. + // We will define a part of type message/rfc822 to be the + // parent of its body and header. + // A multipart is a parent of its child parts. + // All other leafs do not have children. + + // Other / Helpers +public: + virtual ~TIMAPBodypart(); + virtual XP_Bool GetNextLineForParser(char **nextLine); + virtual XP_Bool ContinueParse(); // overrides the parser, but calls it anyway + virtual TIMAPBodypartMessage *GetTIMAPBodypartMessage() { return NULL; } + + +protected: + virtual void QueuePrefetchMIMEHeader(); + //virtual void PrefetchMIMEHeader(); // Initiates a prefetch for the MIME header of this part. + virtual XP_Bool ParseIntoObjects() = 0; // Parses buffer and fills in both this and any children with associated objects + // Returns TRUE if it produced a valid Shell + // Must be overridden in the concerte derived class + const char *GetBodyType() { return m_bodyType; } + const char *GetBodySubType() { return m_bodySubType; } + + TIMAPBodypart(TIMAPBodyShell *shell, char *partNumber, const char *buf, TIMAPBodypart *parentPart); + +protected: + TIMAPBodyShell *m_shell; // points back to the shell + XP_Bool m_isValid; // If this part is valid. + int m_partNumber; // part number on this hierarchy level + char *m_partNumberString; // string representation of this part's full-hierarchy number. Define 0 to be the top-level message + char *m_partData; // data for this part. NULL if not filled in yet. + char *m_headerData; // data for this part's MIME header. NULL if not filled in yet. + char *m_boundaryData; // MIME boundary for this part + int32 m_partLength; + int32 m_contentLength; // Total content length which will be Generate()'d. -1 if not filled in yet. + char *m_responseBuffer; // The buffer for this object + TIMAPBodypart *m_parentPart; // Parent of this part + + // Fields - Filled in from parsed BODYSTRUCTURE response (as well as others) + char *m_contentType; // constructed from m_bodyType and m_bodySubType + char *m_bodyType; + char *m_bodySubType; + char *m_bodyID; + char *m_bodyDescription; + char *m_bodyEncoding; + // we ignore extension data for now + + +}; + + + +// Message headers +// A special type of TIMAPBodypart +// These may be headers for the top-level message, +// or any body part of type message/rfc822. +class TIMAPMessageHeaders : public TIMAPBodypart +{ +public: + TIMAPMessageHeaders(TIMAPBodyShell *shell, char *partNum, TIMAPBodypart *parentPart); + virtual TIMAPBodypartType GetType(); + virtual int32 Generate(XP_Bool stream, XP_Bool prefetch); // Generates an HTML representation of this part. Returns content length generated, -1 if failed. + virtual XP_Bool ShouldFetchInline(); + virtual void QueuePrefetchMessageHeaders(); +protected: + virtual XP_Bool ParseIntoObjects(); // Parses m_responseBuffer and fills in m_partList with associated objects + // Returns TRUE if it produced a valid Shell + +}; + + +class TIMAPBodypartMultipart : public TIMAPBodypart +{ +public: + TIMAPBodypartMultipart(TIMAPBodyShell *shell, char *partNum, const char *buf, TIMAPBodypart *parentPart); + virtual TIMAPBodypartType GetType(); + virtual ~TIMAPBodypartMultipart(); + virtual XP_Bool ShouldFetchInline(); + virtual XP_Bool PreflightCheckAllInline(); + virtual int32 Generate(XP_Bool stream, XP_Bool prefetch); // Generates an HTML representation of this part. Returns content length generated, -1 if failed. + virtual TIMAPBodypart *FindPartWithNumber(const char *partNum); // Returns the part object with the given number + +protected: + virtual XP_Bool ParseIntoObjects(); + +protected: + XPPtrArray *m_partList; // An ordered list of top-level body parts for this shell +}; + + +// The name "leaf" is somewhat misleading, since a part of type message/rfc822 is technically +// a leaf, even though it can contain other parts within it. +class TIMAPBodypartLeaf : public TIMAPBodypart +{ +public: + TIMAPBodypartLeaf(TIMAPBodyShell *shell, char *partNum, const char *buf, TIMAPBodypart *parentPart); + virtual TIMAPBodypartType GetType(); + virtual int32 Generate(XP_Bool stream, XP_Bool prefetch); // Generates an HTML representation of this part. Returns content length generated, -1 if failed. + virtual XP_Bool ShouldFetchInline(); // returns TRUE if this part should be fetched inline for generation. + virtual XP_Bool PreflightCheckAllInline(); + +protected: + virtual XP_Bool ParseIntoObjects(); + +}; + + +class TIMAPBodypartMessage : public TIMAPBodypartLeaf +{ +public: + TIMAPBodypartMessage(TIMAPBodyShell *shell, char *partNum, const char *buf, TIMAPBodypart *parentPart, XP_Bool topLevelMessage); + virtual TIMAPBodypartType GetType(); + virtual ~TIMAPBodypartMessage(); + virtual int32 Generate(XP_Bool stream, XP_Bool prefetch); + virtual XP_Bool ShouldFetchInline(); + virtual XP_Bool PreflightCheckAllInline(); + virtual TIMAPBodypart *FindPartWithNumber(const char *partNum); // Returns the part object with the given number + void AdoptMessageHeaders(char *headers); // Fills in buffer (and adopts storage) for header object + // partNum specifies the message part number to which the + // headers correspond. NULL indicates the top-level message + virtual TIMAPBodypartMessage *GetTIMAPBodypartMessage() { return this; } + virtual XP_Bool GetIsTopLevelMessage() { return m_topLevelMessage; } + +protected: + virtual XP_Bool ParseIntoObjects(); + +protected: + TIMAPMessageHeaders *m_headers; // Every body shell should have headers + TIMAPBodypart *m_body; + XP_Bool m_topLevelMessage; // Whether or not this is the top-level message + +}; + + +class TIMAPMessagePartIDArray; + +// We will refer to a Body "Shell" as a hierarchical object representation of a parsed BODYSTRUCTURE +// response. A shell contains representations of Shell "Parts." A Body Shell can undergo essentially +// two operations: Construction and Generation. +// Shell Construction occurs by parsing a BODYSTRUCTURE response into empty Parts. +// Shell Generation generates a "MIME Shell" of the message and streams it to libmime for +// display. The MIME Shell has selected (inline) parts filled in, and leaves all others +// for on-demand retrieval through explicit part fetches. + +class TIMAPBodyShell +{ +public: + + // Construction + TIMAPBodyShell(TNavigatorImapConnection *connection, const char *bs, uint32 UID, const char *folderName); // Constructor takes in a buffer containing an IMAP + // bodystructure response from the server, with the associated + // tag/command/etc. stripped off. + // That is, it takes in something of the form: + // (("TEXT" "PLAIN" ..... )) + virtual ~TIMAPBodyShell(); + void SetConnection(TNavigatorImapConnection *con) { m_connection = con; } // To be used after a shell is uncached + virtual XP_Bool GetIsValid() { return m_isValid; } + virtual void SetIsValid(XP_Bool valid); + + // Prefetch + void AddPrefetchToQueue(TIMAP4BlockingConnection::eFetchFields, const char *partNum); // Adds a message body part to the queue to be prefetched + // in a single, pipelined command + void FlushPrefetchQueue(); // Runs a single pipelined command which fetches all of the + // elements in the prefetch queue + void AdoptMessageHeaders(char *headers, const char *partNum); // Fills in buffer (and adopts storage) for header object + // partNum specifies the message part number to which the + // headers correspond. NULL indicates the top-level message + void AdoptMimeHeader(const char *partNum, char *mimeHeader); // Fills in buffer (and adopts storage) for MIME headers in appropriate object. + // If object can't be found, sets isValid to FALSE. + + // Generation + virtual int32 Generate(char *partNum); // Streams out an HTML representation of this IMAP message, going along and + // fetching parts it thinks it needs, and leaving empty shells for the parts + // it doesn't. + // Returns number of bytes generated, or -1 if invalid. + // If partNum is not NULL, then this works to generates a MIME part that hasn't been downloaded yet + // and leaves out all other parts. By default, to generate a normal message, partNum should be NULL. + + XP_Bool GetShowAttachmentsInline(); // Returns TRUE if the user has the pref "Show Attachments Inline" set. + // Returns FALSE if the setting is "Show Attachments as Links" + XP_Bool PreflightCheckAllInline(); // Returns TRUE if all parts are inline, FALSE otherwise. Does not generate anything. + + // Helpers + TNavigatorImapConnection *GetConnection() { return m_connection; } + XP_Bool GetPseudoInterrupted(); + XP_Bool DeathSignalReceived(); + const char *GetUID() { return m_UID; } + const char *GetFolderName() { return m_folderName; } + char *GetGeneratingPart() { return m_generatingPart; } + XP_Bool IsBeingGenerated() { return m_isBeingGenerated; } // Returns TRUE if this is in the process of being + // generated, so we don't re-enter + XP_Bool IsShellCached() { return m_cached; } + void SetIsCached(XP_Bool isCached) { m_cached = isCached; } + XP_Bool GetGeneratingWholeMessage() { return m_generatingWholeMessage; } + +protected: + + TIMAPBodypartMessage *m_message; + + TIMAPMessagePartIDArray *m_prefetchQueue; // array of pipelined part prefetches. Ok, so it's not really a queue. + + XP_Bool m_isValid; + TNavigatorImapConnection *m_connection; // Connection, for filling in parts + char *m_UID; // UID of this message + char *m_folderName; // folder that contains this message + char *m_generatingPart; // If a specific part is being generated, this is it. Otherwise, NULL. + XP_Bool m_isBeingGenerated; // TRUE if this body shell is in the process of being generated + XP_Bool m_showAttachmentsInline; + XP_Bool m_gotAttachmentPref; + XP_Bool m_cached; // Whether or not this shell is cached + XP_Bool m_generatingWholeMessage; // whether or not we are generating the whole (non-MPOD) message + // Set to FALSE if we are generating by parts +}; + + + +// This class caches shells, so we don't have to always go and re-fetch them. +// This does not cache any of the filled-in inline parts; those are cached individually +// in the libnet memory cache. (ugh, how will we do that?) +// Since we'll only be retrieving shells for messages over a given size, and since the +// shells themselves won't be very large, this cache will not grow very big (relatively) +// and should handle most common usage scenarios. + +// A body cache is associated with a given host, spanning folders. +// It should pay attention to UIDVALIDITY. + +class TIMAPBodyShellCache +{ + +public: + static TIMAPBodyShellCache *Create(); + virtual ~TIMAPBodyShellCache(); + + XP_Bool AddShellToCache(TIMAPBodyShell *shell); // Adds shell to cache, possibly ejecting + // another entry based on scheme in EjectEntry(). + TIMAPBodyShell *FindShellForUID(const char *UID, const char *mailboxName); // Looks up a shell in the cache given the message's UID. + TIMAPBodyShell *FindShellForUID(uint32 UID, const char *mailboxName); // Looks up a shell in the cache given the message's UID. + // Returns the shell if found, NULL if not found. + +protected: + TIMAPBodyShellCache(); + XP_Bool EjectEntry(); // Chooses an entry to eject; deletes that entry; and ejects it from the cache, + // clearing up a new space. Returns TRUE if it found an entry to eject, FALSE otherwise. + uint32 GetSize() { return m_shellList->GetSize(); } + uint32 GetMaxSize() { return 20; } + + +protected: + XPPtrArray *m_shellList; // For maintenance + XP_HashTable m_shellHash; // For quick lookup based on UID + +}; + + + + +// MessagePartID and MessagePartIDArray are used for pipelining prefetches. + +class TIMAPMessagePartID +{ +public: + TIMAPMessagePartID(TIMAP4BlockingConnection::eFetchFields fields, const char *partNumberString); + TIMAP4BlockingConnection::eFetchFields GetFields() { return m_fields; } + const char *GetPartNumberString() { return m_partNumberString; } + + +protected: + const char *m_partNumberString; + TIMAP4BlockingConnection::eFetchFields m_fields; +}; + + +class TIMAPMessagePartIDArray : public XPPtrArray { +public: + TIMAPMessagePartIDArray(); + ~TIMAPMessagePartIDArray(); + + void RemoveAndFreeAll(); + int GetNumParts() {return GetSize();} + TIMAPMessagePartID *GetPart(int i) + { + XP_ASSERT(i >= 0 && i < GetSize()); + return (TIMAPMessagePartID *) GetAt(i); + } +}; + + +#endif // IMAPBODY_H diff --git a/mozilla/network/protocol/imap4/imapearl.cpp b/mozilla/network/protocol/imap4/imapearl.cpp index e69de29bb2d..3542c31995e 100644 --- a/mozilla/network/protocol/imap4/imapearl.cpp +++ b/mozilla/network/protocol/imap4/imapearl.cpp @@ -0,0 +1,677 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#include "mkutils.h" +#include "imap.h" +#include "imap4pvt.h" + +#define ISHEX(c) ( ((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F') ) +#define NONHEX(c) (!ISHEX(c)) + +extern "C" char * escape_unescaped_percents(const char *incomingURL); + +extern "C" +{ +char * escape_unescaped_percents(const char *incomingURL) +{ + const char *inC; + char *outC; + char *result = (char *) XP_ALLOC(XP_STRLEN(incomingURL)*3+1); + + if (result) + { + for(inC = incomingURL, outC = result; *inC != '\0'; inC++) + { + if (*inC == '%') + { + // Check if either of the next two characters are non-hex. + if ( !*(inC+1) || NONHEX(*(inC+1)) || !*(inC+2) || NONHEX(*(inC+2)) ) + { + // Hex characters don't follow, escape the + // percent char + *outC++ = '%'; *outC++ = '2'; *outC++ = '5'; + } + else + { + // Hex characters follow, so assume the percent + // is escaping something else + *outC++ = *inC; + } + } + else + *outC++ = *inC; + } + *outC = '\0'; + } + + return result; +} +} + +// member functions of the TIMAPUrl class +TIMAPUrl::TIMAPUrl(const char *url_string, XP_Bool internal) + : fHostSubString(nil), + fUrlidSubString(nil), + fSourceCanonicalFolderPathSubString(nil), + fDestinationCanonicalFolderPathSubString(nil), + fSearchCriteriaString(nil), + fListOfMessageIds(nil), + fTokenPlaceHolder(nil), + fFlags(0), + fIdsAreUids(FALSE), + fIMAPstate(kAuthenticatedStateURL), + fUrlType(kTest), + fValidURL(FALSE), + fMimePartSelectorDetected(FALSE), + fInternal(internal), + fDiscoveryDepth(0) +{ + fOnlineSubDirSeparator = '/'; // initial guess + fUrlString = escape_unescaped_percents(url_string); // this duplicates url_string + fUrlString = NET_UnEscape(fUrlString); // ### mwelch - Put spaces and '>'s back in. + Parse(); +} + + +TIMAPUrl::~TIMAPUrl() +{ + FREEIF( fUrlString); +} + +void TIMAPUrl::ParseFolderPath(char **resultingCanonicalPath) +{ + *resultingCanonicalPath = fTokenPlaceHolder ? XP_STRTOK_R(nil, IMAP_URL_TOKEN_SEPARATOR, &fTokenPlaceHolder) : (char *)NULL; + + if (!*resultingCanonicalPath) + { + fValidURL = FALSE; + return; + } + + // The delimiter will be set for a given URL, but will not be statically available + // from an arbitrary URL. It is the creator's responsibility to fill in the correct + // delimiter from the folder's namespace when creating the URL. + char dirSeparator = *(*resultingCanonicalPath)++; + if (dirSeparator != kOnlineHierarchySeparatorUnknown) + SetOnlineSubDirSeparator( dirSeparator); + + // if dirSeparator == kOnlineHierarchySeparatorUnknown, then this must be a create + // of a top level imap box. If there is an online subdir, we will automatically + // use its separator. If there is not an online subdir, we don't need a separator. + + /* + + // I don't think we want to do any of this anymore (for 5.0). + + const char *hostDir = TIMAPHostInfo::GetPersonalOrDefaultOnlineSubDirForHost(GetUrlHost()); + if (!hostDir) + { + // couldn't find the host in our list + fValidURL = FALSE; + return; + } + int lengthOfImapSubDirString = XP_STRLEN(hostDir); + + if (*resultingCanonicalPath && + ((lengthOfImapSubDirString + XP_STRLEN("INBOX")) == XP_STRLEN(*resultingCanonicalPath)) && + !XP_STRCASECMP("INBOX", *resultingCanonicalPath + lengthOfImapSubDirString)) + { + // this is the inbox + *resultingCanonicalPath = "INBOX"; + } + */ +} + + +void TIMAPUrl::ParseSearchCriteriaString() +{ + fSearchCriteriaString = fTokenPlaceHolder ? XP_STRTOK_R(nil, IMAP_URL_TOKEN_SEPARATOR, &fTokenPlaceHolder) : (char *)NULL; + if (!fSearchCriteriaString) + fValidURL = FALSE; +} + + +void TIMAPUrl::ParseChildDiscoveryDepth() +{ + char *discoveryDepth = fTokenPlaceHolder ? XP_STRTOK_R(nil, IMAP_URL_TOKEN_SEPARATOR, &fTokenPlaceHolder) : (char *)NULL; + if (!discoveryDepth) + { + fValidURL = FALSE; + fDiscoveryDepth = 0; + return; + } + fDiscoveryDepth = atoi(discoveryDepth); +} + +void TIMAPUrl::ParseUidChoice() +{ + char *uidChoiceString = fTokenPlaceHolder ? XP_STRTOK_R(nil, IMAP_URL_TOKEN_SEPARATOR, &fTokenPlaceHolder) : (char *)NULL; + if (!uidChoiceString) + fValidURL = FALSE; + else + fIdsAreUids = XP_STRCMP(uidChoiceString, "UID") == 0; +} + +void TIMAPUrl::ParseMsgFlags() +{ + char *flagsPtr = fTokenPlaceHolder ? XP_STRTOK_R(nil, IMAP_URL_TOKEN_SEPARATOR, &fTokenPlaceHolder) : (char *)NULL; + if (flagsPtr) + { + // the url is encodes the flags byte as ascii + int intFlags = atoi(flagsPtr); + fFlags = (imapMessageFlagsType) intFlags; // cast here + } + else + fFlags = 0; +} + +void TIMAPUrl::ParseListofMessageIds() +{ + fListOfMessageIds = fTokenPlaceHolder ? XP_STRTOK_R(nil, IMAP_URL_TOKEN_SEPARATOR, &fTokenPlaceHolder) : (char *)NULL; + if (!fListOfMessageIds) + fValidURL = FALSE; + else + { + fMimePartSelectorDetected = XP_STRSTR(fListOfMessageIds, "&part=") != 0; + } +} + +void TIMAPUrl::Parse() +{ + fValidURL = TRUE; // hope for the best + + // first token separator is a "?" so others can grab the host + char *urlStartToken = XP_STRTOK_R(fUrlString, "?", &fTokenPlaceHolder); + + if (!XP_STRNCASECMP(urlStartToken, "IMAP://",7) ) + { + fHostSubString = urlStartToken + 7; + fUrlidSubString = fTokenPlaceHolder ? XP_STRTOK_R(nil, IMAP_URL_TOKEN_SEPARATOR, &fTokenPlaceHolder) : (char *)NULL; + if (!fUrlidSubString) + { + fValidURL = FALSE; + return; + } + + if (!XP_STRCASECMP(fUrlidSubString, "fetch")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kMsgFetch; + ParseUidChoice(); + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + ParseListofMessageIds(); + } + else if (fInternal) + { + if (!XP_STRCASECMP(fUrlidSubString, "header")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kMsgHeader; + ParseUidChoice(); + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + ParseListofMessageIds(); + } + else if (!XP_STRCASECMP(fUrlidSubString, "deletemsg")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kDeleteMsg; + ParseUidChoice(); + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + ParseListofMessageIds(); + } + else if (!XP_STRCASECMP(fUrlidSubString, "deleteallmsgs")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kDeleteAllMsgs; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "addmsgflags")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kAddMsgFlags; + ParseUidChoice(); + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + ParseListofMessageIds(); + ParseMsgFlags(); + } + else if (!XP_STRCASECMP(fUrlidSubString, "subtractmsgflags")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kSubtractMsgFlags; + ParseUidChoice(); + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + ParseListofMessageIds(); + ParseMsgFlags(); + } + else if (!XP_STRCASECMP(fUrlidSubString, "setmsgflags")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kSetMsgFlags; + ParseUidChoice(); + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + ParseListofMessageIds(); + ParseMsgFlags(); + } + else if (!XP_STRCASECMP(fUrlidSubString, "onlinecopy")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kOnlineCopy; + ParseUidChoice(); + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + ParseListofMessageIds(); + ParseFolderPath(&fDestinationCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "onlinemove")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kOnlineMove; + ParseUidChoice(); + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + ParseListofMessageIds(); + ParseFolderPath(&fDestinationCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "onlinetoofflinecopy")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kOnlineToOfflineCopy; + ParseUidChoice(); + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + ParseListofMessageIds(); + ParseFolderPath(&fDestinationCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "onlinetoofflinemove")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kOnlineToOfflineMove; + ParseUidChoice(); + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + ParseListofMessageIds(); + ParseFolderPath(&fDestinationCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "offlinetoonlinecopy")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kOfflineToOnlineMove; + ParseFolderPath(&fDestinationCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "search")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kSearch; + ParseUidChoice(); + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + ParseSearchCriteriaString(); + } + else if (!XP_STRCASECMP(fUrlidSubString, "test")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kTest; + } + else if (!XP_STRCASECMP(fUrlidSubString, "select")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kSelectFolder; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + if (fTokenPlaceHolder && *fTokenPlaceHolder) + ParseListofMessageIds(); + else + fListOfMessageIds = ""; + } + else if (!XP_STRCASECMP(fUrlidSubString, "liteselect")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kLiteSelectFolder; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "expunge")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kExpungeFolder; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + fListOfMessageIds = ""; // no ids to UNDO + } + else if (!XP_STRCASECMP(fUrlidSubString, "create")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kCreateFolder; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "discoverchildren")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kDiscoverChildrenUrl; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "discoverlevelchildren")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kDiscoverLevelChildrenUrl; + ParseChildDiscoveryDepth(); + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "discoverallboxes")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kDiscoverAllBoxesUrl; + } + else if (!XP_STRCASECMP(fUrlidSubString, "discoverallandsubscribedboxes")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kDiscoverAllAndSubscribedBoxesUrl; + } + else if (!XP_STRCASECMP(fUrlidSubString, "delete")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kDeleteFolder; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "rename")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kRenameFolder; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + ParseFolderPath(&fDestinationCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "movefolderhierarchy")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kMoveFolderHierarchy; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + if (fTokenPlaceHolder && *fTokenPlaceHolder) // handle promote to root + ParseFolderPath(&fDestinationCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "list")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kLsubFolders; + ParseFolderPath(&fDestinationCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "biff")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kBiff; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + ParseListofMessageIds(); + } + else if (!XP_STRCASECMP(fUrlidSubString, "netscape")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kGetMailAccountUrl; + } + else if (!XP_STRCASECMP(fUrlidSubString, "appendmsgfromfile")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kAppendMsgFromFile; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "appenddraftfromfile")) + { + fIMAPstate = kSelectedStateURL; + fUrlType = kAppendMsgFromFile; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "subscribe")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kSubscribe; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "unsubscribe")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kUnsubscribe; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "refreshacl")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kRefreshACL; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "refreshfolderurls")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kRefreshFolderUrls; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "refreshallacls")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kRefreshAllACLs; + } + else if (!XP_STRCASECMP(fUrlidSubString, "listfolder")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kListFolder; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "upgradetosubscription")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kUpgradeToSubscription; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + } + else if (!XP_STRCASECMP(fUrlidSubString, "folderstatus")) + { + fIMAPstate = kAuthenticatedStateURL; + fUrlType = kFolderStatus; + ParseFolderPath(&fSourceCanonicalFolderPathSubString); + } + else + fValidURL = FALSE; + } + else fValidURL = FALSE; + } + else + fValidURL = FALSE; +} + +TIMAPUrl::EUrlIMAPstate TIMAPUrl::GetUrlIMAPstate() +{ + return fIMAPstate; +} + +TIMAPUrl::EIMAPurlType TIMAPUrl::GetIMAPurlType() +{ + return fUrlType; +} + + + +char *TIMAPUrl::CreateCanonicalSourceFolderPathString() +{ + return XP_STRDUP(fSourceCanonicalFolderPathSubString ? fSourceCanonicalFolderPathSubString : ""); +} + +char *TIMAPUrl::CreateCanonicalDestinationFolderPathString() +{ + return XP_STRDUP(fDestinationCanonicalFolderPathSubString); +} + +char *TIMAPUrl::CreateServerSourceFolderPathString() +{ + return AllocateServerPath(fSourceCanonicalFolderPathSubString); +} + +char *TIMAPUrl::CreateServerDestinationFolderPathString() +{ + // its possible for the destination folder path to be the root + if (!fDestinationCanonicalFolderPathSubString) + return XP_STRDUP(""); + else + return AllocateServerPath(fDestinationCanonicalFolderPathSubString); +} + +char *TIMAPUrl::CreateSearchCriteriaString() +{ + return XP_STRDUP(fSearchCriteriaString); +} + +char *TIMAPUrl::CreateListOfMessageIdsString() +{ + char *returnIdString = XP_STRDUP(fListOfMessageIds); + if (returnIdString) + { + // mime may have glommed a "&part=" for a part download + // we return the entire message and let mime extract + // the part. Pop and news work this way also. + // this algorithm truncates the "&part" string. + char *currentChar = returnIdString; + while (*currentChar && (*currentChar != '&')) + currentChar++; + if (*currentChar == '&') + *currentChar = 0; + + // we should also strip off anything after "/;section=" + // since that can specify an IMAP MIME part + char *wherepart = XP_STRSTR(returnIdString, "/;section="); + if (wherepart) + *wherepart = 0; + } + return returnIdString; +} + +char *TIMAPUrl::GetIMAPPartToFetch() +{ + char *wherepart = NULL, *rv = NULL; + if (fListOfMessageIds && (wherepart = XP_STRSTR(fListOfMessageIds, "/;section=")) != NULL) + { + wherepart += 10; // XP_STRLEN("/;section=") + if (wherepart) + { + char *wherelibmimepart = XP_STRSTR(wherepart, "&part="); + int len = XP_STRLEN(fListOfMessageIds), numCharsToCopy = 0; + if (wherelibmimepart) + numCharsToCopy = (wherelibmimepart - wherepart); + else + numCharsToCopy = XP_STRLEN(fListOfMessageIds) - (wherepart - fListOfMessageIds); + if (numCharsToCopy) + { + rv = (char *) XP_ALLOC(sizeof(char) * (numCharsToCopy + 1)); + if (rv) + XP_STRNCPY_SAFE(rv, wherepart, numCharsToCopy + 1); // appends a \0 + } + } + } + return rv; +} + + +imapMessageFlagsType TIMAPUrl::GetMsgFlags() // kAddMsgFlags or kSubtractMsgFlags only +{ + return fFlags; +} + + + +XP_Bool TIMAPUrl::ValidIMAPUrl() +{ + return fValidURL; +} + +XP_Bool TIMAPUrl::MessageIdsAreUids() +{ + return fIdsAreUids; +} + +char *TIMAPUrl::AllocateServerPath(const char *canonicalPath) +{ + XP_ASSERT(GetOnlineSubDirSeparator() != kOnlineHierarchySeparatorUnknown); + return ReplaceCharsInCopiedString(canonicalPath, '/', GetOnlineSubDirSeparator()); +} + +char *TIMAPUrl::AllocateCanonicalPath(const char *serverPath) +{ + char *canonicalPath = ReplaceCharsInCopiedString(serverPath, GetOnlineSubDirSeparator() , '/'); + + // eat any escape characters for escaped dir separators + if (canonicalPath) + { + char *currentEscapeSequence = XP_STRSTR(canonicalPath, "\\/"); + while (currentEscapeSequence) + { + XP_STRCPY(currentEscapeSequence, currentEscapeSequence+1); + currentEscapeSequence = XP_STRSTR(currentEscapeSequence+1, "\\/"); + } + } + + return canonicalPath; +} + +char *TIMAPUrl::ReplaceCharsInCopiedString(const char *stringToCopy, char oldChar, char newChar) +{ + char oldCharString[2]; + *oldCharString = oldChar; + *(oldCharString+1) = 0; + + char *translatedString = XP_STRDUP(stringToCopy); + char *currentSeparator = strstr(translatedString, oldCharString); + + while(currentSeparator) + { + *currentSeparator = newChar; + currentSeparator = strstr(currentSeparator+1, oldCharString); + } + + return translatedString; +} + +void TIMAPUrl::SetOnlineSubDirSeparator(char onlineDirSeparator) +{ + fOnlineSubDirSeparator = onlineDirSeparator; +} + +char TIMAPUrl::GetOnlineSubDirSeparator() +{ + return fOnlineSubDirSeparator; +} + +XP_Bool TIMAPUrl::GetShouldSubscribeToAll() +{ + return (GetOnlineSubDirSeparator() == '.'); +} + +#if 0 +// According to the comment in imap.h, where the prototype lives and has been commented out, +// this is obsolete. So let's get rid of the "no prototype" warning. +extern "C" { + void + IMAP_SetNamespacesFromPrefs(const char *hostName, char *personalPrefix, char *publicPrefixes, char *otherUsersPrefixes) + { + TIMAPHostInfo::ClearPrefsNamespacesForHost(hostName); + if (XP_STRCMP(personalPrefix,"")) + { + TIMAPNamespace *ns = new TIMAPNamespace(kPersonalNamespace, personalPrefix, '/', TRUE); + if (ns) + TIMAPHostInfo::AddNewNamespaceForHost(hostName, ns); + } + if (XP_STRCMP(publicPrefixes,"")) + { + TIMAPNamespace *ns = new TIMAPNamespace(kPublicNamespace, publicPrefixes, '/', TRUE); + if (ns) + TIMAPHostInfo::AddNewNamespaceForHost(hostName, ns); + } + if (XP_STRCMP(otherUsersPrefixes,"")) + { + TIMAPNamespace *ns = new TIMAPNamespace(kOtherUsersNamespace, otherUsersPrefixes, '/', TRUE); + if (ns) + TIMAPHostInfo::AddNewNamespaceForHost(hostName, ns); + } + } +} // extern "C" +#endif // 0 + diff --git a/mozilla/network/protocol/imap4/imaphier.cpp b/mozilla/network/protocol/imap4/imaphier.cpp index e69de29bb2d..87c14ef1e09 100644 --- a/mozilla/network/protocol/imap4/imaphier.cpp +++ b/mozilla/network/protocol/imap4/imaphier.cpp @@ -0,0 +1,203 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#include "mkutils.h" + +#include "imaphier.h" +#include "imap4pvt.h" +#include "imap.h" + +ImapHierarchyMover::ImapHierarchyMover( + const char *destinationNodeName, + const char *sourceNodeName, + TNavigatorImapConnection &connection) + : fIMAPConnection(connection) +{ + fDestinationNodeName = XP_STRDUP(destinationNodeName); + fSourceNodeName = XP_STRDUP(sourceNodeName); + fMailboxHierarchy = XP_ListNew(); +} + + + +ImapHierarchyMover::~ImapHierarchyMover() +{ + // this is a leak, delete the structs + if (fMailboxHierarchy) + XP_ListDestroy(fMailboxHierarchy); + FREEIF(fDestinationNodeName); + FREEIF(fSourceNodeName); +} + + + +void ImapHierarchyMover::AddToHierarchy(const char *nodeName) +{ + int stringLength = XP_STRLEN(nodeName); + char *nodeTokens = new char [stringLength + 2]; // end the string in double null so AddToSubTree + XP_STRCPY(nodeTokens, nodeName); // will work + *(nodeTokens + stringLength) = 0; + *(nodeTokens + stringLength+1) = 0; + AddToSubTree(nodeTokens, nodeName, fMailboxHierarchy); + delete nodeTokens; +} + + +void ImapHierarchyMover::AddToSubTree(char *nodeTokens, const char *nodeName, XP_List *subTree) +{ + char *positionOfNodeTokens = XP_STRSTR(nodeName, nodeTokens); + // make sure we find the last one + char *currentPositionOfNodeTokens = positionOfNodeTokens; + while (currentPositionOfNodeTokens && strlen(nodeTokens)) + { + currentPositionOfNodeTokens = XP_STRSTR(currentPositionOfNodeTokens + 1, nodeTokens); + if (currentPositionOfNodeTokens) + positionOfNodeTokens = currentPositionOfNodeTokens; + } + + char *placeHolderInTokenString = nil; + char *currentNodeToken = XP_STRTOK_R(nodeTokens, "/",&placeHolderInTokenString); + if (currentNodeToken) + { + // find the current node, if it exists + HierarchyNode *currentNode = NULL; + int numberOfNodes = XP_ListCount(subTree); + XP_Bool nodeFound = FALSE; + while (numberOfNodes && !nodeFound) + { + currentNode = (HierarchyNode *) XP_ListGetObjectNum(subTree, numberOfNodes--); + nodeFound = XP_STRCMP(currentNode->fNodeToken, currentNodeToken) == 0; + } + + if (!nodeFound) + { + // create the node + currentNode = (HierarchyNode *) XP_ALLOC(sizeof(HierarchyNode)); + if (currentNode) + { + currentNode->fPresentName = XP_STRDUP(nodeName); + // less the remaining tokens + *(currentNode->fPresentName + (positionOfNodeTokens-nodeName) + XP_STRLEN(currentNodeToken)) = 0; + + currentNode->fNodeToken = XP_STRDUP(currentNodeToken); + currentNode->fSubFolders = XP_ListNew(); + XP_ListAddObjectToEnd(subTree, currentNode); + } + } + + // recurse! + char *subNodeTokens = nodeTokens + XP_STRLEN(currentNodeToken) + 1; // +1 see note in AddToHierarchy + AddToSubTree(subNodeTokens, nodeName, currentNode->fSubFolders); + } +} + + +// depth first, post process unsubscribe and delete everything including and below source node name +void ImapHierarchyMover::DestroyOldFolderTree(XP_List *subTree) +{ + int numberOfNodesAtThisLevel = XP_ListCount(subTree); + for (int nodeIndex=numberOfNodesAtThisLevel; nodeIndex > 0; nodeIndex--) + { + HierarchyNode *currentNode = (HierarchyNode *) XP_ListGetObjectNum(subTree, nodeIndex); + DestroyOldFolderTree(currentNode->fSubFolders); + + if (fIMAPConnection.GetServerStateParser().LastCommandSuccessful() && + XP_STRSTR(currentNode->fPresentName, fSourceNodeName)) + { + fIMAPConnection.Unsubscribe(currentNode->fPresentName); + if (fIMAPConnection.GetServerStateParser().LastCommandSuccessful()) + fIMAPConnection.DeleteMailbox(currentNode->fPresentName); + } + } +} + + + +// depth first, pre-process create, subscribe, and fill with messages +void ImapHierarchyMover::CreateNewFolderTree(const char *destinationNodeName, XP_List *subTree) +{ + int numberOfNodesAtThisLevel = XP_ListCount(subTree); + for (int nodeIndex=1; nodeIndex <= numberOfNodesAtThisLevel; nodeIndex++) + { + HierarchyNode *currentNode = (HierarchyNode *) XP_ListGetObjectNum(subTree, nodeIndex); + + if (!XP_STRSTR(fSourceNodeName, currentNode->fPresentName) || + !XP_STRCMP(fSourceNodeName, currentNode->fPresentName)) // if we are not yet at the node name, don't rename + { + char *newNodeName = (char *) XP_ALLOC(XP_STRLEN(destinationNodeName) + + XP_STRLEN(currentNode->fNodeToken) + 2); + XP_STRCPY(newNodeName, destinationNodeName); + XP_STRCAT(newNodeName, "/"); + XP_STRCAT(newNodeName, currentNode->fNodeToken); + + fIMAPConnection.CreateMailbox(newNodeName); + if (fIMAPConnection.GetServerStateParser().LastCommandSuccessful()) + { + fIMAPConnection.Subscribe(newNodeName); + if (fIMAPConnection.GetServerStateParser().LastCommandSuccessful()) + { + // close the current folder if one is selected + if (fIMAPConnection.GetServerStateParser().GetIMAPstate() == + TImapServerState::kFolderSelected) + fIMAPConnection.Close(); + + // select the old folder + fIMAPConnection.SelectMailbox(currentNode->fPresentName); + if (fIMAPConnection.GetServerStateParser().LastCommandSuccessful()) + { + // copy its entire contents to the destination + int numOfMsgs = fIMAPConnection.GetServerStateParser().NumberOfMessages(); + if (numOfMsgs) + { + char msgList[100]; // enough for trillions? + sprintf(msgList, "1:%d", numOfMsgs); + fIMAPConnection.Copy(msgList, newNodeName, FALSE); // not uids + } + if (fIMAPConnection.GetServerStateParser().LastCommandSuccessful()) + fIMAPConnection.Close(); + } + } + } + // recurse to my children + if (fIMAPConnection.GetServerStateParser().LastCommandSuccessful()) + CreateNewFolderTree(newNodeName, currentNode->fSubFolders); + FREEIF( newNodeName); + } + else + CreateNewFolderTree(destinationNodeName, currentNode->fSubFolders); + } +} + + +int ImapHierarchyMover::DoMove() +{ + int returnValue = 0; + + CreateNewFolderTree(fDestinationNodeName, fMailboxHierarchy); + + if (fIMAPConnection.GetServerStateParser().LastCommandSuccessful()) + { + DestroyOldFolderTree(fMailboxHierarchy); + if (!fIMAPConnection.GetServerStateParser().LastCommandSuccessful()) + returnValue = -1; + } + else + returnValue = -1; + + return returnValue; +} + diff --git a/mozilla/network/protocol/imap4/imaphier.h b/mozilla/network/protocol/imap4/imaphier.h index e69de29bb2d..673c710589a 100644 --- a/mozilla/network/protocol/imap4/imaphier.h +++ b/mozilla/network/protocol/imap4/imaphier.h @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#ifndef IMAPHIER +#define IMAPHIER + +#include "xp_list.h" + + +class TNavigatorImapConnection; + +struct HierarchyNode { + char *fPresentName; + char *fNodeToken; // leaf name of node + XP_List *fSubFolders; +}; + +class ImapHierarchyMover { +public: + ImapHierarchyMover(const char *destinationNodeName, + const char *sourceNodeName, + TNavigatorImapConnection &connection); + virtual ~ImapHierarchyMover(); + + virtual void AddToHierarchy(const char *nodeName); + virtual int DoMove(); +protected: + virtual void AddToSubTree(char *nodeTokens, const char *nodeName, XP_List *subTree); + virtual void CreateNewFolderTree(const char *destinationNodeName, XP_List *subTree); + virtual void DestroyOldFolderTree(XP_List *subTree); + +private: + XP_List *fMailboxHierarchy; + + TNavigatorImapConnection &fIMAPConnection; + char *fDestinationNodeName; + char *fSourceNodeName; +}; + +#endif + diff --git a/mozilla/network/protocol/imap4/imappars.cpp b/mozilla/network/protocol/imap4/imappars.cpp index e69de29bb2d..3f111459dd9 100644 --- a/mozilla/network/protocol/imap4/imappars.cpp +++ b/mozilla/network/protocol/imap4/imappars.cpp @@ -0,0 +1,3298 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#include "mkutils.h" +#include "imap4pvt.h" +#include "prlog.h" +#include "imapbody.h" + +extern "C" { +#include "xpgetstr.h" + +extern int MK_OUT_OF_MEMORY; +extern int MK_SERVER_DISCONNECTED; +extern int MK_IMAP_DOWNLOADING_MESSAGE; +} + +#define WHITESPACE " \015\012" // token delimiter + +/* 45678901234567890123456789012345678901234567890123456789012345678901234567890 +*/ +TImapFlagAndUidState::TImapFlagAndUidState(int numberOfMessages, XP_Bool bSupportUserFlag) +{ + fNumberOfMessagesAdded = 0; + fNumberOfMessageSlotsAllocated = numberOfMessages; + if (!fNumberOfMessageSlotsAllocated) + fNumberOfMessageSlotsAllocated = kFlagEntrySize; + fFlags = (imapMessageFlagsType*) XP_ALLOC(sizeof(imapMessageFlagsType) * fNumberOfMessageSlotsAllocated); // new imapMessageFlagsType[fNumberOfMessageSlotsAllocated]; + + fUids.SetSize(fNumberOfMessageSlotsAllocated); + XP_MEMSET(fFlags, 0, sizeof(imapMessageFlagsType) * fNumberOfMessageSlotsAllocated); + fSupportUserFlag = bSupportUserFlag; + fNumberDeleted = 0; +} + +TImapFlagAndUidState::TImapFlagAndUidState(const TImapFlagAndUidState& state, XP_Bool bSupportUserFlag) +{ + fNumberOfMessagesAdded = state.fNumberOfMessagesAdded; + + fNumberOfMessageSlotsAllocated = state.fNumberOfMessageSlotsAllocated; + fFlags = (imapMessageFlagsType*) XP_ALLOC(sizeof(imapMessageFlagsType) * fNumberOfMessageSlotsAllocated); // new imapMessageFlagsType[fNumberOfMessageSlotsAllocated]; + fUids.CopyArray((IDArray*) &state.fUids); + + XP_MEMCPY(fFlags, state.fFlags, sizeof(imapMessageFlagsType) * fNumberOfMessageSlotsAllocated); + fSupportUserFlag = bSupportUserFlag; + fNumberDeleted = 0; +} + +TImapFlagAndUidState::~TImapFlagAndUidState() +{ + FREEIF(fFlags); +} + + +// we need to reset our flags, (re-read all) but chances are the memory allocation needed will be +// very close to what we were already using + +void TImapFlagAndUidState::Reset(uint32 howManyLeft) +{ + if (!howManyLeft) + fNumberOfMessagesAdded = fNumberDeleted = 0; // used space is still here +} + + +// Remove (expunge) a message from our array, since now it is gone for good + +void TImapFlagAndUidState::ExpungeByIndex(uint32 index) +{ + uint32 counter = 0; + + if ((uint32) fNumberOfMessagesAdded < index) + return; + index--; + fNumberOfMessagesAdded--; + if (fFlags[index] & kImapMsgDeletedFlag) // see if we already had counted this one as deleted + fNumberDeleted--; + for (counter = index; counter < (uint32) fNumberOfMessagesAdded; counter++) + { + fUids.SetAt(counter, fUids[counter + 1]); + fFlags[counter] = fFlags[counter + 1]; + } +} + + + // adds to sorted list. protects against duplicates and going past fNumberOfMessageSlotsAllocated +void TImapFlagAndUidState::AddUidFlagPair(imap_uid uid, imapMessageFlagsType flags) +{ + // make sure there is room for this pair + if (fNumberOfMessagesAdded >= fNumberOfMessageSlotsAllocated) + { + fNumberOfMessageSlotsAllocated += kFlagEntrySize; + fUids.SetSize(fNumberOfMessageSlotsAllocated); + fFlags = (imapMessageFlagsType*) XP_REALLOC(fFlags, sizeof(imapMessageFlagsType) * fNumberOfMessageSlotsAllocated); // new imapMessageFlagsType[fNumberOfMessageSlotsAllocated]; + } + + // optimize the common case of placing on the end + if (!fNumberOfMessagesAdded || (uid > (int32) fUids[fNumberOfMessagesAdded - 1])) + { + fUids.SetAt(fNumberOfMessagesAdded, uid); + fFlags[fNumberOfMessagesAdded] = flags; + fNumberOfMessagesAdded++; + if (flags & kImapMsgDeletedFlag) + fNumberDeleted++; + return; + } + + // search for the slot for this uid-flag pair + + int32 insertionIndex = -1; + XP_Bool foundIt = FALSE; + + GetMessageFlagsFromUID(uid, &foundIt, &insertionIndex); + + // Hmmm, is the server sending back unordered fetch responses? + if (((int32) fUids[insertionIndex]) != uid) + { + // shift the uids and flags to the right + for (int32 shiftIndex = fNumberOfMessagesAdded; shiftIndex > insertionIndex; shiftIndex--) + { + fUids.SetAt(shiftIndex, fUids[shiftIndex - 1]); + fFlags[shiftIndex] = fFlags[shiftIndex - 1]; + } + fFlags[insertionIndex] = flags; + fUids.SetAt(insertionIndex, uid); + fNumberOfMessagesAdded++; + if (fFlags[insertionIndex] & kImapMsgDeletedFlag) + fNumberDeleted++; + } else { + if ((fFlags[insertionIndex] & kImapMsgDeletedFlag) && !(flags & kImapMsgDeletedFlag)) + fNumberDeleted--; + else + if (!(fFlags[insertionIndex] & kImapMsgDeletedFlag) && (flags & kImapMsgDeletedFlag)) + fNumberDeleted++; + fFlags[insertionIndex] = flags; + } +} + + +int TImapFlagAndUidState::GetNumberOfMessages() +{ + return fNumberOfMessagesAdded; +} + +int TImapFlagAndUidState::GetNumberOfDeletedMessages() +{ + return fNumberDeleted; +} + +// since the uids are sorted, start from the back (rb) + +imap_uid TImapFlagAndUidState::GetHighestNonDeletedUID() +{ + uint32 index = fNumberOfMessagesAdded; + do { + if (index <= 0) + return(0); + index--; + if (fUids[index] && !(fFlags[index] & kImapMsgDeletedFlag)) + return fUids[index]; + } while (index > 0); + return 0; +} + + +// Has the user read the last message here ? Used when we first open the inbox to see if there +// really is new mail there. + +XP_Bool TImapFlagAndUidState::IsLastMessageUnseen() +{ + uint32 index = fNumberOfMessagesAdded; + + if (index <= 0) + return FALSE; + index--; + // if last message is deleted, it was probably filtered the last time around + if (fUids[index] && (fFlags[index] & (kImapMsgSeenFlag | kImapMsgDeletedFlag))) + return FALSE; + return TRUE; +} + + +imap_uid TImapFlagAndUidState::GetUidOfMessage(int zeroBasedIndex) +{ + if (zeroBasedIndex < fNumberOfMessagesAdded) + return fUids[zeroBasedIndex]; + return (-1); // so that value is non-zero and we don't ask for bad msgs +} + + +// find a message flag given a key with non-recursive binary search, since some folders +// may have thousand of messages, once we find the key set its index, or the index of +// where the key should be inserted + +imapMessageFlagsType TImapFlagAndUidState::GetMessageFlagsFromUID(imap_uid uid, XP_Bool *foundIt, int32 *ndx) +{ + int index = 0; + int hi = fNumberOfMessagesAdded - 1; + int lo = 0; + + *foundIt = FALSE; + *ndx = -1; + while (lo <= hi) + { + index = (lo + hi) / 2; + if (fUids[index] == (uint32) uid) + { + int returnFlags = fFlags[index]; + + *foundIt = TRUE; + *ndx = index; + if (fSupportUserFlag) + returnFlags |= kImapMsgSupportUserFlag; + return returnFlags; + } + if (fUids[index] > (uint32) uid) + hi = index -1; + else if (fUids[index] < (uint32) uid) + lo = index + 1; + } + index = lo; + while ((index > 0) && (fUids[index] > (uint32) uid)) + index--; + if (index < 0) + index = 0; + *ndx = index; + return 0; +} + + + +imapMessageFlagsType TImapFlagAndUidState::GetMessageFlags(int zeroBasedIndex) +{ + imapMessageFlagsType returnFlags = kNoImapMsgFlag; + if (zeroBasedIndex < fNumberOfMessagesAdded) + returnFlags = fFlags[zeroBasedIndex]; + + if (fSupportUserFlag) + returnFlags |= kImapMsgSupportUserFlag; + + return returnFlags; +} + +extern "C" { +TImapFlagAndUidState *IMAP_CreateFlagState(int numberOfMessages) +{ + return new TImapFlagAndUidState(numberOfMessages); +} + + +void IMAP_DeleteFlagState(TImapFlagAndUidState *state) +{ + delete state; +} + + +int IMAP_GetFlagStateNumberOfMessages(TImapFlagAndUidState *state) +{ + return state->GetNumberOfMessages(); +} + + +imap_uid IMAP_GetUidOfMessage(int zeroBasedIndex, TImapFlagAndUidState *state) +{ + return state->GetUidOfMessage(zeroBasedIndex); +} + + +imapMessageFlagsType IMAP_GetMessageFlags(int zeroBasedIndex, TImapFlagAndUidState *state) +{ + return state->GetMessageFlags(zeroBasedIndex); +} + + +imapMessageFlagsType IMAP_GetMessageFlagsFromUID(imap_uid uid, XP_Bool *foundIt, TImapFlagAndUidState *state) +{ + int32 index = -1; + return state->GetMessageFlagsFromUID(uid, foundIt, &index); +} + + +} // extern "C" + +TSearchResultSequence::TSearchResultSequence() +{ + fListOfLines = XP_ListNew(); +} + +TSearchResultSequence *TSearchResultSequence::CreateSearchResultSequence() +{ + TSearchResultSequence *returnObject = new TSearchResultSequence; + if (!returnObject || + !returnObject->fListOfLines) + { + delete returnObject; + returnObject = nil; + } + + return returnObject; +} + +TSearchResultSequence::~TSearchResultSequence() +{ + if (fListOfLines) + XP_ListDestroy(fListOfLines); +} + + +void TSearchResultSequence::ResetSequence() +{ + if (fListOfLines) + XP_ListDestroy(fListOfLines); + fListOfLines = XP_ListNew(); +} + +void TSearchResultSequence::AddSearchResultLine(const char *searchLine) +{ + // The first add becomes node 2. Fix this. + char *copiedSequence = XP_STRDUP(searchLine + 9); // 9 == "* SEARCH " + + if (copiedSequence) // if we can't allocate this then the search won't hit + XP_ListAddObjectToEnd(fListOfLines, copiedSequence); +} + + +TSearchResultIterator::TSearchResultIterator(TSearchResultSequence &sequence) : + fSequence(sequence) +{ + ResetIterator(); +} + +TSearchResultIterator::~TSearchResultIterator() +{} + +void TSearchResultIterator::ResetIterator() +{ + fCurrentLine = fSequence.fListOfLines->next; + if (fCurrentLine) + fPositionInCurrentLine = (char *) fCurrentLine->object; + else + fPositionInCurrentLine = nil; +} + +int32 TSearchResultIterator::GetNextMessageNumber() +{ + int32 returnValue = 0; + if (fPositionInCurrentLine) + { + returnValue = atoint32(fPositionInCurrentLine); + + // eat the current number + while (isdigit(*++fPositionInCurrentLine)) + ; + + if (*fPositionInCurrentLine == 0xD) // found CR, no more digits on line + { + fCurrentLine = fCurrentLine->next; + if (fCurrentLine) + fPositionInCurrentLine = (char *) fCurrentLine->object; + else + fPositionInCurrentLine = nil; + } + else // eat the space + fPositionInCurrentLine++; + } + + return returnValue; +} + + + +////////////////// TIMAPGenericParser ///////////////////////// + + +TIMAPGenericParser::TIMAPGenericParser() : + fNextToken(nil), + fCurrentLine(nil), + fLineOfTokens(nil), + fCurrentTokenPlaceHolder(nil), + fStartOfLineOfTokens(nil), + fSyntaxErrorLine(nil), + fAtEndOfLine(FALSE), + fDisconnected(FALSE), + fSyntaxError(FALSE), + fTokenizerAdvanced(FALSE) +{ +} + +TIMAPGenericParser::~TIMAPGenericParser() +{ + FREEIF( fCurrentLine ); + FREEIF( fStartOfLineOfTokens); + FREEIF( fSyntaxErrorLine ); +} + +void TIMAPGenericParser::HandleMemoryFailure() +{ + SetConnected(FALSE); +} + +void TIMAPGenericParser::ResetLexAnalyzer() +{ + FREEIF( fCurrentLine ); + FREEIF( fStartOfLineOfTokens ); + fTokenizerAdvanced = FALSE; + + fCurrentLine = fNextToken = fLineOfTokens = fStartOfLineOfTokens = fCurrentTokenPlaceHolder = nil; + fAtEndOfLine = FALSE; +} + +XP_Bool TIMAPGenericParser::LastCommandSuccessful() +{ + return Connected() && !SyntaxError(); +} + +void TIMAPGenericParser::SetSyntaxError(XP_Bool error) +{ + fSyntaxError = error; + FREEIF( fSyntaxErrorLine ); + if (error) + { + XP_ASSERT(FALSE); // If you hit this assert, file a bug on chrisf. Promise to include a protocol log. + fSyntaxErrorLine = XP_STRDUP(fCurrentLine); + if (!fSyntaxErrorLine) + { + HandleMemoryFailure(); +// PR_LOG(IMAP, out, ("PARSER: Internal Syntax Error: ")); + } + else + { +// if (!XP_STRCMP(fSyntaxErrorLine, CRLF)) +// PR_LOG(IMAP, out, ("PARSER: Internal Syntax Error: ")); +// else +// PR_LOG(IMAP, out, ("PARSER: Internal Syntax Error: %s", fSyntaxErrorLine)); + } + } + else + fSyntaxErrorLine = NULL; +} + +char *TIMAPGenericParser::CreateSyntaxErrorLine() +{ + return XP_STRDUP(fSyntaxErrorLine); +} + + +XP_Bool TIMAPGenericParser::SyntaxError() +{ + return fSyntaxError; +} + +void TIMAPGenericParser::SetConnected(XP_Bool connected) +{ + fDisconnected = !connected; +} + +XP_Bool TIMAPGenericParser::Connected() +{ + return !fDisconnected; +} + +XP_Bool TIMAPGenericParser::ContinueParse() +{ + return !fSyntaxError && !fDisconnected; +} + + +XP_Bool TIMAPGenericParser::at_end_of_line() +{ + return XP_STRCMP(fNextToken, CRLF) == 0; +} + +void TIMAPGenericParser::skip_to_CRLF() +{ + while (Connected() && !at_end_of_line()) + fNextToken = GetNextToken(); +} + +// fNextToken initially should point to +// a string after the initial open paren ("(") +// After this call, fNextToken points to the +// first character after the matching close +// paren. Only call GetNextToken to get the NEXT +// token after the one returned in fNextToken. +void TIMAPGenericParser::skip_to_close_paren() +{ + int numberOfCloseParensNeeded = 1; + if (fNextToken && *fNextToken == ')') + { + numberOfCloseParensNeeded--; + fNextToken++; + if (!fNextToken || !*fNextToken) + fNextToken = GetNextToken(); + } + + while (ContinueParse() && numberOfCloseParensNeeded > 0) + { + // go through fNextToken, count the number + // of open and close parens, to account + // for nested parens which might come in + // the response + char *loc = 0; + for (loc = fNextToken; loc && *loc; loc++) + { + if (*loc == '(') + numberOfCloseParensNeeded++; + else if (*loc == ')') + numberOfCloseParensNeeded--; + if (numberOfCloseParensNeeded == 0) + { + fNextToken = loc + 1; + if (!fNextToken || !*fNextToken) + fNextToken = GetNextToken(); + break; // exit the loop + } + } + + if (numberOfCloseParensNeeded > 0) + fNextToken = GetNextToken(); + } +} + +char *TIMAPGenericParser::GetNextToken() +{ + if (!fCurrentLine || fAtEndOfLine) + AdvanceToNextLine(); + else if (Connected()) + { + if (fTokenizerAdvanced) + { + fNextToken = XP_STRTOK_R(fLineOfTokens, WHITESPACE, &fCurrentTokenPlaceHolder); + fTokenizerAdvanced = FALSE; + } + else + { + fNextToken = XP_STRTOK_R(nil, WHITESPACE, &fCurrentTokenPlaceHolder); + } + if (!fNextToken) + { + fAtEndOfLine = TRUE; + fNextToken = CRLF; + } + } + + return fNextToken; +} + +void TIMAPGenericParser::AdvanceToNextLine() +{ + FREEIF( fCurrentLine ); + FREEIF( fStartOfLineOfTokens); + fTokenizerAdvanced = FALSE; + + XP_Bool ok = GetNextLineForParser(&fCurrentLine); + if (!ok) + { + SetConnected(FALSE); + fStartOfLineOfTokens = nil; + fLineOfTokens = nil; + fCurrentTokenPlaceHolder = nil; + fNextToken = CRLF; + } + else if (fCurrentLine) // might be NULL if we are would_block ? + { + fStartOfLineOfTokens = XP_STRDUP(fCurrentLine); + if (fStartOfLineOfTokens) + { + fLineOfTokens = fStartOfLineOfTokens; + fNextToken = XP_STRTOK_R(fLineOfTokens, WHITESPACE, &fCurrentTokenPlaceHolder); + if (!fNextToken) + { + fAtEndOfLine = TRUE; + fNextToken = CRLF; + } + else + fAtEndOfLine = FALSE; + } + else + HandleMemoryFailure(); + } + else + HandleMemoryFailure(); +} + +void TIMAPGenericParser::AdvanceTokenizerStartingPoint(int32 bytesToAdvance) +{ + FREEIF(fStartOfLineOfTokens); + if (fCurrentLine) + { + fStartOfLineOfTokens = XP_STRDUP(fCurrentLine); + if (fStartOfLineOfTokens && ((int32) XP_STRLEN(fStartOfLineOfTokens) >= bytesToAdvance)) + { + fLineOfTokens = fStartOfLineOfTokens + bytesToAdvance; + fTokenizerAdvanced = TRUE; + } + else + HandleMemoryFailure(); + } + else + HandleMemoryFailure(); +} + +// Lots of things in the IMAP protocol are defined as an "astring." +// An astring is either an atom or a string. +// An atom is just a series of one or more characters such as: hello +// A string can either be quoted or literal. +// Quoted: "Test Folder 1" +// Literal: {13}Test Folder 1 +// This function leaves us off with fCurrentTokenPlaceHolder immediately after +// the end of the Astring. Call GetNextToken() to get the token after it. +char *TIMAPGenericParser::CreateAstring() +{ + if (*fNextToken == '{') + { + return CreateLiteral(); // literal + } + else if (*fNextToken == '"') + { + return CreateQuoted(); // quoted + } + else + { + return CreateAtom(); // atom + } +} + + +// Create an atom +// This function does not advance the parser. +// Call GetNextToken() to get the next token after the atom. +char *TIMAPGenericParser::CreateAtom() +{ + char *rv = XP_STRDUP(fNextToken); + //fNextToken = GetNextToken(); + return (rv); +} + +// CreateNilString creates either NIL (reutrns NULL) or a string +// Call with fNextToken pointing to the thing which we think is the nilstring. +// This function leaves us off with fCurrentTokenPlaceHolder immediately after +// the end of the string, if it is a string, or at the NIL. +// Regardless of type, call GetNextToken() to get the token after it. +char *TIMAPGenericParser::CreateNilString() +{ + if (!XP_STRCASECMP(fNextToken, "NIL")) + { + //fNextToken = GetNextToken(); + return NULL; + } + else + return CreateString(); +} + + +// Create a string, which can either be quoted or literal, +// but not an atom. +// This function leaves us off with fCurrentTokenPlaceHolder immediately after +// the end of the String. Call GetNextToken() to get the token after it. +char *TIMAPGenericParser::CreateString() +{ + if (*fNextToken == '{') + { + char *rv = CreateLiteral(); // literal + return (rv); + } + else if (*fNextToken == '"') + { + char *rv = CreateQuoted(); // quoted + //fNextToken = GetNextToken(); + return (rv); + } + else + { + SetSyntaxError(TRUE); + return NULL; + } +} + + +// This function leaves us off with fCurrentTokenPlaceHolder immediately after +// the end of the closing quote. Call GetNextToken() to get the token after it. +// Note that if the current line ends without the +// closed quote then we have to fetch another line from the server, until +// we find the close quote. +char *TIMAPGenericParser::CreateQuoted(XP_Bool /*skipToEnd*/) +{ + char *currentChar = fCurrentLine + + (fNextToken - fStartOfLineOfTokens) + + 1; // one char past opening '"' + + int charIndex = 0; + int tokenIndex = 0; + XP_Bool closeQuoteFound = FALSE; + char *returnString = XP_STRDUP(currentChar); + + while (returnString && !closeQuoteFound && ContinueParse()) + { + if (!*(returnString + charIndex)) + { + AdvanceToNextLine(); + StrAllocCat(returnString, fCurrentLine); + charIndex++; + } + else if (*(returnString + charIndex) == '"') + { + // don't check to see if it was escaped, + // that was handled in the next clause + closeQuoteFound = TRUE; + } + else if (*(returnString + charIndex) == '\\') + { + // eat the escape character + XP_STRCPY(returnString + charIndex, returnString + charIndex + 1); + // whatever the escaped character was, we want it + charIndex++; + + // account for charIndex not reflecting the eat of the escape character + tokenIndex++; + } + else + charIndex++; + } + + if (closeQuoteFound && returnString) + { + *(returnString + charIndex) = 0; + //if ((charIndex == 0) && skipToEnd) // it's an empty string. Why skip to end? + // skip_to_CRLF(); + //else if (charIndex == XP_STRLEN(fCurrentLine)) // should we have this? + //AdvanceToNextLine(); + //else + if (charIndex < (int) (XP_STRLEN(fNextToken) - 2)) // -2 because of the start and end quotes + { + // the quoted string was fully contained within fNextToken, + // and there is text after the quote in fNextToken that we + // still need + int charDiff = XP_STRLEN(fNextToken) - charIndex - 1; + fCurrentTokenPlaceHolder -= charDiff; + if (!XP_STRCMP(fCurrentTokenPlaceHolder, CRLF)) + fAtEndOfLine = TRUE; + } + else + { + fCurrentTokenPlaceHolder += tokenIndex + charIndex + 2 - XP_STRLEN(fNextToken); + if (!XP_STRCMP(fCurrentTokenPlaceHolder, CRLF)) + fAtEndOfLine = TRUE; + /* + tokenIndex += charIndex; + fNextToken = currentChar + tokenIndex + 1; + if (!XP_STRCMP(fNextToken, CRLF)) + fAtEndOfLine = TRUE; + */ + } + } + + return returnString; +} + + +// This function leaves us off with fCurrentTokenPlaceHolder immediately after +// the end of the literal string. Call GetNextToken() to get the token after it +// the literal string. +char *TIMAPGenericParser::CreateLiteral() +{ + int32 numberOfCharsInMessage = atoint32(fNextToken + 1); + int32 charsReadSoFar = 0, currentLineLength = 0; + int32 bytesToCopy = 0; + + char *returnString = (char *) XP_ALLOC(numberOfCharsInMessage + 1); + + if (returnString) + { + *(returnString + numberOfCharsInMessage) = 0; // Null terminate it first + + while (ContinueParse() && (charsReadSoFar < numberOfCharsInMessage)) + { + AdvanceToNextLine(); + currentLineLength = XP_STRLEN(fCurrentLine); + bytesToCopy = (currentLineLength > numberOfCharsInMessage - charsReadSoFar ? + numberOfCharsInMessage - charsReadSoFar : currentLineLength); + XP_ASSERT (bytesToCopy); + + if (ContinueParse()) + { + XP_MEMCPY(returnString + charsReadSoFar, fCurrentLine, bytesToCopy); + charsReadSoFar += bytesToCopy; + } + } + + if (ContinueParse()) + { + if (bytesToCopy == 0) + { + skip_to_CRLF(); + fAtEndOfLine = TRUE; + //fNextToken = GetNextToken(); + } + else if (currentLineLength == bytesToCopy) + { + fAtEndOfLine = TRUE; + //AdvanceToNextLine(); + } + else + { + // Move fCurrentTokenPlaceHolder + + //fCurrentTokenPlaceHolder = fStartOfLineOfTokens + bytesToCopy; + AdvanceTokenizerStartingPoint (bytesToCopy); + if (!*fCurrentTokenPlaceHolder) // landed on a token boundary + fCurrentTokenPlaceHolder++; + if (!XP_STRCMP(fCurrentTokenPlaceHolder, CRLF)) + fAtEndOfLine = TRUE; + + // The first token on the line might not + // be at the beginning of the line. There might be ONLY + // whitespace before it, in which case, fNextToken + // will be pointing to the right thing. Otherwise, + // we want to advance to the next token. + /* + int32 numCharsChecked = 0; + XP_Bool allWhitespace = TRUE; + while ((numCharsChecked < bytesToCopy)&& allWhitespace) + { + allWhitespace = (XP_STRCHR(WHITESPACE, fCurrentLine[numCharsChecked]) != NULL); + numCharsChecked++; + } + + if (!allWhitespace) + { + //fNextToken = fCurrentLine + bytesToCopy; + fNextToken = GetNextToken(); + if (!XP_STRCMP(fNextToken, CRLF)) + fAtEndOfLine = TRUE; + } + */ + } + } + } + + return returnString; +} + + +// Call this to create a buffer containing all characters within +// a given set of parentheses. +// Call this with fNextToken[0]=='(', that is, the open paren +// of the group. +// It will allocate and return all characters up to and including the corresponding +// closing paren, and leave the parser in the right place afterwards. +char *TIMAPGenericParser::CreateParenGroup() +{ + int numOpenParens = 1; + // count the number of parens in the current token + int count, tokenLen = XP_STRLEN(fNextToken); + for (count = 1; (count < tokenLen) && (numOpenParens > 0); count++) + { + if (fNextToken[count] == '(') + numOpenParens++; + else if (fNextToken[count] == ')') + numOpenParens--; + } + + // build up a buffer with the paren group. + char *buf = NULL; + if ((numOpenParens > 0) && ContinueParse()) + { + // First, copy that first token from before + StrAllocCat(buf, fNextToken); + StrAllocCat(buf, " "); // space that got stripped off the token + + // Go through the current line and look for the last close paren. + // We're not trying to parse it just yet, just separate it out. + int len = XP_STRLEN(fCurrentTokenPlaceHolder); + for (count = 0; (count < len) && (numOpenParens > 0); count++) + { + if (fCurrentTokenPlaceHolder[count] == '(') + numOpenParens++; + else if (fCurrentTokenPlaceHolder[count] == ')') + numOpenParens--; + } + + if (count < len) + { + // we found the last close paren. + // Set fNextToken, fCurrentTokenPlaceHolder, etc. + char oldChar = fCurrentTokenPlaceHolder[count]; + fCurrentTokenPlaceHolder[count] = 0; + StrAllocCat(buf, fCurrentTokenPlaceHolder); + fCurrentTokenPlaceHolder[count] = oldChar; + fCurrentTokenPlaceHolder = fCurrentTokenPlaceHolder + count; + fNextToken = GetNextToken(); + } + else + { + // there should always be either a space or CRLF after the response, right? + SetSyntaxError(TRUE); + } + } + else if ((numOpenParens == 0) && ContinueParse()) + { + // the whole paren group response was a single token + buf = XP_STRDUP(fNextToken); + } + + if (numOpenParens < 0) + SetSyntaxError(TRUE); + + return buf; +} + + +////////////////// TIMAPServerState ///////////////////////// + + +TImapServerState::TImapServerState(TIMAP4BlockingConnection &imapConnection) : + TIMAPGenericParser(), + fServerConnection(imapConnection), + fCurrentCommandTag(nil), + fCurrentFolderReadOnly(FALSE), + fNumberOfExistingMessages(0), + fNumberOfRecentMessages(0), + fNumberOfUnseenMessages(0), + fSizeOfMostRecentMessage(0), + fTotalDownloadSize(0), + fIMAPstate(kNonAuthenticated), + fSelectedMailboxName(nil), + fFlagState(nil), + fCurrentLineContainedFlagInfo(FALSE), + fZeroLengthMessageUidString(nil), + fReportingErrors(TRUE), + fLastChunk(FALSE), + fServerIsNetscape3xServer(FALSE), + m_shell(NULL) +{ + fSearchResults = TSearchResultSequence::CreateSearchResultSequence(); + fMailAccountUrl = NULL; + fManageFiltersUrl = NULL; + fManageListsUrl = NULL; + fFolderAdminUrl = NULL; + fNetscapeServerVersionString = NULL; + fXSenderInfo = NULL; + fSupportsUserDefinedFlags = FALSE; + fCapabilityFlag = kCapabilityUndefined; + fLastAlert = NULL; +} + +TImapServerState::~TImapServerState() +{ + FREEIF( fCurrentCommandTag ); + //delete fFlagState; // not our object + delete fSearchResults; + FREEIF( fZeroLengthMessageUidString ); + FREEIF( fMailAccountUrl ); + FREEIF( fFolderAdminUrl ); + FREEIF( fNetscapeServerVersionString ); + FREEIF( fXSenderInfo ); + FREEIF( fLastAlert ); + FREEIF( fManageListsUrl ); + FREEIF( fManageFiltersUrl ); +} + +XP_Bool TImapServerState::LastCommandSuccessful() +{ + return (!CommandFailed() && + !fServerConnection.DeathSignalReceived() && + TIMAPGenericParser::LastCommandSuccessful()); +} + +// returns TRUE if things look ok to continue +XP_Bool TImapServerState::GetNextLineForParser(char **nextLine) +{ + XP_Bool rv = TRUE; + *nextLine = fServerConnection.CreateNewLineFromSocket(); + if (fServerConnection.DeathSignalReceived() || (fServerConnection.GetConnectionStatus() <= 0)) + rv = FALSE; + // we'd really like to try to silently reconnect, but we shouldn't put this + // message up just in the interrupt case + if (fServerConnection.GetConnectionStatus() <= 0 && !fServerConnection.DeathSignalReceived()) + fServerConnection.AlertUserEvent_UsingId(MK_SERVER_DISCONNECTED); + return rv; +} + +// This should probably be in the base class... +void TImapServerState::end_of_line() +{ + // if current commands failed, don't reset the lex analyzer + // we need the info for the user + if (!at_end_of_line()) + SetSyntaxError(TRUE); + else if (fProcessingTaggedResponse && !fCurrentCommandFailed) + ResetLexAnalyzer(); // no more tokens until we send a command + else if (!fCurrentCommandFailed) + fNextToken = GetNextToken(); +} + +XP_Bool TImapServerState::CommandFailed() +{ + return fCurrentCommandFailed; +} + +void TImapServerState::SetFlagState(TImapFlagAndUidState *state) +{ + fFlagState = state; +} + +int32 TImapServerState::SizeOfMostRecentMessage() +{ + return fSizeOfMostRecentMessage; +} + +// Call this when adding a pipelined command to the session +void TImapServerState::IncrementNumberOfTaggedResponsesExpected(const char *newExpectedTag) +{ + fNumberOfTaggedResponsesExpected++; + FREEIF( fCurrentCommandTag ); + fCurrentCommandTag = XP_STRDUP(newExpectedTag); + if (!fCurrentCommandTag) + HandleMemoryFailure(); +} +/* + response ::= *response_data response_done +*/ + + +void TImapServerState::InitializeState() +{ + fProcessingTaggedResponse = FALSE; + fCurrentCommandFailed = FALSE; +} + +void TImapServerState::ParseIMAPServerResponse(const char *currentCommand) +{ + + // Reinitialize the parser + SetConnected(TRUE); + SetSyntaxError(FALSE); + + // Reinitialize our state + InitializeState(); + + // the default is to not pipeline + fNumberOfTaggedResponsesExpected = 1; + int numberOfTaggedResponsesReceived = 0; + + char *copyCurrentCommand = XP_STRDUP(currentCommand); + if (copyCurrentCommand && !fServerConnection.DeathSignalReceived()) + { + char *placeInTokenString = NULL; + char *tagToken = XP_STRTOK_R(copyCurrentCommand, WHITESPACE,&placeInTokenString); + char *commandToken = XP_STRTOK_R(nil, WHITESPACE,&placeInTokenString); + if (tagToken) + { + FREEIF( fCurrentCommandTag ); + fCurrentCommandTag = XP_STRDUP(tagToken); + if (!fCurrentCommandTag) + HandleMemoryFailure(); + } + + if (commandToken && ContinueParse()) + PreProcessCommandToken(commandToken, currentCommand); + + if (ContinueParse()) + { + // Clears any syntax error lines + SetSyntaxError(FALSE); + ResetLexAnalyzer(); + + do { + fNextToken = GetNextToken(); + while (ContinueParse() && !XP_STRCMP(fNextToken, "*") ) + { + response_data(); + } + + if (*fNextToken == '+') // never pipeline APPEND or AUTHENTICATE + { + XP_ASSERT((fNumberOfTaggedResponsesExpected - numberOfTaggedResponsesReceived) == 1); + numberOfTaggedResponsesReceived = fNumberOfTaggedResponsesExpected; + } + else + numberOfTaggedResponsesReceived++; + + if (numberOfTaggedResponsesReceived < fNumberOfTaggedResponsesExpected) + { + response_tagged(); + fProcessingTaggedResponse = FALSE; + } + + } while (ContinueParse() && (numberOfTaggedResponsesReceived < fNumberOfTaggedResponsesExpected)); + + // check and see if the server is waiting for more input + if (*fNextToken == '+') + fIMAPstate = kWaitingForMoreClientInput; + else + { + if (ContinueParse()) + response_done(); + + if (ContinueParse() && !CommandFailed()) + { + // a sucessful command may change the eIMAPstate + ProcessOkCommand(commandToken); + } + else if (CommandFailed()) + { + // a failed command may change the eIMAPstate + ProcessBadCommand(commandToken); + if (fReportingErrors) + fServerConnection.AlertUserEventFromServer(fCurrentLine); + } + } + } + } + else if (!fServerConnection.DeathSignalReceived()) + HandleMemoryFailure(); + + FREEIF(copyCurrentCommand); +} + +void TImapServerState::HandleMemoryFailure() +{ +#ifdef DEBUG_chrisf + XP_ASSERT(FALSE); +#endif + fServerConnection.AlertUserEvent(XP_GetString(MK_OUT_OF_MEMORY)); + TIMAPGenericParser::HandleMemoryFailure(); +} + + +// SEARCH is the only command that requires pre-processing for now. +// others will be added here. +void TImapServerState::PreProcessCommandToken(const char *commandToken, + const char *currentCommand) +{ + fCurrentCommandIsSingleMessageFetch = FALSE; + + if (!XP_STRCASECMP(commandToken, "SEARCH")) + fSearchResults->ResetSequence(); + else if (!XP_STRCASECMP(commandToken, "SELECT") && currentCommand) + { + // the mailbox name must be quoted, so strip the quotes + const char *openQuote = XP_STRSTR(currentCommand, "\""); + XP_ASSERT(openQuote); + + FREEIF( fSelectedMailboxName); + fSelectedMailboxName = XP_STRDUP(openQuote + 1); + if (fSelectedMailboxName) + { + // strip the escape chars and the ending quote + char *currentChar = fSelectedMailboxName; + while (*currentChar) + { + if (*currentChar == '\\') + { + XP_STRCPY(currentChar, currentChar+1); + currentChar++; // skip what we are escaping + } + else if (*currentChar == '\"') + *currentChar = 0; // end quote + else + currentChar++; + } + } + else + HandleMemoryFailure(); + + // we don't want bogus info for this new box + //delete fFlagState; // not our object + //fFlagState = NULL; + } + else if (!XP_STRCASECMP(commandToken, "CLOSE")) + { + return; // just for debugging + // we don't want bogus info outside the selected state + //delete fFlagState; // not our object + //fFlagState = NULL; + } + else if (!XP_STRCASECMP(commandToken, "UID")) + { + + char *copyCurrentCommand = XP_STRDUP(currentCommand); + if (copyCurrentCommand && !fServerConnection.DeathSignalReceived()) + { + char *placeInTokenString = NULL; + char *tagToken = XP_STRTOK_R(copyCurrentCommand, WHITESPACE,&placeInTokenString); + char *uidToken = XP_STRTOK_R(nil, WHITESPACE,&placeInTokenString); + char *fetchToken = XP_STRTOK_R(nil, WHITESPACE,&placeInTokenString); + + if (!XP_STRCASECMP(fetchToken, "FETCH") ) + { + char *uidStringToken = XP_STRTOK_R(nil, WHITESPACE,&placeInTokenString); + if (!XP_STRCHR(uidStringToken, ',') && !XP_STRCHR(uidStringToken, ':')) // , and : are uid delimiters + { + fCurrentCommandIsSingleMessageFetch = TRUE; + fUidOfSingleMessageFetch = atoint32(uidStringToken); + } + } + XP_FREE(copyCurrentCommand); + } + } +} + +const char *TImapServerState::GetSelectedMailboxName() +{ + return fSelectedMailboxName; +} + +TSearchResultIterator *TImapServerState::CreateSearchResultIterator() +{ + return new TSearchResultIterator(*fSearchResults); +} + +TImapServerState::eIMAPstate TImapServerState::GetIMAPstate() +{ + return fIMAPstate; +} + +void TImapServerState::PreauthSetAuthenticatedState() +{ + fIMAPstate = kAuthenticated; +} + +void TImapServerState::ProcessOkCommand(const char *commandToken) +{ + if (!XP_STRCASECMP(commandToken, "LOGIN") || + !XP_STRCASECMP(commandToken, "AUTHENTICATE")) + fIMAPstate = kAuthenticated; + else if (!XP_STRCASECMP(commandToken, "LOGOUT")) + fIMAPstate = kNonAuthenticated; + else if (!XP_STRCASECMP(commandToken, "SELECT") || + !XP_STRCASECMP(commandToken, "EXAMINE")) + fIMAPstate = kFolderSelected; + else if (!XP_STRCASECMP(commandToken, "CLOSE")) + fIMAPstate = kAuthenticated; + else if ((!XP_STRCASECMP(commandToken, "LIST")) || + (!XP_STRCASECMP(commandToken, "LSUB"))) + { + //fServerConnection.MailboxDiscoveryFinished(); + // This used to be reporting that we were finished + // discovering folders for each time we issued a + // LIST or LSUB. So if we explicitly listed the + // INBOX, or Trash, or namespaces, we would get multiple + // "done" states, even though we hadn't finished. + // Move this to be called from the connection object + // itself. + } + else if (!XP_STRCASECMP(commandToken, "FETCH")) + { + if (fZeroLengthMessageUidString) + { + // "Deleting zero length message"); + fServerConnection.Store(fZeroLengthMessageUidString, "+Flags (\\Deleted)", TRUE); + if (LastCommandSuccessful()) + fServerConnection.Expunge(); + + FREEIF( fZeroLengthMessageUidString ); + fZeroLengthMessageUidString = nil; + } + } + if (GetFillingInShell()) + { + // There is a BODYSTRUCTURE response. Now let's generate the stream... + // that is, if we're not doing it already + if (!m_shell->IsBeingGenerated()) + { + TNavigatorImapConnection *navCon = fServerConnection.GetNavigatorConnection(); + XP_ASSERT(navCon); // we should always have this + + m_shell->Generate(navCon ? navCon->GetCurrentUrl()->GetIMAPPartToFetch() : (char *)NULL); + + if ((navCon && navCon->GetPseudoInterrupted()) + || fServerConnection.DeathSignalReceived()) + { + // we were pseudointerrupted or interrupted + if (!m_shell->IsShellCached()) + { + // if it's not in the cache, then we were (pseudo)interrupted while generating + // for the first time. Delete it. + delete m_shell; + } + navCon->PseudoInterrupt(FALSE); + } + else if (m_shell->GetIsValid()) + { + // If we have a valid shell that has not already been cached, then cache it. + if (!m_shell->IsShellCached()) // cache is responsible for destroying it + { +// PR_LOG(IMAP, out, ("BODYSHELL: Adding shell to cache.")); + TIMAPHostInfo::AddShellToCacheForHost(fServerConnection.GetHostName(), m_shell); + } + } + else + { + // The shell isn't valid, so we don't cache it. + // Therefore, we have to destroy it here. + delete m_shell; + } + m_shell = NULL; + } + } +} + +void TImapServerState::ProcessBadCommand(const char *commandToken) +{ + if (!XP_STRCASECMP(commandToken, "LOGIN") || + !XP_STRCASECMP(commandToken, "AUTHENTICATE")) + fIMAPstate = kNonAuthenticated; + else if (!XP_STRCASECMP(commandToken, "LOGOUT")) + fIMAPstate = kNonAuthenticated; // ?? + else if (!XP_STRCASECMP(commandToken, "SELECT") || + !XP_STRCASECMP(commandToken, "EXAMINE")) + fIMAPstate = kAuthenticated; // nothing selected + else if (!XP_STRCASECMP(commandToken, "CLOSE")) + fIMAPstate = kAuthenticated; // nothing selected + if (GetFillingInShell()) + { + if (!m_shell->IsBeingGenerated()) + { + delete m_shell; + m_shell = NULL; + } + } +} +/* + response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye / + mailbox_data / message_data / capability_data) + CRLF + + The RFC1730 grammar spec did not allow one symbol look ahead to determine + between mailbox_data / message_data so I combined the numeric possibilities + of mailbox_data and all of message_data into numeric_mailbox_data. + + The production implemented here is + + response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye / + mailbox_data / numeric_mailbox_data / + capability_data) + CRLF + +Instead of comparing lots of strings and make function calls, try to pre-flight +the possibilities based on the first letter of the token. + +*/ +void TImapServerState::response_data() +{ + fNextToken = GetNextToken(); + + if (ContinueParse()) + { + switch (toupper(fNextToken[0])) + { + case 'O': // OK + if (toupper(fNextToken[1]) == 'K') + resp_cond_state(); + else SetSyntaxError(TRUE); + break; + case 'N': // NO + if (toupper(fNextToken[1]) == 'O') + resp_cond_state(); + else if (!XP_STRCASECMP(fNextToken, "NAMESPACE")) + namespace_data(); + else SetSyntaxError(TRUE); + break; + case 'B': // BAD + if (!XP_STRCASECMP(fNextToken, "BAD")) + resp_cond_state(); + else if (!XP_STRCASECMP(fNextToken, "BYE")) + resp_cond_bye(); + else SetSyntaxError(TRUE); + break; + case 'F': + if (!XP_STRCASECMP(fNextToken, "FLAGS")) + mailbox_data(); + else SetSyntaxError(TRUE); + break; + case 'P': + if (XP_STRCASECMP(fNextToken, "PERMANENTFLAGS")) + mailbox_data(); + else SetSyntaxError(TRUE); + break; + case 'L': + if (!XP_STRCASECMP(fNextToken, "LIST") || !XP_STRCASECMP(fNextToken, "LSUB")) + mailbox_data(); + else SetSyntaxError(TRUE); + break; + case 'M': + if (!XP_STRCASECMP(fNextToken, "MAILBOX")) + mailbox_data(); + else if (!XP_STRCASECMP(fNextToken, "MYRIGHTS")) + myrights_data(); + else SetSyntaxError(TRUE); + break; + case 'S': + if (!XP_STRCASECMP(fNextToken, "SEARCH")) + mailbox_data(); + else if (!XP_STRCASECMP(fNextToken, "STATUS")) + { + XP_Bool gotMailboxName = FALSE; + while ( ContinueParse() && + !at_end_of_line() ) + { + fNextToken = GetNextToken(); + if (!fNextToken) + break; + + if (!gotMailboxName) // if we haven't got the mailbox name, get it + { + // this couldn't be more bogus, but I don't know how to parse the status response. + // I need to find the next open parenthesis, and it looks like folder names with + // parentheses are quoted...but not ones with spaces... + fCurrentTokenPlaceHolder = XP_STRCHR(fCurrentTokenPlaceHolder, '('); + + gotMailboxName = TRUE; + continue; + } + + if (*fNextToken == '(') fNextToken++; + if (!XP_STRCASECMP(fNextToken, "UIDNEXT")) + { + fNextToken = GetNextToken(); + if (fNextToken) + { + fCurrentResponseUID = atoint32(fNextToken); + // if this token ends in ')', then it is the last token + // else we advance + if ( *(fNextToken + XP_STRLEN(fNextToken) - 1) == ')') + fNextToken += XP_STRLEN(fNextToken) - 1; + } + } + else if (!XP_STRCASECMP(fNextToken, "MESSAGES")) + { + fNextToken = GetNextToken(); + if (fNextToken) + { + fNumberOfExistingMessages = atoint32(fNextToken); + // if this token ends in ')', then it is the last token + // else we advance + if ( *(fNextToken + XP_STRLEN(fNextToken) - 1) == ')') + fNextToken += XP_STRLEN(fNextToken) - 1; + } + } + else if (!XP_STRCASECMP(fNextToken, "UNSEEN")) + { + fNextToken = GetNextToken(); + if (fNextToken) + { + fNumberOfUnseenMessages = atoint32(fNextToken); + // if this token ends in ')', then it is the last token + // else we advance + if ( *(fNextToken + XP_STRLEN(fNextToken) - 1) == ')') + fNextToken += XP_STRLEN(fNextToken) - 1; + } + } + else if (*fNextToken == ')') + break; + else if (!at_end_of_line()) + SetSyntaxError(TRUE); + } + } else SetSyntaxError(TRUE); + break; + case 'C': + if (!XP_STRCASECMP(fNextToken, "CAPABILITY")) + capability_data(); + else SetSyntaxError(TRUE); + break; + case 'V': + if (!XP_STRCASECMP(fNextToken, "VERSION")) + { + // figure out the version of the Netscape server here + FREEIF(fNetscapeServerVersionString); + fNextToken = GetNextToken(); + if (! fNextToken) + SetSyntaxError(TRUE); + else + { + fNetscapeServerVersionString = CreateAstring(); + fNextToken = GetNextToken(); + if (fNetscapeServerVersionString) + { + fServerIsNetscape3xServer = (*fNetscapeServerVersionString == '3'); + } + } + skip_to_CRLF(); + } + else SetSyntaxError(TRUE); + break; + case 'A': + if (!XP_STRCASECMP(fNextToken, "ACL")) + { + acl_data(); + } + else if (!XP_STRCASECMP(fNextToken, "ACCOUNT-URL")) + { + FREEIF(fMailAccountUrl); + fNextToken = GetNextToken(); + if (! fNextToken) + SetSyntaxError(TRUE); + else + { + fMailAccountUrl = CreateAstring(); + fNextToken = GetNextToken(); + } + } + else SetSyntaxError(TRUE); + break; + case 'X': + if (!XP_STRCASECMP(fNextToken, "XSERVERINFO")) + xserverinfo_data(); + else if (!XP_STRCASECMP(fNextToken, "XMAILBOXINFO")) + xmailboxinfo_data(); + else + SetSyntaxError(TRUE); + break; + default: + if (IsNumericString(fNextToken)) + numeric_mailbox_data(); + else + SetSyntaxError(TRUE); + break; + } + + if (ContinueParse()) + { + PostProcessEndOfLine(); + end_of_line(); + } + } +} + + +void TImapServerState::PostProcessEndOfLine() +{ + // for now we only have to do one thing here + // a fetch response to a 'uid store' command might return the flags + // before it returns the uid of the message. So we need both before + // we report the new flag info to the front end + + // also check and be sure that there was a UID in the current response + if (fCurrentLineContainedFlagInfo && CurrentResponseUID()) + { + fCurrentLineContainedFlagInfo = FALSE; + fServerConnection.NotifyMessageFlags(fSavedFlagInfo, CurrentResponseUID()); + } +} + + +/* + mailbox_data ::= "FLAGS" SPACE flag_list / + "LIST" SPACE mailbox_list / + "LSUB" SPACE mailbox_list / + "MAILBOX" SPACE text / + "SEARCH" [SPACE 1#nz_number] / + number SPACE "EXISTS" / number SPACE "RECENT" + +This production was changed to accomodate predictive parsing + + mailbox_data ::= "FLAGS" SPACE flag_list / + "LIST" SPACE mailbox_list / + "LSUB" SPACE mailbox_list / + "MAILBOX" SPACE text / + "SEARCH" [SPACE 1#nz_number] +*/ +void TImapServerState::mailbox_data() +{ + XP_Bool userDefined = FALSE; + XP_Bool perm = FALSE; + + if (!XP_STRCASECMP(fNextToken, "FLAGS")) { + skip_to_CRLF(); + } + else if (!XP_STRCASECMP(fNextToken, "LIST")) + { + fNextToken = GetNextToken(); + if (ContinueParse()) + mailbox_list(FALSE); + } + else if (!XP_STRCASECMP(fNextToken, "LSUB")) + { + fNextToken = GetNextToken(); + if (ContinueParse()) + mailbox_list(TRUE); + } + else if (!XP_STRCASECMP(fNextToken, "MAILBOX")) + skip_to_CRLF(); + else if (!XP_STRCASECMP(fNextToken, "SEARCH")) + { + fSearchResults->AddSearchResultLine(fCurrentLine); + fServerConnection.NotifySearchHit(fCurrentLine); + skip_to_CRLF(); + } +} + +/* + mailbox_list ::= "(" #("\Marked" / "\Noinferiors" / + "\Noselect" / "\Unmarked" / flag_extension) ")" + SPACE (<"> QUOTED_CHAR <"> / nil) SPACE mailbox +*/ + +void TImapServerState::mailbox_list(XP_Bool discoveredFromLsub) +{ + mailbox_spec *boxSpec = (mailbox_spec *)XP_ALLOC(sizeof(mailbox_spec)); + if (!boxSpec) + HandleMemoryFailure(); + else + { + boxSpec->folderSelected = FALSE; + boxSpec->box_flags = kNoFlags; + boxSpec->allocatedPathName = NULL; + boxSpec->hostName = NULL; + boxSpec->connection = fServerConnection.GetNavigatorConnection(); + boxSpec->flagState = NULL; + boxSpec->discoveredFromLsub = discoveredFromLsub; + boxSpec->onlineVerified = TRUE; + + XP_Bool endOfFlags = FALSE; + fNextToken++; // eat the first "(" + do { + if (!XP_STRNCASECMP(fNextToken, "\\Marked", 7)) + boxSpec->box_flags |= kMarked; + else if (!XP_STRNCASECMP(fNextToken, "\\Unmarked", 9)) + boxSpec->box_flags |= kUnmarked; + else if (!XP_STRNCASECMP(fNextToken, "\\Noinferiors", 12)) + boxSpec->box_flags |= kNoinferiors; + else if (!XP_STRNCASECMP(fNextToken, "\\Noselect", 9)) + boxSpec->box_flags |= kNoselect; + // we ignore flag extensions + + endOfFlags = *(fNextToken + XP_STRLEN(fNextToken) - 1) == ')'; + fNextToken = GetNextToken(); + } while (!endOfFlags && ContinueParse()); + + if (ContinueParse()) + { + if (*fNextToken == '"') + { + fNextToken++; + if (*fNextToken == '\\') // handle escaped char + boxSpec->hierarchySeparator = *(fNextToken + 1); + else + boxSpec->hierarchySeparator = *fNextToken; + } + else // likely NIL. Discovered late in 4.02 that we do not handle literals here (e.g. {10} <10 chars>), although this is almost impossibly unlikely + boxSpec->hierarchySeparator = kOnlineHierarchySeparatorUnknown; + fNextToken = GetNextToken(); + if (ContinueParse()) + mailbox(boxSpec); + } + } +} + +/* mailbox ::= "INBOX" / astring +*/ +void TImapServerState::mailbox(mailbox_spec *boxSpec) +{ + char *boxname = NULL; + + if (!XP_STRCASECMP(fNextToken, "INBOX")) + { + boxname = XP_STRDUP("INBOX"); + fNextToken = GetNextToken(); + } + else + { + boxname = CreateAstring(); + fNextToken = GetNextToken(); + } + + if (boxname) + { + // sometimes the uwash server will return a list response with a '/' hanging on the + // end. + + /* + // chrisf: I think we should "discover" whatever the server tells us, and do any munging in + // libmsg if it is necessary. + if (XP_STRLEN(boxname) && *(boxname + XP_STRLEN(boxname) - 1) == TIMAPUrl::GetOnlineSubDirSeparator()) + *(boxname + XP_STRLEN(boxname) - 1) = '\0'; + */ + + // should the namespace check go before or after the Utf7 conversion? + TIMAPHostInfo::SetNamespaceHierarchyDelimiterFromMailboxForHost(fServerConnection.GetHostName(), boxname, boxSpec->hierarchySeparator); + + + TIMAPNamespace *ns = TIMAPHostInfo::GetNamespaceForMailboxForHost(fServerConnection.GetHostName(),boxname); + if (ns) + { + switch (ns->GetType()) + { + case kPersonalNamespace: + boxSpec->box_flags |= kPersonalMailbox; + break; + case kPublicNamespace: + boxSpec->box_flags |= kPublicMailbox; + break; + case kOtherUsersNamespace: + boxSpec->box_flags |= kOtherUsersMailbox; + break; + default: // (kUnknownNamespace) + break; + } + } + + char *convertedName = fServerConnection.CreateUtf7ConvertedString(boxname, FALSE); + XP_FREE(boxname); + boxname = convertedName; + } + + if (!boxname) + { + if (!fServerConnection.DeathSignalReceived()) + HandleMemoryFailure(); + } + else + { + XP_ASSERT(boxSpec->connection); + XP_ASSERT(boxSpec->connection->GetCurrentUrl()); + //boxSpec->hostName = NULL; + //if (boxSpec->connection && boxSpec->connection->GetCurrentUrl()) + boxSpec->allocatedPathName = boxSpec->connection->GetCurrentUrl()->AllocateCanonicalPath(boxname); + boxSpec->hostName = boxSpec->connection->GetCurrentUrl()->GetUrlHost(); + FREEIF( boxname); + // storage for the boxSpec is now owned by server connection + fServerConnection.DiscoverMailboxSpec(boxSpec); + + // if this was cancelled by the user,then we sure don't want to + // send more mailboxes their way + if (fServerConnection.GetConnectionStatus() < 0) + SetConnected(FALSE); + } +} + + +/* + message_data ::= nz_number SPACE ("EXPUNGE" / + ("FETCH" SPACE msg_fetch) / msg_obsolete) + +was changed to + +numeric_mailbox_data ::= number SPACE "EXISTS" / number SPACE "RECENT" + / nz_number SPACE ("EXPUNGE" / + ("FETCH" SPACE msg_fetch) / msg_obsolete) + +*/ +void TImapServerState::numeric_mailbox_data() +{ + int32 tokenNumber = atoint32(fNextToken); + fNextToken = GetNextToken(); + + if (ContinueParse()) + { + if (!XP_STRCASECMP(fNextToken, "FETCH")) + { + fFetchResponseIndex = tokenNumber; + fNextToken = GetNextToken(); + if (ContinueParse()) + msg_fetch(); + } + else if (!XP_STRCASECMP(fNextToken, "EXISTS")) + { + fNumberOfExistingMessages = tokenNumber; + fNextToken = GetNextToken(); + } + else if (!XP_STRCASECMP(fNextToken, "RECENT")) + { + fNumberOfRecentMessages = tokenNumber; + fNextToken = GetNextToken(); + } + else if (!XP_STRCASECMP(fNextToken, "EXPUNGE")) + { + fFlagState->ExpungeByIndex((uint32) tokenNumber); + skip_to_CRLF(); + } + else + msg_obsolete(); + } +} + +/* + msg_fetch ::= "(" 1#("BODY" SPACE body / + "BODYSTRUCTURE" SPACE body / + "BODY[" section "]" SPACE nstring / + "ENVELOPE" SPACE envelope / + "FLAGS" SPACE "(" #(flag / "\Recent") ")" / + "INTERNALDATE" SPACE date_time / + "RFC822" [".HEADER" / ".TEXT"] SPACE nstring / + "RFC822.SIZE" SPACE number / + "UID" SPACE uniqueid) ")" + +*/ + +void TImapServerState::msg_fetch() +{ + // we have not seen a uid response or flags for this fetch, yet + fCurrentResponseUID = 0; + fCurrentLineContainedFlagInfo = FALSE; + + // show any incremental progress, for instance, for header downloading + fServerConnection.ShowProgress(); + + fNextToken++; // eat the '(' character + + // some of these productions are ignored for now + while (ContinueParse() && (*fNextToken != ')') ) + { + if (!XP_STRCASECMP(fNextToken, "FLAGS")) + { + if (fCurrentResponseUID == 0) + fCurrentResponseUID = fFlagState->GetUidOfMessage(fFetchResponseIndex - 1); + + fNextToken = GetNextToken(); + if (ContinueParse()) + flags(); + + if (ContinueParse()) + { // eat the closing ')' + fNextToken++; + // there may be another ')' to close out + // msg_fetch. If there is then don't advance + if (*fNextToken != ')') + fNextToken = GetNextToken(); + } + } + else if (!XP_STRCASECMP(fNextToken, "UID")) + { + fNextToken = GetNextToken(); + if (ContinueParse()) + { + fCurrentResponseUID = atoint32(fNextToken); + if (fCurrentResponseUID > fHighestRecordedUID) + fHighestRecordedUID = fCurrentResponseUID; + + // if this token ends in ')', then it is the last token + // else we advance + if ( *(fNextToken + XP_STRLEN(fNextToken) - 1) == ')') + fNextToken += XP_STRLEN(fNextToken) - 1; + else + fNextToken = GetNextToken(); + } + } + else if (!XP_STRCASECMP(fNextToken, "RFC822") || + !XP_STRCASECMP(fNextToken, "RFC822.HEADER") || + !XP_STRNCASECMP(fNextToken, "BODY[HEADER",11) || + !XP_STRNCASECMP(fNextToken, "BODY[]", 6) || + !XP_STRCASECMP(fNextToken, "RFC822.TEXT") || + (!XP_STRNCASECMP(fNextToken, "BODY[", 5) && + XP_STRSTR(fNextToken, "HEADER")) + ) + { + if (fCurrentResponseUID == 0) + fCurrentResponseUID = fFlagState->GetUidOfMessage(fFetchResponseIndex - 1); + + if (!XP_STRCASECMP(fNextToken, "RFC822.HEADER") || + !XP_STRCASECMP(fNextToken, "BODY[HEADER]")) + { + // all of this message's headers + fNextToken = GetNextToken(); + fDownloadingHeaders = TRUE; + if (ContinueParse()) + msg_fetch_headers(NULL); + } + else if (!XP_STRNCASECMP(fNextToken, "BODY[HEADER.FIELDS",19)) + { + fDownloadingHeaders = TRUE; + // specific message header fields + while (ContinueParse() && fNextToken[XP_STRLEN(fNextToken)-1] != ']') + fNextToken = GetNextToken(); + if (ContinueParse()) + { + fNextToken = GetNextToken(); + if (ContinueParse()) + msg_fetch_headers(NULL); + } + } + else + { + char *whereHeader = XP_STRSTR(fNextToken, "HEADER"); + if (whereHeader) + { + char *startPartNum = fNextToken + 5; + char *partNum = (char *)XP_ALLOC((whereHeader - startPartNum) * sizeof (char)); + if (partNum) + { + XP_STRNCPY_SAFE(partNum, startPartNum, (whereHeader - startPartNum)); + if (ContinueParse()) + { + if (XP_STRSTR(fNextToken, "FIELDS")) + { + while (ContinueParse() && fNextToken[XP_STRLEN(fNextToken)-1] != ']') + fNextToken = GetNextToken(); + } + if (ContinueParse()) + { + fNextToken = GetNextToken(); + if (ContinueParse()) + msg_fetch_headers(partNum); + } + } + XP_FREE(partNum); + } + } + else + { + fDownloadingHeaders = FALSE; + + XP_Bool chunk = FALSE; + int32 origin = 0; + if (!XP_STRNCASECMP(fNextToken, "BODY[]<", 7)) + { + char *tokenCopy = 0; + StrAllocCopy(tokenCopy, fNextToken); + if (tokenCopy) + { + char *originString = tokenCopy + 7; // where the byte number starts + char *closeBracket = XP_STRCHR(tokenCopy,'>'); + if (closeBracket && originString && *originString) + { + *closeBracket = 0; + origin = atoint32(originString); + chunk = TRUE; + } + XP_FREE(tokenCopy); + } + } + + fNextToken = GetNextToken(); + if (ContinueParse()) + { + msg_fetch_content(chunk, origin, MESSAGE_RFC822); + } + } + } + } + else if (!XP_STRCASECMP(fNextToken, "RFC822.SIZE")) + { + fNextToken = GetNextToken(); + if (ContinueParse()) + { + fSizeOfMostRecentMessage = atoint32(fNextToken); + + if (fSizeOfMostRecentMessage == 0 && CurrentResponseUID()) + { + // on no, bogus Netscape 2.0 mail server bug + char uidString[100]; + sprintf(uidString, "%ld", (long)CurrentResponseUID()); + + if (!fZeroLengthMessageUidString) + fZeroLengthMessageUidString = XP_STRDUP(uidString); + else + { + StrAllocCat(fZeroLengthMessageUidString, ","); + StrAllocCat(fZeroLengthMessageUidString, uidString); + } + } + + // if this token ends in ')', then it is the last token + // else we advance + if ( *(fNextToken + XP_STRLEN(fNextToken) - 1) == ')') + fNextToken += XP_STRLEN(fNextToken) - 1; + else + fNextToken = GetNextToken(); + } + } + else if (!XP_STRCASECMP(fNextToken, "XSENDER")) + { + FREEIF(fXSenderInfo); + fNextToken = GetNextToken(); + if (! fNextToken) + SetSyntaxError(TRUE); + else + { + fXSenderInfo = CreateAstring(); + fNextToken = GetNextToken(); + } + } + // I only fetch RFC822 so I should never see these BODY responses + else if (!XP_STRCASECMP(fNextToken, "BODY")) + skip_to_CRLF(); // I never ask for this + else if (!XP_STRCASECMP(fNextToken, "BODYSTRUCTURE")) + { + bodystructure_data(); + } + else if (!XP_STRNCASECMP(fNextToken, "BODY[", 5) && XP_STRNCASECMP(fNextToken, "BODY[]", 6)) + { + fDownloadingHeaders = FALSE; + // A specific MIME part, or MIME part header + mime_data(); + } + else if (!XP_STRCASECMP(fNextToken, "ENVELOPE")) + skip_to_CRLF(); // I never ask for this + else if (!XP_STRCASECMP(fNextToken, "INTERNALDATE")) + skip_to_CRLF(); // I will need to implement this + else + SetSyntaxError(TRUE); + + } + + if (ContinueParse()) + { + if (CurrentResponseUID() && fCurrentLineContainedFlagInfo && fFlagState) + { + fFlagState->AddUidFlagPair(CurrentResponseUID(), fSavedFlagInfo); + fCurrentLineContainedFlagInfo = FALSE; // do not fire if in PostProcessEndOfLine + } + fCurrentLineContainedFlagInfo = FALSE; // do not fire if in PostProcessEndOfLine + + fNextToken = GetNextToken(); // eat the ')' ending token + // should be at end of line + } +} + +void TImapServerState::flags() +{ + imapMessageFlagsType messageFlags = kNoImapMsgFlag; + + // eat the opening '(' + fNextToken++; + + while (ContinueParse() && (*fNextToken != ')')) + { + switch (toupper(fNextToken[1])) { + case 'S': + if (!XP_STRNCASECMP(fNextToken, "\\Seen",5)) + messageFlags |= kImapMsgSeenFlag; + break; + case 'A': + if (!XP_STRNCASECMP(fNextToken, "\\Answered",9)) + messageFlags |= kImapMsgAnsweredFlag; + break; + case 'F': + if (!XP_STRNCASECMP(fNextToken, "\\Flagged",8)) + messageFlags |= kImapMsgFlaggedFlag; + else if (fSupportsUserDefinedFlags && !XP_STRNCASECMP(fNextToken, "\\Forwarded",10)) + messageFlags |= kImapMsgForwardedFlag; + break; + case 'D': + if (!XP_STRNCASECMP(fNextToken, "\\Deleted",8)) + messageFlags |= kImapMsgDeletedFlag; + else if (!XP_STRNCASECMP(fNextToken, "\\Draft",6)) + messageFlags |= kImapMsgDraftFlag; + break; + case 'R': + if (!XP_STRNCASECMP(fNextToken, "\\Recent",7)) + messageFlags |= kImapMsgRecentFlag; + break; + case 'M': + if (fSupportsUserDefinedFlags && !XP_STRNCASECMP(fNextToken, "\\MDNSent",7)) + messageFlags |= kImapMsgMDNSentFlag; + break; + default: + break; + } + + if (strcasestr(fNextToken, ")")) + { + // eat token chars until we get the ')' + while (*fNextToken != ')') + fNextToken++; + } + else + fNextToken = GetNextToken(); + } + + if (ContinueParse()) + while(*fNextToken != ')') + fNextToken++; + + fCurrentLineContainedFlagInfo = TRUE; // handled in PostProcessEndOfLine + fSavedFlagInfo = messageFlags; +} + +/* + resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text +*/ +void TImapServerState::resp_cond_state() +{ + if ((!XP_STRCASECMP(fNextToken, "NO") || + !XP_STRCASECMP(fNextToken, "BAD") ) && + fProcessingTaggedResponse) + fCurrentCommandFailed = TRUE; + + fNextToken = GetNextToken(); + if (ContinueParse()) + resp_text(); +} + +/* + resp_text ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text) + + was changed to in order to enable a one symbol look ahead predictive + parser. + + resp_text ::= ["[" resp_text_code SPACE] (text_mime2 / text) + */ +void TImapServerState::resp_text() +{ + if (ContinueParse() && (*fNextToken == '[')) + resp_text_code(); + + if (ContinueParse()) + { + if (!XP_STRCMP(fNextToken, "=?")) + text_mime2(); + else + text(); + } +} +/* + text_mime2 ::= "=?" "?" "?" + "?=" + ;; Syntax defined in [MIME-2] +*/ +void TImapServerState::text_mime2() +{ + skip_to_CRLF(); +} + +/* + text ::= 1*TEXT_CHAR + +*/ +void TImapServerState::text() +{ + skip_to_CRLF(); +} + + +/* + resp_text_code ::= "ALERT" / "PARSE" / + "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" / + "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / + "UIDVALIDITY" SPACE nz_number / + "UNSEEN" SPACE nz_number / + atom [SPACE 1*] + + was changed to in order to enable a one symbol look ahead predictive + parser. + + resp_text_code ::= ("ALERT" / "PARSE" / + "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" / + "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / + "UIDVALIDITY" SPACE nz_number / + "UNSEEN" SPACE nz_number / + atom [SPACE 1*] ) + "]" + + +*/ +void TImapServerState::resp_text_code() +{ + // this is a special case way of advancing the token + // strtok won't break up "[ALERT]" into separate tokens + if (XP_STRLEN(fNextToken) > 1) + fNextToken++; + else + fNextToken = GetNextToken(); + + if (ContinueParse()) + { + if (!XP_STRCASECMP(fNextToken,"ALERT]")) + { + char *alertMsg = fCurrentTokenPlaceHolder; // advance past ALERT] + if (alertMsg && *alertMsg && (!fLastAlert || XP_STRCMP(fNextToken, fLastAlert))) + { + fServerConnection.AlertUserEvent(alertMsg); + FREEIF(fLastAlert); + fLastAlert = XP_STRDUP(alertMsg); + } + fNextToken = GetNextToken(); + } + else if (!XP_STRCASECMP(fNextToken,"PARSE]")) + { + // do nothing for now + fNextToken = GetNextToken(); + } + else if (!XP_STRCASECMP(fNextToken,"NETSCAPE]")) + { + skip_to_CRLF(); + } + else if (!XP_STRCASECMP(fNextToken,"PERMANENTFLAGS")) + { + // do nothing but eat tokens until we see the ] or CRLF + // we should see the ] but we don't want to go into an + // endless loop if the CRLF is not there +#if 0 + do { + fNextToken = GetNextToken(); + } while (!strcasestr(fNextToken, "]") && + !at_end_of_line() && + ContinueParse()); +#else + fSupportsUserDefinedFlags = FALSE; // assume no unless told + do { + fNextToken = GetNextToken(); + if (!XP_STRNCASECMP(fNextToken, "\\*)]", 4)) + fSupportsUserDefinedFlags = TRUE; + } while (!at_end_of_line() && ContinueParse()); +#endif + } + else if (!XP_STRCASECMP(fNextToken,"READ-ONLY]")) + { + fCurrentFolderReadOnly = TRUE; + fNextToken = GetNextToken(); + } + else if (!XP_STRCASECMP(fNextToken,"READ-WRITE]")) + { + fCurrentFolderReadOnly = FALSE; + fNextToken = GetNextToken(); + } + else if (!XP_STRCASECMP(fNextToken,"TRYCREATE]")) + { + // do nothing for now + fNextToken = GetNextToken(); + } + else if (!XP_STRCASECMP(fNextToken,"UIDVALIDITY")) + { + fNextToken = GetNextToken(); + if (ContinueParse()) + { + fFolderUIDValidity = atoint32(fNextToken); + fHighestRecordedUID = 0; + fNextToken = GetNextToken(); + } + } + else if (!XP_STRCASECMP(fNextToken,"UNSEEN")) + { + fNextToken = GetNextToken(); + if (ContinueParse()) + { + fNumberOfUnseenMessages = atoint32(fNextToken); + fNextToken = GetNextToken(); + } + } + else // just text + { + // do nothing but eat tokens until we see the ] or CRLF + // we should see the ] but we don't want to go into an + // endless loop if the CRLF is not there + do { + fNextToken = GetNextToken(); + } while (!strcasestr(fNextToken, "]") && + !at_end_of_line() && + ContinueParse()); + } + } +} + +/* + response_done ::= response_tagged / response_fatal + */ +void TImapServerState::response_done() +{ + if (ContinueParse()) + { + if (!XP_STRCMP(fCurrentCommandTag, fNextToken)) + response_tagged(); + else + response_fatal(); + } +} + +/* response_tagged ::= tag SPACE resp_cond_state CRLF +*/ +void TImapServerState::response_tagged() +{ + // eat the tag + fNextToken = GetNextToken(); + if (ContinueParse()) + { + fProcessingTaggedResponse = TRUE; + resp_cond_state(); + if (ContinueParse()) + end_of_line(); + } +} + +/* response_fatal ::= "*" SPACE resp_cond_bye CRLF +*/ +void TImapServerState::response_fatal() +{ + // eat the "*" + fNextToken = GetNextToken(); + if (ContinueParse()) + { + resp_cond_bye(); + if (ContinueParse()) + end_of_line(); + } +} +/* +resp_cond_bye ::= "BYE" SPACE resp_text + ;; Server will disconnect condition + */ +void TImapServerState::resp_cond_bye() +{ + SetConnected(FALSE); + fIMAPstate = kNonAuthenticated; + skip_to_CRLF(); +} + + +void TImapServerState::msg_fetch_headers(const char *partNum) +{ + if (GetFillingInShell()) + { + char *headerData = CreateAstring(); + fNextToken = GetNextToken(); + m_shell->AdoptMessageHeaders(headerData, partNum); + } + else + { + msg_fetch_content(FALSE, 0, MESSAGE_RFC822); + } +} + + +/* nstring ::= string / nil +string ::= quoted / literal + nil ::= "NIL" + +*/ +void TImapServerState::msg_fetch_content(XP_Bool chunk, int32 origin, const char *content_type) +{ + // setup the stream for downloading this message. + // Don't do it if we are filling in a shell or downloading a part. + // DO do it if we are downloading a whole message as a result of + // an invalid shell trying to generate. + if ((!chunk || (origin == 0)) && + (GetFillingInShell() ? m_shell->GetGeneratingWholeMessage() : TRUE)) + { + XP_ASSERT(fSizeOfMostRecentMessage > 0); + fServerConnection.BeginMessageDownLoad(fSizeOfMostRecentMessage, + content_type); + } + + if (XP_STRCASECMP(fNextToken, "NIL")) + { + if (*fNextToken == '"') + fLastChunk = msg_fetch_quoted(chunk, origin); + else + fLastChunk = msg_fetch_literal(chunk, origin); + } + else + fNextToken = GetNextToken(); // eat "NIL" + + if (fLastChunk && (GetFillingInShell() ? m_shell->GetGeneratingWholeMessage() : TRUE)) + { + // complete the message download + if (ContinueParse()) + fServerConnection.NormalMessageEndDownload(); + else + fServerConnection.AbortMessageDownLoad(); + } +} + + +/* + quoted ::= <"> *QUOTED_CHAR <"> + + QUOTED_CHAR ::= / + "\" quoted_specials + + quoted_specials ::= <"> / "\" +*/ +// This algorithm seems gross. Isn't there an easier way? +XP_Bool TImapServerState::msg_fetch_quoted(XP_Bool chunk, int32 origin) +{ + uint32 numberOfCharsInThisChunk = 0; + XP_Bool closeQuoteFound = FALSE; + XP_Bool lastChunk = TRUE; // whether or not this is the last chunk of this message + char *scanLine = fCurrentLine++; // eat the first " + // this won't work! The '"' char does not start + // the line! why would anybody format a message like this! + while (!closeQuoteFound && ContinueParse()) + { + char *quoteSubString = strcasestr(scanLine, "\""); + while (quoteSubString && !closeQuoteFound) + { + if (quoteSubString > scanLine) + { + closeQuoteFound = *(quoteSubString - 1) != '\\'; + if (!closeQuoteFound) + quoteSubString = strcasestr(quoteSubString+1, "\""); + } + else + closeQuoteFound = TRUE; // 1st char is a '"' + } + + // send the string to the connection object so he can display or + // cache or whatever + fServerConnection.HandleMessageDownLoadLine(fCurrentLine, FALSE); + numberOfCharsInThisChunk += XP_STRLEN(fCurrentLine); + AdvanceToNextLine(); + scanLine = fCurrentLine; + } + + lastChunk = !chunk || ((origin + numberOfCharsInThisChunk) >= (unsigned int) fTotalDownloadSize); + return lastChunk; +} +/* msg_obsolete ::= "COPY" / ("STORE" SPACE msg_fetch) + ;; OBSOLETE untagged data responses */ +void TImapServerState::msg_obsolete() +{ + if (!XP_STRCASECMP(fNextToken, "COPY")) + fNextToken = GetNextToken(); + else if (!XP_STRCASECMP(fNextToken, "STORE")) + { + fNextToken = GetNextToken(); + if (ContinueParse()) + msg_fetch(); + } + else + SetSyntaxError(TRUE); + +} +void TImapServerState::capability_data() +{ + fCapabilityFlag = fCapabilityFlag | kCapabilityDefined; + do { + fNextToken = GetNextToken(); + // for now we only care about AUTH=LOGIN + if (fNextToken) { + if(! XP_STRCASECMP(fNextToken, "AUTH=LOGIN")) + fCapabilityFlag |= kHasAuthLoginCapability; + else if (! XP_STRCASECMP(fNextToken, "X-NETSCAPE")) + fCapabilityFlag |= kHasXNetscapeCapability; + else if (! XP_STRCASECMP(fNextToken, "XSENDER")) + fCapabilityFlag |= kHasXSenderCapability; + else if (! XP_STRCASECMP(fNextToken, "IMAP4")) + fCapabilityFlag |= kIMAP4Capability; + else if (! XP_STRCASECMP(fNextToken, "IMAP4rev1")) + fCapabilityFlag |= kIMAP4rev1Capability; + else if (! XP_STRNCASECMP(fNextToken, "IMAP4", 5)) + fCapabilityFlag |= kIMAP4other; + else if (! XP_STRCASECMP(fNextToken, "X-NO-ATOMIC-RENAME")) + fCapabilityFlag |= kNoHierarchyRename; + else if (! XP_STRCASECMP(fNextToken, "X-NON-HIERARCHICAL-RENAME")) + fCapabilityFlag |= kNoHierarchyRename; + else if (! XP_STRCASECMP(fNextToken, "NAMESPACE")) + fCapabilityFlag |= kNamespaceCapability; + else if (! XP_STRCASECMP(fNextToken, "MAILBOXDATA")) + fCapabilityFlag |= kMailboxDataCapability; + else if (! XP_STRCASECMP(fNextToken, "ACL")) + fCapabilityFlag |= kACLCapability; + else if (! XP_STRCASECMP(fNextToken, "XSERVERINFO")) + fCapabilityFlag |= kXServerInfoCapability; + } + } while (fNextToken && + !at_end_of_line() && + ContinueParse()); + + TIMAPHostInfo::SetCapabilityForHost(fServerConnection.GetHostName(), fCapabilityFlag); + TNavigatorImapConnection *navCon = fServerConnection.GetNavigatorConnection(); + XP_ASSERT(navCon); // we should always have this + if (navCon) + navCon->CommitCapabilityForHostEvent(); + skip_to_CRLF(); +} + +void TImapServerState::xmailboxinfo_data() +{ + fNextToken = GetNextToken(); + if (!fNextToken) + return; + + char *mailboxName = CreateAstring(); // XP_STRDUP(fNextToken); + if (mailboxName) + { + do + { + fNextToken = GetNextToken(); + if (fNextToken) + { + if (!XP_STRCMP("MANAGEURL", fNextToken)) + { + fNextToken = GetNextToken(); + fFolderAdminUrl = CreateAstring(); + } + else if (!XP_STRCMP("POSTURL", fNextToken)) + { + fNextToken = GetNextToken(); + // ignore this for now... + } + } + } while (fNextToken && !at_end_of_line() && ContinueParse()); + } +} + +void TImapServerState::xserverinfo_data() +{ + do + { + fNextToken = GetNextToken(); + if (!fNextToken) + break; + if (!XP_STRCMP("MANAGEACCOUNTURL", fNextToken)) + { + fNextToken = GetNextToken(); + fMailAccountUrl = CreateAstring(); + } + else if (!XP_STRCMP("MANAGELISTSURL", fNextToken)) + { + fNextToken = GetNextToken(); + fManageListsUrl = CreateAstring(); + } + else if (!XP_STRCMP("MANAGEFILTERSURL", fNextToken)) + { + fNextToken = GetNextToken(); + fManageFiltersUrl = CreateAstring(); + } + } while (fNextToken && !at_end_of_line() && ContinueParse()); +} + + +void TImapServerState::namespace_data() +{ + // flush the old one and create a new one, initialized with no namespaces + TIMAPHostInfo::ClearServerAdvertisedNamespacesForHost(fServerConnection.GetHostName()); + EIMAPNamespaceType namespaceType = kPersonalNamespace; + while ((namespaceType != kUnknownNamespace) && ContinueParse()) + { + fNextToken = GetNextToken(); + while (at_end_of_line() && ContinueParse()) + fNextToken = GetNextToken(); + if (!XP_STRCASECMP(fNextToken,"NIL")) + { + // No namespace for this type. + // Don't add anything to the Namespace object. + } + else if (fNextToken[0] == '(') + { + // There may be multiple namespaces of the same type. + // Go through each of them and add them to our Namespace object. + + fNextToken++; + while (fNextToken[0] == '(' && ContinueParse()) + { + // we have another namespace for this namespace type + fNextToken++; + if (fNextToken[0] != '"') + { + SetSyntaxError(TRUE); + } + else + { + char *namespacePrefix = CreateQuoted(FALSE); + + fNextToken = GetNextToken(); + char *quotedDelimiter = fNextToken; + char namespaceDelimiter = '\0'; + + if (quotedDelimiter[0] == '"') + { + quotedDelimiter++; + namespaceDelimiter = quotedDelimiter[0]; + } + else if (!XP_STRNCASECMP(quotedDelimiter, "NIL", 3)) + { + // NIL hierarchy delimiter. Leave namespace delimiter NULL. + } + else + { + // not quoted or NIL. + SetSyntaxError(TRUE); + } + if (ContinueParse()) + { + TIMAPNamespace *newNamespace = new TIMAPNamespace(namespaceType, namespacePrefix, namespaceDelimiter, FALSE); + if (newNamespace) + TIMAPHostInfo::AddNewNamespaceForHost(fServerConnection.GetHostName(), newNamespace); + + skip_to_close_paren(); // Ignore any extension data + + XP_Bool endOfThisNamespaceType = (fNextToken[0] == ')'); + if (!endOfThisNamespaceType && fNextToken[0] != '(') // no space between namespaces of the same type + { + SetSyntaxError(TRUE); + } + } + } + } + } + else + { + SetSyntaxError(TRUE); + } + switch (namespaceType) + { + case kPersonalNamespace: + namespaceType = kOtherUsersNamespace; + break; + case kOtherUsersNamespace: + namespaceType = kPublicNamespace; + break; + default: + namespaceType = kUnknownNamespace; + break; + } + } + if (ContinueParse()) + { + TNavigatorImapConnection *navCon = fServerConnection.GetNavigatorConnection(); + XP_ASSERT(navCon); // we should always have this + if (navCon) + navCon->CommitNamespacesForHostEvent(); + } + skip_to_CRLF(); +} + +void TImapServerState::myrights_data() +{ + fNextToken = GetNextToken(); + if (ContinueParse() && !at_end_of_line()) + { + char *mailboxName = CreateAstring(); // XP_STRDUP(fNextToken); + if (mailboxName) + { + fNextToken = GetNextToken(); + if (ContinueParse()) + { + char *myrights = CreateAstring(); // XP_STRDUP(fNextToken); + if (myrights) + { + TNavigatorImapConnection *navCon = fServerConnection.GetNavigatorConnection(); + XP_ASSERT(navCon); // we should always have this + if (navCon) + navCon->AddFolderRightsForUser(mailboxName, NULL /* means "me" */, myrights); + XP_FREE(myrights); + } + else + { + HandleMemoryFailure(); + } + if (ContinueParse()) + fNextToken = GetNextToken(); + } + XP_FREE(mailboxName); + } + else + { + HandleMemoryFailure(); + } + } + else + { + SetSyntaxError(TRUE); + } +} + +void TImapServerState::acl_data() +{ + fNextToken = GetNextToken(); + if (ContinueParse() && !at_end_of_line()) + { + char *mailboxName = CreateAstring(); // XP_STRDUP(fNextToken); + if (mailboxName && ContinueParse()) + { + fNextToken = GetNextToken(); + while (ContinueParse() && !at_end_of_line()) + { + char *userName = CreateAstring(); // XP_STRDUP(fNextToken); + if (userName && ContinueParse()) + { + fNextToken = GetNextToken(); + if (ContinueParse()) + { + char *rights = CreateAstring(); // XP_STRDUP(fNextToken); + if (rights) + { + TNavigatorImapConnection *navCon = fServerConnection.GetNavigatorConnection(); + XP_ASSERT(navCon); // we should always have this + if (navCon) + navCon->AddFolderRightsForUser(mailboxName, userName, rights); + XP_FREE(rights); + } + else + HandleMemoryFailure(); + + if (ContinueParse()) + fNextToken = GetNextToken(); + } + XP_FREE(userName); + } + else + HandleMemoryFailure(); + } + } + else + HandleMemoryFailure(); + } +} + + +void TImapServerState::mime_data() +{ + if (XP_STRSTR(fNextToken, "MIME")) + mime_header_data(); + else + mime_part_data(); +} + +// mime_header_data should not be streamed out; rather, it should be +// buffered in the TIMAPBodyShell. +// This is because we are still in the process of generating enough +// information from the server (such as the MIME header's size) so that +// we can construct the final output stream. +void TImapServerState::mime_header_data() +{ + char *partNumber = XP_STRDUP(fNextToken); + if (partNumber) + { + char *start = partNumber+5, *end = partNumber+5; // 5 == XP_STRLEN("BODY[") + while (ContinueParse() && end && *end != 'M' && *end != 'm') + { + end++; + } + if (end && (*end == 'M' || *end == 'm')) + { + *(end-1) = 0; + fNextToken = GetNextToken(); + char *mimeHeaderData = CreateAstring(); // is it really this simple? + fNextToken = GetNextToken(); + if (m_shell) + { + m_shell->AdoptMimeHeader(start, mimeHeaderData); + } + } + else + { + SetSyntaxError(TRUE); + } + XP_FREE(partNumber); // partNumber is not adopted by the body shell. + } + else + { + HandleMemoryFailure(); + } +} + +// Actual mime parts are filled in on demand (either from shell generation +// or from explicit user download), so we need to stream these out. +void TImapServerState::mime_part_data() +{ + char *checkOriginToken = XP_STRDUP(fNextToken); + if (checkOriginToken) + { + uint32 origin = 0; + XP_Bool originFound = FALSE; + char *whereStart = XP_STRCHR(checkOriginToken, '<'); + if (whereStart) + { + char *whereEnd = XP_STRCHR(whereStart, '>'); + if (whereEnd) + { + *whereEnd = 0; + whereStart++; + origin = atoint32(whereStart); + originFound = TRUE; + } + } + XP_FREE(checkOriginToken); + fNextToken = GetNextToken(); + msg_fetch_content(originFound, origin, MESSAGE_RFC822); // keep content type as message/rfc822, even though the + // MIME part might not be, because then libmime will + // still handle and decode it. + } + else + HandleMemoryFailure(); +} + +// FETCH BODYSTRUCTURE parser +// After exit, set fNextToken and fCurrentLine to the right things +void TImapServerState::bodystructure_data() +{ + fNextToken = GetNextToken(); + + // separate it out first + if (fNextToken && *fNextToken == '(') // It has to start with an open paren. + { + char *buf = CreateParenGroup(); + + if (ContinueParse()) + { + if (!buf) + HandleMemoryFailure(); + else + { + // Looks like we have what might be a valid BODYSTRUCTURE response. + // Try building the shell from it here. + TNavigatorImapConnection *navCon = fServerConnection.GetNavigatorConnection(); + XP_ASSERT(navCon); + m_shell = new TIMAPBodyShell(navCon, buf, CurrentResponseUID(), GetSelectedMailboxName()); + /* + if (m_shell) + { + if (!m_shell->GetIsValid()) + { + SetSyntaxError(TRUE); + } + } + */ + XP_FREE(buf); + } + } + } + else + SetSyntaxError(TRUE); +} + +XP_Bool TImapServerState::GetFillingInShell() +{ + return (m_shell != NULL); +} + +// Tells the server state parser to use a previously cached shell. +void TImapServerState::UseCachedShell(TIMAPBodyShell *cachedShell) +{ + // We shouldn't already have another shell we're dealing with. + if (m_shell && cachedShell) + { +// PR_LOG(IMAP, out, ("PARSER: Shell Collision")); + XP_ASSERT(FALSE); + } + m_shell = cachedShell; +} + + +void TImapServerState::ResetCapabilityFlag() +{ + TIMAPHostInfo::SetCapabilityForHost(fServerConnection.GetHostName(), kCapabilityUndefined); +} + +/* + literal ::= "{" number "}" CRLF *CHAR8 + ;; Number represents the number of CHAR8 octets +*/ +// returns TRUE if this is the last chunk and we should close the stream +XP_Bool TImapServerState::msg_fetch_literal(XP_Bool chunk, int32 origin) +{ + numberOfCharsInThisChunk = atoint32(fNextToken + 1); // might be the whole message + charsReadSoFar = 0; + static XP_Bool lastCRLFwasCRCRLF = FALSE; + + XP_Bool lastChunk = !chunk || (origin + numberOfCharsInThisChunk >= fTotalDownloadSize); + + TNavigatorImapConnection *navCon = fServerConnection.GetNavigatorConnection(); + if (navCon) + { + if (!lastCRLFwasCRCRLF && navCon->GetIOTunnellingEnabled() && (numberOfCharsInThisChunk > navCon->GetTunnellingThreshold())) + { + // One day maybe we'll make this smarter and know how to handle CR/LF boundaries across tunnels. + // For now, we won't, even though it might not be too hard, because it is very rare and will add + // some complexity. + charsReadSoFar = navCon->OpenTunnel(numberOfCharsInThisChunk); + } + } + + // If we opened a tunnel, finish everything off here. Otherwise, get everything here. + // (just like before) + + while (ContinueParse() && (charsReadSoFar < numberOfCharsInThisChunk)) + { + AdvanceToNextLine(); + if (ContinueParse()) + { + if (lastCRLFwasCRCRLF && (*fCurrentLine == CR)) + { + char *usableCurrentLine = 0; + StrAllocCopy(usableCurrentLine, fCurrentLine + 1); + FREEIF(fCurrentLine); + if (usableCurrentLine) + fCurrentLine = usableCurrentLine; + else + fCurrentLine = 0; + } + + if (ContinueParse()) + { + charsReadSoFar += XP_STRLEN(fCurrentLine); + if (!fDownloadingHeaders && fCurrentCommandIsSingleMessageFetch) + { + fServerConnection.ProgressEventFunction_UsingId(MK_IMAP_DOWNLOADING_MESSAGE); + if (fTotalDownloadSize > 0) + fServerConnection.PercentProgressUpdateEvent(0,(100*(charsReadSoFar + origin))/fTotalDownloadSize); + } + if (charsReadSoFar > numberOfCharsInThisChunk) + { // this is rare. If this msg ends in the middle of a line then only display the actual message. + char *displayEndOfLine = (fCurrentLine + XP_STRLEN(fCurrentLine) - (charsReadSoFar - numberOfCharsInThisChunk)); + char saveit = *displayEndOfLine; + *displayEndOfLine = 0; + fServerConnection.HandleMessageDownLoadLine(fCurrentLine, !lastChunk); + *displayEndOfLine = saveit; + lastCRLFwasCRCRLF = (*(displayEndOfLine - 1) == CR); + } + else + { + lastCRLFwasCRCRLF = (*(fCurrentLine + XP_STRLEN(fCurrentLine) - 1) == CR); + fServerConnection.HandleMessageDownLoadLine(fCurrentLine, !lastChunk && (charsReadSoFar == numberOfCharsInThisChunk)); + } + } + } + } + + // This would be a good thing to log. +// if (lastCRLFwasCRCRLF) +// PR_LOG(IMAP, out, ("PARSER: CR/LF fell on chunk boundary.")); + + if (ContinueParse()) + { + if (charsReadSoFar > numberOfCharsInThisChunk) + { + // move the lexical analyzer state to the end of this message because this message + // fetch ends in the middle of this line. + //fCurrentTokenPlaceHolder = fLineOfTokens + XP_STRLEN(fCurrentLine) - (charsReadSoFar - numberOfCharsInThisChunk); + AdvanceTokenizerStartingPoint(XP_STRLEN(fCurrentLine) - (charsReadSoFar - numberOfCharsInThisChunk)); + fNextToken = GetNextToken(); + } + else + { + skip_to_CRLF(); + fNextToken = GetNextToken(); + } + } + else + { + lastCRLFwasCRCRLF = FALSE; + } + return lastChunk; +} + +XP_Bool TImapServerState::CurrentFolderReadOnly() +{ + return fCurrentFolderReadOnly; +} + +int32 TImapServerState::NumberOfMessages() +{ + return fNumberOfExistingMessages; +} + +int32 TImapServerState::NumberOfRecentMessages() +{ + return fNumberOfRecentMessages; +} + +int32 TImapServerState::NumberOfUnseenMessages() +{ + return fNumberOfUnseenMessages; +} + +int32 TImapServerState::FolderUID() +{ + return fFolderUIDValidity; +} + +uint32 TImapServerState::CurrentResponseUID() +{ + return fCurrentResponseUID; +} + +uint32 TImapServerState::HighestRecordedUID() +{ + return fHighestRecordedUID; +} + + +XP_Bool TImapServerState::IsNumericString(const char *string) +{ + int i; + for(i = 0; i < (int) strlen(string); i++) + { + if (! isdigit(string[i])) + { + return FALSE; + } + } + + return TRUE; +} + +struct mailbox_spec *TImapServerState::CreateCurrentMailboxSpec(const char *mailboxName /* = NULL */) +{ + mailbox_spec *returnSpec = (mailbox_spec *) XP_CALLOC(1, sizeof(mailbox_spec) ); + if (returnSpec) + { + char *convertedMailboxName = NULL; + const char *mailboxNameToConvert = (mailboxName) ? mailboxName : fSelectedMailboxName; + if (mailboxNameToConvert) + { + char *convertedName = fServerConnection.CreateUtf7ConvertedString(mailboxNameToConvert, FALSE); + if (convertedName) + { + convertedMailboxName = fServerConnection.GetNavigatorConnection()->GetCurrentUrl()->AllocateCanonicalPath(convertedName); + XP_FREE(convertedName); + } + } + + returnSpec->folderSelected = TRUE; + returnSpec->folder_UIDVALIDITY = fFolderUIDValidity; + returnSpec->number_of_messages = fNumberOfExistingMessages; + returnSpec->number_of_unseen_messages = fNumberOfUnseenMessages; + returnSpec->number_of_recent_messages = fNumberOfRecentMessages; + + returnSpec->box_flags = kNoFlags; // stub + returnSpec->onlineVerified = FALSE; // we're fabricating this. The flags aren't verified. + returnSpec->allocatedPathName = convertedMailboxName; + returnSpec->connection = fServerConnection.GetNavigatorConnection(); + if (returnSpec->connection) + returnSpec->hostName = returnSpec->connection->GetCurrentUrl()->GetUrlHost(); + else + returnSpec->hostName = NULL; + if (fFlagState) + returnSpec->flagState = fFlagState; //copies flag state + else + returnSpec->flagState = NULL; + } + else + HandleMemoryFailure(); + + return returnSpec; + +} + +// zero stops a list recording of flags and causes the flags for +// each individual message to be sent back to libmsg +void TImapServerState::ResetFlagInfo(int numberOfInterestingMessages) +{ + if (fFlagState) + fFlagState->Reset(numberOfInterestingMessages); +} + + +XP_Bool TImapServerState::GetLastFetchChunkReceived() +{ + return fLastChunk; +} + +void TImapServerState::ClearLastFetchChunkReceived() +{ + fLastChunk = FALSE; +} + + + +//////////////////// TIMAPNamespace ///////////////////////////////////////////////////////////// + + + +TIMAPNamespace::TIMAPNamespace(EIMAPNamespaceType type, const char *prefix, char delimiter, XP_Bool from_prefs) +{ + m_namespaceType = type; + m_prefix = XP_STRDUP(prefix); + m_fromPrefs = from_prefs; + + m_delimiter = delimiter; + m_delimiterFilledIn = !m_fromPrefs; // if it's from the prefs, we can't be sure about the delimiter until we list it. +} + +TIMAPNamespace::~TIMAPNamespace() +{ + FREEIF(m_prefix); +} + +void TIMAPNamespace::SetDelimiter(char delimiter) +{ + m_delimiter = delimiter; + m_delimiterFilledIn = TRUE; +} + +// returns -1 if this box is not part of this namespace, +// or the length of the prefix if it is part of this namespace +int TIMAPNamespace::MailboxMatchesNamespace(const char *boxname) +{ + if (!boxname) return -1; + + // If the namespace is part of the boxname + if (XP_STRSTR(boxname, m_prefix) == boxname) + return XP_STRLEN(m_prefix); + + // If the boxname is part of the prefix + // (Used for matching Personal mailbox with Personal/ namespace, etc.) + if (XP_STRSTR(m_prefix, boxname) == m_prefix) + return XP_STRLEN(boxname); + return -1; +} + + +TIMAPNamespaceList *TIMAPNamespaceList::CreateTIMAPNamespaceList() +{ + TIMAPNamespaceList *rv = new TIMAPNamespaceList(); + if (rv) + { + if (!rv->m_NamespaceList) + { + delete rv; + rv = 0; + } + } + return rv; +} + +TIMAPNamespaceList::TIMAPNamespaceList() +{ + // Create a new list + m_NamespaceList = XP_ListNew(); +} + +int TIMAPNamespaceList::GetNumberOfNamespaces() +{ + return m_NamespaceList ? XP_ListCount(m_NamespaceList) : 0; +} + +int TIMAPNamespaceList::GetNumberOfNamespaces(EIMAPNamespaceType type) +{ + int nodeIndex = 0, count = 0; + for (nodeIndex=XP_ListCount(m_NamespaceList); nodeIndex > 0; nodeIndex--) + { + TIMAPNamespace *nspace = (TIMAPNamespace *)XP_ListGetObjectNum(m_NamespaceList, nodeIndex); + if (nspace->GetType() == type) + { + count++; + } + } + return count; +} + +int TIMAPNamespaceList::AddNewNamespace(TIMAPNamespace *ns) +{ + if (!m_NamespaceList) + return -1; + + // If the namespace is from the NAMESPACE response, then we should see if there + // are any namespaces previously set by the preferences, or the default namespace. If so, remove these. + + if (!ns->GetIsNamespaceFromPrefs()) + { + int nodeIndex = 0; + for (nodeIndex=XP_ListCount(m_NamespaceList); nodeIndex > 0; nodeIndex--) + { + TIMAPNamespace *nspace = (TIMAPNamespace *)XP_ListGetObjectNum(m_NamespaceList, nodeIndex); + if (nspace->GetIsNamespaceFromPrefs()) + { + XP_ListRemoveObject(m_NamespaceList, nspace); + delete nspace; + } + } + } + + // Add the new namespace to the list. This must come after the removing code, + // or else we could never add the initial kDefaultNamespace type to the list. + XP_ListAddObjectToEnd(m_NamespaceList, ns); + + return 0; +} + + +// chrisf - later, fix this to know the real concept of "default" namespace of a given type +TIMAPNamespace *TIMAPNamespaceList::GetDefaultNamespaceOfType(EIMAPNamespaceType type) +{ + TIMAPNamespace *rv = 0, *firstOfType = 0; + if (!m_NamespaceList) + return 0; + + int nodeIndex = 1, count = XP_ListCount(m_NamespaceList); + for (nodeIndex= 1; nodeIndex <= count && !rv; nodeIndex++) + { + TIMAPNamespace *ns = (TIMAPNamespace *)XP_ListGetObjectNum(m_NamespaceList, nodeIndex); + if (ns->GetType() == type) + { + if (!firstOfType) + firstOfType = ns; + if (!(*(ns->GetPrefix()))) + { + // This namespace's prefix is "" + // Therefore it is the default + rv = ns; + } + } + } + if (!rv) + rv = firstOfType; + return rv; +} + +TIMAPNamespaceList::~TIMAPNamespaceList() +{ + ClearNamespaces(TRUE, TRUE); + if (m_NamespaceList) + XP_ListDestroy (m_NamespaceList); +} + +// ClearNamespaces removes and deletes the namespaces specified, and if there are no namespaces left, +void TIMAPNamespaceList::ClearNamespaces(XP_Bool deleteFromPrefsNamespaces, XP_Bool deleteServerAdvertisedNamespaces) +{ + int nodeIndex = 0; + + if (m_NamespaceList) + { + for (nodeIndex=XP_ListCount(m_NamespaceList); nodeIndex > 0; nodeIndex--) + { + TIMAPNamespace *ns = (TIMAPNamespace *) XP_ListGetObjectNum(m_NamespaceList, nodeIndex); + if (ns->GetIsNamespaceFromPrefs()) + { + if (deleteFromPrefsNamespaces) + { + XP_ListRemoveObject(m_NamespaceList, ns); + delete ns; + } + } + else if (deleteServerAdvertisedNamespaces) + { + XP_ListRemoveObject(m_NamespaceList, ns); + delete ns; + } + } + } +} + +TIMAPNamespace *TIMAPNamespaceList::GetNamespaceNumber(int nodeIndex) +{ + int total = GetNumberOfNamespaces(); + XP_ASSERT(nodeIndex >= 1 && nodeIndex <= total); + if (nodeIndex < 1) nodeIndex = 1; + if (nodeIndex > total) nodeIndex = total; + + XP_ASSERT(m_NamespaceList); + if (m_NamespaceList) + { + return (TIMAPNamespace *)XP_ListGetObjectNum(m_NamespaceList, nodeIndex); + } + + return NULL; +} + +TIMAPNamespace *TIMAPNamespaceList::GetNamespaceNumber(int nodeIndex, EIMAPNamespaceType type) +{ + int nodeCount = 0, count = 0; + for (nodeCount=XP_ListCount(m_NamespaceList); nodeCount > 0; nodeCount--) + { + TIMAPNamespace *nspace = (TIMAPNamespace *)XP_ListGetObjectNum(m_NamespaceList, nodeCount); + if (nspace->GetType() == type) + { + count++; + if (count == nodeIndex) + return nspace; + } + } + return NULL; +} + +TIMAPNamespace *TIMAPNamespaceList::GetNamespaceForMailbox(const char *boxname) +{ + // We want to find the LONGEST substring that matches the beginning of this mailbox's path. + // This accounts for nested namespaces (i.e. "Public/" and "Public/Users/") + + // Also, we want to match the namespace's mailbox to that namespace also: + // The Personal box will match the Personal/ namespace, etc. + + // these lists shouldn't be too long (99% chance there won't be more than 3 or 4) + // so just do a linear search + + int lengthMatched = -1; + int currentMatchedLength = -1; + TIMAPNamespace *rv = NULL; + int nodeIndex = 0; + + if (!XP_STRCASECMP(boxname, "INBOX")) + return GetDefaultNamespaceOfType(kPersonalNamespace); + + if (m_NamespaceList) + { + for (nodeIndex=XP_ListCount(m_NamespaceList); nodeIndex > 0; nodeIndex--) + { + TIMAPNamespace *nspace = (TIMAPNamespace *)XP_ListGetObjectNum(m_NamespaceList, nodeIndex); + currentMatchedLength = nspace->MailboxMatchesNamespace(boxname); + if (currentMatchedLength > lengthMatched) + { + rv = nspace; + lengthMatched = currentMatchedLength; + } + } + } + + return rv; +} + + diff --git a/mozilla/network/protocol/imap4/makefile.win b/mozilla/network/protocol/imap4/makefile.win index e69de29bb2d..080532b58bc 100644 --- a/mozilla/network/protocol/imap4/makefile.win +++ b/mozilla/network/protocol/imap4/makefile.win @@ -0,0 +1,100 @@ +#!gmake +# +# The contents of this file are subject to the Netscape Public License +# Version 1.0 (the "NPL"); you may not use this file except in +# compliance with the NPL. You may obtain a copy of the NPL at +# http://www.mozilla.org/NPL/ +# +# Software distributed under the NPL is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL +# for the specific language governing rights and limitations under the +# NPL. +# +# The Initial Developer of this code under the NPL is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All Rights +# Reserved. + +IGNORE_MANIFEST=1 +# + +#------------------------------------------------------------------------ +# +# Makefile to build the Network file protocol LIB +# +#------------------------------------------------------------------------ + + +# +# Make sure we have MOZILLA_CLIENT defined so we get the +# proper JS includes +# +LCFLAGS = $(LCFLAGS) -DMOZILLA_CLIENT + +!ifdef BUILD_DEBUG_GC +LCFLAGS = $(LCFLAGS) -DDEBUG_GC +!endif + +LLIBS= \ + $(NULL) +MISCDEP=$(LLIBS) +OBJS= \ + .\$(OBJDIR)\imap4url.obj \ + .\$(OBJDIR)\imapbody.obj \ + .\$(OBJDIR)\imapearl.obj \ + .\$(OBJDIR)\imaphier.obj \ + .\$(OBJDIR)\imappars.obj \ + .\$(OBJDIR)\mkimap4.obj \ + $(NULL) + +CSRCS = \ + imap4url.c \ + $(NULL) + +CPPSRCS = \ + imapbody.cpp \ + imapearl.cpp \ + imaphier.cpp \ + imappars.cpp \ + mkimap4.cpp \ + $(NULL) + + +LIBRARY_NAME=imap4url +MODULE=imap4url +DEPTH=..\..\.. + +LOCAL_INCLUDES=-I. -I$(DEPTH)/dist/public/zlib -I$(DEPTH)/dist/public/parse +INCLUDES = $(LOCAL_INCLUDES) + + +EXTRA_LIBS= + +REQUIRES= ldap network nspr2 security +EXPORTS= imap4pvt.h imap4url.h imapbody.h imaphier.h mkimap4.h + +# use LINCS on win32 for now since REQUIRES seems to be broken +#!if "$(MOZ_BITS)" != "16" +LINCS= \ + -I$(PUBLIC)\nspr2 \ + -I$(PUBLIC)\util \ + -I$(PUBLIC)\java \ + -I$(PUBLIC)\pref \ + -I$(PUBLIC)\htmldlgs \ + -I$(PUBLIC)\js \ + -I$(PUBLIC)\security \ + -I$(PUBLIC)\netcache \ + -I$(PUBLIC)\network \ + -I$(DEPTH)\lib\libmsg \ + -I$(PUBLIC)\mimetype \ + -I$(PUBLIC)\ldap \ +#!endif + +include <$(DEPTH)\config\rules.mak> + +libs:: $(LIBRARY) + $(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib + +symbols:: + @echo "LIBRARY_NAME is $(LIBRARY_NAME)" + @echo "LIBRARY is $(LIBRARY)" diff --git a/mozilla/network/protocol/imap4/mkimap4.cpp b/mozilla/network/protocol/imap4/mkimap4.cpp index e69de29bb2d..66e21fbf004 100644 --- a/mozilla/network/protocol/imap4/mkimap4.cpp +++ b/mozilla/network/protocol/imap4/mkimap4.cpp @@ -0,0 +1,10505 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#define FORCE_PR_LOG /* Allow logging in the release build */ + +#include "rosetta.h" +#include "mkutils.h" + +#include "imap4pvt.h" +#include "imap.h" +#include "imapbody.h" +#include "msgcom.h" +#include "msgnet.h" +#include "xpgetstr.h" +#include "secnav.h" +#include HG26363 +#include "sslerr.h" +#include "prefapi.h" +#include "prlog.h" +#include "libi18n.h" +#include "prtime.h" +#include "netutils.h" +#include "nslocks.h" + +#ifdef NSPR20 +#ifdef XP_MAC +#include "prpriv.h" /* for NewNamedMonitor */ +#else +#include "private/prpriv.h" +#endif +#endif /* NSPR20 */ + +#define MAX_NEW_IMAP_FOLDER_COUNT 50 +#define IMAP_DB_HEADERS "From To Cc Subject Date Priority X-Priority Message-ID References Newsgroups" + +#ifndef NSPR20 +#define IMAP_YIELD(A) PR_Yield() +#else +#define IMAP_YIELD(A) PR_Sleep(PR_INTERVAL_NO_WAIT) +#endif + +extern "C" +{ +extern int MK_OUT_OF_MEMORY; + +/* 45678901234567890123456789012345678901234567890123456789012345678901234567890 +*/ + +extern int XP_ERRNO_EWOULDBLOCK; +extern int XP_ERRNO_ENOTCONN; +extern int MK_TCP_READ_ERROR; +extern int MK_TCP_WRITE_ERROR; +extern int MK_POP3_NO_MESSAGES; +extern int MK_BAD_CONNECT; +extern int XP_FOLDER_RECEIVING_MESSAGE_OF; +extern int XP_RECEIVING_MESSAGE_HEADERS_OF; +extern int XP_RECEIVING_MESSAGE_FLAGS_OF; +extern int XP_IMAP_DELETING_MESSAGES; +extern int XP_IMAP_DELETING_MESSAGE; +extern int XP_IMAP_COPYING_MESSAGES_TO; +extern int XP_IMAP_COPYING_MESSAGE_TO; +extern int XP_IMAP_MOVING_MESSAGES_TO; +extern int XP_IMAP_MOVING_MESSAGE_TO; +extern int MK_MSG_IMAP_SERVER_NOT_IMAP4; +extern int MK_MSG_IMAP_SERVER_SAID; +extern int MK_MSG_TRASH_L10N_NAME; +extern int MK_IMAP_STATUS_CREATING_MAILBOX; +extern int MK_IMAP_STATUS_SELECTING_MAILBOX; +extern int MK_IMAP_STATUS_DELETING_MAILBOX; +extern int MK_IMAP_STATUS_RENAMING_MAILBOX; +extern int MK_IMAP_STATUS_LOOKING_FOR_MAILBOX; +extern int MK_IMAP_STATUS_SUBSCRIBE_TO_MAILBOX; +extern int MK_IMAP_STATUS_UNSUBSCRIBE_MAILBOX; +extern int MK_IMAP_STATUS_SEARCH_MAILBOX; +extern int MK_IMAP_STATUS_MSG_INFO; +extern int MK_IMAP_STATUS_CLOSE_MAILBOX; +extern int MK_IMAP_STATUS_EXPUNGING_MAILBOX; +extern int MK_IMAP_STATUS_LOGGING_OUT; +extern int MK_IMAP_STATUS_CHECK_COMPAT; +extern int MK_IMAP_STATUS_SENDING_LOGIN; +extern int MK_IMAP_STATUS_SENDING_AUTH_LOGIN; +extern int MK_IMAP_CREATE_FOLDER_BUT_NO_SUBSCRIBE; +extern int MK_IMAP_DELETE_FOLDER_BUT_NO_UNSUBSCRIBE; +extern int MK_IMAP_RENAME_FOLDER_BUT_NO_SUBSCRIBE; +extern int MK_IMAP_RENAME_FOLDER_BUT_NO_UNSUBSCRIBE; +extern int MK_IMAP_STATUS_GETTING_NAMESPACE; +extern int MK_IMAP_UPGRADE_NO_PERSONAL_NAMESPACE; +extern int MK_IMAP_UPGRADE_TOO_MANY_FOLDERS; +extern int MK_MSG_IMAP_DISCOVERING_MAILBOX; +extern int MK_IMAP_UPGRADE_PROMPT_USER; +extern int MK_IMAP_UPGRADE_PROMPT_USER_2; +extern int MK_IMAP_UPGRADE_WAIT_WHILE_UPGRADE; +extern int MK_IMAP_UPGRADE_PROMPT_QUESTION; +extern int MK_IMAP_UPGRADE_CUSTOM; +extern int MK_IMAP_UPGRADE_SUCCESSFUL; +extern int MK_MSG_DRAFTS_L10N_NAME; +extern int MK_IMAP_GETTING_ACL_FOR_FOLDER; +extern int MK_IMAP_GETTING_SERVER_INFO; +extern int MK_IMAP_GETTING_MAILBOX_INFO; +extern void NET_SetPopPassword2(const char *password); +extern int XP_PROMPT_ENTER_PASSWORD; +extern int XP_PASSWORD_FOR_POP3_USER; +extern int XP_MSG_IMAP_LOGIN_FAILED; + +extern void net_graceful_shutdown(PRFileDesc* sock, XP_Bool isSecure); +} + +#ifdef NSPR20 +extern PRLogModuleInfo* IMAP; +#define out PR_LOG_ALWAYS +#endif + +static int32 gMIMEOnDemandThreshold = 15000; +static XP_Bool gMIMEOnDemand = FALSE; +static XP_Bool gOptimizedHeaders = FALSE; + +static int32 gTunnellingThreshold = 2000; +static XP_Bool gIOTunnelling = FALSE; // off for now + +typedef struct _ImapConData { + TNavigatorImapConnection *netConnection; + + + void *offLineRetrievalData; // really a DisplayOfflineImapState object + + uint32 offLineMsgFlags; + uint32 offLineMsgKey; + + NET_StreamClass *offlineDisplayStream; +} ImapConData; + +typedef struct _GenericInfo { + char *c, *hostName; +} GenericInfo; + +typedef struct _StreamInfo { + uint32 size; + char *content_type; +} StreamInfo; + +typedef struct _ProgressInfo { + char *message; + int percent; +} ProgressInfo; + +typedef struct _FolderQueryInfo { + char *name, *hostName; + XP_Bool rv; +} FolderQueryInfo; + + +typedef struct _StatusMessageInfo { + uint32 msgID; + char * extraInfo; +} StatusMessageInfo; + +typedef struct _UploadMessageInfo { + uint32 newMsgID; + XP_File fileId; + char *dataBuffer; + int32 bytesRemain; +} UploadMessageInfo; + +struct utf_name_struct { + XP_Bool toUtf7Imap; + unsigned char *sourceString; + unsigned char *convertedString; +}; + + +typedef struct _TunnelInfo { + int32 maxSize; + PRFileDesc* ioSocket; + char** inputSocketBuffer; + int32* inputBufferSize; +} TunnelInfo; + +const char *ImapTRASH_FOLDER_NAME = NULL; + +const int32 kImapSleepTime = 1000000; + +int32 atoint32(char *ascii) +{ + char *endptr; + int32 rvalue = XP_STRTOUL(ascii, &endptr, 10); + return rvalue; +} + +XP_Bool IMAP_ContextIsBiff(ActiveEntry *ce) +{ + return (ce->window_id == MSG_GetBiffContext() || ce->window_id->type == MWContextBiff); +} + +XP_Bool IMAP_URLIsBiff(ActiveEntry *ce, TIMAPUrl ¤tUrl) +{ + return (IMAP_ContextIsBiff(ce) && currentUrl.GetIMAPurlType() == TIMAPUrl::kSelectFolder); +} + +static void +IMAP_LoadTrashFolderName(void) +{ + if (!ImapTRASH_FOLDER_NAME) + ImapTRASH_FOLDER_NAME = MSG_GetSpecialFolderName(MK_MSG_TRASH_L10N_NAME); +} + +void IMAP_DoNotDownLoadAnyMessageHeadersForMailboxSelect(TNavigatorImapConnection *connection) +{ + if (connection) + connection->NotifyKeyList(NULL,0); +} + +void IMAP_DownLoadMessageBodieForMailboxSelect(TNavigatorImapConnection *connection, + uint32 *messageKeys, /* uint32* is adopted */ + uint32 numberOfKeys) +{ + connection->NotifyKeyList(messageKeys, numberOfKeys); +} + +void IMAP_BodyIdMonitor(TNavigatorImapConnection *connection, XP_Bool enter) +{ + connection->BodyIdMonitor(enter); +} + +void TNavigatorImapConnection::BodyIdMonitor(XP_Bool enter) +{ + if (enter) + PR_EnterMonitor(fWaitForBodyIdsMonitor); + else + PR_ExitMonitor(fWaitForBodyIdsMonitor); +} + +void IMAP_DownLoadMessagesForMailboxSelect(TNavigatorImapConnection *connection, + uint32 *messageKeys, /* uint32* is adopted */ + uint32 numberOfKeys) +{ + connection->NotifyKeyList(messageKeys, numberOfKeys); +} + +char *IMAP_GetCurrentConnectionUrl(TNavigatorImapConnection *connection) +{ + return connection->GetCurrentConnectionURL(); +} + +void IMAP_UploadAppendMessageSize(TNavigatorImapConnection *connection, uint32 msgSize, imapMessageFlagsType flags) +{ + connection->NotifyAppendSize(msgSize, flags); +} + +void IMAP_TerminateConnection (TNavigatorImapConnection *conn) +{ + conn->TellThreadToDie(); +} + +char *IMAP_CreateOnlineSourceFolderNameFromUrl(const char *url) +{ + TIMAPUrl urlObject(url, TRUE); + return urlObject.CreateCanonicalSourceFolderPathString(); +} + +void IMAP_FreeBoxSpec(mailbox_spec *victim) +{ + if (victim) + { + FREEIF(victim->allocatedPathName); + // delete victim->flagState; // not owned by us, leave it alone + XP_FREE(victim); + } +} + +XP_Bool IMAP_CheckNewMail(TNavigatorImapConnection *connection) +{ + return connection->CheckNewMail(); +} + +XP_Bool IMAP_NewMailDetected(TNavigatorImapConnection *connection) +{ + return connection->NewMailDetected(); +} + + +// These C functions implemented here are usually executed as TImapFEEvent's + +static +void SetBiffIndicator(void *biffStateVoid, void *blockingConnectionVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + MSG_BIFF_STATE biffState = (MSG_BIFF_STATE) (uint32) biffStateVoid; + MSG_SetBiffStateAndUpdateFE(biffState); + imapConnection->NotifyEventCompletionMonitor(); + +} + + +#ifndef XP_OS2 +static +#else +extern "OPTLINK" +#endif +void MessageUploadComplete(void *blockingConnectionVoid) // called when upload is complete +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + imapConnection->NotifyMessageUploadMonitor(); +} + +static +void UploadMessageEvent(void *blockingConnectionVoid, void *) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + BeginMessageUpload(imapConnection->GetActiveEntry()->window_id, + imapConnection->GetIOSocket(), + MessageUploadComplete, + imapConnection); +} + +static +void msgSetUserAuthenticated(void *blockingConnectionVoid, void *trueOrFalseVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + XP_Bool authenticated = (XP_Bool) (uint32) trueOrFalseVoid; + if (ce && ce->URL_s->msg_pane) + MSG_SetUserAuthenticated(MSG_GetMaster(ce->URL_s->msg_pane), + authenticated); +} + +static +void LiteSelectEvent(void *blockingConnectionVoid, void * /*unused*/) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + if (ce && ce->URL_s->msg_pane) + ReportLiteSelectUIDVALIDITY(ce->URL_s->msg_pane, + imapConnection->GetServerStateParser().FolderUID()); + imapConnection->NotifyEventCompletionMonitor(); +} + +static +void msgSetMailAccountURL(void *blockingConnectionVoid, void *hostName) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + if (ce && ce->URL_s->msg_pane) + MSG_SetHostMailAccountURL(MSG_GetMaster(ce->URL_s->msg_pane), (const char *) hostName, + imapConnection->GetServerStateParser().GetMailAccountUrl()); +} + +static void msgSetMailServerURLs(void *blockingConnectionVoid, void *hostName) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + ActiveEntry *ce = imapConnection->GetActiveEntry(); + if (ce && ce->URL_s->msg_pane) + { + MSG_SetHostMailAccountURL(MSG_GetMaster(ce->URL_s->msg_pane), (const char *) hostName, + imapConnection->GetServerStateParser().GetMailAccountUrl()); + MSG_SetHostManageListsURL(MSG_GetMaster(ce->URL_s->msg_pane), (const char *) hostName, + imapConnection->GetServerStateParser().GetManageListsUrl()); + MSG_SetHostManageFiltersURL(MSG_GetMaster(ce->URL_s->msg_pane), (const char *) hostName, + imapConnection->GetServerStateParser().GetManageFiltersUrl()); + } +} + +static void MOZ_THREADmsgSetFolderURL(void *blockingConnectionVoid, void *folderQueryInfo) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + ActiveEntry *ce = imapConnection->GetActiveEntry(); + if (ce && ce->window_id->mailMaster) + { + FolderQueryInfo *queryInfo = (FolderQueryInfo *) folderQueryInfo; + MSG_SetFolderAdminURL(ce->window_id->mailMaster, queryInfo->hostName, queryInfo->name, imapConnection->GetServerStateParser().GetManageFolderUrl()); + } +} + +struct tFlagsKeyStruct { + imapMessageFlagsType flags; + MessageKey key; +}; + +typedef struct tFlagsKeyStruct tFlagsKeyStruct; + +static +void NotifyMessageFlagsEvent( void *blockingConnectionVoid, void *flagsVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + struct tFlagsKeyStruct *flagAndKey = (struct tFlagsKeyStruct *) flagsVoid; + + MSG_RecordImapMessageFlags(imapConnection->GetActiveEntry()->window_id->imapURLPane, + flagAndKey->key, + flagAndKey->flags); + FREEIF( flagAndKey); +} + +struct delete_message_struct { + char *onlineFolderName; + XP_Bool deleteAllMsgs; + char *msgIdString; +}; + +static +void ConvertImapUtf7(void *utf_name_structVoid, + void *blockingConnectionVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + utf_name_struct *names = (utf_name_struct *) utf_name_structVoid; + + // NULL if bad conversion + names->convertedString = NULL; + +#ifdef XP_WIN16 + names->convertedString = (unsigned char *) XP_STRDUP((const char *) names->sourceString); +#else + int16 fromCsid = names->toUtf7Imap ? (INTL_DocToWinCharSetID(INTL_DefaultDocCharSetID(0)) & ~CS_AUTO): CS_IMAP4_UTF7; + int16 toCsid = !names->toUtf7Imap ? (INTL_DocToWinCharSetID(INTL_DefaultDocCharSetID(0)) & ~CS_AUTO): CS_IMAP4_UTF7; + + // convert from whatever to CS_UTF8 + unsigned char *utf8String = INTL_ConvertLineWithoutAutoDetect(fromCsid, CS_UTF8, names->sourceString, XP_STRLEN((const char *) names->sourceString)); + if (utf8String) + { + // convert from CS_UTF8 to whatever + names->convertedString = INTL_ConvertLineWithoutAutoDetect(CS_UTF8, toCsid, utf8String, XP_STRLEN((const char *) utf8String)); + XP_FREE(utf8String); + } + +#endif + if (imapConnection) + imapConnection->NotifyEventCompletionMonitor(); +} + +static +void NotifyMessageDeletedEvent( void *blockingConnectionVoid, void *delete_message_structVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + struct delete_message_struct *msgStruct = (struct delete_message_struct *) delete_message_structVoid; + + if (msgStruct->onlineFolderName) + MSG_ImapMsgsDeleted(imapConnection->GetActiveEntry()->window_id->imapURLPane, + msgStruct->onlineFolderName, imapConnection->GetHostName(), + msgStruct->deleteAllMsgs, + msgStruct->msgIdString); + + FREEIF( msgStruct->onlineFolderName ); + FREEIF( msgStruct->msgIdString); + FREEIF( msgStruct); +} + +static +void AddSearchResultEvent( void *blockingConnectionVoid, void *hitLineVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + MSG_AddImapSearchHit(imapConnection->GetActiveEntry()->window_id, + (const char *) hitLineVoid); + + imapConnection->NotifyEventCompletionMonitor(); +} + + +static +void HeaderFetchCompletedEvent(void *blockingConnectionVoid, void *) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + NotifyHeaderFetchCompleted(imapConnection->GetActiveEntry()->window_id,imapConnection); +} + +#ifdef XP_MAC +// stuff used to measure performance of down loads +// #define MACPERF 1 +#ifdef MACPERF +#include "Events.h" +UInt32 g_StartTimeOfDownLoad; +UInt32 g_TimeSpendInDownLoadFunction; +UInt32 g_NumberOfVisits; +#endif + +#endif + + +/* this function ensures that the msg dislay stream is set up so that + replying to a message causes the compose window to appear with the + correct header info filled in and quoting works. +*/ + +#ifndef XP_OS2 +static +#else +extern "OPTLINK" +#endif +char * imap_mail_generate_html_header_fn (const char* /*dest*/, void *closure, + MimeHeaders *headers) +{ + // This function is only called upon creation of a message pane. + // If this is true then ce->URL_s->msg_pane will be set by + // MSG_Pane::GetURL. + ActiveEntry *ce = (ActiveEntry *) closure; + MSG_Pane *messagepane = ce->URL_s->msg_pane; + + if (!messagepane && ce->window_id) + messagepane = ce->window_id->imapURLPane; + + if (messagepane && (MSG_MESSAGEPANE == MSG_GetPaneType(messagepane))) + MSG_ActivateReplyOptions (messagepane, headers); + return 0; +} + +/* this function generates the correct reference URLs for +IMAP messages */ + +#ifndef XP_OS2 +static +#else +extern "OPTLINK" +#endif +char * imap_mail_generate_reference_url_fn (const char *dest, void *closure, + MimeHeaders*) +{ + ActiveEntry *cur_entry = (ActiveEntry *) closure; + char *addr = cur_entry->URL_s->address; + char *search = (addr ? XP_STRRCHR (addr, '>') + 1 : 0); + char *id2; + char *new_dest = 0; + char *result = 0; + MessageKey key = MSG_MESSAGEKEYNONE; + char *keyStr = 0; + + if (!dest || !*dest) return 0; + id2 = XP_STRDUP (dest); + if (!id2) return 0; + if (id2[XP_STRLEN (id2)-1] == '>') + id2[XP_STRLEN (id2)-1] = 0; + + // convert the Message-ID to a Key + MSG_Pane *pane = cur_entry->window_id->imapURLPane; + if (pane) + { + char *idInDB = id2; + if (id2[0] == '<') + { + new_dest = NET_Escape (id2+1, URL_PATH); + idInDB = id2 + 1; + } + else + { + new_dest = NET_Escape (id2, URL_PATH); + } + + if (0 != MSG_GetKeyFromMessageId(pane, idInDB, &key) || key == MSG_MESSAGEKEYNONE) + goto CLEANUP; + + } + else + { + goto CLEANUP; + } + + + // now convert the message key to a string + keyStr = PR_smprintf ("%ld", (long) key); + + result = (char *) XP_ALLOC ((search ? search - addr : 0) + + (new_dest ? XP_STRLEN (new_dest) : 0) + + 40); + if (result && new_dest) + { + if (search) + { + XP_MEMCPY (result, addr, search - addr); + result[search - addr] = 0; + } + else if (addr) + XP_STRCPY (result, addr); + else + *result = 0; + XP_STRCAT (result, keyStr); + } +CLEANUP: + FREEIF (id2); + FREEIF (new_dest); + FREEIF (keyStr); + return result; +} + +MSG_Pane *IMAP_GetActiveEntryPane(ImapActiveEntry * imap_entry) +{ + ActiveEntry * ce = (ActiveEntry *) imap_entry; + MSG_Pane *returnPane = ce->URL_s->msg_pane; + + if (!returnPane) // can happen when libmime or layout reload a url + returnPane = MSG_FindPaneOfContext(ce->window_id, MSG_ANYPANE); + + return returnPane; +} + +NET_StreamClass *IMAP_CreateDisplayStream(ImapActiveEntry * imap_entry, XP_Bool clearCacheBit, uint32 size, const char *content_type) +{ + ActiveEntry * ce = (ActiveEntry *) imap_entry; + + if (clearCacheBit) + ce->format_out = CLEAR_CACHE_BIT(ce->format_out); // no need to use libnet cache + + if (ce->format_out == FO_PRESENT || ce->format_out == FO_CACHE_AND_PRESENT) + { + IMAP_InitializeImapFeData(ce); + } + + ce->URL_s->content_length = size; + StrAllocCopy(ce->URL_s->content_type, content_type); + NET_StreamClass *displayStream = NET_StreamBuilder(ce->format_out, ce->URL_s, ce->window_id); + + return displayStream; +} + + +int +IMAP_InitializeImapFeData (ImapActiveEntry * imap_entry) +{ + ActiveEntry * ce = (ActiveEntry *) imap_entry; + + MimeDisplayOptions *opt; + + { // make sure this is a valid imap url + TIMAPUrl imapurl(ce->URL_s->address, ce->URL_s->internal_url); + if (!imapurl.ValidIMAPUrl()) + { + XP_ASSERT(0); + return -1; + } + } + + if (ce->URL_s->fe_data) + { + XP_ASSERT(0); + return -1; + } + + opt = XP_NEW (MimeDisplayOptions); + if (!opt) return MK_OUT_OF_MEMORY; + XP_MEMSET (opt, 0, sizeof(*opt)); + + opt->generate_reference_url_fn = imap_mail_generate_reference_url_fn; + opt->generate_header_html_fn = imap_mail_generate_html_header_fn; + opt->html_closure = ce; + opt->missing_parts = ce->URL_s->content_modified; + + ce->URL_s->fe_data = opt; + return 0; +} + + +extern "C" void IMAP_PseudoInterruptFetch(MWContext *context) +{ + // OK, we're going to lie and stick the connection object in the context. + TNavigatorImapConnection *imapConnection = context->imapConnection; + + if (imapConnection) + { + imapConnection->PseudoInterrupt(TRUE); + } +} + + + +// This function creates an output stream used to present an RFC822 message or one of its parts +static +void SetupMsgWriteStream(void *blockingConnectionVoid, void *StreamInfoVoid) +{ +#ifdef MACPERF + g_StartTimeOfDownLoad = TickCount(); + g_TimeSpendInDownLoadFunction = 0; + g_NumberOfVisits = 0; +#endif + + PR_LOG(IMAP, out, ("STREAM: Begin Message Download Stream Event")); + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + StreamInfo *si = (StreamInfo *)StreamInfoVoid; + + NET_StreamClass *outputStream = NULL; + + TIMAPUrl currentUrl(ce->URL_s->address, ce->URL_s->internal_url); + if ((currentUrl.GetIMAPurlType() == TIMAPUrl::kOnlineToOfflineCopy) || + (currentUrl.GetIMAPurlType() == TIMAPUrl::kOnlineToOfflineMove)) + MSG_StartImapMessageToOfflineFolderDownload(ce->window_id); + + if (ce->window_id->currentIMAPfolder) + { + outputStream = CreateIMAPDownloadMessageStream(ce, si->size, si->content_type, ce->URL_s->content_modified); + } + else + { + if (ce->format_out == FO_PRESENT || ce->format_out == FO_CACHE_AND_PRESENT) + { + IMAP_InitializeImapFeData(ce); + } + + ce->URL_s->content_length = si->size; + StrAllocCopy(ce->URL_s->content_type, si->content_type); + outputStream = + NET_StreamBuilder(ce->format_out, ce->URL_s, ce->window_id); + } + + if (outputStream) + imapConnection->SetOutputStream(outputStream); + else + { + // this should be impossible but be paranoid + imapConnection->SetConnectionStatus(-1); + imapConnection->GetServerStateParser().SetConnected(FALSE); + } + + FREEIF(si->content_type); + XP_FREE(si); + + imapConnection->NotifyEventCompletionMonitor(); +} + + +// report the success of the recent online copy +static +void OnlineCopyReport(void *blockingConnectionVoid, + void *adoptedCopyStateVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + ImapOnlineCopyState *copyState = (ImapOnlineCopyState *) adoptedCopyStateVoid; + + ReportSuccessOfOnlineCopy(imapConnection->GetActiveEntry()->window_id, *copyState); + + FREEIF( copyState); +} + +static +void OnlineFolderDelete(void *blockingConnectionVoid, + void *adoptedmailboxNameVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + char *boxNameAdopted = (char *) adoptedmailboxNameVoid; + + ReportSuccessOfOnlineDelete(imapConnection->GetActiveEntry()->window_id, imapConnection->GetHostName(), boxNameAdopted); + + FREEIF( boxNameAdopted); +} + +static +void OnlineFolderCreateFailed(void *blockingConnectionVoid, + void *serverMessageVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + char *serverMessage = (char *) serverMessageVoid; + + ReportFailureOfOnlineCreate(imapConnection->GetActiveEntry()->window_id, serverMessage); + + FREEIF( serverMessage); +} + +static +void OnlineFolderRename(void *blockingConnectionVoid, + void *renameStructVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + folder_rename_struct *renameStruct = (folder_rename_struct *) renameStructVoid; + + ReportSuccessOfOnlineRename(imapConnection->GetActiveEntry()->window_id, renameStruct); + + FREEIF( renameStruct->fOldName); + FREEIF( renameStruct->fNewName); + FREEIF( renameStruct->fHostName); + FREEIF( renameStruct); +} + +static +void MailboxDiscoveryDoneEvent(void *blockingConnectionVoid, void * /*unused*/) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ReportMailboxDiscoveryDone(imapConnection->GetActiveEntry()->window_id, imapConnection->GetActiveEntry()->URL_s); +} + + +// This function tells mail master about a discovered imap mailbox +static +void PossibleIMAPmailbox(void *blockingConnectionVoid, + void *boxSpecVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + MSG_Master *master = NULL; + MWContext *context = imapConnection->GetActiveEntry()->window_id; + if (context) + master = context->mailMaster; + XP_Bool broadcastDiscovery = TRUE; + char *url = imapConnection->GetCurrentConnectionURL(); + if (url) + { + TIMAPUrl urlc(url, TRUE); + if (urlc.GetIMAPurlType() == TIMAPUrl::kCreateFolder) + broadcastDiscovery = FALSE; + XP_FREE(url); + } + + + imapConnection->SetMailboxDiscoveryStatus( + DiscoverIMAPMailbox((mailbox_spec *) boxSpecVoid, + master, + imapConnection->GetActiveEntry()->window_id, + broadcastDiscovery)); + + imapConnection->NotifyEventCompletionMonitor(); +} + +// tell the mail master about the newly selected mailbox +static +void UpdatedIMAPmailbox(void *blockingConnectionVoid, + void *boxSpecVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + UpdateIMAPMailboxInfo((mailbox_spec *) boxSpecVoid, + imapConnection->GetActiveEntry()->window_id); + +} + +// tell the mail master about the newly selected mailbox +static +void UpdatedIMAPmailboxStatus(void *blockingConnectionVoid, + void *boxSpecVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + UpdateIMAPMailboxStatus((mailbox_spec *) boxSpecVoid, + imapConnection->GetActiveEntry()->window_id); + +} + + +struct uid_validity_info { + char *canonical_boxname; + const char *hostName; + int32 returnValidity; +}; + +static +void GetStoredUIDValidity(void *blockingConnectionVoid, + void *ValidityVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + uid_validity_info *info = (uid_validity_info *) ValidityVoid; + + info->returnValidity = GetUIDValidityForSpecifiedImapFolder(info->hostName, info->canonical_boxname, + imapConnection->GetActiveEntry()->window_id); + imapConnection->NotifyEventCompletionMonitor(); +} + + +struct msg_line_info { + char *adoptedMessageLine; + uint32 uidOfMessage; +}; + + +// This function adds one line to current message presentation +static +void ParseAdoptedMsgLine(void *blockingConnectionVoid, + void *adoptedmsg_line_info_Void) +{ +#ifdef MACPERF + UInt32 startTicks = TickCount(); +#endif + + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + msg_line_info *downloadLineDontDelete = (msg_line_info *) adoptedmsg_line_info_Void; + NET_StreamClass *outputStream = imapConnection->GetOutputStream(); + + unsigned int streamBytesMax = (*outputStream->is_write_ready) (outputStream); + char *stringToPut = downloadLineDontDelete->adoptedMessageLine; + XP_Bool allocatedString = FALSE; + + if ( streamBytesMax < (XP_STRLEN(downloadLineDontDelete->adoptedMessageLine) + 2)) + { + // dup the streamBytesMax number of chars, then put the rest of the string back into event queue + if (streamBytesMax != 0) + { + stringToPut = (char *) XP_ALLOC(streamBytesMax + 1); // 1 for \0 + if (stringToPut) + { + XP_MEMCPY(stringToPut, downloadLineDontDelete->adoptedMessageLine, streamBytesMax); + *(stringToPut + streamBytesMax) = 0; + allocatedString = TRUE; + } + + // shift buffer bytes + XP_MEMMOVE(downloadLineDontDelete->adoptedMessageLine, + downloadLineDontDelete->adoptedMessageLine + streamBytesMax, + (XP_STRLEN(downloadLineDontDelete->adoptedMessageLine) + 1) - streamBytesMax); + } + + // the output stream can't handle this event yet! put it back on the + // queue + TImapFEEvent *parseLineEvent = + new TImapFEEvent(ParseAdoptedMsgLine, // function to call + blockingConnectionVoid, // access to current entry + adoptedmsg_line_info_Void, + FALSE); // line to display + if (parseLineEvent) + imapConnection->GetFEEventQueue(). + AdoptEventToBeginning(parseLineEvent); + else + imapConnection->HandleMemoryFailure(); + } + + + if (streamBytesMax != 0) + { + imapConnection->SetBytesMovedSoFarForProgress(imapConnection->GetBytesMovedSoFarForProgress() + + XP_STRLEN(stringToPut)); + + if (!imapConnection->GetPseudoInterrupted()) + { + int bytesPut = 0; + + if (ce->window_id->currentIMAPfolder) + { + // the definition of put_block in net.h defines + // the 3rd parameter as the length of the block, + // but since we are always sending a null terminated + // string, I am able to sent the uid instead + bytesPut = (*outputStream->put_block) (outputStream, + stringToPut, + downloadLineDontDelete->uidOfMessage); + } + else // this is a download for display + { + bytesPut = (*outputStream->put_block) (outputStream, + stringToPut, + XP_STRLEN(stringToPut)); + } + + if (bytesPut < 0) + { + imapConnection->SetConnectionStatus(-1); // something bad happened, stop this url + imapConnection->GetServerStateParser().SetConnected(FALSE); + } + } + } +#ifdef MACPERF + g_TimeSpendInDownLoadFunction += TickCount() - startTicks; + g_NumberOfVisits++; +#endif + + if (allocatedString) + XP_FREE(stringToPut); // event not done yet + else if (streamBytesMax != 0) + imapConnection->NotifyEventCompletionMonitor(); + +} + +// This function closes the message presentation stream +static +void NormalEndMsgWriteStream(void *blockingConnectionVoid, void *) +{ + PR_LOG(IMAP, out, ("STREAM: Normal End Message Download Stream Event")); + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + NET_StreamClass *outputStream = imapConnection->GetOutputStream(); + if (outputStream) + { + if (imapConnection->GetPseudoInterrupted()) + (*outputStream->abort)(outputStream, -1); + else + (*outputStream->complete) (outputStream); + outputStream->data_object = NULL; + } + + ActiveEntry *ce = imapConnection->GetActiveEntry(); + +#ifdef MACPERF + char perfMessage[500]; + sprintf(perfMessage, + (const char *) "Download time = %ld, mkimap4 time = %ld, for %ld visits", + TickCount() - g_StartTimeOfDownLoad, + g_TimeSpendInDownLoadFunction, g_NumberOfVisits); + + DebugStr((const unsigned char *) c2pstr(perfMessage)); +#endif + +} + +// This function aborts the message presentation stream +static +void AbortMsgWriteStream(void *blockingConnectionVoid, void *) +{ + PR_LOG(IMAP, out, ("STREAM: Abort Message Download Stream Event")); + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + NET_StreamClass *outputStream = imapConnection->GetOutputStream(); + if (outputStream && outputStream->data_object) + { + (*outputStream->abort) (outputStream, -1); + outputStream->data_object = NULL; + } + + ActiveEntry *ce = imapConnection->GetActiveEntry(); +#ifdef MACPERF + char perfMessage[500]; + sprintf(perfMessage, + (const char *) "Download time = %ld, mkimap4 time = %ld, for %ld visits", + TickCount() - g_StartTimeOfDownLoad, + g_TimeSpendInDownLoadFunction, g_NumberOfVisits); + + DebugStr((const unsigned char *) c2pstr(perfMessage)); +#endif +} + +// This function displays a modal alert dialog based on +// a XP_GetString of the passed id +static +void AlertEventFunction_UsingId(void *blockingConnectionVoid, + void *errorMessageIdVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + // if this is a biff context, + // don't put up the alert + if (IMAP_ContextIsBiff(ce)) + { + imapConnection->NotifyEventCompletionMonitor(); + return; + } + + uint32 errorId = (uint32) errorMessageIdVoid; + char *errorString = XP_GetString(errorId); + + if (errorString) +#ifdef XP_UNIX + FE_Alert_modal(ce->window_id, errorString); +#else + FE_Alert(ce->window_id, errorString); +#endif + + imapConnection->NotifyEventCompletionMonitor(); +} + +// This function displays a modal alert dialog +static +void AlertEventFunction(void *blockingConnectionVoid, + void *errorMessageVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + // if this is a biff context, + // don't put up the alert + if (IMAP_ContextIsBiff(ce)) + { + imapConnection->NotifyEventCompletionMonitor(); + return; + } + + FE_Alert(ce->window_id, (const char *) errorMessageVoid); + + imapConnection->NotifyEventCompletionMonitor(); +} + +// This function displays a modal alert dialog, appending the +// message to "The IMAP server responded: " +static +void AlertEventFunctionFromServer(void *blockingConnectionVoid, + void *serverSaidVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + // if this is a biff context, + // don't put up the alert + if (IMAP_ContextIsBiff(ce)) + { + imapConnection->NotifyEventCompletionMonitor(); + return; + } + + const char *serverSaid = (const char *) serverSaidVoid; + if (serverSaid) + { + char *whereRealMessage = XP_STRCHR(serverSaid, ' '); + if (whereRealMessage) + whereRealMessage++; + if (whereRealMessage) + whereRealMessage = XP_STRCHR(whereRealMessage, ' '); + if (whereRealMessage) + whereRealMessage++; + + char *message = PR_smprintf(XP_GetString(MK_MSG_IMAP_SERVER_SAID), whereRealMessage ? whereRealMessage : serverSaid); + + if (message) FE_Alert(ce->window_id, message); + FREEIF(message); + } + + imapConnection->NotifyEventCompletionMonitor(); +} + +// until preferences are worked out, save a copy of the password here +static char *gIMAPpassword=NULL; // gross +static XP_Bool preAuthSucceeded = FALSE; + +const char * IMAP_GetPassword() +{ + return gIMAPpassword; +} + +void IMAP_SetPassword(const char *password) +{ + FREEIF(gIMAPpassword); + if (password) + gIMAPpassword = XP_STRDUP(password); +} + +void IMAP_SetPasswordForHost(const char *host, const char *password) +{ + TIMAPHostInfo::SetPasswordForHost(host, password); +} + +void IMAP_ResetAnyCachedConnectionInfo() +{ + if (gIMAPpassword) + { + FREEIF(gIMAPpassword); + gIMAPpassword = NULL; + } + preAuthSucceeded = FALSE; + + TNavigatorImapConnection::ResetCachedConnectionInfo(); +} + +// This function displays a modal alert dialog +static +void GetPasswordEventFunction(void *blockingConnectionVoid, + void *userNameVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + +#if 1 + char* fmt = XP_GetString( XP_PASSWORD_FOR_POP3_USER ); + + if (fmt) + { + const char *host = imapConnection->GetHostName(); + size_t len = (XP_STRLEN(fmt) + + (host ? XP_STRLEN(host) : 0) + 300) + * sizeof(char); + char *prompt = (char*)XP_ALLOC(len); + if (prompt) + { + PR_snprintf(prompt, len, fmt, (char*)userNameVoid, host); + + const char* password = FE_PromptPassword(ce->window_id, prompt); + if (password != NULL) + TIMAPHostInfo::SetPasswordForHost(host, password); + XP_FREE(prompt); + } + } +#else + char *prompt = NULL; + char *promptText = XP_GetString(XP_PROMPT_ENTER_PASSWORD); + + if (promptText) + prompt = PR_smprintf(promptText, (char *) userNameVoid); + + if (prompt) + { + const char *password = FE_PromptPassword(ce->window_id, prompt); + if (password != NULL) + TIMAPHostInfo::SetPasswordForHost(imapConnection->GetHostName(), password); + XP_FREE(prompt); + } +#endif + imapConnection->NotifyEventCompletionMonitor(); +} + + +// This function alerts the FE that we are past the password check +// (The WinFE needs this for progress updates) +static +void PastPasswordCheckFunction(void *blockingConnectionVoid, + void* /*unused*/) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + if (ce->window_id->imapURLPane != NULL && + MSG_GetPaneType(ce->window_id->imapURLPane) != MSG_COMPOSITIONPANE) + FE_PaneChanged (ce->window_id->imapURLPane, FALSE, MSG_PanePastPasswordCheck, 0); + + imapConnection->NotifyEventCompletionMonitor(); +} + +// This function updates the progress message at the bottom of the +// window +static +void ProgressEventFunction(void *blockingConnectionVoid, + void *progressMessageVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + NET_Progress(ce->window_id, (char *)progressMessageVoid); + + imapConnection->NotifyEventCompletionMonitor(); +} + +// This function updates the progress message at the bottom of the +// window based on a XP_GetString of the passed id +static +void ProgressStatusFunction_UsingId(void *blockingConnectionVoid, + void *errorMessageIdVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + uint32 errorId = (uint32) errorMessageIdVoid; + char *errorString = XP_GetString(errorId); + + if (errorString) + NET_Progress(ce->window_id, errorString); + + imapConnection->NotifyEventCompletionMonitor(); +} + +// This function updates the progress message at the bottom of the +// window based on a XP_GetString of the passed id and formats +// it with the passed in string +static +void ProgressStatusFunction_UsingIdWithString(void *blockingConnectionVoid, + void *errorMessageStruct) +{ + char * progressString = NULL; + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + StatusMessageInfo * msgInfo = (StatusMessageInfo *) errorMessageStruct; + if (msgInfo) + progressString = PR_sprintf_append(progressString, XP_GetString(msgInfo->msgID), msgInfo->extraInfo); + + if (progressString) + NET_Progress(ce->window_id, progressString); + + FREEIF(msgInfo->extraInfo); + FREEIF(msgInfo); + FREEIF(progressString); + + imapConnection->NotifyEventCompletionMonitor(); +} + +// This function updates the progress message at the bottom of the +// window, along with setting the percent bar +static +void PercentProgressEventFunction(void *blockingConnectionVoid, + void *progressVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + ProgressInfo *progress = (ProgressInfo *)progressVoid; + + if (progress && progress->message) + NET_Progress(ce->window_id, progress->message); + + FE_SetProgressBarPercent(ce->window_id, progress->percent); + + FREEIF(progress->message); + FREEIF(progress); + + imapConnection->NotifyEventCompletionMonitor(); +} + + + + +// This function uses the existing NET code to establish +// a connection with the IMAP server. +static +void StartIMAPConnection(TNavigatorImapConnection *imapConnection) +{ + + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + FE_SetProgressBarPercent(ce->window_id, -1); + + HG83344 + + ce->status = NET_BeginConnect(ce->URL_s->address, + ce->URL_s->IPAddressString, + "IMAP4", + (HG26227 IMAP4_PORT), + &ce->socket, + HG27327, + imapConnection->GetTCPConData(), + ce->window_id, + &ce->URL_s->error_msg, + ce->socks_host, + ce->socks_port); + + imapConnection->SetIOSocket(ce->socket); + if(ce->socket != 0) + NET_TotalNumberOfOpenConnections++; + + if(ce->status == MK_CONNECTED) + { + imapConnection->SetNeedsGreeting(TRUE); + NET_SetReadSelect(ce->window_id, ce->socket); + } + else if(ce->status > -1) + { + ce->con_sock = ce->socket; /* set con sock so we can select on it */ + NET_SetConnectSelect(ce->window_id, ce->con_sock); + } + else if(ce->status < 0) + { + /* close and clear the socket here + * so that we don't try and send a RSET + */ + if(ce->socket != 0) + { + NET_TotalNumberOfOpenConnections--; + NET_ClearConnectSelect(ce->window_id, ce->socket); + TRACEMSG(("Closing and clearing socket ce->socket: %d", + ce->socket)); + net_graceful_shutdown(ce->socket, HG38373); + PR_Close(ce->socket); + ce->socket = NULL; + imapConnection->SetIOSocket(ce->socket); + } + } + + imapConnection->SetConnectionStatus(ce->status); +} + +// This function uses the existing NET code to establish +// a connection with the IMAP server. +static +void FinishIMAPConnection(void *blockingConnectionVoid, + void* /*unused*/) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + HG83344 + + ce->status = NET_FinishConnect(ce->URL_s->address, + "IMAP4", + (HG72524 IMAP4_PORT), + &ce->socket, + imapConnection->GetTCPConData(), + ce->window_id, + &ce->URL_s->error_msg); + + imapConnection->SetIOSocket(ce->socket); + if(ce->status == MK_CONNECTED) + { + NET_ClearConnectSelect(ce->window_id, ce->con_sock); + NET_SetReadSelect(ce->window_id, ce->socket); + } + else if(ce->status > -1) + { + + /* unregister the old CE_SOCK from the select list + * and register the new value in the case that it changes + */ + if(ce->con_sock != ce->socket) + { + NET_ClearConnectSelect(ce->window_id, ce->con_sock); + ce->con_sock = ce->socket; + NET_SetConnectSelect(ce->window_id, ce->con_sock); + } + } + else if(ce->status < 0) + { + /* close and clear the socket here + * so that we don't try and send a RSET + */ + NET_TotalNumberOfOpenConnections--; + NET_ClearConnectSelect(ce->window_id, ce->socket); + TRACEMSG(("Closing and clearing socket ce->socket: %d", ce->socket)); + net_graceful_shutdown(ce->socket, HG73654); + PR_Close(ce->socket); + ce->socket = NULL; + imapConnection->SetIOSocket(ce->socket); + } + + imapConnection->SetConnectionStatus(ce->status); + imapConnection->NotifyEventCompletionMonitor(); +} + + + +// member functions of the TImapFEEvent class +TImapFEEvent::TImapFEEvent(FEEventFunctionPointer *function, + void *arg1, void*arg2, + XP_Bool executeWhenInterrupted) : + fEventFunction(function), + fArg1(arg1), fArg2(arg2), + fExecuteWhenInterrupted(executeWhenInterrupted) +{ + +} + +TImapFEEvent::~TImapFEEvent() +{ +} + +void TImapFEEvent::DoEvent() +{ + (*fEventFunction) (fArg1, fArg2); +} + +// member functions of the TImapFEEventQueue class +TImapFEEventQueue::TImapFEEventQueue() +{ + fEventList = XP_ListNew(); + fListMonitor = PR_NewNamedMonitor("list-monitor"); + fNumberOfEvents = 0; +} + +TImapFEEventQueue *TImapFEEventQueue::CreateFEEventQueue() +{ + TImapFEEventQueue *returnQueue = new TImapFEEventQueue; + if (!returnQueue || + !returnQueue->fEventList || + !returnQueue->fListMonitor) + { + delete returnQueue; + returnQueue = nil; + } + + return returnQueue; +} + +TImapFEEventQueue::~TImapFEEventQueue() +{ + if (fEventList) + XP_ListDestroy(fEventList); + if (fListMonitor) + PR_DestroyMonitor(fListMonitor); +} + +void TImapFEEventQueue::AdoptEventToEnd(TImapFEEvent *event) +{ + PR_EnterMonitor(fListMonitor); + XP_ListAddObjectToEnd(fEventList, event); + fNumberOfEvents++; + PR_ExitMonitor(fListMonitor); +} + +void TImapFEEventQueue::AdoptEventToBeginning(TImapFEEvent *event) +{ + PR_EnterMonitor(fListMonitor); + XP_ListAddObject(fEventList, event); + fNumberOfEvents++; + PR_ExitMonitor(fListMonitor); +} + +TImapFEEvent* TImapFEEventQueue::OrphanFirstEvent() +{ + TImapFEEvent *returnEvent = nil; + PR_EnterMonitor(fListMonitor); + if (fNumberOfEvents) + { + fNumberOfEvents--; + if (fNumberOfEvents == 0) + PR_Notify(fListMonitor); + returnEvent = (TImapFEEvent *) XP_ListRemoveTopObject(fEventList); + } + PR_ExitMonitor(fListMonitor); + + return returnEvent; +} + +int TImapFEEventQueue::NumberOfEventsInQueue() +{ + int returnEvents; + PR_EnterMonitor(fListMonitor); + returnEvents = fNumberOfEvents; + PR_ExitMonitor(fListMonitor); + return returnEvents; +} + + +TIMAP4BlockingConnection::TIMAP4BlockingConnection() : + fConnectionStatus(0), // we want NET_ProcessIMAP4 to continue for now + fServerState(*this), + fBlockedForIO(FALSE), + fCloseNeededBeforeSelect(FALSE), + fCurrentServerCommandTagNumber(0) +{ + fDataMemberMonitor = PR_NewNamedMonitor("data-member-monitor"); + fIOMonitor = PR_NewNamedMonitor("io-monitor"); + + fDeleteModelIsMoveToTrash = FALSE; +} + + +XP_Bool TIMAP4BlockingConnection::ConstructionSuccess() +{ + return (fDataMemberMonitor && fIOMonitor); +} + + + + + + +TIMAP4BlockingConnection::~TIMAP4BlockingConnection() +{ + if (fDataMemberMonitor) + PR_DestroyMonitor(fDataMemberMonitor); + if (fIOMonitor) + PR_DestroyMonitor(fIOMonitor); +} + + +TNavigatorImapConnection *TIMAP4BlockingConnection::GetNavigatorConnection() +{ + return NULL; +} + + +void TIMAP4BlockingConnection::NotifyIOCompletionMonitor() +{ + PR_EnterMonitor(fIOMonitor); + fBlockedForIO = FALSE; + PR_Notify(fIOMonitor); + PR_ExitMonitor(fIOMonitor); +} + + +// escape any backslashes or quotes. Backslashes are used a lot with our NT server +char *TIMAP4BlockingConnection::CreateEscapedMailboxName(const char *rawName) +{ + int numberOfEscapes = 0; + char *escapedName = (char *) XP_ALLOC(XP_STRLEN(rawName) + 20); // enough for 20 deep + if (escapedName) + { + char *currentChar = escapedName; + XP_STRCPY(escapedName,rawName); + + while (*currentChar) + { + if ((*currentChar == '\\') || (*currentChar == '\"')) + { + XP_MEMMOVE(currentChar+1, // destination + currentChar, // source + XP_STRLEN(currentChar) + 1); // remaining string + nil + *currentChar++ = '\\'; + + if (++numberOfEscapes == 20) + { + char *oldString = escapedName; + escapedName = (char *) XP_ALLOC(XP_STRLEN(oldString) + 20); // enough for 20 deep + XP_STRCPY(escapedName,oldString); + currentChar = escapedName + (currentChar - oldString); + FREEIF( oldString); + numberOfEscapes = 0; + } + } + currentChar++; + } + } + + return escapedName; +} + + + +void TIMAP4BlockingConnection::IncrementCommandTagNumber() +{ + sprintf(fCurrentServerCommandTag,"%ld", (long) ++fCurrentServerCommandTagNumber); +} + +#ifdef KMCENTEE_DEBUG +int WaitForIOLoopCount; +int WaitForFEEventLoopCount; +#endif + +void TIMAP4BlockingConnection::WaitForIOCompletion() +{ +#ifdef NSPR20 + PRIntervalTime sleepTime = PR_MicrosecondsToInterval(kImapSleepTime); +#else + int64 sleepTime; + LL_I2L(sleepTime, kImapSleepTime); +#endif + + PR_EnterMonitor(fIOMonitor); + fBlockedForIO = TRUE; + +#ifdef KMCENTEE_DEBUG + WaitForIOLoopCount=0; +#endif + + while (fBlockedForIO && !DeathSignalReceived() ) + { + PR_Wait(fIOMonitor, sleepTime); +#ifdef KMCENTEE_DEBUG + WaitForIOLoopCount++; + if (WaitForIOLoopCount == 10) + XP_ASSERT(FALSE); +#endif + } + PR_ExitMonitor(fIOMonitor); +} + +XP_Bool TIMAP4BlockingConnection::BlockedForIO() +{ + XP_Bool returnValue; + + PR_EnterMonitor(fIOMonitor); + returnValue = fBlockedForIO; + PR_ExitMonitor(fIOMonitor); + + return returnValue; +} + + +TImapServerState &TIMAP4BlockingConnection::GetServerStateParser() +{ + return fServerState; +} + + +void TIMAP4BlockingConnection::SetConnectionStatus(int status) +{ + PR_EnterMonitor(GetDataMemberMonitor()); + fConnectionStatus = status; + PR_ExitMonitor(GetDataMemberMonitor()); +} + + +int TIMAP4BlockingConnection::GetConnectionStatus() +{ + int returnStatus; + PR_EnterMonitor(GetDataMemberMonitor()); + returnStatus = fConnectionStatus; + PR_ExitMonitor(GetDataMemberMonitor()); + return returnStatus; +} + + +extern "C" void MSG_IMAP_KillConnection(TNavigatorImapConnection *imapConnection) +{ + if (imapConnection) + { + imapConnection->DeathSignalReceived(); // stop anything going on and kill thread + //delete imapConnection; // destroy anything else, not mine to destroy. + } +} + + +void TNavigatorImapConnection::SelectMailbox(const char *mailboxName) +{ + TIMAP4BlockingConnection::SelectMailbox(mailboxName); + + int32 numOfMessagesInFlagState = fFlagState->GetNumberOfMessages(); + TIMAPUrl::EIMAPurlType urlType = fCurrentUrl->GetIMAPurlType(); + // if we've selected a mailbox, and we're not going to do an update because of the + // url type, but don't have the flags, go get them! + if (urlType != TIMAPUrl::kSelectFolder && urlType != TIMAPUrl::kExpungeFolder && urlType != TIMAPUrl::kLiteSelectFolder && + urlType != TIMAPUrl::kDeleteAllMsgs && + ((GetServerStateParser().NumberOfMessages() != numOfMessagesInFlagState) && (numOfMessagesInFlagState == 0))) + ProcessMailboxUpdate(FALSE); + +} + + // authenticated state commands +void TIMAP4BlockingConnection::SelectMailbox(const char *mailboxName) +{ + ProgressEventFunction_UsingId (MK_IMAP_STATUS_SELECTING_MAILBOX); + IncrementCommandTagNumber(); + + fCloseNeededBeforeSelect = FALSE; // initial value + GetServerStateParser().ResetFlagInfo(0); + char *escapedName = CreateEscapedMailboxName(mailboxName); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s select \"%s\"" CRLF, // format string + GetServerCommandTag(), // command tag + escapedName); + + FREEIF( escapedName); + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + ParseIMAPandCheckForNewMail(); +} + +void TIMAP4BlockingConnection::CreateMailbox(const char *mailboxName) +{ + ProgressEventFunction_UsingId (MK_IMAP_STATUS_CREATING_MAILBOX); + + IncrementCommandTagNumber(); + + char *escapedName = CreateEscapedMailboxName(mailboxName); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s create \"%s\"" CRLF, // format string + GetServerCommandTag(), // command tag + escapedName); + + FREEIF( escapedName); + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + +static void setup_message_flags_string(char *flagString, + imapMessageFlagsType flags, XP_Bool userDefined) +{ + XP_ASSERT(flagString); + flagString[0] = '\0'; + if (flags & kImapMsgSeenFlag) + XP_STRCAT(flagString, "\\Seen "); + if (flags & kImapMsgAnsweredFlag) + XP_STRCAT(flagString, "\\Answered "); + if (flags & kImapMsgFlaggedFlag) + XP_STRCAT(flagString, "\\Flagged "); + if (flags & kImapMsgDeletedFlag) + XP_STRCAT(flagString, "\\Deleted "); + if (flags & kImapMsgDraftFlag) + XP_STRCAT(flagString, "\\Draft "); + if (flags & kImapMsgRecentFlag) + XP_STRCAT(flagString, "\\Recent "); + if ((flags & kImapMsgForwardedFlag) && userDefined) + XP_STRCAT(flagString, "Forwarded "); // Not always available + if ((flags & kImapMsgMDNSentFlag) && userDefined) + XP_STRCAT(flagString, "MDNSent "); // Not always available + + // eat the last space + if (*flagString) + *(flagString + XP_STRLEN(flagString) - 1) = '\0'; +} + +void TIMAP4BlockingConnection::AppendMessage(const char *mailboxName, + const char *messageSizeString, + imapMessageFlagsType flags) +{ + //ProgressUpdateEvent("Appending a message..."); + IncrementCommandTagNumber(); + + char *escapedName = CreateEscapedMailboxName(mailboxName); + + // enough room for "\\Seen \\Answered \\Flagged \\Deleted \\Draft \\Recent \\Replied" + char flagString[90]; + + setup_message_flags_string(flagString, flags, SupportsUserDefinedFlags()); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s append \"%s\" (%s) {%s}" CRLF, // format string + GetServerCommandTag(), // command tag + escapedName, + flagString, + messageSizeString); + + FREEIF( escapedName); + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); + + if (GetServerStateParser().GetIMAPstate() == TImapServerState::kWaitingForMoreClientInput) + { + OnlineCopyCompleted(kReadyForAppendData); + UploadMessage(); + ParseIMAPandCheckForNewMail(); + if (GetServerStateParser().LastCommandSuccessful()) + OnlineCopyCompleted(kInProgress); + else + OnlineCopyCompleted(kFailedAppend); + } +} + + +void TIMAP4BlockingConnection::DeleteMailbox(const char *mailboxName) +{ + ProgressEventFunction_UsingIdWithString (MK_IMAP_STATUS_DELETING_MAILBOX, mailboxName); + + IncrementCommandTagNumber(); + + char *escapedName = CreateEscapedMailboxName(mailboxName); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s delete \"%s\"" CRLF, // format string + GetServerCommandTag(), // command tag + escapedName); + + FREEIF( escapedName); + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + +void TIMAP4BlockingConnection::RenameMailbox(const char *existingName, + const char *newName) +{ + ProgressEventFunction_UsingIdWithString (MK_IMAP_STATUS_RENAMING_MAILBOX, existingName); + + IncrementCommandTagNumber(); + + char *escapedExistingName = CreateEscapedMailboxName(existingName); + char *escapedNewName = CreateEscapedMailboxName(newName); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s rename \"%s\" \"%s\"" CRLF, // format string + GetServerCommandTag(), // command tag + escapedExistingName, + escapedNewName); + + FREEIF( escapedExistingName); + FREEIF( escapedNewName); + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + +int64 TIMAP4BlockingConnection::fgTimeStampOfNonPipelinedList = LL_ZERO; + + +void TIMAP4BlockingConnection::TimeStampListNow() +{ + fgTimeStampOfNonPipelinedList = PR_Now(); +} + +extern "C" { +int64 IMAP_GetTimeStampOfNonPipelinedList() +{ + return TIMAP4BlockingConnection::GetTimeStampOfNonPipelinedList(); +} +} + +void TIMAP4BlockingConnection::List(const char *mailboxPattern, XP_Bool pipeLined /* = FALSE */, XP_Bool dontTimeStamp /* = FALSE */) +{ + ProgressEventFunction_UsingId (MK_IMAP_STATUS_LOOKING_FOR_MAILBOX); + + IncrementCommandTagNumber(); + + char *escapedPattern = CreateEscapedMailboxName(mailboxPattern); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s list \"\" \"%s\"" CRLF, // format string + GetServerCommandTag(), // command tag + escapedPattern); + + FREEIF( escapedPattern); + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + if (pipeLined) + GetServerStateParser().IncrementNumberOfTaggedResponsesExpected(GetServerCommandTag()); + else + { + if (!dontTimeStamp) + TimeStampListNow(); + ParseIMAPandCheckForNewMail(); + } +} + +void TIMAP4BlockingConnection::Lsub(const char *mailboxPattern, + XP_Bool pipelined) +{ + ProgressEventFunction_UsingId (MK_IMAP_STATUS_LOOKING_FOR_MAILBOX); + + IncrementCommandTagNumber(); + + char *escapedPattern = CreateEscapedMailboxName(mailboxPattern); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s lsub \"\" \"%s\"" CRLF, // format string + GetServerCommandTag(), // command tag + escapedPattern); + + FREEIF( escapedPattern); + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + if (pipelined) + GetServerStateParser().IncrementNumberOfTaggedResponsesExpected(GetServerCommandTag()); + else + { + TimeStampListNow(); + ParseIMAPandCheckForNewMail(); + } +} + +void TIMAP4BlockingConnection::Subscribe(const char *mailboxName) +{ + ProgressEventFunction_UsingIdWithString (MK_IMAP_STATUS_SUBSCRIBE_TO_MAILBOX, mailboxName); + + IncrementCommandTagNumber(); + + char *escapedName = CreateEscapedMailboxName(mailboxName); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s subscribe \"%s\"" CRLF, // format string + GetServerCommandTag(), // command tag + escapedName); + + FREEIF( escapedName); + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + +void TIMAP4BlockingConnection::Unsubscribe(const char *mailboxName) +{ + ProgressEventFunction_UsingIdWithString (MK_IMAP_STATUS_UNSUBSCRIBE_MAILBOX, mailboxName); + IncrementCommandTagNumber(); + + char *escapedName = CreateEscapedMailboxName(mailboxName); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s unsubscribe \"%s\"" CRLF, // format string + GetServerCommandTag(), // command tag + escapedName); + + FREEIF( escapedName); + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + +void TIMAP4BlockingConnection::Search(const char *searchCriteria, + XP_Bool useUID, + XP_Bool notifyHit /* TRUE */) +{ + fNotifyHit = notifyHit; + ProgressEventFunction_UsingId (MK_IMAP_STATUS_SEARCH_MAILBOX); + IncrementCommandTagNumber(); + + char *formatString; + // the searchCriteria string contains the 'search ....' string + if (useUID) + formatString = "%s uid %s\015\012"; + else + formatString = "%s %s\015\012"; + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + formatString, // format string + GetServerCommandTag(), // command tag + searchCriteria); + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + +void TIMAP4BlockingConnection::Copy(const char *messageList, + const char *destinationMailbox, + XP_Bool idsAreUid) +{ + IncrementCommandTagNumber(); + + char *escapedDestination = CreateEscapedMailboxName(destinationMailbox); + + char *formatString; + if (idsAreUid) + formatString = "%s uid copy %s \"%s\"\015\012"; + else + formatString = "%s copy %s \"%s\"\015\012"; + + // since messageIds can be infinitely long, use a dynamic buffer rather than the fixed one + const char *commandTag = GetServerCommandTag(); + int protocolStringSize = XP_STRLEN(formatString) + XP_STRLEN(messageList) + XP_STRLEN(commandTag) + XP_STRLEN(escapedDestination) + 1; + char *protocolString = (char *) XP_ALLOC( protocolStringSize ); + + if (protocolString) + { + PR_snprintf(protocolString, // string to create + protocolStringSize, // max size + formatString, // format string + commandTag, // command tag + messageList, + escapedDestination); + + + int ioStatus = WriteLineToSocket(protocolString); + + ParseIMAPandCheckForNewMail(protocolString); + XP_FREE(protocolString); + } + else + HandleMemoryFailure(); + + FREEIF( escapedDestination); +} + +void TIMAP4BlockingConnection::Store(const char *messageList, + const char *messageData, + XP_Bool idsAreUid) +{ + //ProgressUpdateEvent("Saving server message flags..."); + IncrementCommandTagNumber(); + + char *formatString; + if (idsAreUid) + formatString = "%s uid store %s %s\015\012"; + else + formatString = "%s store %s %s\015\012"; + + // we might need to close this mailbox after this + fCloseNeededBeforeSelect = fDeleteModelIsMoveToTrash && (XP_STRCASESTR(messageData, "\\Deleted")); + + const char *commandTag = GetServerCommandTag(); + int protocolStringSize = XP_STRLEN(formatString) + XP_STRLEN(messageList) + XP_STRLEN(messageData) + XP_STRLEN(commandTag) + 1; + char *protocolString = (char *) XP_ALLOC( protocolStringSize ); + + if (protocolString) + { + PR_snprintf(protocolString, // string to create + protocolStringSize, // max size + formatString, // format string + commandTag, // command tag + messageList, + messageData); + + int ioStatus = WriteLineToSocket(protocolString); + + ParseIMAPandCheckForNewMail(protocolString); + XP_FREE(protocolString); + } + else + HandleMemoryFailure(); +} + +void TIMAP4BlockingConnection::Close() +{ + ProgressEventFunction_UsingId (MK_IMAP_STATUS_CLOSE_MAILBOX); + + IncrementCommandTagNumber(); + GetServerStateParser().ResetFlagInfo(0); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s close" CRLF, // format string + GetServerCommandTag()); // command tag + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + +void TIMAP4BlockingConnection::Check() +{ + //ProgressUpdateEvent("Checking mailbox..."); + IncrementCommandTagNumber(); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s check" CRLF, // format string + GetServerCommandTag()); // command tag + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + +void TIMAP4BlockingConnection::Expunge() +{ + ProgressEventFunction_UsingId (MK_IMAP_STATUS_EXPUNGING_MAILBOX); + IncrementCommandTagNumber(); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s expunge" CRLF, // format string + GetServerCommandTag()); // command tag + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + +// any state commands +void TIMAP4BlockingConnection::Logout() +{ + ProgressEventFunction_UsingId (MK_IMAP_STATUS_LOGGING_OUT); + IncrementCommandTagNumber(); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s logout" CRLF, // format string + GetServerCommandTag()); // command tag + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + // the socket may be dead before we read the response, so drop it. + ParseIMAPandCheckForNewMail(); +} + +void TIMAP4BlockingConnection::Noop() +{ + //ProgressUpdateEvent("noop..."); + IncrementCommandTagNumber(); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s noop" CRLF, // format string + GetServerCommandTag()); // command tag + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + + +void TIMAP4BlockingConnection::Capability() +{ + + ProgressEventFunction_UsingId (MK_IMAP_STATUS_CHECK_COMPAT); + IncrementCommandTagNumber(); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s capability" CRLF, // format string + GetServerCommandTag()); // command tag + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + +void TIMAP4BlockingConnection::XServerInfo() +{ + + ProgressEventFunction_UsingId (MK_IMAP_GETTING_SERVER_INFO); + IncrementCommandTagNumber(); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s XSERVERINFO MANAGEACCOUNTURL MANAGELISTSURL MANAGEFILTERSURL" CRLF, + GetServerCommandTag()); // command tag + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + +void TIMAP4BlockingConnection::XMailboxInfo(const char *mailboxName) +{ + + ProgressEventFunction_UsingId (MK_IMAP_GETTING_MAILBOX_INFO); + IncrementCommandTagNumber(); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s XMAILBOXINFO %s MANAGEURL POSTURL" CRLF, + GetServerCommandTag(), + mailboxName); // command tag + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + + + + + +void TIMAP4BlockingConnection::Namespace() +{ + + ProgressEventFunction_UsingId (MK_IMAP_STATUS_GETTING_NAMESPACE); + IncrementCommandTagNumber(); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s namespace" CRLF, // format string + GetServerCommandTag()); // command tag + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + + +void TIMAP4BlockingConnection::MailboxData() +{ + IncrementCommandTagNumber(); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s mailboxdata" CRLF, // format string + GetServerCommandTag()); // command tag + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + ParseIMAPandCheckForNewMail(); +} + + +void TIMAP4BlockingConnection::GetMyRightsForFolder(const char *mailboxName) +{ + IncrementCommandTagNumber(); + + char *escapedName = CreateEscapedMailboxName(mailboxName); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s myrights \"%s\"" CRLF, // format string + GetServerCommandTag(), // command tag + escapedName); + + FREEIF( escapedName); + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + +void TIMAP4BlockingConnection::GetACLForFolder(const char *mailboxName) +{ + IncrementCommandTagNumber(); + + char *escapedName = CreateEscapedMailboxName(mailboxName); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s getacl \"%s\"" CRLF, // format string + GetServerCommandTag(), // command tag + escapedName); + + FREEIF( escapedName); + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + +void TIMAP4BlockingConnection::StatusForFolder(const char *mailboxName) +{ + IncrementCommandTagNumber(); + + char *escapedName = CreateEscapedMailboxName(mailboxName); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s STATUS \"%s\" (UIDNEXT MESSAGES UNSEEN)" CRLF, // format string + GetServerCommandTag(), // command tag + escapedName); + + FREEIF( escapedName); + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + +void TNavigatorImapConnection::StatusForFolder(const char *mailboxName) +{ + TIMAP4BlockingConnection::StatusForFolder(mailboxName); + mailbox_spec *new_spec = GetServerStateParser().CreateCurrentMailboxSpec(mailboxName); + if (new_spec) + UpdateMailboxStatus(new_spec); +} + +void TIMAP4BlockingConnection::Netscape() +{ + //ProgressUpdateEvent("Netscape..."); + IncrementCommandTagNumber(); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s netscape" CRLF, // format string + GetServerCommandTag()); // command tag + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); +} + + +void TIMAP4BlockingConnection::InsecureLogin(const char *userName, + const char *password) +{ + + ProgressEventFunction_UsingId (MK_IMAP_STATUS_SENDING_LOGIN); + IncrementCommandTagNumber(); + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s login \"%s\" \"%s\"" CRLF, // format string + GetServerCommandTag(), // command tag + userName, + password); + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + ParseIMAPandCheckForNewMail(); +} + + +void TIMAP4BlockingConnection::AuthLogin(const char *userName, + const char *password) +{ + ProgressEventFunction_UsingId (MK_IMAP_STATUS_SENDING_AUTH_LOGIN); + IncrementCommandTagNumber(); + + const char *currentTagToken = GetServerCommandTag(); + char *currentCommand = NULL; + + PR_snprintf(GetOutputBuffer(), // string to create + kOutputBufferSize, // max size + "%s authenticate login" CRLF, // format string + GetServerCommandTag()); // command tag + + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + + + StrAllocCopy(currentCommand, GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); + + if (GetServerStateParser().LastCommandSuccessful()) { + char *base64Str = NET_Base64Encode((char*)userName, XP_STRLEN(userName)); + PR_snprintf(GetOutputBuffer(), + kOutputBufferSize, + "%s" CRLF, + base64Str); + XP_FREEIF(base64Str); + ioStatus = WriteLineToSocket(GetOutputBuffer()); + ParseIMAPandCheckForNewMail(currentCommand); + if (GetServerStateParser().LastCommandSuccessful()) { + char *base64Str = NET_Base64Encode((char*)password, XP_STRLEN(password)); + PR_snprintf(GetOutputBuffer(), + kOutputBufferSize, + "%s" CRLF, + base64Str); + XP_FREEIF(base64Str); + ioStatus = WriteLineToSocket(GetOutputBuffer()); + ParseIMAPandCheckForNewMail(currentCommand); + if (GetServerStateParser().LastCommandSuccessful()) + { + FREEIF(currentCommand); + return; + } + } + } + + FREEIF(currentCommand); + // fall back to use InsecureLogin() + InsecureLogin(userName, password); +} + + + +void TIMAP4BlockingConnection::ProcessBIFFRequest() +{ + if (GetConnectionStatus() == MK_WAITING_FOR_CONNECTION) + EstablishServerConnection(); + if (GetConnectionStatus() >= 0) + SetConnectionStatus(-1); // stub! +} + + + +char *TIMAP4BlockingConnection::GetOutputBuffer() +{ + return fOutputBuffer; +} + +char *TIMAP4BlockingConnection::GetServerCommandTag() +{ + return fCurrentServerCommandTag; +} + +PRMonitor *TIMAP4BlockingConnection::GetDataMemberMonitor() +{ + return fDataMemberMonitor; +} + +XP_Bool TIMAP4BlockingConnection::HandlingMultipleMessages(char *messageIdString) +{ + return (XP_STRCHR(messageIdString,',') != NULL || + XP_STRCHR(messageIdString,':') != NULL); +} + + + + +/******************* TNavigatorImapConnection *******************************/ + + +void TNavigatorImapConnection::StartProcessingActiveEntries() +{ + XP_Bool receivedNullEntry = FALSE; + + while (!receivedNullEntry && !DeathSignalReceived()) + { + WaitForNextActiveEntry(); + + if (!DeathSignalReceived()) + { + // while I'm processing this URL nobody can change it! + PR_EnterMonitor(fActiveEntryMonitor); + if (fCurrentEntry) + { + XP_Bool killedEarly = FALSE; + // fCurrentUrl is accessed repeatedly by CurrentConnectionIsMove + // CurrentConnectionIsMove is called from Mozilla thread + LIBNET_LOCK(); + if (!DeathSignalReceived()) + { + delete fCurrentUrl; + fCurrentUrl = new TIMAPUrl(fCurrentEntry->URL_s->address, fCurrentEntry->URL_s->internal_url); + } + else + killedEarly = TRUE; + LIBNET_UNLOCK(); + + if (!fCurrentUrl && !killedEarly) + HandleMemoryFailure(); + else if (!killedEarly) + { + // process the url + ProcessCurrentURL(); + + // wait for the event queue to empty before we trash + // fCurrentEntry. Some of the events reference it. + WaitForEventQueueEmptySignal(); + } + + // this URL is finished so delete it in case + // it is referenced before we create the new one + // the next time through this loop + PR_EnterMonitor(GetDataMemberMonitor()); + delete fCurrentUrl; + fCurrentUrl = NULL; + PR_ExitMonitor(GetDataMemberMonitor()); + + + fCurrentEntry = NULL; + } + else + { + receivedNullEntry = TRUE; + if (GetIOSocket() != 0) + Logout(); + } + PR_ExitMonitor(fActiveEntryMonitor); + } + } + + +#ifndef NSPR20 + int64 sleepTime; + LL_I2L(sleepTime, kImapSleepTime); +#else + PRIntervalTime sleepTime = PR_MicrosecondsToInterval(kImapSleepTime); +#endif + PR_EnterMonitor(fPermissionToDieMonitor); + while (!fIsSafeToDie) + PR_Wait(fPermissionToDieMonitor, sleepTime); + PR_ExitMonitor(fPermissionToDieMonitor); +} + + +XP_Bool TNavigatorImapConnection::BlockedWaitingForActiveEntry() +{ + return !PR_InMonitor(fActiveEntryMonitor); +} + +char *TNavigatorImapConnection::GetCurrentConnectionURL() +{ + PR_EnterMonitor(GetDataMemberMonitor()); + char *rv = fCurrentEntry->URL_s->address ? + XP_STRDUP(fCurrentEntry->URL_s->address) : (char *)NULL; + PR_ExitMonitor(GetDataMemberMonitor()); + return rv; +} + + +void TNavigatorImapConnection::WaitForEventQueueEmptySignal() +{ + // notice that this Wait function is not like the others in that + // it does not wake up and check DeathSignalReceived() + // sometimes it is run after DeathSignalReceived and the thread + // has to drain its final fe events before death. +#ifdef NSPR20 + PRIntervalTime sleepTime = kImapSleepTime; +#else + int64 sleepTime; + LL_I2L(sleepTime, kImapSleepTime); +#endif + + PR_EnterMonitor(fEventQueueEmptySignalMonitor); + while(!fEventQueueEmptySignalHappened) + PR_Wait(fEventQueueEmptySignalMonitor, sleepTime); + fEventQueueEmptySignalHappened = FALSE; + PR_ExitMonitor(fEventQueueEmptySignalMonitor); +} + +void TNavigatorImapConnection::SignalEventQueueEmpty() +{ + PR_EnterMonitor(fEventQueueEmptySignalMonitor); + fEventQueueEmptySignalHappened = TRUE; + PR_Notify(fEventQueueEmptySignalMonitor); + PR_ExitMonitor(fEventQueueEmptySignalMonitor); +} + +void TNavigatorImapConnection::WaitForNextAppendMessageSize() +{ +#ifdef NSPR20 + PRIntervalTime sleepTime = kImapSleepTime; +#else + int64 sleepTime; + LL_I2L(sleepTime, kImapSleepTime); +#endif + + PR_EnterMonitor(fMsgCopyDataMonitor); + while(!fMsgAppendSizeIsNew && !DeathSignalReceived()) + PR_Wait(fMsgCopyDataMonitor, sleepTime); + PR_ExitMonitor(fMsgCopyDataMonitor); +} + + +void TNavigatorImapConnection::WaitForPotentialListOfMsgsToFetch(uint32 **msgIdList, uint32 &msgCount) +{ +#ifdef NSPR20 + PRIntervalTime sleepTime = kImapSleepTime; +#else + int64 sleepTime; + LL_I2L(sleepTime, kImapSleepTime); +#endif + + PR_EnterMonitor(fMsgCopyDataMonitor); + while(!fFetchMsgListIsNew && !DeathSignalReceived()) + PR_Wait(fMsgCopyDataMonitor, sleepTime); + fFetchMsgListIsNew = FALSE; + + *msgIdList = fFetchMsgIdList; + msgCount = fFetchCount; + + PR_ExitMonitor(fMsgCopyDataMonitor); +} + + +void TNavigatorImapConnection::NotifyKeyList(uint32 *keys, uint32 keyCount) +{ + PR_EnterMonitor(fMsgCopyDataMonitor); + fFetchMsgIdList = keys; + fFetchCount = keyCount; + fFetchMsgListIsNew = TRUE; + PR_Notify(fMsgCopyDataMonitor); + PR_ExitMonitor(fMsgCopyDataMonitor); +} + +void TNavigatorImapConnection::WaitForMessageUploadToComplete() +{ +#ifdef NSPR20 + PRIntervalTime sleepTime = kImapSleepTime; +#else + int64 sleepTime; + LL_I2L(sleepTime, kImapSleepTime); +#endif + + PR_EnterMonitor(fMessageUploadMonitor); + while(!fMessageUploadCompleted && !DeathSignalReceived()) + PR_Wait(fMessageUploadMonitor, sleepTime); + fMessageUploadCompleted = FALSE; + PR_ExitMonitor(fMessageUploadMonitor); +} + +void TNavigatorImapConnection::NotifyMessageUploadMonitor() +{ + PR_EnterMonitor(fMessageUploadMonitor); + fMessageUploadCompleted = TRUE; + PR_Notify(fMessageUploadMonitor); + PR_ExitMonitor(fMessageUploadMonitor); +} + +void TNavigatorImapConnection::NotifyAppendSize(uint32 msgSize, imapMessageFlagsType flags) +{ + PR_EnterMonitor(fMsgCopyDataMonitor); + fAppendMessageSize = msgSize; + fAppendMsgFlags = flags & ~kImapMsgRecentFlag; // don't let recent flag through, since we can't store it. + fMsgAppendSizeIsNew = TRUE; + PR_Notify(fMsgCopyDataMonitor); + PR_ExitMonitor(fMsgCopyDataMonitor); +} + +imapMessageFlagsType TNavigatorImapConnection::GetAppendFlags() +{ + imapMessageFlagsType returnValue; + PR_EnterMonitor(fMsgCopyDataMonitor); + returnValue = fAppendMsgFlags; + PR_ExitMonitor(fMsgCopyDataMonitor); + + return returnValue; +} + +uint32 TNavigatorImapConnection::GetAppendSize() +{ + uint32 returnValue; + PR_EnterMonitor(fMsgCopyDataMonitor); + returnValue = fAppendMessageSize; + fMsgAppendSizeIsNew = FALSE; + PR_ExitMonitor(fMsgCopyDataMonitor); + + return returnValue; +} + +void TNavigatorImapConnection::SetFolderInfo(MSG_FolderInfo *folder, XP_Bool folderNeedsSubscribing, + XP_Bool folderNeedsACLRefreshed) +{ + PR_EnterMonitor(GetDataMemberMonitor()); + fFolderInfo = folder; + fFolderNeedsSubscribing = folderNeedsSubscribing; + fFolderNeedsACLRefreshed = folderNeedsACLRefreshed; + PR_ExitMonitor(GetDataMemberMonitor()); +} + +void TNavigatorImapConnection::WaitForNextActiveEntry() +{ +#ifdef NSPR20 + PRIntervalTime sleepTime = kImapSleepTime; +#else + int64 sleepTime; + LL_I2L(sleepTime, kImapSleepTime); +#endif + + PR_EnterMonitor(fActiveEntryMonitor); + while(!fNextEntryEventSignalHappened && !DeathSignalReceived()) + PR_Wait(fActiveEntryMonitor, sleepTime); + fNextEntryEventSignalHappened = FALSE; + PR_ExitMonitor(fActiveEntryMonitor); +} + +void TNavigatorImapConnection::SubmitActiveEntry(ActiveEntry * ce, XP_Bool newConnection) +{ + PR_EnterMonitor(fActiveEntryMonitor); + fCurrentEntry = ce; + if (newConnection) + StartIMAPConnection(this); + else + SetConnectionStatus(0); // keep netlib running, connection is reused + fNextEntryEventSignalHappened = TRUE; + PR_Notify(fActiveEntryMonitor); + PR_ExitMonitor(fActiveEntryMonitor); +} + +XPPtrArray *TNavigatorImapConnection::connectionList; + +TNavigatorImapConnection::TNavigatorImapConnection(const char *hostName) : + TIMAP4BlockingConnection(), + fCurrentEntry(nil), + fFEeventCompleted(FALSE), + fTunnelCompleted(FALSE), + fInputBufferSize(0), + fInputSocketBuffer(nil), + fBlockingThread(nil), + fTCPConData(nil), + fIOSocket(NULL), + fCurrentUrl(nil), + fEventQueueEmptySignalHappened(FALSE), + fMessageUploadCompleted(FALSE), + fHierarchyNameState(kNoOperationInProgress), + fHierarchyMover(nil), + fBytesMovedSoFarForProgress(0), + fDeletableChildren(nil), + fOnlineBaseFolderExists(FALSE), + fFetchMsgIdList(NULL), + fFetchCount(0), + fFetchMsgListIsNew(FALSE), + fMsgAppendSizeIsNew(FALSE), + fAppendMessageSize(0), + fNextEntryEventSignalHappened(FALSE), + fThreadShouldDie(FALSE), + fIsSafeToDie(FALSE), + fProgressStringId(0), + fProgressIndex(0), + fNeedGreeting(FALSE), + fNeedNoop(FALSE), + fProgressCount(0), + fOutputStream(nil), + fPseudoInterrupted(FALSE), + fAutoSubscribe(TRUE), + fAutoUnsubscribe(TRUE), + fShouldUpgradeToSubscription(FALSE), + fUpgradeShouldLeaveAlone(FALSE), + fUpgradeShouldSubscribeAll(FALSE), + fFolderInfo(NULL), + fFolderNeedsSubscribing(FALSE), + fFolderNeedsACLRefreshed(FALSE) +{ + fEventCompletionMonitor = PR_NewNamedMonitor("event-completion-monitor"); + fActiveEntryMonitor = PR_NewNamedMonitor("active-entry-monitor"); + fEventQueueEmptySignalMonitor = PR_NewNamedMonitor("event-queue-empty-signal-monitor"); + fMessageUploadMonitor = PR_NewNamedMonitor("message-upload-monitor"); + fMsgCopyDataMonitor = PR_NewNamedMonitor("msg-copy-data-monitor"); + fThreadDeathMonitor = PR_NewNamedMonitor("thread-death-monitor"); + fPermissionToDieMonitor = PR_NewNamedMonitor("die-monitor"); + fPseudoInterruptMonitor = PR_NewNamedMonitor("imap-pseudo-interrupt-monitor"); + fWaitForBodyIdsMonitor = PR_NewNamedMonitor("wait-for-body-ids-monitor"); + fTunnelCompletionMonitor = PR_NewNamedMonitor("imap-io-tunnelling-monitor"); + + fFEEventQueue = TImapFEEventQueue::CreateFEEventQueue(); + fSocketInfo = new TIMAPSocketInfo(); + fListedMailboxList = XP_ListNew(); + fHostName = XP_STRDUP(hostName); + fFlagState = new TImapFlagAndUidState(kFlagEntrySize, FALSE); + fMailToFetch = fGetHeaders = fNewMail = FALSE; + fLastProgressStringId = 0; + fLastPercent = -1; + LL_I2L(fLastProgressTime, 0); + ResetProgressInfo(); + + fActive = fTrackingTime = FALSE; + fStartTime = fEndTime = 0; + fTooFastTime = 2; + fIdealTime = 4; + fChunkAddSize = 2048; + fChunkStartSize = fChunkSize = 10240; + fFetchByChunks = TRUE; + fChunkThreshold = fChunkSize + (fChunkSize / 2); + fMaxChunkSize = 40960; + + connectionList->Add(this); +} + + +void TNavigatorImapConnection::Configure(XP_Bool GetHeaders, int32 TooFastTime, int32 IdealTime, + int32 ChunkAddSize, int32 ChunkSize, int32 ChunkThreshold, + XP_Bool FetchByChunks, int32 MaxChunkSize) +{ + PR_EnterMonitor(fMsgCopyDataMonitor); + fGetHeaders = GetHeaders; + fTooFastTime = TooFastTime; // secs we read too little too fast + fIdealTime = IdealTime; // secs we read enough in good time + fChunkAddSize = ChunkAddSize; // buffer size to add when wasting time + fChunkSize = ChunkSize; + fChunkThreshold = ChunkThreshold; + fFetchByChunks = FetchByChunks; + fMaxChunkSize = MaxChunkSize; + PR_ExitMonitor(fMsgCopyDataMonitor); +} + + +TIMAPHostInfo::TIMAPHostInfo(const char *hostName) +{ + fHostName = XP_STRDUP(hostName); + fNextHost = NULL; + fCachedPassword = NULL; + fCapabilityFlags = kCapabilityUndefined; + fHierarchyDelimiters = NULL; + fHaveWeEverDiscoveredFolders = FALSE; + fCanonicalOnlineSubDir = NULL; + fNamespaceList = TIMAPNamespaceList::CreateTIMAPNamespaceList(); + fUsingSubscription = TRUE; + fOnlineTrashFolderExists = FALSE; + fShouldAlwaysListInbox = TRUE; + fShellCache = TIMAPBodyShellCache::Create(); + fPasswordVerifiedOnline = FALSE; +} + +TIMAPHostInfo::~TIMAPHostInfo() +{ + FREEIF(fHostName); + if (fCachedPassword) + XP_FREE(fCachedPassword); + FREEIF(fHierarchyDelimiters); + delete fNamespaceList; + delete fShellCache; +} + +TIMAPHostInfo *TIMAPHostInfo::FindHost(const char *hostName) +{ + TIMAPHostInfo *host; + + for (host = fHostInfoList; host; host = host->fNextHost) + { + if (!XP_STRCASECMP(hostName, host->fHostName)) + return host; + } + return host; +} + +// reset any cached connection info - delete the lot of 'em +void TIMAPHostInfo::ResetAll() +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *nextHost = NULL; + for (TIMAPHostInfo *host = fHostInfoList; host; host = nextHost) + { + nextHost = host->fNextHost; + delete host; + } + fHostInfoList = NULL; + PR_ExitMonitor(gCachedHostInfoMonitor); +} + +/* static */ XP_Bool TIMAPHostInfo::AddHostToList(const char *hostName) +{ + TIMAPHostInfo *newHost=NULL; + PR_EnterMonitor(gCachedHostInfoMonitor); + if (!FindHost(hostName)) + { + // stick it on the front + newHost = new TIMAPHostInfo(hostName); + if (newHost) + { + newHost->fNextHost = fHostInfoList; + fHostInfoList = newHost; + } + } + PR_ExitMonitor(gCachedHostInfoMonitor); + return newHost != NULL; +} + +XP_Bool IMAP_HostHasACLCapability(const char *hostName) +{ + return (TIMAPHostInfo::GetCapabilityForHost(hostName) & kACLCapability); +} + +uint32 IMAP_GetCapabilityForHost(const char *hostName) +{ + return TIMAPHostInfo::GetCapabilityForHost(hostName); +} + +/* static */ uint32 TIMAPHostInfo::GetCapabilityForHost(const char *hostName) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + + TIMAPHostInfo *host = FindHost(hostName); + uint32 ret = (host) ? host->fCapabilityFlags : 0; + PR_ExitMonitor(gCachedHostInfoMonitor); + return ret; +} +/* static */ XP_Bool TIMAPHostInfo::SetCapabilityForHost(const char *hostName, uint32 capability) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + host->fCapabilityFlags = capability; + PR_ExitMonitor(gCachedHostInfoMonitor); + return (host != 0); +} + +/* static */ char *TIMAPHostInfo::GetPasswordForHost(const char *hostName) +{ + char *ret = NULL; + + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + ret = host->fCachedPassword; + PR_ExitMonitor(gCachedHostInfoMonitor); + return ret; +} + +/* static */ XP_Bool TIMAPHostInfo::SetPasswordForHost(const char *hostName, const char *password) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + { + FREEIF(host->fCachedPassword); + if (password) + host->fCachedPassword = XP_STRDUP(password); + } + PR_ExitMonitor(gCachedHostInfoMonitor); + return (host != 0); +} + +/* static */ XP_Bool TIMAPHostInfo::SetPasswordVerifiedOnline(const char *hostName) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + host->fPasswordVerifiedOnline = TRUE; + PR_ExitMonitor(gCachedHostInfoMonitor); + return (host != 0); +} + +/* static */ XP_Bool TIMAPHostInfo::GetPasswordVerifiedOnline(const char *hostName) +{ + XP_Bool ret = FALSE; + + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + ret = host->fPasswordVerifiedOnline; + PR_ExitMonitor(gCachedHostInfoMonitor); + return ret; +} + +/* static */ char *TIMAPHostInfo::GetHierarchyDelimiterStringForHost(const char *hostName) +{ + char *ret = NULL; + + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + ret = host->fHierarchyDelimiters; + PR_ExitMonitor(gCachedHostInfoMonitor); + return ret; +} + +/* static */ XP_Bool TIMAPHostInfo::AddHierarchyDelimiter(const char *hostName, char delimiter) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + { + if (!host->fHierarchyDelimiters) + { + host->fHierarchyDelimiters = PR_smprintf("%c",delimiter); + } + else if (!XP_STRCHR(host->fHierarchyDelimiters, delimiter)) + { + char *tmpDelimiters = PR_smprintf("%s%c",host->fHierarchyDelimiters,delimiter); + FREEIF(host->fHierarchyDelimiters); + host->fHierarchyDelimiters = tmpDelimiters; + } + } + PR_ExitMonitor(gCachedHostInfoMonitor); + return (host != 0); +} + +/* static */ XP_Bool TIMAPHostInfo::GetHostIsUsingSubscription(const char *hostName) +{ + XP_Bool ret = FALSE; + + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + ret = host->fUsingSubscription; + PR_ExitMonitor(gCachedHostInfoMonitor); + return ret; +} + +/* static */ XP_Bool TIMAPHostInfo::SetHostIsUsingSubscription(const char *hostName, XP_Bool usingSubscription) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + host->fUsingSubscription = usingSubscription; + PR_ExitMonitor(gCachedHostInfoMonitor); + return (host != 0); +} + +/* static */ XP_Bool TIMAPHostInfo::GetHostHasAdminURL(const char *hostName) +{ + XP_Bool ret = FALSE; + + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + ret = host->fHaveAdminURL; + PR_ExitMonitor(gCachedHostInfoMonitor); + return ret; +} + +/* static */ XP_Bool TIMAPHostInfo::SetHostHasAdminURL(const char *hostName, XP_Bool haveAdminURL) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + host->fHaveAdminURL = haveAdminURL; + PR_ExitMonitor(gCachedHostInfoMonitor); + return (host != 0); +} + + +/* static */ XP_Bool TIMAPHostInfo::GetHaveWeEverDiscoveredFoldersForHost(const char *hostName) +{ + XP_Bool ret = FALSE; + + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + ret = host->fHaveWeEverDiscoveredFolders; + PR_ExitMonitor(gCachedHostInfoMonitor); + return ret; +} + +/* static */ XP_Bool TIMAPHostInfo::SetHaveWeEverDiscoveredFoldersForHost(const char *hostName, XP_Bool discovered) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + host->fHaveWeEverDiscoveredFolders = discovered; + PR_ExitMonitor(gCachedHostInfoMonitor); + return (host != 0); +} + +/* static */ XP_Bool TIMAPHostInfo::SetOnlineTrashFolderExistsForHost(const char *hostName, XP_Bool exists) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + host->fOnlineTrashFolderExists = exists; + PR_ExitMonitor(gCachedHostInfoMonitor); + return (host != 0); +} + +/* static */ XP_Bool TIMAPHostInfo::GetOnlineTrashFolderExistsForHost(const char *hostName) +{ + XP_Bool ret = FALSE; + + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + ret = host->fOnlineTrashFolderExists; + PR_ExitMonitor(gCachedHostInfoMonitor); + return ret; +} + +/* static */ XP_Bool TIMAPHostInfo::AddNewNamespaceForHost(const char *hostName, TIMAPNamespace *ns) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + { + host->fNamespaceList->AddNewNamespace(ns); + } + PR_ExitMonitor(gCachedHostInfoMonitor); + return (host != 0); +} + + +/* static */ TIMAPNamespace *TIMAPHostInfo::GetNamespaceForMailboxForHost(const char *hostName, const char *mailbox_name) +{ + TIMAPNamespace *ret = 0; + + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + { + ret = host->fNamespaceList->GetNamespaceForMailbox(mailbox_name); + } + PR_ExitMonitor(gCachedHostInfoMonitor); + return ret; +} + + +/* static */ XP_Bool TIMAPHostInfo::ClearPrefsNamespacesForHost(const char *hostName) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + { + host->fNamespaceList->ClearNamespaces(TRUE, FALSE); + } + PR_ExitMonitor(gCachedHostInfoMonitor); + return (host != 0); +} + + +/* static */ XP_Bool TIMAPHostInfo::ClearServerAdvertisedNamespacesForHost(const char *hostName) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + { + host->fNamespaceList->ClearNamespaces(FALSE, TRUE); + } + PR_ExitMonitor(gCachedHostInfoMonitor); + return (host != 0); +} + +/* static */ TIMAPNamespace *TIMAPHostInfo::GetDefaultNamespaceOfTypeForHost(const char *hostName, EIMAPNamespaceType type) +{ + TIMAPNamespace *ret = NULL; + + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + { + ret = host->fNamespaceList->GetDefaultNamespaceOfType(type); + } + PR_ExitMonitor(gCachedHostInfoMonitor); + return ret; +} + +/* static */ XP_Bool TIMAPHostInfo::GetNamespacesOverridableForHost(const char *hostName) +{ + XP_Bool ret = FALSE; + + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + ret = host->fNamespacesOverridable; + PR_ExitMonitor(gCachedHostInfoMonitor); + return ret; +} + +/* static */ XP_Bool TIMAPHostInfo::SetNamespacesOverridableForHost(const char *hostName, XP_Bool overridable) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + host->fNamespacesOverridable = overridable; + PR_ExitMonitor(gCachedHostInfoMonitor); + return (host != 0); +} + +/* static */ int TIMAPHostInfo::GetNumberOfNamespacesForHost(const char *hostName) +{ + int ret = 0; + + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + { + ret = host->fNamespaceList->GetNumberOfNamespaces(); + } + PR_ExitMonitor(gCachedHostInfoMonitor); + XP_ASSERT(ret > 0); + return ret; +} + +/* static */ TIMAPNamespace *TIMAPHostInfo::GetNamespaceNumberForHost(const char *hostName, int n) +{ + TIMAPNamespace *ret = 0; + + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + { + ret = host->fNamespaceList->GetNamespaceNumber(n); + } + PR_ExitMonitor(gCachedHostInfoMonitor); + return ret; +} + +// Make sure this is running in the Mozilla thread when called +/* static */ XP_Bool TIMAPHostInfo::CommitNamespacesForHost(const char *hostName, MSG_Master *master) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + { + EIMAPNamespaceType type = kPersonalNamespace; + for (int i = 1; i <= 3; i++) + { + switch(i) + { + case 1: + type = kPersonalNamespace; + break; + case 2: + type = kPublicNamespace; + break; + case 3: + type = kOtherUsersNamespace; + break; + default: + type = kPersonalNamespace; + break; + } + + int32 numInNS = host->fNamespaceList->GetNumberOfNamespaces(type); + if (numInNS == 0) + { + MSG_SetNamespacePrefixes(master, host->fHostName, type, NULL); + } + else if (numInNS >= 1) + { + char *pref = PR_smprintf(""); + for (int count = 1; count <= numInNS; count++) + { + TIMAPNamespace *ns = host->fNamespaceList->GetNamespaceNumber(count, type); + if (ns) + { + if (count > 1) + { + // append the comma + char *tempPref = PR_smprintf("%s,",pref); + FREEIF(pref); + pref = tempPref; + } + char *tempPref = PR_smprintf("%s\"%s\"",pref,ns->GetPrefix()); + FREEIF(pref); + pref = tempPref; + } + } + if (pref) + { + MSG_SetNamespacePrefixes(master, host->fHostName, type, pref); + XP_FREE(pref); + } + } + } + } + PR_ExitMonitor(gCachedHostInfoMonitor); + return (host != 0); +} + +// Caller must free the returned string +// Returns NULL if there is no personal namespace on the given host +/* static */ char *TIMAPHostInfo::GetOnlineInboxPathForHost(const char *hostName) +{ + char *ret = NULL; + + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + { + TIMAPNamespace *ns = NULL; + ns = host->fNamespaceList->GetDefaultNamespaceOfType(kPersonalNamespace); + if (ns) + { + ret = PR_smprintf("%sINBOX",ns->GetPrefix()); + } + } + PR_ExitMonitor(gCachedHostInfoMonitor); + return ret; +} + +/* static */ XP_Bool TIMAPHostInfo::GetShouldAlwaysListInboxForHost(const char* /*hostName*/) +{ + XP_Bool ret = TRUE; + + /* + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + ret = host->fShouldAlwaysListInbox; + PR_ExitMonitor(gCachedHostInfoMonitor); + */ + return ret; +} + +/* static */ XP_Bool TIMAPHostInfo::SetShouldAlwaysListInboxForHost(const char *hostName, XP_Bool shouldList) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + host->fShouldAlwaysListInbox = shouldList; + PR_ExitMonitor(gCachedHostInfoMonitor); + return (host != 0); +} + +/* static */ XP_Bool TIMAPHostInfo::SetNamespaceHierarchyDelimiterFromMailboxForHost(const char *hostName, const char *boxName, char delimiter) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + { + TIMAPNamespace *ns = host->fNamespaceList->GetNamespaceForMailbox(boxName); + if (ns && !ns->GetIsDelimiterFilledIn()) + { + ns->SetDelimiter(delimiter); + } + } + PR_ExitMonitor(gCachedHostInfoMonitor); + return (host != 0); +} + +/* static */ XP_Bool TIMAPHostInfo::AddShellToCacheForHost(const char *hostName, TIMAPBodyShell *shell) +{ + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + { + if (host->fShellCache) + { + XP_Bool rv = host->fShellCache->AddShellToCache(shell); + PR_ExitMonitor(gCachedHostInfoMonitor); + return rv; + } + else + { + PR_ExitMonitor(gCachedHostInfoMonitor); + return FALSE; + } + } + PR_ExitMonitor(gCachedHostInfoMonitor); + return (host != 0); +} + +/* static */ TIMAPBodyShell *TIMAPHostInfo::FindShellInCacheForHost(const char *hostName, const char *mailboxName, const char *UID) +{ + TIMAPBodyShell *ret = NULL; + + PR_EnterMonitor(gCachedHostInfoMonitor); + TIMAPHostInfo *host = FindHost(hostName); + if (host) + { + if (host->fShellCache) + ret = host->fShellCache->FindShellForUID(UID, mailboxName); + } + PR_ExitMonitor(gCachedHostInfoMonitor); + return ret; +} + + + +extern "C" { + +extern int IMAP_AddIMAPHost(const char *hostName, XP_Bool usingSubscription, XP_Bool overrideNamespaces, + const char *personalNamespacePrefixes, const char *publicNamespacePrefixes, + const char *otherUsersNamespacePrefixes, XP_Bool haveAdminURL) +{ + TIMAPHostInfo::AddHostToList(hostName); + TIMAPHostInfo::SetHostIsUsingSubscription(hostName,usingSubscription); + TIMAPHostInfo::SetNamespacesOverridableForHost(hostName, overrideNamespaces); + TIMAPHostInfo::SetHostHasAdminURL(hostName, haveAdminURL); + + if (personalNamespacePrefixes) + { + int numNamespaces = IMAP_UnserializeNamespaces(personalNamespacePrefixes, NULL, 0); + char **prefixes = (char**) XP_CALLOC(numNamespaces, sizeof(char*)); + if (prefixes) + { + int len = IMAP_UnserializeNamespaces(personalNamespacePrefixes, prefixes, numNamespaces); + for (int i = 0; i < len; i++) + { + char *thisns = prefixes[i]; + char delimiter = '/'; // a guess + if (XP_STRLEN(thisns) >= 1) + delimiter = thisns[XP_STRLEN(thisns)-1]; + TIMAPNamespace *ns = new TIMAPNamespace(kPersonalNamespace, thisns, delimiter, TRUE); + if (ns) + TIMAPHostInfo::AddNewNamespaceForHost(hostName, ns); + FREEIF(thisns); + } + } + } + if (publicNamespacePrefixes) + { + int numNamespaces = IMAP_UnserializeNamespaces(publicNamespacePrefixes, NULL, 0); + char **prefixes = (char**) XP_CALLOC(numNamespaces, sizeof(char*)); + if (prefixes) + { + int len = IMAP_UnserializeNamespaces(publicNamespacePrefixes, prefixes, numNamespaces); + for (int i = 0; i < len; i++) + { + char *thisns = prefixes[i]; + char delimiter = '/'; // a guess + if (XP_STRLEN(thisns) >= 1) + delimiter = thisns[XP_STRLEN(thisns)-1]; + TIMAPNamespace *ns = new TIMAPNamespace(kPublicNamespace, thisns, delimiter, TRUE); + if (ns) + TIMAPHostInfo::AddNewNamespaceForHost(hostName, ns); + FREEIF(thisns); + } + } + } + if (otherUsersNamespacePrefixes) + { + int numNamespaces = IMAP_UnserializeNamespaces(otherUsersNamespacePrefixes, NULL, 0); + char **prefixes = (char**) XP_CALLOC(numNamespaces, sizeof(char*)); + if (prefixes) + { + int len = IMAP_UnserializeNamespaces(otherUsersNamespacePrefixes, prefixes, numNamespaces); + for (int i = 0; i < len; i++) + { + char *thisns = prefixes[i]; + char delimiter = '/'; // a guess + if (XP_STRLEN(thisns) >= 1) + delimiter = thisns[XP_STRLEN(thisns)-1]; + TIMAPNamespace *ns = new TIMAPNamespace(kOtherUsersNamespace, thisns, delimiter, TRUE); + if (ns) + TIMAPHostInfo::AddNewNamespaceForHost(hostName, ns); + FREEIF(thisns); + } + } + } + return 0; +} + +extern void IMAP_SetShouldAlwaysListInboxForHost(const char *hostName, XP_Bool shouldList) +{ + TIMAPHostInfo::SetShouldAlwaysListInboxForHost(hostName, shouldList); +} + +extern int IMAP_GetNumberOfNamespacesForHost(const char *hostName) +{ + return TIMAPHostInfo::GetNumberOfNamespacesForHost(hostName); +} + +extern XP_Bool IMAP_SetHostIsUsingSubscription(const char *hostname, XP_Bool using_subscription) +{ + return TIMAPHostInfo::SetHostIsUsingSubscription(hostname, using_subscription); +} + +#define SERIALIZER_SEPARATORS "," + +/* prefixes is an array of strings; len is the length of that array. + If there is only one string, simply copy it and return it. + Otherwise, put them in quotes and comma-delimit them. + Returns a newly allocated string. */ +extern char *IMAP_SerializeNamespaces(char **prefixes, int len) +{ + char *rv = NULL; + if (len <= 0) + return rv; + if (len == 1) + { + rv = XP_STRDUP(prefixes[0]); + return rv; + } + else + { + for (int i = 0; i < len; i++) + { + char *temp = NULL; + if (i == 0) + temp = PR_smprintf("\"%s\"",prefixes[i]); /* quote the string */ + else + temp = PR_smprintf(",\"%s\"",prefixes[i]); /* comma, then quote the string */ + + char *next = PR_smprintf("%s%s",rv,temp); + FREEIF(rv); + rv = next; + } + return rv; + } +} + +/* str is the string which needs to be unserialized. + If prefixes is NULL, simply returns the number of namespaces in str. (len is ignored) + If prefixes is not NULL, it should be an array of length len which is to be filled in + with newly-allocated string. Returns the number of strings filled in. +*/ +extern int IMAP_UnserializeNamespaces(const char *str, char **prefixes, int len) +{ + if (!str) + return 0; + if (!prefixes) + { + if (str[0] != '"') + return 1; + else + { + int count = 0; + char *ourstr = XP_STRDUP(str); + if (ourstr) + { + char *token = XP_STRTOK( ourstr, SERIALIZER_SEPARATORS ); + while (token != NULL) + { + token = XP_STRTOK( NULL, SERIALIZER_SEPARATORS ); + count++; + } + XP_FREE(ourstr); + } + return count; + } + } + else + { + if ((str[0] != '"') && (len >= 1)) + { + prefixes[0] = XP_STRDUP(str); + return 1; + } + else + { + int count = 0; + char *ourstr = XP_STRDUP(str); + if (ourstr) + { + char *token = XP_STRTOK( ourstr, SERIALIZER_SEPARATORS ); + while ((count < len) && (token != NULL)) + { + char *current = XP_STRDUP(token), *where = current; + if (where[0] == '"') + where++; + if (where[XP_STRLEN(where)-1] == '"') + where[XP_STRLEN(where)-1] = 0; + prefixes[count] = XP_STRDUP(where); + FREEIF(current); + token = XP_STRTOK( NULL, SERIALIZER_SEPARATORS ); + count++; + } + XP_FREE(ourstr); + } + return count; + } + } +} + +} // extern "C" + +///////////////// TNavigatorImapConnection ////////////////////////////// + + + +//static +void TNavigatorImapConnection::ImapStartup() +{ + if (!fFindingMailboxesMonitor) + fFindingMailboxesMonitor = PR_NewNamedMonitor("finding-mailboxes-monitor"); + if (!fUpgradeToSubscriptionMonitor) + fUpgradeToSubscriptionMonitor = PR_NewNamedMonitor("upgrade-to-imap-subscription-monitor"); + if (!TIMAPHostInfo::gCachedHostInfoMonitor) + TIMAPHostInfo::gCachedHostInfoMonitor = PR_NewNamedMonitor("accessing-hostlist-monitor"); +} + +//static +void TNavigatorImapConnection::ImapShutDown() +{ + if (fFindingMailboxesMonitor) + { + PR_DestroyMonitor(fFindingMailboxesMonitor); + fFindingMailboxesMonitor = NULL; + } + if (fUpgradeToSubscriptionMonitor) + { + PR_DestroyMonitor(fUpgradeToSubscriptionMonitor); + fUpgradeToSubscriptionMonitor = NULL; + } + TIMAPHostInfo::ResetAll(); + if (TIMAPHostInfo::gCachedHostInfoMonitor) + { + PR_DestroyMonitor(TIMAPHostInfo::gCachedHostInfoMonitor); + TIMAPHostInfo::gCachedHostInfoMonitor = NULL; + } +} + + +TNavigatorImapConnection * +TNavigatorImapConnection::Create(const char *hostName) +{ + TNavigatorImapConnection *connection = new TNavigatorImapConnection(hostName); + + if (!connection || + !connection->ConstructionSuccess()) + { + delete connection; + connection = nil; + return NULL; + } + connection->fServerState.SetCapabilityFlag(TIMAPHostInfo::GetCapabilityForHost(hostName)); + connection->fServerState.SetFlagState(connection->fFlagState); + + return connection; +} + +XP_Bool TNavigatorImapConnection::ConstructionSuccess() +{ + return (TIMAP4BlockingConnection::ConstructionSuccess() && + fEventCompletionMonitor && + fFEEventQueue && + fActiveEntryMonitor && + fFindingMailboxesMonitor && + fUpgradeToSubscriptionMonitor && + fEventQueueEmptySignalMonitor && + fMessageUploadMonitor && + fMsgCopyDataMonitor && + fThreadDeathMonitor && + fPermissionToDieMonitor && + fPseudoInterruptMonitor && + fTunnelCompletionMonitor && + fSocketInfo && + fFlagState && + fListedMailboxList); +} + +XP_Bool TNavigatorImapConnection::GetPseudoInterrupted() +{ + XP_Bool rv = FALSE; + PR_EnterMonitor(fPseudoInterruptMonitor); + rv = fPseudoInterrupted; + PR_ExitMonitor(fPseudoInterruptMonitor); + return rv; +} + +void TNavigatorImapConnection::PseudoInterrupt(XP_Bool the_interrupt) +{ + PR_EnterMonitor(fPseudoInterruptMonitor); + fPseudoInterrupted = the_interrupt; + if (the_interrupt) + { + Log("CONTROL", NULL, "PSEUDO-Interrupted"); + //PR_LOG(IMAP, out, ("PSEUDO-Interrupted")); + } + PR_ExitMonitor(fPseudoInterruptMonitor); +} + +void TNavigatorImapConnection::SetActive(XP_Bool active) +{ + PR_EnterMonitor(GetDataMemberMonitor()); + fActive = active; + PR_ExitMonitor(GetDataMemberMonitor()); +} + +XP_Bool TNavigatorImapConnection::GetActive() +{ + XP_Bool ret; + PR_EnterMonitor(GetDataMemberMonitor()); + ret = fActive; + PR_ExitMonitor(GetDataMemberMonitor()); + return ret; +} + +/* static */ XP_Bool TNavigatorImapConnection::IsConnectionActive(const char *hostName, const char *folderName) +{ + XP_Bool ret = FALSE; + if (!connectionList) + connectionList = new XPPtrArray; + + // go through connection list looking for connection in selected state on folder + for (int32 i = 0; i < connectionList->GetSize() && !ret; i++) + { + TNavigatorImapConnection *curConnection = (TNavigatorImapConnection *) connectionList->GetAt(i); + // we should add semaphores for these variables (state and selectedmailboxname). + PR_EnterMonitor(curConnection->GetDataMemberMonitor()); + char *mailboxName = (curConnection->fCurrentUrl) ? curConnection->fCurrentUrl->CreateServerSourceFolderPathString() : 0; + + if (curConnection->fActive && !XP_STRCMP(hostName, curConnection->fHostName) + && folderName && mailboxName && !XP_STRCMP(folderName, mailboxName/* curConnection->GetServerStateParser().GetSelectedMailboxName() */)) + ret = TRUE; +#ifdef DEBUG_bienvenu + XP_Trace("connection active = %s state = %d selected mailbox name = %s\n", (curConnection->fActive) ? "TRUE" : "FALSE", + curConnection->GetServerStateParser().GetIMAPstate(), (mailboxName) ? mailboxName : "NULL" /* curConnection->GetServerStateParser().GetSelectedMailboxName()*/); +#endif + FREEIF(mailboxName); + PR_ExitMonitor(curConnection->GetDataMemberMonitor()); + } + return ret; +} + +// a thread safe accessor to fCurrentUrl +XP_Bool TNavigatorImapConnection::CurrentConnectionIsMove() +{ + XP_Bool isMove; + PR_EnterMonitor(GetDataMemberMonitor()); + + isMove = (fCurrentUrl != NULL) && + ((fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineCopy) || + (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineMove) || + (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineToOfflineCopy) || + (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineToOfflineMove) || + (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOfflineToOnlineMove)); + + PR_ExitMonitor(GetDataMemberMonitor()); + + return isMove; +} + + + + +void TNavigatorImapConnection::SetSubscribeParameters(XP_Bool autoSubscribe, + XP_Bool autoUnsubscribe, XP_Bool autoSubscribeOnOpen, XP_Bool autoUnsubscribeFromNoSelect, + XP_Bool shouldUpgrade, XP_Bool upgradeLeaveAlone, XP_Bool upgradeSubscribeAll) +{ + PR_EnterMonitor(GetDataMemberMonitor()); + fAutoSubscribe = autoSubscribe; + fAutoUnsubscribe = autoUnsubscribe; + fAutoSubscribeOnOpen = autoSubscribeOnOpen; + fAutoUnsubscribeFromNoSelect = autoUnsubscribeFromNoSelect; + PR_ExitMonitor(GetDataMemberMonitor()); + + PR_EnterMonitor(fUpgradeToSubscriptionMonitor); + fShouldUpgradeToSubscription = shouldUpgrade; + fUpgradeShouldLeaveAlone = upgradeLeaveAlone; + fUpgradeShouldSubscribeAll = upgradeSubscribeAll; + PR_ExitMonitor(fUpgradeToSubscriptionMonitor); +} + + +void TNavigatorImapConnection::TellThreadToDie(XP_Bool isSafeToDie) +{ + if (GetIOSocket() != 0 && (fCurrentEntry && fCurrentEntry->status != + MK_WAITING_FOR_CONNECTION)) + { + char *logoutString = PR_smprintf("xxxx logout\r\n"); + if (logoutString) + { + NET_BlockingWrite(GetIOSocket(), logoutString, + XP_STRLEN(logoutString)); + Log("NET","WR",logoutString); + //PR_LOG(IMAP, out, ("%s",logoutString)); + Log("CONTROL","From TellThreadToDie","LOGOUT"); + //PR_LOG(IMAP, out, ("Logged out from TellThreadToDie")); + XP_FREEIF(logoutString); + } + } + + PR_EnterMonitor(fThreadDeathMonitor); + fThreadShouldDie = TRUE; + PR_ExitMonitor(fThreadDeathMonitor); + + // signal all of the monitors that might be in PR_Wait + // they will wake up and see fThreadShouldDie==TRUE; + PR_EnterMonitor(fEventCompletionMonitor); + PR_Notify(fEventCompletionMonitor); + PR_ExitMonitor(fEventCompletionMonitor); + + PR_EnterMonitor(fTunnelCompletionMonitor); + PR_Notify(fTunnelCompletionMonitor); + PR_ExitMonitor(fTunnelCompletionMonitor); + + PR_EnterMonitor(fMessageUploadMonitor); + PR_Notify(fMessageUploadMonitor); + PR_ExitMonitor(fMessageUploadMonitor); + + PR_EnterMonitor(fMsgCopyDataMonitor); + PR_Notify(fMsgCopyDataMonitor); + PR_ExitMonitor(fMsgCopyDataMonitor); + + PR_EnterMonitor(fIOMonitor); + PR_Notify(fIOMonitor); + PR_ExitMonitor(fIOMonitor); + + if (isSafeToDie) + SetIsSafeToDie(); +} + +XP_Bool TNavigatorImapConnection::DeathSignalReceived() +{ + XP_Bool returnValue; + PR_EnterMonitor(fThreadDeathMonitor); + returnValue = fThreadShouldDie; + PR_ExitMonitor(fThreadDeathMonitor); + + return returnValue; +} + + + +TNavigatorImapConnection::~TNavigatorImapConnection() +{ + // This is either being deleted as the last thing that the + // imap thread does or during a url interrupt, in which case + // the thread was already killed, so don't destroy the thread + // here + + if (GetIOSocket() != 0) + { + // Logout(); *** We still need to close down the socket + net_graceful_shutdown(GetIOSocket(), HG32830); + PR_Close(GetIOSocket()); + SetIOSocket(NULL); // delete connection + if (GetActiveEntry()) + GetActiveEntry()->socket = 0; + } + GetServerStateParser().SetFlagState(NULL); + + if (fEventCompletionMonitor) + PR_DestroyMonitor(fEventCompletionMonitor); + + if (fTunnelCompletionMonitor) + PR_DestroyMonitor(fTunnelCompletionMonitor); + + if (fActiveEntryMonitor) + PR_DestroyMonitor(fActiveEntryMonitor); + + if (fEventQueueEmptySignalMonitor) + PR_DestroyMonitor(fEventQueueEmptySignalMonitor); + + if (fMessageUploadMonitor) + PR_DestroyMonitor(fMessageUploadMonitor); + + if (fMsgCopyDataMonitor) + PR_DestroyMonitor(fMsgCopyDataMonitor); + + if (fThreadDeathMonitor) + PR_DestroyMonitor(fThreadDeathMonitor); + + if (fPermissionToDieMonitor) + PR_DestroyMonitor(fPermissionToDieMonitor); + + if (fPseudoInterruptMonitor) + PR_DestroyMonitor(fPseudoInterruptMonitor); + + if (fWaitForBodyIdsMonitor) + PR_DestroyMonitor(fWaitForBodyIdsMonitor); + if (fFEEventQueue) + delete fFEEventQueue; + + if (fHierarchyMover) + delete fHierarchyMover; + + if (fCurrentUrl) + delete fCurrentUrl; + + if (fSocketInfo) + delete (fSocketInfo); + + if (fFlagState) + delete fFlagState; + + while ((TIMAPMailboxInfo *) XP_ListRemoveTopObject(fListedMailboxList)) + { + } + XP_ListDestroy(fListedMailboxList); + preAuthSucceeded = FALSE; + FREEIF(fInputSocketBuffer); + FREEIF(fHostName); + connectionList->Remove(this); +} + + +TNavigatorImapConnection *TNavigatorImapConnection::GetNavigatorConnection() +{ + return this; +} + + +void TNavigatorImapConnection::HandleMemoryFailure() +{ + PR_EnterMonitor(GetDataMemberMonitor()); + AlertUserEvent(XP_GetString(MK_OUT_OF_MEMORY)); + SetConnectionStatus(-1); // stop this midnight train to crashville + PR_ExitMonitor(GetDataMemberMonitor()); +} + +void TNavigatorImapConnection::SetMailboxDiscoveryStatus(EMailboxDiscoverStatus status) +{ + PR_EnterMonitor(GetDataMemberMonitor()); + fDiscoveryStatus = status; + PR_ExitMonitor(GetDataMemberMonitor()); +} + +EMailboxDiscoverStatus TNavigatorImapConnection::GetMailboxDiscoveryStatus( ) +{ + EMailboxDiscoverStatus returnStatus; + PR_EnterMonitor(GetDataMemberMonitor()); + returnStatus = fDiscoveryStatus; + PR_ExitMonitor(GetDataMemberMonitor()); + + return returnStatus; +} + + +void TNavigatorImapConnection::WaitForFEEventCompletion() +{ +#ifdef NSPR20 + PRIntervalTime sleepTime = kImapSleepTime; +#else + int64 sleepTime; + LL_I2L(sleepTime, kImapSleepTime); +#endif + + PR_EnterMonitor(fEventCompletionMonitor); +//#ifdef KMCENTEE_DEBUG +// WaitForFEEventLoopCount=0; +//#endif + + while (!fFEeventCompleted && !DeathSignalReceived()) + { + PR_Wait(fEventCompletionMonitor, sleepTime); +//#ifdef KMCENTEE_DEBUG +// WaitForFEEventLoopCount++; +// if (WaitForFEEventLoopCount == 10) +// XP_ASSERT(FALSE); +//#endif + } + fFEeventCompleted = FALSE; + PR_ExitMonitor(fEventCompletionMonitor); +} + +void TNavigatorImapConnection::WaitForTunnelCompletion() +{ +#ifdef NSPR20 + PRIntervalTime sleepTime = kImapSleepTime; +#else + int64 sleepTime; + LL_I2L(sleepTime, kImapSleepTime); +#endif + + PR_EnterMonitor(fTunnelCompletionMonitor); + + while (!fTunnelCompleted && !DeathSignalReceived()) + { + PR_Wait(fTunnelCompletionMonitor, sleepTime); + } + fTunnelCompleted = FALSE; + PR_ExitMonitor(fTunnelCompletionMonitor); +} + + +void TNavigatorImapConnection::NotifyEventCompletionMonitor() +{ + PR_EnterMonitor(fEventCompletionMonitor); + fFEeventCompleted = TRUE; + PR_Notify(fEventCompletionMonitor); + PR_ExitMonitor(fEventCompletionMonitor); +} + +void TNavigatorImapConnection::NotifyTunnelCompletionMonitor() +{ + PR_EnterMonitor(fTunnelCompletionMonitor); + fTunnelCompleted = TRUE; + PR_Notify(fTunnelCompletionMonitor); + PR_ExitMonitor(fTunnelCompletionMonitor); +} + +TImapFEEventQueue &TNavigatorImapConnection::GetFEEventQueue() +{ + return *fFEEventQueue; +} + + +void TNavigatorImapConnection::BeginMessageDownLoad( + uint32 total_message_size, // for user, headers and body + const char *content_type) +{ + char *sizeString = PR_smprintf("OPEN Size: %ld", total_message_size); + Log("STREAM",sizeString,"Begin Message Download Stream"); + FREEIF(sizeString); + //PR_LOG(IMAP, out, ("STREAM: Begin Message Download Stream. Size: %ld", total_message_size)); + StreamInfo *si = (StreamInfo *) XP_ALLOC (sizeof (StreamInfo)); // This will be freed in the event + if (si) + { + si->size = total_message_size; + si->content_type = XP_STRDUP(content_type); + if (si->content_type) + { + TImapFEEvent *setupStreamEvent = + new TImapFEEvent(SetupMsgWriteStream, // function to call + this, // access to current entry + (void *) si, + TRUE); // ok to run when interrupted because si is FREE'd in the event + + if (setupStreamEvent) + { + fFEEventQueue->AdoptEventToEnd(setupStreamEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + fFromHeaderSeen = FALSE; + } + else + HandleMemoryFailure(); + } + else + HandleMemoryFailure(); +} + + +void TNavigatorImapConnection::AddXMozillaStatusLine(uint16 /* flags */) // flags not use now +{ + static char statusLine[] = "X-Mozilla-Status: 0201\r\n"; + HandleMessageDownLoadLine(statusLine, FALSE); +} + + +void TNavigatorImapConnection::HandleMessageDownLoadLine(const char *line, XP_Bool chunkEnd) +{ + // when we duplicate this line, whack it into the native line + // termination. Do not assume that it really ends in CRLF + // to start with, even though it is supposed to be RFC822 + + // If we are fetching by chunks, we can make no assumptions about + // the end-of-line terminator, and we shouldn't mess with it. + + // leave enough room for two more chars. (CR and LF) + char *localMessageLine = (char *) XP_ALLOC(strlen(line) + 3); + if (localMessageLine) + strcpy(localMessageLine,line); + char *endOfLine = localMessageLine + strlen(localMessageLine); + + if (!chunkEnd) + { +#if (LINEBREAK_LEN == 1) + if ((endOfLine - localMessageLine) >= 2 && + endOfLine[-2] == CR && + endOfLine[-1] == LF) + { + /* CRLF -> CR or LF */ + endOfLine[-2] = LINEBREAK[0]; + endOfLine[-1] = '\0'; + } + else if (endOfLine > localMessageLine + 1 && + endOfLine[-1] != LINEBREAK[0] && + ((endOfLine[-1] == CR) || (endOfLine[-1] == LF))) + { + /* CR -> LF or LF -> CR */ + endOfLine[-1] = LINEBREAK[0]; + } + else // no eol characters at all + { + endOfLine[0] = LINEBREAK[0]; // CR or LF + endOfLine[1] = '\0'; + } +#else + if (((endOfLine - localMessageLine) >= 2 && endOfLine[-2] != CR) || + ((endOfLine - localMessageLine) >= 1 && endOfLine[-1] != LF)) + { + if ((endOfLine[-1] == CR) || (endOfLine[-1] == LF)) + { + /* LF -> CRLF or CR -> CRLF */ + endOfLine[-1] = LINEBREAK[0]; + endOfLine[0] = LINEBREAK[1]; + endOfLine[1] = '\0'; + } + else // no eol characters at all + { + endOfLine[0] = LINEBREAK[0]; // CR + endOfLine[1] = LINEBREAK[1]; // LF + endOfLine[2] = '\0'; + } + } +#endif + } + + const char *xSenderInfo = GetServerStateParser().GetXSenderInfo(); + + if (xSenderInfo && *xSenderInfo && !fFromHeaderSeen) + { + if (!XP_STRNCMP("From: ", localMessageLine, 6)) + { + fFromHeaderSeen = TRUE; + if (XP_STRSTR(localMessageLine, xSenderInfo) != NULL) + AddXMozillaStatusLine(0); + GetServerStateParser().FreeXSenderInfo(); + } + } + // if this line is for a different message, or the incoming line is too big + if (((fDownLoadLineCache.CurrentUID() != GetServerStateParser().CurrentResponseUID()) && !fDownLoadLineCache.CacheEmpty()) || + (fDownLoadLineCache.SpaceAvailable() < (strlen(localMessageLine) + 1)) ) + { + if (!fDownLoadLineCache.CacheEmpty()) + { + msg_line_info *downloadLineDontDelete = fDownLoadLineCache.GetCurrentLineInfo(); + PostLineDownLoadEvent(downloadLineDontDelete); + } + fDownLoadLineCache.ResetCache(); + } + + // so now the cache is flushed, but this string might still be to big + if (fDownLoadLineCache.SpaceAvailable() < (strlen(localMessageLine) + 1) ) + { + // has to be dynamic to pass to other win16 thread + msg_line_info *downLoadInfo = (msg_line_info *) XP_ALLOC(sizeof(msg_line_info)); + if (downLoadInfo) + { + downLoadInfo->adoptedMessageLine = localMessageLine; + downLoadInfo->uidOfMessage = GetServerStateParser().CurrentResponseUID(); + PostLineDownLoadEvent(downLoadInfo); + if (!DeathSignalReceived()) + XP_FREE(downLoadInfo); + else + { + // this is very rare, interrupt while waiting to display a huge single line + // Net_InterruptIMAP will read this line so leak the downLoadInfo + + // set localMessageLine to NULL so the FREEIF( localMessageLine) leaks also + localMessageLine = NULL; + } + } + } + else + fDownLoadLineCache.CacheLine(localMessageLine, GetServerStateParser().CurrentResponseUID()); + FREEIF( localMessageLine); +} + +char* TNavigatorImapConnection::CreateUtf7ConvertedString(const char *sourceString, XP_Bool toUtf7Imap) +{ + char *convertedString = NULL; + utf_name_struct *conversion = (utf_name_struct *) XP_ALLOC(sizeof(utf_name_struct)); + if (conversion) + { + conversion->toUtf7Imap = toUtf7Imap; + conversion->sourceString = (unsigned char *) XP_STRDUP(sourceString); + conversion->convertedString = NULL; + } + + TImapFEEvent *convertEvent = + new TImapFEEvent(ConvertImapUtf7, // function to call + conversion, + this, + TRUE); + + if (convertEvent && sourceString && conversion) + { + fFEEventQueue->AdoptEventToEnd(convertEvent); + WaitForFEEventCompletion(); + convertedString = (char *) conversion->convertedString; + } + + if (!DeathSignalReceived()) + { + FREEIF(conversion->sourceString); + FREEIF(conversion); // leak these 8 bytes if we were interrupted here. + } + + return convertedString; +} + + +void TNavigatorImapConnection::PostLineDownLoadEvent(msg_line_info *downloadLineDontDelete) +{ + TImapFEEvent *endEvent = + new TImapFEEvent(ParseAdoptedMsgLine, // function to call + this, // access to current entry + (void *) downloadLineDontDelete, + FALSE); // line/msg info + + if (endEvent && downloadLineDontDelete && downloadLineDontDelete->adoptedMessageLine) + { + fFEEventQueue->AdoptEventToEnd(endEvent); + + // I just put a big buffer in the event queue that I need to reuse, so wait. + // The alternative would be to realloc the buffer each time it gets placed in the + // queue, but I don't want the perf hit a continuously doing an alloc/delete + // of a big buffer. Also, I don't a queue full of huge buffers laying around! + WaitForFEEventCompletion(); + + // we better yield here, dealing with a huge buffer made for a slow fe event. + // because this event was processed, the queue is empty. Yielding here will + // cause NET_ProcessIMAP4 to exit. + if (!DeathSignalReceived()) + PR_Sleep(PR_INTERVAL_NO_WAIT); + } + else + HandleMemoryFailure(); +} + + +// Make sure that we receive data chunks in a size that allows the user to see some +// progress. Dont want to bore them just yet. +// If we received our chunk too fast, ask for more +// if we received our chunk too slow, ask for less, etc + +void TNavigatorImapConnection::AdjustChunkSize() +{ + fEndTime = XP_TIME(); + fTrackingTime = FALSE; + time_t t = fEndTime - fStartTime; + if (t < 0) + return; // bogus for some reason + if (t <= fTooFastTime) { + fChunkSize += fChunkAddSize; + fChunkThreshold = fChunkSize + (fChunkSize / 2); + if (fChunkSize > fMaxChunkSize) + fChunkSize = fMaxChunkSize; + } + else if (t <= fIdealTime) + return; + else { + if (fChunkSize > fChunkStartSize) + fChunkSize = fChunkStartSize; + else if (fChunkSize > (fChunkAddSize * 2)) + fChunkSize -= fChunkAddSize; + fChunkThreshold = fChunkSize + (fChunkSize / 2); + } +} + + + +void TNavigatorImapConnection::NormalMessageEndDownload() +{ + Log("STREAM", "CLOSE", "Normal Message End Download Stream"); + //PR_LOG(IMAP, out, ("STREAM: Normal End Message Download Stream")); + if (fTrackingTime) + AdjustChunkSize(); + if (!fDownLoadLineCache.CacheEmpty()) + { + msg_line_info *downloadLineDontDelete = fDownLoadLineCache.GetCurrentLineInfo(); + PostLineDownLoadEvent(downloadLineDontDelete); + fDownLoadLineCache.ResetCache(); + } + + TImapFEEvent *endEvent = + new TImapFEEvent(NormalEndMsgWriteStream, // function to call + this, // access to current entry + nil, // unused + TRUE); + + if (endEvent) + fFEEventQueue->AdoptEventToEnd(endEvent); + else + HandleMemoryFailure(); +} + +void TNavigatorImapConnection::AbortMessageDownLoad() +{ + Log("STREAM", "CLOSE", "Abort Message Download Stream"); + //PR_LOG(IMAP, out, ("STREAM: Abort Message Download Stream")); + if (fTrackingTime) + AdjustChunkSize(); + if (!fDownLoadLineCache.CacheEmpty()) + { + msg_line_info *downloadLineDontDelete = fDownLoadLineCache.GetCurrentLineInfo(); + PostLineDownLoadEvent(downloadLineDontDelete); + fDownLoadLineCache.ResetCache(); + } + + TImapFEEvent *endEvent = + new TImapFEEvent(AbortMsgWriteStream, // function to call + this, // access to current entry + nil, // unused + TRUE); + + if (endEvent) + fFEEventQueue->AdoptEventToEnd(endEvent); + else + HandleMemoryFailure(); + //MSG_FolderSetGettingMail(fFolderInfo, FALSE); +} + +char *TNavigatorImapConnection::CreatePossibleTrashName(const char *prefix) +{ + IMAP_LoadTrashFolderName(); + + char *returnTrash = (char *) XP_ALLOC(XP_STRLEN(prefix) + XP_STRLEN(ImapTRASH_FOLDER_NAME) + 1); + if (returnTrash) + { + XP_STRCPY(returnTrash, prefix); + XP_STRCAT(returnTrash, ImapTRASH_FOLDER_NAME); + } + return returnTrash; +} + + + +void TNavigatorImapConnection::CanonicalChildList(const char *canonicalPrefix, XP_Bool pipeLined) +{ + char *truncatedPrefix = XP_STRDUP(canonicalPrefix); + if (truncatedPrefix) + { + if (*(truncatedPrefix + XP_STRLEN(truncatedPrefix) - 1) == '/') + *(truncatedPrefix + XP_STRLEN(truncatedPrefix) - 1) = '\0'; + + char *serverPrefix = fCurrentUrl->AllocateServerPath(truncatedPrefix); + if (serverPrefix) + { + char *utf7ListArg = CreateUtf7ConvertedString(serverPrefix,TRUE); + if (utf7ListArg) + { + char *pattern = PR_smprintf("%s%c%c",utf7ListArg, fCurrentUrl->GetOnlineSubDirSeparator(),'%'); + if (pattern) + { + List(pattern,pipeLined); + XP_FREE(pattern); + } + XP_FREE(utf7ListArg); + } + XP_FREE(serverPrefix); + } + XP_FREE(truncatedPrefix); + } +} + +void TNavigatorImapConnection::NthLevelChildList(const char *canonicalPrefix, int depth) +{ + XP_ASSERT(depth >= 0); + if (depth < 0) return; + char *truncatedPrefix = XP_STRDUP(canonicalPrefix); + if (truncatedPrefix) + { + if (*(truncatedPrefix + XP_STRLEN(truncatedPrefix) - 1) == '/') + *(truncatedPrefix + XP_STRLEN(truncatedPrefix) - 1) = '\0'; + + char *serverPrefix = fCurrentUrl->AllocateServerPath(truncatedPrefix); + if (serverPrefix) + { + char *utf7ListArg = CreateUtf7ConvertedString(serverPrefix,TRUE); + if (utf7ListArg) + { + char *pattern = PR_smprintf("%s",utf7ListArg); + int count = 0; + char *suffix = PR_smprintf("%c%c",fCurrentUrl->GetOnlineSubDirSeparator(),'%'); + if (suffix) + { + while (pattern && (count < depth)) + { + StrAllocCat(pattern, suffix); + count++; + } + if (pattern) + { + List(pattern); + } + XP_FREE(suffix); + } + XP_FREEIF(pattern); + XP_FREE(utf7ListArg); + } + XP_FREE(serverPrefix); + } + XP_FREE(truncatedPrefix); + } +} + +static +void MOZTHREAD_ChildDiscoverySucceeded(void *blockingConnectionVoid, + void* /*unused*/) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + ReportSuccessOfChildMailboxDiscovery(imapConnection->GetActiveEntry()->window_id); +} + + +void TNavigatorImapConnection::ChildDiscoverySucceeded() +{ + TImapFEEvent *succeedEvent = + new TImapFEEvent(MOZTHREAD_ChildDiscoverySucceeded, // function to call + this, // access to current entry/context + NULL, + TRUE); + + + if (succeedEvent) + fFEEventQueue->AdoptEventToEnd(succeedEvent); + else + HandleMemoryFailure(); +} + +void TNavigatorImapConnection::DiscoverMailboxSpec(mailbox_spec *adoptedBoxSpec) +{ + IMAP_LoadTrashFolderName(); + + TIMAPNamespace *ns = TIMAPHostInfo::GetDefaultNamespaceOfTypeForHost(fCurrentUrl->GetUrlHost(), kPersonalNamespace); + const char *hostDir = ns ? ns->GetPrefix() : 0; + // if (!hostDir) // no personal or default namespaces - only public? or none altogether? + char *canonicalSubDir = NULL; + if (hostDir) + { + canonicalSubDir = XP_STRDUP(hostDir); + if (canonicalSubDir && *canonicalSubDir && (*(canonicalSubDir + XP_STRLEN(canonicalSubDir) - 1) == '/')) + *(canonicalSubDir + XP_STRLEN(canonicalSubDir) - 1) = 0; + } + + switch (fHierarchyNameState) + { + case kNoOperationInProgress: + case kDiscoverTrashFolderInProgress: + case kListingForInfoAndDiscovery: + { + if (canonicalSubDir && XP_STRSTR(adoptedBoxSpec->allocatedPathName, canonicalSubDir)) + fOnlineBaseFolderExists = TRUE; + + if (ns && hostDir) // if no personal namespace, there can be no Trash folder + { + if (!TIMAPHostInfo::GetOnlineTrashFolderExistsForHost(fCurrentUrl->GetUrlHost()) && XP_STRSTR(adoptedBoxSpec->allocatedPathName, ImapTRASH_FOLDER_NAME)) + { + XP_Bool trashExists = FALSE; + char *trashMatch = CreatePossibleTrashName(hostDir); + if (trashMatch) + { + trashExists = XP_STRCMP(trashMatch, adoptedBoxSpec->allocatedPathName) == 0; + TIMAPHostInfo::SetOnlineTrashFolderExistsForHost(fCurrentUrl->GetUrlHost(), trashExists); + FREEIF(trashMatch); + + // special case check for cmu trash, child of inbox + if (!TIMAPHostInfo::GetOnlineTrashFolderExistsForHost(fCurrentUrl->GetUrlHost()) && (ns->GetDelimiter() == '.')) + { + //char *inboxPath = TIMAPHostInfo::GetOnlineInboxPathForHost(fCurrentUrl->GetUrlHost()); + char *inboxPath = PR_smprintf("INBOX"); + if (inboxPath) + { + char *inboxAsParent = PR_smprintf("%s/",inboxPath); + if (inboxAsParent) + { + trashMatch = CreatePossibleTrashName(inboxAsParent); // "INBOX/" + if (trashMatch) + { + trashExists = XP_STRCMP(trashMatch, adoptedBoxSpec->allocatedPathName) == 0; + TIMAPHostInfo::SetOnlineTrashFolderExistsForHost(fCurrentUrl->GetUrlHost(), trashExists); + FREEIF(trashMatch); + } + XP_FREE(inboxAsParent); + } + XP_FREE(inboxPath); + } + } + } + + if (trashExists) + adoptedBoxSpec->box_flags |= kImapTrash; + } + } + + // Discover the folder (shuttle over to libmsg, yay) + // Do this only if the folder name is not empty (i.e. the root) + if (*adoptedBoxSpec->allocatedPathName) + { + char *boxNameCopy = XP_STRDUP(adoptedBoxSpec->allocatedPathName); + + TImapFEEvent *endEvent = + new TImapFEEvent(PossibleIMAPmailbox, // function to call + this, // access to current entry/context + (void *) adoptedBoxSpec, + TRUE); + // now owned by msg folder + + if (endEvent) + { + char *listArg = XP_STRDUP(adoptedBoxSpec->allocatedPathName); + fFEEventQueue->AdoptEventToEnd(endEvent); + WaitForFEEventCompletion(); + + if ((GetMailboxDiscoveryStatus() != eContinue) && + (GetMailboxDiscoveryStatus() != eContinueNew) && + (GetMailboxDiscoveryStatus() != eListMyChildren)) + { + SetConnectionStatus(-1); + } + else if (listArg && (GetMailboxDiscoveryStatus() == eListMyChildren) && + (!TIMAPHostInfo::GetHostIsUsingSubscription(fCurrentUrl->GetUrlHost()) || GetSubscribingNow())) + { + XP_ASSERT(FALSE); // we should never get here anymore. + SetMailboxDiscoveryStatus(eContinue); + if (listArg) + CanonicalChildList(listArg,TRUE); + } + else if (GetMailboxDiscoveryStatus() == eContinueNew) + { + if (fHierarchyNameState == kListingForInfoAndDiscovery && boxNameCopy) + { + // remember the info here also + TIMAPMailboxInfo *mb = new TIMAPMailboxInfo(boxNameCopy); + XP_ListAddObject(fListedMailboxList, mb); + } + SetMailboxDiscoveryStatus(eContinue); + } + FREEIF(listArg); + } + else + HandleMemoryFailure(); + + FREEIF(boxNameCopy); + } + } + break; + case kDiscoverBaseFolderInProgress: + { + if (canonicalSubDir && XP_STRSTR(adoptedBoxSpec->allocatedPathName, canonicalSubDir)) + fOnlineBaseFolderExists = TRUE; + } + break; + case kDeleteSubFoldersInProgress: + { + XP_ListAddObject(fDeletableChildren, adoptedBoxSpec->allocatedPathName); + delete adoptedBoxSpec->flagState; + FREEIF( adoptedBoxSpec); + } + break; + case kListingForInfoOnly: + { + //UpdateProgressWindowForUpgrade(adoptedBoxSpec->allocatedPathName); + ProgressEventFunction_UsingIdWithString(MK_MSG_IMAP_DISCOVERING_MAILBOX, adoptedBoxSpec->allocatedPathName); + TIMAPMailboxInfo *mb = new TIMAPMailboxInfo(adoptedBoxSpec->allocatedPathName); + XP_ListAddObject(fListedMailboxList, mb); + IMAP_FreeBoxSpec(adoptedBoxSpec); + } + break; + case kDiscoveringNamespacesOnly: + { + IMAP_FreeBoxSpec(adoptedBoxSpec); + } + break; + default: + XP_ASSERT(FALSE); + break; + } + FREEIF(canonicalSubDir); +} + +void TNavigatorImapConnection::OnlineCopyCompleted(ImapOnlineCopyState copyState) +{ + ImapOnlineCopyState *orphanedCopyState = (ImapOnlineCopyState *) XP_ALLOC(sizeof(ImapOnlineCopyState)); + if (orphanedCopyState) + *orphanedCopyState = copyState; + + TImapFEEvent *endEvent = + new TImapFEEvent(OnlineCopyReport, // function to call + this, // access to current entry/context + (void *) orphanedCopyState, + TRUE); // storage passed + + + if (endEvent && orphanedCopyState) + fFEEventQueue->AdoptEventToEnd(endEvent); + else + HandleMemoryFailure(); +} + +void TNavigatorImapConnection::FolderDeleted(const char *mailboxName) +{ + char *convertedName = CreateUtf7ConvertedString((char *) mailboxName,FALSE); + char *orphanedMailboxName = convertedName ? fCurrentUrl->AllocateCanonicalPath(convertedName) : 0; + + TImapFEEvent *deleteEvent = + new TImapFEEvent(OnlineFolderDelete, // function to call + this, // access to current entry/context + (void *) orphanedMailboxName, + TRUE); // storage passed + + + if (deleteEvent && orphanedMailboxName) + fFEEventQueue->AdoptEventToEnd(deleteEvent); + else + HandleMemoryFailure(); + + FREEIF(convertedName); +} + +void TNavigatorImapConnection::FolderNotCreated(const char *serverMessageResponse) +{ + char *serverMessage = XP_STRDUP(serverMessageResponse); + + TImapFEEvent *noCreateEvent = + new TImapFEEvent(OnlineFolderCreateFailed, // function to call + this, // access to current entry/context + (void *) serverMessage, + TRUE); // storage passed + + + if (noCreateEvent && serverMessage) + fFEEventQueue->AdoptEventToEnd(noCreateEvent); + else + HandleMemoryFailure(); +} + +void TNavigatorImapConnection::FolderRenamed(const char *oldName, + const char *newName) +{ + if ((fHierarchyNameState == kNoOperationInProgress) || + (fHierarchyNameState == kListingForInfoAndDiscovery)) + + { + char *oldConvertedName = CreateUtf7ConvertedString((char *) oldName,FALSE); + char *newConvertedName = CreateUtf7ConvertedString((char *) newName,FALSE); + + if (oldConvertedName && newConvertedName) + { + folder_rename_struct *orphanRenameStruct = (folder_rename_struct *) XP_ALLOC(sizeof(folder_rename_struct)); + orphanRenameStruct->fHostName = XP_STRDUP(GetHostName()); + orphanRenameStruct->fOldName = fCurrentUrl->AllocateCanonicalPath(oldConvertedName); + orphanRenameStruct->fNewName = fCurrentUrl->AllocateCanonicalPath(newConvertedName); + + + TImapFEEvent *renameEvent = + new TImapFEEvent(OnlineFolderRename, // function to call + this, // access to current entry/context + (void *) orphanRenameStruct, + TRUE); // storage passed + + + if (renameEvent && orphanRenameStruct && + orphanRenameStruct->fOldName && + orphanRenameStruct->fNewName) + fFEEventQueue->AdoptEventToEnd(renameEvent); + else + HandleMemoryFailure(); + } + else + HandleMemoryFailure(); + + FREEIF(oldConvertedName); + FREEIF(newConvertedName); + } +} + +void TNavigatorImapConnection::MailboxDiscoveryFinished() +{ + if (!GetSubscribingNow() && + ((fHierarchyNameState == kNoOperationInProgress) || + (fHierarchyNameState == kListingForInfoAndDiscovery))) + { + TIMAPNamespace *ns = TIMAPHostInfo::GetDefaultNamespaceOfTypeForHost(fCurrentUrl->GetUrlHost(), kPersonalNamespace); + const char *personalDir = ns ? ns->GetPrefix() : 0; + // if (!personalDir) // no personal or default NS - only public folders + if (!TIMAPHostInfo::GetOnlineTrashFolderExistsForHost(fCurrentUrl->GetUrlHost()) && + fDeleteModelIsMoveToTrash && TIMAPHostInfo::GetHostIsUsingSubscription(fCurrentUrl->GetUrlHost())) + { + // maybe we're not subscribed to the Trash folder + if (personalDir) + { + char *originalTrashName = CreatePossibleTrashName(personalDir); + fHierarchyNameState = kDiscoverTrashFolderInProgress; + List(originalTrashName); + fHierarchyNameState = kNoOperationInProgress; + } + } + + // There is no Trash folder (either LIST'd or LSUB'd), and we're using the + // Delete-is-move-to-Trash model, and there is a personal namespace + if (!TIMAPHostInfo::GetOnlineTrashFolderExistsForHost(fCurrentUrl->GetUrlHost()) && + fDeleteModelIsMoveToTrash && personalDir) + { + const char *trashPrefix = personalDir; + char *trashName = CreatePossibleTrashName(trashPrefix); + if (trashName) + { + GetServerStateParser().SetReportingErrors(FALSE); + XP_Bool created = CreateMailboxRespectingSubscriptions(trashName); + /* + // we shouldn't need this case anymore, since we're handling namespaces + // the right way, I think. + if (!created && (TIMAPUrl::GetOnlineSubDirSeparator() == '.')) + { + trashPrefix = "INBOX."; + FREEIF(trashName); + trashName = CreatePossibleTrashName(trashPrefix); + if (trashName) + created = CreateMailboxRespectingSubscriptions(trashName); + } + */ + GetServerStateParser().SetReportingErrors(TRUE); + + // force discovery of new trash folder. + if (created) + { + fHierarchyNameState = kDiscoverTrashFolderInProgress; + List(trashName); + fHierarchyNameState = kNoOperationInProgress; + } + else + TIMAPHostInfo::SetOnlineTrashFolderExistsForHost(fCurrentUrl->GetUrlHost(), TRUE); // we only try this once, not every time we create any folder. + FREEIF(trashName); + } + } + TIMAPHostInfo::SetHaveWeEverDiscoveredFoldersForHost(fCurrentUrl->GetUrlHost(), TRUE); + SetFolderDiscoveryFinished(); + } +} + + +void TNavigatorImapConnection::SetFolderDiscoveryFinished() +{ + TImapFEEvent *discoveryDoneEvent = + new TImapFEEvent(MailboxDiscoveryDoneEvent, // function to call + this, // access to current entry/context + NULL, + TRUE); + + + if (discoveryDoneEvent) + fFEEventQueue->AdoptEventToEnd(discoveryDoneEvent); + else + HandleMemoryFailure(); +} + +void TNavigatorImapConnection::UpdatedMailboxSpec(mailbox_spec *adoptedBoxSpec) +{ + TImapFEEvent *endEvent = + new TImapFEEvent(UpdatedIMAPmailbox, // function to call + this, // access to current entry/context + (void *) adoptedBoxSpec, + TRUE); // storage passed + // now owned by msg folder + + if (endEvent) + fFEEventQueue->AdoptEventToEnd(endEvent); + else + HandleMemoryFailure(); +} + +void TNavigatorImapConnection::UpdateMailboxStatus(mailbox_spec *adoptedBoxSpec) +{ + TImapFEEvent *endEvent = + new TImapFEEvent(UpdatedIMAPmailboxStatus, // function to call + this, // access to current entry/context + (void *) adoptedBoxSpec, + TRUE); // storage passed + // now owned by msg folder + + if (endEvent) + fFEEventQueue->AdoptEventToEnd(endEvent); + else + HandleMemoryFailure(); +} + +void TNavigatorImapConnection::UploadMessage() +{ + TImapFEEvent *endEvent = + new TImapFEEvent(UploadMessageEvent, // function to call + this, // access to current entry/context + nil, + TRUE); // we are the knights who say nyet! + + if (endEvent) + { + fFEEventQueue->AdoptEventToEnd(endEvent); + WaitForMessageUploadToComplete(); + } + else + HandleMemoryFailure(); +} + + +static void +UploadMessageFileHandler(void *blockingConnectionVoid, void *msgInfo) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + UploadMessageInfo *uploadMsgInfo = (UploadMessageInfo*) msgInfo; + uint32 bytesRead = 0; + int ioStatus = 0; + + if (! uploadMsgInfo->dataBuffer) + { // use 4k buffer + uploadMsgInfo->dataBuffer = + (char *) XP_ALLOC((kOutputBufferSize << 1)+1); + } + + XP_ASSERT(uploadMsgInfo->fileId && uploadMsgInfo->dataBuffer); + + if(!uploadMsgInfo->fileId || !uploadMsgInfo->dataBuffer) + { + if (uploadMsgInfo->fileId) + XP_FileClose(uploadMsgInfo->fileId); + XP_FREEIF(uploadMsgInfo->dataBuffer); + XP_FREEIF(uploadMsgInfo); + XP_InterruptContext(ce->window_id); + return; + } + + XP_ASSERT(uploadMsgInfo->bytesRemain > 0); + + bytesRead = XP_FileRead(uploadMsgInfo->dataBuffer, + (kOutputBufferSize << 1), + uploadMsgInfo->fileId); + *(uploadMsgInfo->dataBuffer+bytesRead) = 0; + ioStatus = NET_BlockingWrite(ce->socket, uploadMsgInfo->dataBuffer, + bytesRead); + // PR_LOG(IMAP, out, ("%s", uploadMsgInfo->dataBuffer)); + + uploadMsgInfo->bytesRemain -= bytesRead; + + if (uploadMsgInfo->bytesRemain <= 0) + { + ioStatus = NET_BlockingWrite(ce->socket, CRLF, XP_STRLEN(CRLF)); + imapConnection->NotifyMessageUploadMonitor(); + } + else + { + TImapFEEvent *uploadMsgFromFileEvent = + new TImapFEEvent(UploadMessageFileHandler, + (void *)imapConnection, + (void *)msgInfo, + TRUE); + if (uploadMsgFromFileEvent) + { + imapConnection->GetFEEventQueue().AdoptEventToEnd(uploadMsgFromFileEvent); + } + else + { + imapConnection->HandleMemoryFailure(); + } + } +} + +XP_Bool TNavigatorImapConnection::NewMailDetected() +{ + return fNewMail; +} + + +void TNavigatorImapConnection::ParseIMAPandCheckForNewMail(char *buff /* = NULL */) +{ + XP_Bool oldMail = fNewMail; + + if (buff) + GetServerStateParser().ParseIMAPServerResponse(buff); + else + GetServerStateParser().ParseIMAPServerResponse(GetOutputBuffer()); + + int32 numOfMessagesInFlagState = fFlagState->GetNumberOfMessages(); + + if ((GetServerStateParser().NumberOfMessages() != numOfMessagesInFlagState) && (numOfMessagesInFlagState > 0)) { + fNewMail = TRUE; + if (!fFlagState->IsLastMessageUnseen()) + fNewMail = FALSE; + } else { + fNewMail = FALSE; + } + if (fNewMail != oldMail) + { + if (fNewMail) + fCurrentBiffState = MSG_BIFF_NewMail; + else + fCurrentBiffState = MSG_BIFF_NoMail; + SendSetBiffIndicatorEvent(fCurrentBiffState); + } +} + + +void TNavigatorImapConnection::UploadMessageFromFile(const char *srcFilePath, + const char *mailboxName, + imapMessageFlagsType flags) +{ + XP_StatStruct st; + + if (-1 != XP_Stat (srcFilePath, &st, xpFileToPost)) + { + char *escapedName = CreateEscapedMailboxName(mailboxName); + + if (!escapedName) + { + XP_FREEIF(escapedName); + HandleMemoryFailure(); + return; + } + /* status command only available on IMAP4rev1 and beyond + * lets try the status first to get the next UID + */ + MessageKey newkey = MSG_MESSAGEKEYNONE; + int ioStatus; + + char flagString[70]; + long bytesRead = 0; + setup_message_flags_string(flagString, flags, SupportsUserDefinedFlags()); + + IncrementCommandTagNumber(); + PR_snprintf (GetOutputBuffer(), kOutputBufferSize, + "%s append \"%s\" (%s) {%ld}" CRLF, + GetServerCommandTag(), + escapedName, + flagString, + (long) st.st_size); + + ioStatus = WriteLineToSocket(GetOutputBuffer()); + + ParseIMAPandCheckForNewMail(); + + UploadMessageInfo *msgInfo = + (UploadMessageInfo*) XP_ALLOC(sizeof(UploadMessageInfo)); + if (!msgInfo) + { + HandleMemoryFailure(); + return; + } + msgInfo->bytesRemain = st.st_size; + msgInfo->newMsgID = newkey; + msgInfo->dataBuffer = 0; + msgInfo->fileId = XP_FileOpen(srcFilePath, xpFileToPost, XP_FILE_READ_BIN); + + TImapFEEvent *uploadMsgFromFileEvent = + new TImapFEEvent(UploadMessageFileHandler, + (void *)this, + (void *)msgInfo, + TRUE); + if (uploadMsgFromFileEvent) + { + fFEEventQueue->AdoptEventToEnd(uploadMsgFromFileEvent); + PR_Sleep(PR_INTERVAL_NO_WAIT); + WaitForMessageUploadToComplete(); + + ParseIMAPandCheckForNewMail(); + + if (GetServerStateParser().LastCommandSuccessful() + && MSG_IsSaveDraftDeliveryState(GetActiveEntry()->URL_s->fe_data)) + { + GetServerStateParser().SetIMAPstate(TImapServerState::kFolderSelected); // *** sucks..... + + if ((GetServerStateParser().GetSelectedMailboxName() && + XP_STRCMP(GetServerStateParser().GetSelectedMailboxName(), + escapedName))) + { + if (fCloseNeededBeforeSelect) + Close(); + SelectMailbox(escapedName); + } + + const char *messageId = NULL; + if (GetActiveEntry()->URL_s->fe_data) + messageId = MSG_GetMessageIdFromState( + GetActiveEntry()->URL_s->fe_data); + if (GetServerStateParser().LastCommandSuccessful() && + messageId) + { + char *tmpBuffer = PR_smprintf ( + "SEARCH SEEN HEADER Message-ID %s", + messageId); + GetServerStateParser().ResetSearchResultSequence(); + Search(tmpBuffer, TRUE, FALSE); + if (GetServerStateParser().LastCommandSuccessful()) + { + TSearchResultIterator *searchResult = + GetServerStateParser().CreateSearchResultIterator(); + newkey = searchResult->GetNextMessageNumber(); + delete searchResult; + if (newkey != MSG_MESSAGEKEYNONE) + MSG_SetIMAPMessageUID(newkey, GetActiveEntry()->URL_s->fe_data); + XP_Bool bSuc = GetServerStateParser().LastCommandSuccessful(); + } + } + } + + XP_FREEIF(msgInfo->dataBuffer); + XP_FileClose(msgInfo->fileId); + XP_FREEIF(msgInfo); + } + else + { + HandleMemoryFailure(); + } + + XP_FREEIF(escapedName); + } +} + + +void TNavigatorImapConnection::NotifyMessageFlags( imapMessageFlagsType flags, MessageKey key) +{ + tFlagsKeyStruct *keyAndFlag = (tFlagsKeyStruct *) XP_ALLOC( sizeof(tFlagsKeyStruct)); + keyAndFlag->flags = flags; + keyAndFlag->key = key; + + TImapFEEvent *flagsEvent = + new TImapFEEvent(NotifyMessageFlagsEvent, // function to call + this, // access to current entry/context + keyAndFlag, + TRUE); // storage passed + + if (flagsEvent) + fFEEventQueue->AdoptEventToEnd(flagsEvent); + else + HandleMemoryFailure(); +} + +void TNavigatorImapConnection::NotifySearchHit(const char *hitLine) +{ + if (!fNotifyHit) + return; + TImapFEEvent *hitEvent = + new TImapFEEvent(AddSearchResultEvent, // function to call + this, // access to current entry/context + (void *)hitLine, + TRUE); + + if (hitEvent) + { + fFEEventQueue->AdoptEventToEnd(hitEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + +} + +static void +ArbitraryHeadersEvent(void *blockingConnectionVoid, void *valueVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + GenericInfo *value = (GenericInfo *)valueVoid; + + value->c = MSG_GetArbitraryHeadersForHost(ce->window_id->mailMaster, value->hostName); + imapConnection->NotifyEventCompletionMonitor(); +} + +// Returns a newly allocated, space-delimited string of arbitrary headers +// which are used for filters on this host. +char *TNavigatorImapConnection::GetArbitraryHeadersToDownload() +{ + GenericInfo *value = (GenericInfo *)XP_ALLOC(sizeof(GenericInfo)); + if (!value) + return NULL; + + value->c = NULL; + value->hostName = XP_STRDUP(fCurrentUrl->GetUrlHost()); + if (!value->hostName) + { + XP_FREE(value->c); + XP_FREE(value); + return NULL; + } + TImapFEEvent *headerEvent = + new TImapFEEvent(ArbitraryHeadersEvent, // function to call + this, // access to current entry/context + value, + FALSE); + + if (headerEvent) + { + fFEEventQueue->AdoptEventToEnd(headerEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + + char *rv = NULL; + if (!DeathSignalReceived()) + rv = value->c; + XP_FREE(value->hostName); + XP_FREE(value); + return rv; +} + +void TNavigatorImapConnection::HeaderFetchCompleted() +{ + TImapFEEvent *endEvent = + new TImapFEEvent(HeaderFetchCompletedEvent, // function to call + this, // access to current entry/context + nil, + TRUE); // we are the knights who say nyet! + + if (endEvent) + fFEEventQueue->AdoptEventToEnd(endEvent); + else + HandleMemoryFailure(); +} + +// Please call only with a single message ID +void TNavigatorImapConnection::Bodystructure(const char *messageId, + XP_Bool idIsUid) +{ + IncrementCommandTagNumber(); + + char commandString[256]; + if (idIsUid) + XP_STRCPY(commandString, "%s UID fetch"); + else + XP_STRCPY(commandString, "%s fetch"); + + XP_STRCAT(commandString, " %s (BODYSTRUCTURE)"); + XP_STRCAT(commandString,CRLF); + + const char *commandTag = GetServerCommandTag(); + int protocolStringSize = XP_STRLEN(commandString) + XP_STRLEN(messageId) + XP_STRLEN(commandTag) + 1; + char *protocolString = (char *) XP_ALLOC( protocolStringSize ); + + if (protocolString) + { + PR_snprintf(protocolString, // string to create + protocolStringSize, // max size + commandString, // format string + commandTag, // command tag + messageId); + + int ioStatus = WriteLineToSocket(protocolString); + + + ParseIMAPandCheckForNewMail(protocolString); + XP_FREE(protocolString); + } + else + HandleMemoryFailure(); +} + +void TNavigatorImapConnection::PipelinedFetchMessageParts(const char *uid, + TIMAPMessagePartIDArray *parts) +{ + // assumes no chunking + + // build up a string to fetch + char *stringToFetch = NULL, *what = NULL; + int32 currentPartNum = 0; + while ((parts->GetNumParts() > currentPartNum) && !DeathSignalReceived()) + { + TIMAPMessagePartID *currentPart = parts->GetPart(currentPartNum); + if (currentPart) + { + // Do things here depending on the type of message part + // Append it to the fetch string + if (currentPartNum > 0) + StrAllocCat(stringToFetch, " "); + + switch (currentPart->GetFields()) + { + case kMIMEHeader: + what = PR_smprintf("BODY[%s.MIME]",currentPart->GetPartNumberString()); + if (what) + { + StrAllocCat(stringToFetch, what); + XP_FREE(what); + } + else + HandleMemoryFailure(); + break; + case kRFC822HeadersOnly: + if (currentPart->GetPartNumberString()) + { + what = PR_smprintf("BODY[%s.HEADER]", currentPart->GetPartNumberString()); + if (what) + { + StrAllocCat(stringToFetch, what); + XP_FREE(what); + } + else + HandleMemoryFailure(); + } + else + { + // headers for the top-level message + StrAllocCat(stringToFetch, "BODY[HEADER]"); + } + break; + default: + XP_ASSERT(FALSE); // we should only be pipelining MIME headers and Message headers + break; + } + + } + currentPartNum++; + } + + // Run the single, pipelined fetch command + if ((parts->GetNumParts() > 0) && !DeathSignalReceived() && !GetPseudoInterrupted() && stringToFetch) + { + IncrementCommandTagNumber(); + + char *commandString = PR_smprintf("%s UID fetch %s (%s)%s", GetServerCommandTag(), uid, stringToFetch, CRLF); + if (commandString) + { + int ioStatus = WriteLineToSocket(commandString); + ParseIMAPandCheckForNewMail(commandString); + XP_FREE(commandString); + } + else + HandleMemoryFailure(); + XP_FREE(stringToFetch); + } +} + + +void TNavigatorImapConnection::FetchMessage(const char *messageIds, + eFetchFields whatToFetch, + XP_Bool idIsUid, + uint32 startByte, uint32 endByte, + char *part) +{ + IncrementCommandTagNumber(); + + char commandString[350]; + if (idIsUid) + XP_STRCPY(commandString, "%s UID fetch"); + else + XP_STRCPY(commandString, "%s fetch"); + + switch (whatToFetch) { + case kEveryThingRFC822: + if (fTrackingTime) + AdjustChunkSize(); // we started another segment + fStartTime = XP_TIME(); // save start of download time + fTrackingTime = TRUE; + if (GetServerStateParser().ServerHasIMAP4Rev1Capability()) + { + if (GetServerStateParser().GetCapabilityFlag() & kHasXSenderCapability) + XP_STRCAT(commandString, " %s (XSENDER UID RFC822.SIZE BODY[]"); + else + XP_STRCAT(commandString, " %s (UID RFC822.SIZE BODY[]"); + } + else + { + if (GetServerStateParser().GetCapabilityFlag() & kHasXSenderCapability) + XP_STRCAT(commandString, " %s (XSENDER UID RFC822.SIZE RFC822"); + else + XP_STRCAT(commandString, " %s (UID RFC822.SIZE RFC822"); + } + if (endByte > 0) + { + // if we are retrieving chunks + char *byterangeString = PR_smprintf("<%ld.%ld>",startByte,endByte); + if (byterangeString) + { + XP_STRCAT(commandString, byterangeString); + XP_FREE(byterangeString); + } + } + XP_STRCAT(commandString, ")"); + break; + + case kEveryThingRFC822Peek: + { + char *formatString = ""; + uint32 server_capabilityFlags = GetServerStateParser().GetCapabilityFlag(); + + if (server_capabilityFlags & kIMAP4rev1Capability) + { + // use body[].peek since rfc822.peek is not in IMAP4rev1 + if (server_capabilityFlags & kHasXSenderCapability) + formatString = " %s (XSENDER UID RFC822.SIZE BODY.PEEK[])"; + else + formatString = " %s (UID RFC822.SIZE BODY.PEEK[])"; + } + else + { + if (server_capabilityFlags & kHasXSenderCapability) + formatString = " %s (XSENDER UID RFC822.SIZE RFC822.peek)"; + else + formatString = " %s (UID RFC822.SIZE RFC822.peek)"; + } + + XP_STRCAT(commandString, formatString); + } + break; + case kHeadersRFC822andUid: + if (GetServerStateParser().ServerHasIMAP4Rev1Capability()) + { + if (gOptimizedHeaders) + { + char *headersToDL = NULL; + char *arbitraryHeaders = GetArbitraryHeadersToDownload(); + if (arbitraryHeaders) + { + headersToDL = PR_smprintf("%s %s",IMAP_DB_HEADERS,arbitraryHeaders); + XP_FREE(arbitraryHeaders); + } + else + { + headersToDL = PR_smprintf("%s",IMAP_DB_HEADERS); + } + if (headersToDL) + { + char *what = PR_smprintf(" BODY.PEEK[HEADER.FIELDS (%s)])", headersToDL); + if (what) + { + XP_STRCAT(commandString, " %s (UID RFC822.SIZE FLAGS"); + XP_STRCAT(commandString, what); + XP_FREE(what); + } + else + { + XP_STRCAT(commandString, " %s (UID RFC822.SIZE BODY.PEEK[HEADER] FLAGS)"); + } + XP_FREE(headersToDL); + } + else + { + XP_STRCAT(commandString, " %s (UID RFC822.SIZE BODY.PEEK[HEADER] FLAGS)"); + } + } + else + XP_STRCAT(commandString, " %s (UID RFC822.SIZE BODY.PEEK[HEADER] FLAGS)"); + } + else + XP_STRCAT(commandString, " %s (UID RFC822.SIZE RFC822.HEADER FLAGS)"); + break; + case kUid: + XP_STRCAT(commandString, " %s (UID)"); + break; + case kFlags: + XP_STRCAT(commandString, " %s (FLAGS)"); + break; + case kRFC822Size: + XP_STRCAT(commandString, " %s (RFC822.SIZE)"); + break; + case kRFC822HeadersOnly: + if (GetServerStateParser().ServerHasIMAP4Rev1Capability()) + { + if (part) + { + XP_STRCAT(commandString, " %s (BODY["); + char *what = PR_smprintf("%s.HEADER])", part); + if (what) + { + XP_STRCAT(commandString, what); + XP_FREE(what); + } + else + HandleMemoryFailure(); + } + else + { + // headers for the top-level message + XP_STRCAT(commandString, " %s (BODY[HEADER])"); + } + } + else + XP_STRCAT(commandString, " %s (RFC822.HEADER)"); + break; + case kMIMEPart: + XP_STRCAT(commandString, " %s (BODY[%s]"); + if (endByte > 0) + { + // if we are retrieving chunks + char *byterangeString = PR_smprintf("<%ld.%ld>",startByte,endByte); + if (byterangeString) + { + XP_STRCAT(commandString, byterangeString); + XP_FREE(byterangeString); + } + } + XP_STRCAT(commandString, ")"); + break; + case kMIMEHeader: + XP_STRCAT(commandString, " %s (BODY[%s.MIME])"); + break; + }; + + XP_STRCAT(commandString,CRLF); + + // since messageIds can be infinitely long, use a dynamic buffer rather than the fixed one + const char *commandTag = GetServerCommandTag(); + int protocolStringSize = XP_STRLEN(commandString) + XP_STRLEN(messageIds) + XP_STRLEN(commandTag) + 1 + + (part ? XP_STRLEN(part) : 0); + char *protocolString = (char *) XP_ALLOC( protocolStringSize ); + + if (protocolString) + { + if ((whatToFetch == kMIMEPart) || + (whatToFetch == kMIMEHeader)) + { + PR_snprintf(protocolString, // string to create + protocolStringSize, // max size + commandString, // format string + commandTag, // command tag + messageIds, + part); + } + else + { + PR_snprintf(protocolString, // string to create + protocolStringSize, // max size + commandString, // format string + commandTag, // command tag + messageIds); + } + + int ioStatus = WriteLineToSocket(protocolString); + + + ParseIMAPandCheckForNewMail(protocolString); + XP_FREE(protocolString); + } + else + HandleMemoryFailure(); +} + + +void TNavigatorImapConnection::FetchTryChunking(const char *messageIds, + eFetchFields whatToFetch, + XP_Bool idIsUid, + char *part, + uint32 downloadSize) +{ + GetServerStateParser().SetTotalDownloadSize(downloadSize); + if (fFetchByChunks && GetServerStateParser().ServerHasIMAP4Rev1Capability() && + (downloadSize > (uint32) fChunkThreshold)) + { + uint32 startByte = 0; + GetServerStateParser().ClearLastFetchChunkReceived(); + while (!DeathSignalReceived() && !GetPseudoInterrupted() && + !GetServerStateParser().GetLastFetchChunkReceived() && + GetServerStateParser().ContinueParse()) + { + uint32 sizeToFetch = startByte + fChunkSize > downloadSize ? + downloadSize - startByte : fChunkSize; + FetchMessage(messageIds, + whatToFetch, + idIsUid, + startByte, sizeToFetch, + part); + startByte += sizeToFetch; + } + + // Only abort the stream if this is a normal message download + // Otherwise, let the body shell abort the stream. + if ((whatToFetch == TIMAP4BlockingConnection::kEveryThingRFC822) + && + ((startByte > 0 && (startByte < downloadSize) && + (DeathSignalReceived() || GetPseudoInterrupted())) || + !GetServerStateParser().ContinueParse())) + { + AbortMessageDownLoad(); + PseudoInterrupt(FALSE); + } + } + else + { + // small message, or (we're not chunking and not doing bodystructure), + // or the server is not rev1. + // Just fetch the whole thing. + FetchMessage(messageIds, whatToFetch,idIsUid, 0, 0, part); + } +} + + +PRMonitor *TNavigatorImapConnection::fFindingMailboxesMonitor = nil; +PRMonitor *TNavigatorImapConnection::fUpgradeToSubscriptionMonitor = nil; +XP_Bool TNavigatorImapConnection::fHaveWeEverCheckedForSubscriptionUpgrade = FALSE; +MSG_BIFF_STATE TNavigatorImapConnection::fCurrentBiffState = MSG_BIFF_Unknown; +PRMonitor *TIMAPHostInfo::gCachedHostInfoMonitor = nil; +TIMAPHostInfo *TIMAPHostInfo::fHostInfoList = nil; + +void TNavigatorImapConnection::ResetCachedConnectionInfo() +{ + fCurrentBiffState = MSG_BIFF_Unknown; + TIMAPHostInfo::ResetAll(); +} + + + + +static +void WriteLineEvent(void *blockingConnectionVoid, + void *sockInfoVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + TIMAPSocketInfo *sockInfo = (TIMAPSocketInfo *)sockInfoVoid; + + if (sockInfo && sockInfo->writeLine) + { + LIBNET_LOCK(); + sockInfo->writeStatus = (int) NET_BlockingWrite(ce->socket, + sockInfo->writeLine, + XP_STRLEN(sockInfo->writeLine)); + + FREEIF(sockInfo->writeLine); + LIBNET_UNLOCK(); + } + else + { + XP_ASSERT(FALSE); + } + + imapConnection->NotifyEventCompletionMonitor(); +} + + +static +void ReadFirstLineFromSocket(void *blockingConnectionVoid, + void *sockInfoVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + TIMAPSocketInfo *sockInfo = (TIMAPSocketInfo *)sockInfoVoid; + + if (sockInfo) + { + LIBNET_LOCK(); + Bool pause = sockInfo->GetPauseForRead(); + sockInfo->readStatus = NET_BufferedReadLine(sockInfo->GetIOSocket(), + sockInfo->GetNewLineBuffer(), + sockInfo->GetInputSocketBuffer(), + sockInfo->GetInputBufferSize(), + &pause); + + LIBNET_UNLOCK(); + } + else + { + XP_ASSERT(FALSE); + } + + imapConnection->NotifyEventCompletionMonitor(); +} + +#ifndef XP_WIN16 +#define DO_THREADED_IMAP_SOCKET_IO +#endif + +#ifndef DO_THREADED_IMAP_SOCKET_IO // do socket writes in the mozilla thread + +int TNavigatorImapConnection::WriteLineToSocket(char *line) +{ + int returnValue = 0; + + if (!DeathSignalReceived()) + { + if (line) fSocketInfo->writeLine = XP_STRDUP(line); + + fSocketInfo->writeStatus = 0; + TImapFEEvent *feWriteLineEvent = + new TImapFEEvent(WriteLineEvent, // function to call + this, // for access to current + // entry and monitor + fSocketInfo, + TRUE); + + fFEEventQueue->AdoptEventToEnd(feWriteLineEvent); + PR_Sleep(PR_INTERVAL_NO_WAIT); + + // wait here for the read first line io to finish + WaitForFEEventCompletion(); + + int socketError = SOCKET_ERRNO; + Log("NET","WR",line); + //PR_LOG(IMAP, out, ("WR: %s",line)); + int writeStatus = fSocketInfo->writeStatus; + + if(writeStatus <= 0) + { + if (fCurrentEntry && fCurrentEntry->URL_s) + fCurrentEntry->URL_s->error_msg = + NET_ExplainErrorDetails(MK_TCP_WRITE_ERROR, socketError); + returnValue = MK_TCP_WRITE_ERROR; + GetServerStateParser().SetConnected(FALSE); + } + else + PR_Sleep(PR_INTERVAL_NO_WAIT); + } + + + return returnValue; +} + + +char *TNavigatorImapConnection::CreateNewLineFromSocket() +{ + Bool pauseForRead = TRUE; + char *newLine = nil; + int socketReadStatus = -1; + + while (pauseForRead && !DeathSignalReceived()) + { + fSocketInfo->SetIOSocket(GetIOSocket()); + fSocketInfo->SetPauseForRead(pauseForRead); + fSocketInfo->SetReadStatus(socketReadStatus); + fSocketInfo->SetInputSocketBuffer(&fInputSocketBuffer); + fSocketInfo->SetInputBufferSize(&fInputBufferSize); + + TImapFEEvent *feReadFirstLineEvent = + new TImapFEEvent(ReadFirstLineFromSocket, // function to call + this, // for access to current + // entry and monitor + fSocketInfo, + TRUE); + + fFEEventQueue->AdoptEventToEnd(feReadFirstLineEvent); + PR_Sleep(PR_INTERVAL_NO_WAIT); + + // wait here for the read first line io to finish + WaitForFEEventCompletion(); + + if (fSocketInfo->newLine) + newLine = XP_STRDUP(fSocketInfo->newLine); + socketReadStatus = fSocketInfo->GetReadStatus(); + //if (*(fSocketInfo->GetNewLineBuffer())) newLine = XP_STRDUP(*(socketStuff->pNewLine)); + //FREEIF(socketStuff->pNewLine); + + int socketError = PR_GetError(); + if (newLine) + Log("NET","RD", newLine); + //PR_LOG(IMAP, out, ("RD: %s",newLine)); + + if (socketReadStatus <= 0) // error + { + +#ifdef KMCENTEE_DEBUG + XP_ASSERT(socketError != PR_NOT_CONNECTED_ERROR); +#endif + if (socketError == PR_WOULD_BLOCK_ERROR + || socketError == PR_NOT_CONNECTED_ERROR) + { + pauseForRead = TRUE; + WaitForIOCompletion(); + } + else + { + LIBNET_LOCK(); + if (fCurrentEntry && fCurrentEntry->URL_s) + fCurrentEntry->URL_s->error_msg = + NET_ExplainErrorDetails(MK_TCP_READ_ERROR, + socketError); + pauseForRead = FALSE; + socketReadStatus = MK_TCP_READ_ERROR; + GetServerStateParser().SetConnected(FALSE); + LIBNET_UNLOCK(); + } + } + else if (pauseForRead && !newLine) + WaitForIOCompletion(); + else + pauseForRead = FALSE; + } + + // the comments for NET_BufferedReadLine say that newLine is allocated + // before it is set. TO me this means that the caller owns the newLine + // storage. But I have seen it stepped on and we have assertion failures + // when we delete it! + char *bogusNewLine = NULL; + if (newLine) + { + bogusNewLine = XP_STRDUP(newLine); + if (bogusNewLine) + StrAllocCat(bogusNewLine, CRLF); + + + if (!bogusNewLine) + HandleMemoryFailure(); + } + + SetConnectionStatus(socketReadStatus); + FREEIF(newLine); + + return bogusNewLine; +} + + +#else // not Win16: do it the usual way + +int TNavigatorImapConnection::WriteLineToSocket(char *line) +{ + int returnValue = 0; + + if (!DeathSignalReceived()) + { + LIBNET_LOCK(); + int writeStatus = 0; + + // check for death signal again inside LIBNET_LOCK because + // we may have locked on LIBNET_LOCK because this context + // was being interrupted and interrupting the context means + // DeathSignalReceived is true and fCurrentEntry was deleted. + if (!DeathSignalReceived()) + writeStatus = (int) NET_BlockingWrite(fCurrentEntry->socket, + line, + XP_STRLEN(line)); + + int socketError = SOCKET_ERRNO; + Log("NET","WR",line); + //PR_LOG(IMAP, out, ("WR: %s",line)); + LIBNET_UNLOCK(); + + if(writeStatus <= 0) + { + LIBNET_LOCK(); + if (!DeathSignalReceived()) + fCurrentEntry->URL_s->error_msg = + NET_ExplainErrorDetails(MK_TCP_WRITE_ERROR, socketError); + LIBNET_UNLOCK(); + returnValue = MK_TCP_WRITE_ERROR; + GetServerStateParser().SetConnected(FALSE); + } + else + PR_Sleep(PR_INTERVAL_NO_WAIT); + } + + + return returnValue; +} + +char *TNavigatorImapConnection::CreateNewLineFromSocket() +{ + Bool pauseForRead = TRUE; + char *newLine = nil; + int socketReadStatus = -1; + + while (pauseForRead && !DeathSignalReceived()) + { + LIBNET_LOCK(); + // check for death signal again inside LIBNET_LOCK because + // we may have locked on LIBNET_LOCK because this context + // was being interrupted and interrupting the context means + // DeathSignalReceived is true and fCurrentEntry was deleted. + if (!DeathSignalReceived()) + socketReadStatus = NET_BufferedReadLine(GetIOSocket(), + &newLine, + &fInputSocketBuffer, + &fInputBufferSize, + &pauseForRead); + + int socketError = PR_GetError(); + if (newLine) + Log("NET","RD",newLine); + //PR_LOG(IMAP, out, ("RD: %s",newLine)); + LIBNET_UNLOCK(); + + if (socketReadStatus <= 0) // error + { + +#ifdef KMCENTEE_DEBUG + XP_ASSERT(socketError != PR_NOT_CONNECTED_ERROR); +#endif + if (socketError == PR_WOULD_BLOCK_ERROR + || socketError == PR_NOT_CONNECTED_ERROR) + { + pauseForRead = TRUE; + WaitForIOCompletion(); + } + else + { + LIBNET_LOCK(); + if (!DeathSignalReceived()) + { + fCurrentEntry->URL_s->error_msg = + NET_ExplainErrorDetails(MK_TCP_READ_ERROR, + socketError); + } + pauseForRead = FALSE; + socketReadStatus = MK_TCP_READ_ERROR; + GetServerStateParser().SetConnected(FALSE); + LIBNET_UNLOCK(); + } + } + else if (pauseForRead && !newLine) + WaitForIOCompletion(); + else + pauseForRead = FALSE; + + } + + // the comments for NET_BufferedReadLine say that newLine is allocated + // before it is set. TO me this means that the caller owns the newLine + // storage. But I have seen it stepped on and we have assertion failures + // when we delete it! + char *bogusNewLine = NULL; + if (newLine) + { + bogusNewLine = XP_STRDUP(newLine); + if (bogusNewLine) + StrAllocCat(bogusNewLine, CRLF); + + if (!bogusNewLine) + HandleMemoryFailure(); + } + + SetConnectionStatus(socketReadStatus); + + return bogusNewLine; +} + +#endif // not win16 + + +void TNavigatorImapConnection::SetIOSocket(PRFileDesc *socket) +{ + PR_EnterMonitor(GetDataMemberMonitor()); + fIOSocket = socket; + PR_ExitMonitor(GetDataMemberMonitor()); +} + +PRFileDesc * TNavigatorImapConnection::GetIOSocket() +{ + PRFileDesc * returnSocket; + PR_EnterMonitor(GetDataMemberMonitor()); + returnSocket = fIOSocket; + PR_ExitMonitor(GetDataMemberMonitor()); + return returnSocket; +} + +void TNavigatorImapConnection::Logout() +{ + TIMAP4BlockingConnection::Logout(); + SetIOSocket(0); +} + +void TNavigatorImapConnection::SetOutputStream(NET_StreamClass *outputStream) +{ + PR_EnterMonitor(GetDataMemberMonitor()); + fOutputStream = outputStream; + PR_ExitMonitor(GetDataMemberMonitor()); +} + +NET_StreamClass *TNavigatorImapConnection::GetOutputStream() +{ + NET_StreamClass *returnStream; + PR_EnterMonitor(GetDataMemberMonitor()); + returnStream = fOutputStream; + PR_ExitMonitor(GetDataMemberMonitor()); + return returnStream; +} + +void TNavigatorImapConnection::SetIsSafeToDie() // called by TellThreadToDie +{ + PR_EnterMonitor(fPermissionToDieMonitor); + fIsSafeToDie = TRUE; + PR_Notify(fPermissionToDieMonitor); + PR_ExitMonitor(fPermissionToDieMonitor); +} + + +void TNavigatorImapConnection::SetBlockingThread(PRThread *blockingThread) +{ + fBlockingThread = blockingThread; +} + +ActiveEntry *TNavigatorImapConnection::GetActiveEntry() +{ + return fCurrentEntry; +} + +// (Use Get/SetCurrentEntryStatus()) for passing values back to libnet about the currently running ActiveEntry. +void TNavigatorImapConnection::SetCurrentEntryStatus(int status) +{ + // There was a deadlock when the imap thread was waiting for this libnet lock and the mozilla thread + // held this lock, finished the current url and tried to launch another url from the url exit function. + // This caused the mozilla thread to never release the LIBNET_LOCK. + + XP_ASSERT(GetConnectionStatus() >= 0); + + LIBNET_LOCK(); + if (!DeathSignalReceived()) + { + ActiveEntry *ce = GetActiveEntry(); + if (ce) + ce->status = status; + } + LIBNET_UNLOCK(); +} + + +int TNavigatorImapConnection::GetCurrentEntryStatus() +{ + int returnStatus = 0; + LIBNET_LOCK(); + if (!DeathSignalReceived()) + { + ActiveEntry *ce = GetActiveEntry(); + if (ce) + returnStatus = ce->status; + } + LIBNET_UNLOCK(); + return returnStatus; +} + +TCP_ConData **TNavigatorImapConnection::GetTCPConData() +{ + return &fTCPConData; +} + +void TNavigatorImapConnection::EstablishServerConnection() +{ + // let the fe-thread start the connection since we are using + // old non-thread safe functions to do it. + + // call NET_FinishConnect until we are connected or errored out + while (!DeathSignalReceived() && (GetConnectionStatus() == MK_WAITING_FOR_CONNECTION)) + { + TImapFEEvent *feFinishConnectionEvent = + new TImapFEEvent(FinishIMAPConnection, // function to call + this, // for access to current + // entry and monitor + nil, + TRUE); + + fFEEventQueue->AdoptEventToEnd(feFinishConnectionEvent); + PR_Sleep(PR_INTERVAL_NO_WAIT); + + // wait here for the connection finish io to finish + WaitForFEEventCompletion(); + if (!DeathSignalReceived() && (GetConnectionStatus() == MK_WAITING_FOR_CONNECTION)) + WaitForIOCompletion(); + + } + + if (GetConnectionStatus() == MK_CONNECTED) + { + // get the one line response from the IMAP server + char *serverResponse = CreateNewLineFromSocket(); + if (serverResponse) + { + if (!XP_STRNCASECMP(serverResponse, "* OK", 4)) + { + SetConnectionStatus(0); + preAuthSucceeded = FALSE; + //if (!XP_STRNCASECMP(serverResponse, "* OK Netscape IMAP4rev1 Service 3.0", 35)) + // GetServerStateParser().SetServerIsNetscape30Server(); + } + else if (!XP_STRNCASECMP(serverResponse, "* PREAUTH", 9)) + { + // we've been pre-authenticated. + // we can skip the whole password step, right into the + // kAuthenticated state + GetServerStateParser().PreauthSetAuthenticatedState(); + + // tell the master that we're authenticated + XP_Bool loginSucceeded = TRUE; + TImapFEEvent *alertEvent = + new TImapFEEvent(msgSetUserAuthenticated, // function to call + this, // access to current entry + (void *)loginSucceeded, + TRUE); + if (alertEvent) + { + fFEEventQueue->AdoptEventToEnd(alertEvent); + } + else + HandleMemoryFailure(); + + preAuthSucceeded = TRUE; + if (fCurrentBiffState == MSG_BIFF_Unknown) + { + fCurrentBiffState = MSG_BIFF_NoMail; + SendSetBiffIndicatorEvent(fCurrentBiffState); + } + + if (GetServerStateParser().GetCapabilityFlag() == + kCapabilityUndefined) + Capability(); + + if ( !(GetServerStateParser().GetCapabilityFlag() & + (kIMAP4Capability | + kIMAP4rev1Capability | + kIMAP4other) ) ) + { + AlertUserEvent_UsingId(MK_MSG_IMAP_SERVER_NOT_IMAP4); + SetCurrentEntryStatus(-1); + SetConnectionStatus(-1); // stop netlib + preAuthSucceeded = FALSE; + } + else + { + ProcessAfterAuthenticated(); + // the connection was a success + SetConnectionStatus(0); + } + } + else + { + preAuthSucceeded = FALSE; + SetConnectionStatus(MK_BAD_CONNECT); + } + + fNeedGreeting = FALSE; + FREEIF(serverResponse); + } + else + SetConnectionStatus(MK_BAD_CONNECT); + } + + if ((GetConnectionStatus() < 0) && !DeathSignalReceived()) + { + if (!MSG_Biff_Master_NikiCallingGetNewMail()) + AlertUserEvent_UsingId(MK_BAD_CONNECT); + } + +} + + +void TNavigatorImapConnection::ProcessStoreFlags(const char *messageIds, + XP_Bool idsAreUids, + imapMessageFlagsType flags, + XP_Bool addFlags) +{ + if (!flags) + return; + + char *flagString; + XP_Bool userF = GetServerStateParser().SupportsUserFlags(); + + if (!userF && (flags == kImapMsgForwardedFlag || + flags == kImapMsgMDNSentFlag)) // if only flag, and not supported bail out + return; + if (addFlags) + flagString = XP_STRDUP("+Flags ("); + else + flagString = XP_STRDUP("-Flags ("); + + if (flags & kImapMsgSeenFlag) + StrAllocCat(flagString, "\\Seen "); + if (flags & kImapMsgAnsweredFlag) + StrAllocCat(flagString, "\\Answered "); + if (flags & kImapMsgFlaggedFlag) + StrAllocCat(flagString, "\\Flagged "); + if (flags & kImapMsgDeletedFlag) + StrAllocCat(flagString, "\\Deleted "); + if (flags & kImapMsgDraftFlag) + StrAllocCat(flagString, "\\Draft "); + if ((flags & kImapMsgForwardedFlag) && userF) + StrAllocCat(flagString, "Forwarded "); // if supported + if ((flags & kImapMsgMDNSentFlag) && userF) + StrAllocCat(flagString, "MDNSent "); // if supported + + // replace the final space with ')' + *(flagString + XP_STRLEN(flagString) - 1) = ')'; + + Store(messageIds, flagString, idsAreUids); + FREEIF(flagString); +} + + +void TNavigatorImapConnection::ProcessAfterAuthenticated() +{ + // if we're a netscape server, and we haven't got the admin url, get it + if (!TIMAPHostInfo::GetHostHasAdminURL(fCurrentUrl->GetUrlHost())) + { + if (GetServerStateParser().GetCapabilityFlag() & kXServerInfoCapability) + { + XServerInfo(); + if (GetServerStateParser().LastCommandSuccessful()) + { + TImapFEEvent *alertEvent = + new TImapFEEvent(msgSetMailServerURLs, // function to call + this, // access to current entry + (void *) fCurrentUrl->GetUrlHost(), + TRUE); + if (alertEvent) + { + fFEEventQueue->AdoptEventToEnd(alertEvent); + // WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + } + } + else if (GetServerStateParser().GetCapabilityFlag() & kHasXNetscapeCapability) + { + Netscape(); + if (GetServerStateParser().LastCommandSuccessful()) + { + TImapFEEvent *alertEvent = + new TImapFEEvent(msgSetMailAccountURL, // function to call + this, // access to current entry + (void *) fCurrentUrl->GetUrlHost(), + TRUE); + if (alertEvent) + { + fFEEventQueue->AdoptEventToEnd(alertEvent); + // WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + } + } + } + + if (GetServerStateParser().ServerHasNamespaceCapability() && + TIMAPHostInfo::GetNamespacesOverridableForHost(fCurrentUrl->GetUrlHost())) + { + Namespace(); + } +} + +XP_Bool TNavigatorImapConnection::TryToLogon() +{ + + int32 logonTries = 0; + XP_Bool loginSucceeded = FALSE; + + do + { + char *passwordForHost = TIMAPHostInfo::GetPasswordForHost(fCurrentUrl->GetUrlHost()); + XP_Bool imapPasswordIsNew = FALSE; + XP_Bool setUserAuthenticatedIsSafe = FALSE; + ActiveEntry *ce = GetActiveEntry(); + MSG_Master *master = (ce) ? ce->window_id->mailMaster : 0; + + const char *userName = MSG_GetIMAPHostUsername(master, fCurrentUrl->GetUrlHost()); + if (userName && !passwordForHost) + { + TImapFEEvent *fePasswordEvent = + new TImapFEEvent(GetPasswordEventFunction, + this, + (void*) userName, + TRUE); + + if (fePasswordEvent) + { + fFEEventQueue->AdoptEventToEnd(fePasswordEvent); + IMAP_YIELD(PR_INTERVAL_NO_WAIT); + + // wait here password prompt to finish + WaitForFEEventCompletion(); + imapPasswordIsNew = TRUE; + passwordForHost = TIMAPHostInfo::GetPasswordForHost(fCurrentUrl->GetUrlHost()); + } + else + HandleMemoryFailure(); + } + + if (userName && passwordForHost) + { + XP_Bool prefBool = FALSE; + + XP_Bool lastReportingErrors = GetServerStateParser().GetReportingErrors(); + GetServerStateParser().SetReportingErrors(FALSE); // turn off errors - we'll put up our own. + + PREF_GetBoolPref("mail.auth_login", &prefBool); + if (prefBool) { + if (GetServerStateParser().GetCapabilityFlag() == + kCapabilityUndefined) + Capability(); + if (GetServerStateParser().GetCapabilityFlag() & + kHasAuthLoginCapability) + { + AuthLogin (userName, passwordForHost); + logonTries++; // I think this counts against most servers as a logon try + } + else + InsecureLogin(userName, passwordForHost); + } + else + InsecureLogin(userName, passwordForHost); + + if (!GetServerStateParser().LastCommandSuccessful()) + { + // login failed! + // if we failed because of an interrupt, then do not bother the user + if (!DeathSignalReceived()) + { + AlertUserEvent_UsingId(XP_MSG_IMAP_LOGIN_FAILED); + if (passwordForHost != NULL) + { + TIMAPHostInfo::SetPasswordForHost(fCurrentUrl->GetUrlHost(), NULL); + } + fCurrentBiffState = MSG_BIFF_Unknown; + SendSetBiffIndicatorEvent(fCurrentBiffState); + } + + HandleCurrentUrlError(); +// SetConnectionStatus(-1); // stop netlib + } + else // login succeeded + { + MSG_SetIMAPHostPassword(ce->window_id->mailMaster, fCurrentUrl->GetUrlHost(), passwordForHost); + imapPasswordIsNew = !TIMAPHostInfo::GetPasswordVerifiedOnline(fCurrentUrl->GetUrlHost()); + if (imapPasswordIsNew) + TIMAPHostInfo::SetPasswordVerifiedOnline(fCurrentUrl->GetUrlHost()); + NET_SetPopPassword2(passwordForHost); // bug 53380 + if (imapPasswordIsNew) + { + if (fCurrentBiffState == MSG_BIFF_Unknown) + { + fCurrentBiffState = MSG_BIFF_NoMail; + SendSetBiffIndicatorEvent(fCurrentBiffState); + } + LIBNET_LOCK(); + if (!DeathSignalReceived()) + { + setUserAuthenticatedIsSafe = GetActiveEntry()->URL_s->msg_pane != NULL; + MWContext *context = GetActiveEntry()->window_id; + if (context) + FE_RememberPopPassword(context, SECNAV_MungeString(passwordForHost)); + } + LIBNET_UNLOCK(); + } + loginSucceeded = TRUE; + } + GetServerStateParser().SetReportingErrors(lastReportingErrors); // restore it + + if (loginSucceeded && imapPasswordIsNew) + { + TImapFEEvent *alertEvent = + new TImapFEEvent(msgSetUserAuthenticated, // function to call + this, // access to current entry + (void *)loginSucceeded, + TRUE); + if (alertEvent) + { + fFEEventQueue->AdoptEventToEnd(alertEvent); + // WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + } + + if (loginSucceeded) + { + ProcessAfterAuthenticated(); + } + } + else + { + // The user hit "Cancel" on the dialog box + //AlertUserEvent("Login cancelled."); + HandleCurrentUrlError(); + SetCurrentEntryStatus(-1); + SetConnectionStatus(-1); // stop netlib + break; + } + } + + while (!loginSucceeded && ++logonTries < 4); + + if (!loginSucceeded) + { + fCurrentBiffState = MSG_BIFF_Unknown; + SendSetBiffIndicatorEvent(fCurrentBiffState); + HandleCurrentUrlError(); + SetConnectionStatus(-1); // stop netlib + } + + return loginSucceeded; +} + + +void TNavigatorImapConnection::ProcessCurrentURL() +{ + XP_ASSERT(GetFEEventQueue().NumberOfEventsInQueue() == 0); + + if (fCurrentUrl->ValidIMAPUrl()) + { + // Reinitialize the parser + GetServerStateParser().InitializeState(); + + // establish the connection and login + if ((GetConnectionStatus() == MK_WAITING_FOR_CONNECTION) || + fNeedGreeting) + EstablishServerConnection(); + + if (!DeathSignalReceived() && (GetConnectionStatus() >= 0) && + (GetServerStateParser().GetIMAPstate() == + TImapServerState::kNonAuthenticated)) + { + /* if we got here, the server's greeting should not have + been PREAUTH */ + if (GetServerStateParser().GetCapabilityFlag() == + kCapabilityUndefined) + Capability(); + + if ( !(GetServerStateParser().GetCapabilityFlag() & + (kIMAP4Capability | + kIMAP4rev1Capability | + kIMAP4other) ) ) + { + AlertUserEvent_UsingId(MK_MSG_IMAP_SERVER_NOT_IMAP4); + SetCurrentEntryStatus(-1); + SetConnectionStatus(-1); // stop netlib + } + else + { + TryToLogon(); + } + } + + if (!DeathSignalReceived() && (GetConnectionStatus() >= 0)) + { + FindMailboxesIfNecessary(); + + if (fCurrentUrl->GetUrlIMAPstate() == + TIMAPUrl::kAuthenticatedStateURL) + ProcessAuthenticatedStateURL(); + else // must be kSelectedStateURL + ProcessSelectedStateURL(); + + // The URL has now been processed + if (GetConnectionStatus() < 0) + HandleCurrentUrlError(); + if (GetServerStateParser().LastCommandSuccessful()) + SetCurrentEntryStatus(0); + SetConnectionStatus(-1); // stop netlib + + if (DeathSignalReceived()) + HandleCurrentUrlError(); + } + else + HandleCurrentUrlError(); + } + else + { + if (!fCurrentUrl->ValidIMAPUrl()) + AlertUserEvent("Invalid IMAP4 url"); + SetCurrentEntryStatus(-1); + SetConnectionStatus(-1); + } + + PseudoInterrupt(FALSE); // clear this, because we must be done interrupting? + + //ProgressUpdateEvent("Current IMAP Url Completed"); +} + +void TNavigatorImapConnection::FolderHeaderDump(uint32 *msgUids, uint32 msgCount) +{ + FolderMsgDump(msgUids, msgCount, TIMAP4BlockingConnection::kHeadersRFC822andUid); + + if (GetServerStateParser().NumberOfMessages()) + { + HeaderFetchCompleted(); + + // if an INBOX exists on this host + //char *inboxName = TIMAPHostInfo::GetOnlineInboxPathForHost(fCurrentUrl->GetUrlHost()); + char *inboxName = PR_smprintf("INBOX"); + if (inboxName) + { + // if this was the inbox, turn biff off + if (!XP_STRCASECMP(GetServerStateParser().GetSelectedMailboxName(), inboxName)) + { + fCurrentBiffState = MSG_BIFF_NoMail; + SendSetBiffIndicatorEvent(fCurrentBiffState); + } + XP_FREE(inboxName); + } + } +} + +void TNavigatorImapConnection::ShowProgress() +{ + if (fProgressStringId) + { + char *progressString = NULL; + const char *mailboxName = GetServerStateParser().GetSelectedMailboxName(); + progressString = PR_sprintf_append(progressString, XP_GetString(fProgressStringId), (mailboxName) ? mailboxName : "", ++fProgressIndex, fProgressCount); + if (progressString) + PercentProgressUpdateEvent(progressString,(100*(fProgressIndex))/fProgressCount ); + FREEIF(progressString); + } +} + +void TNavigatorImapConnection::FolderMsgDump(uint32 *msgUids, uint32 msgCount, TIMAP4BlockingConnection::eFetchFields fields) +{ + switch (fields) { + case TIMAP4BlockingConnection::kHeadersRFC822andUid: + fProgressStringId = XP_RECEIVING_MESSAGE_HEADERS_OF; + break; + case TIMAP4BlockingConnection::kFlags: + fProgressStringId = XP_RECEIVING_MESSAGE_FLAGS_OF; + break; + default: + fProgressStringId = XP_FOLDER_RECEIVING_MESSAGE_OF; + break; + } + + fProgressIndex = 0; + fProgressCount = msgCount; + + FolderMsgDumpRecurse(msgUids, msgCount, fields); + + fProgressStringId = 0; +} + +uint32 TNavigatorImapConnection::CountMessagesInIdString(const char *idString) +{ + uint32 numberOfMessages = 0; + char *uidString = XP_STRDUP(idString); + + if (uidString) + { + // This is in the form ,, or : + char curChar = *uidString; + XP_Bool isRange = FALSE; + int32 curToken; + int32 saveStartToken=0; + + for (char *curCharPtr = uidString; curChar && *curCharPtr;) + { + char *currentKeyToken = curCharPtr; + curChar = *curCharPtr; + while (curChar != ':' && curChar != ',' && curChar != '\0') + curChar = *curCharPtr++; + *(curCharPtr - 1) = '\0'; + curToken = atol(currentKeyToken); + if (isRange) + { + while (saveStartToken < curToken) + { + numberOfMessages++; + saveStartToken++; + } + } + + numberOfMessages++; + isRange = (curChar == ':'); + if (isRange) + saveStartToken = curToken + 1; + } + XP_FREE(uidString); + } + return numberOfMessages; +} + +char *IMAP_AllocateImapUidString(uint32 *msgUids, uint32 msgCount) +{ + int blocksAllocated = 1; + char *returnIdString = (char *) XP_ALLOC(256); + if (returnIdString) + { + char *currentidString = returnIdString; + *returnIdString = 0; + + int32 startSequence = (msgCount > 0) ? msgUids[0] : -1; + int32 curSequenceEnd = startSequence; + uint32 total = msgCount; + + for (uint32 keyIndex=0; returnIdString && (keyIndex < total); keyIndex++) + { + int32 curKey = msgUids[keyIndex]; + int32 nextKey = (keyIndex + 1 < total) ? msgUids[keyIndex + 1] : -1; + XP_Bool lastKey = (nextKey == -1); + + if (lastKey) + curSequenceEnd = curKey; + if (nextKey == curSequenceEnd + 1 && !lastKey) + { + curSequenceEnd = nextKey; + continue; + } + else if (curSequenceEnd > startSequence) + { + sprintf(currentidString, "%ld:%ld,", startSequence, curSequenceEnd); + startSequence = nextKey; + curSequenceEnd = startSequence; + } + else + { + startSequence = nextKey; + curSequenceEnd = startSequence; + sprintf(currentidString, "%ld,", msgUids[keyIndex]); + } + currentidString += XP_STRLEN(currentidString); + if ((currentidString + 20) > (returnIdString + (blocksAllocated * 256))) + { + returnIdString = (char *) XP_REALLOC(returnIdString, ++blocksAllocated*256); + if (returnIdString) + currentidString = returnIdString + XP_STRLEN(returnIdString); + } + } + } + + if (returnIdString && *returnIdString) + *(returnIdString + XP_STRLEN(returnIdString) - 1) = 0; // eat the comma + + return returnIdString; +} + + +void TNavigatorImapConnection::FolderMsgDumpRecurse(uint32 *msgUids, uint32 msgCount, TIMAP4BlockingConnection::eFetchFields fields) +{ + PastPasswordCheckEvent(); + + if (msgCount <= 200) + { + char *idString = IMAP_AllocateImapUidString(msgUids, msgCount); // 20 * 200 + if (idString) + { + FetchMessage(idString, + fields, + TRUE); // msg ids are uids + XP_FREE(idString); + } + else + HandleMemoryFailure(); + } + else + { + FolderMsgDumpRecurse(msgUids, 200, fields); + FolderMsgDumpRecurse(msgUids + 200, msgCount - 200, fields); + } +} + + + +void TNavigatorImapConnection::SendSetBiffIndicatorEvent(MSG_BIFF_STATE /*newState*/) +{ + /* TImapFEEvent *biffIndicatorEvent = + new TImapFEEvent(SetBiffIndicator, // function to call + (void *) (unsigned long) newState, + this); + + if (newState == MSG_BIFF_NewMail) + fMailToFetch = TRUE; + else + fMailToFetch = FALSE; + if (biffIndicatorEvent) + { + fFEEventQueue->AdoptEventToEnd(biffIndicatorEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); */ +} + + + +// We get called to see if there is mail waiting for us at the server, even if it may have been +// read elsewhere. We just want to know if we should download headers or not. + +XP_Bool TNavigatorImapConnection::CheckNewMail() +{ + if (!fGetHeaders) + return FALSE; // use biff instead + return TRUE; + /* if (!fMailToFetch && fNewMail) { // This was the old style biff, we don't want this anymore + fMailToFetch = TRUE; + if (fCurrentBiffState != MSG_BIFF_NewMail) + { + fCurrentBiffState = MSG_BIFF_NewMail; + //SendSetBiffIndicatorEvent(fCurrentBiffState); deadlock + } + } + return fMailToFetch; */ +} + + + + +// Use the noop to tell the server we are still here, and therefore we are willing to receive +// status updates. The recent or exists response from the server could tell us that there is +// more mail waiting for us, but we need to check the flags of the mail and the high water mark +// to make sure that we do not tell the user that there is new mail when perhaps they have +// already read it in another machine. + +void TNavigatorImapConnection::PeriodicBiff() +{ + + MSG_BIFF_STATE startingState = fCurrentBiffState; + + //if (fCurrentBiffState == MSG_BIFF_NewMail) // dont do this since another computer could be used to read messages + // return; + if (!fFlagState) + return; + if (GetServerStateParser().GetIMAPstate() == TImapServerState::kFolderSelected) + { + Noop(); // check the latest number of messages + if (GetServerStateParser().NumberOfMessages() != fFlagState->GetNumberOfMessages()) + { + uint32 id = GetServerStateParser().HighestRecordedUID() + 1; + char fetchStr[100]; // only update flags + uint32 added = 0, deleted = 0; + + deleted = fFlagState->GetNumberOfDeletedMessages(); + added = fFlagState->GetNumberOfMessages(); + if (!added || (added == deleted)) // empty keys, get them all + id = 1; + + //sprintf(fetchStr, "%ld:%ld", id, id + GetServerStateParser().NumberOfMessages() - fFlagState->GetNumberOfMessages()); + sprintf(fetchStr, "%ld:*", id); + FetchMessage(fetchStr, TIMAP4BlockingConnection::kFlags, TRUE); + + if (fFlagState && ((uint32) fFlagState->GetHighestNonDeletedUID() >= id) && fFlagState->IsLastMessageUnseen()) + fCurrentBiffState = MSG_BIFF_NewMail; + else + fCurrentBiffState = MSG_BIFF_NoMail; + } + else + fCurrentBiffState = MSG_BIFF_NoMail; + } + else + fCurrentBiffState = MSG_BIFF_Unknown; + + if (startingState != fCurrentBiffState) + SendSetBiffIndicatorEvent(fCurrentBiffState); +} + +void TNavigatorImapConnection::HandleCurrentUrlError() +{ + if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kSelectFolder) + { + // let the front end know the select failed so they + // don't leave the view without a database. + mailbox_spec *notSelectedSpec = (mailbox_spec *) XP_CALLOC(1, sizeof( mailbox_spec)); + if (notSelectedSpec) + { + notSelectedSpec->allocatedPathName = fCurrentUrl->CreateCanonicalSourceFolderPathString(); + notSelectedSpec->hostName = fCurrentUrl->GetUrlHost(); + notSelectedSpec->folderSelected = FALSE; + notSelectedSpec->flagState = NULL; + notSelectedSpec->onlineVerified = FALSE; + UpdatedMailboxSpec(notSelectedSpec); + } + } +} + +XP_Bool TNavigatorImapConnection::RenameHierarchyByHand(const char *oldParentMailboxName, const char *newParentMailboxName) +{ + XP_Bool renameSucceeded = TRUE; + fDeletableChildren = XP_ListNew(); + + XP_Bool nonHierarchicalRename = ((GetServerStateParser().GetCapabilityFlag() & kNoHierarchyRename) + || MailboxIsNoSelectMailbox(oldParentMailboxName)); + + if (fDeletableChildren) + { + fHierarchyNameState = kDeleteSubFoldersInProgress; + TIMAPNamespace *ns = TIMAPHostInfo::GetNamespaceForMailboxForHost(fCurrentUrl->GetUrlHost(), oldParentMailboxName); // for delimiter + if (!ns) + { + if (!XP_STRCASECMP(oldParentMailboxName, "INBOX")) + ns = TIMAPHostInfo::GetDefaultNamespaceOfTypeForHost(fCurrentUrl->GetUrlHost(), kPersonalNamespace); + } + if (ns) + { + char *pattern = PR_smprintf("%s%c*", oldParentMailboxName,ns->GetDelimiter()); + if (pattern) + { + if (TIMAPHostInfo::GetHostIsUsingSubscription(fCurrentUrl->GetUrlHost())) + Lsub(pattern); + else + List(pattern); + XP_FREE(pattern); + } + } + fHierarchyNameState = kNoOperationInProgress; + + if (GetServerStateParser().LastCommandSuccessful()) + renameSucceeded = RenameMailboxRespectingSubscriptions(oldParentMailboxName,newParentMailboxName, TRUE); // rename this, and move subscriptions + + int numberToDelete = XP_ListCount(fDeletableChildren); + + for (int childIndex = 1; (childIndex <= numberToDelete) && renameSucceeded; childIndex++) + { + // the imap parser has already converted to a non UTF7 string in the canonical + // format so convert it back + char *currentName = (char *) XP_ListGetObjectNum(fDeletableChildren, childIndex); + if (currentName) + { + char *serverName = fCurrentUrl->AllocateServerPath(currentName); + char *convertedName = serverName ? CreateUtf7ConvertedString(serverName, TRUE) : (char *)NULL; + FREEIF(serverName); + currentName = convertedName; // currentName not leaked, deleted in XP_ListDestroy + } + + // calculate the new name and do the rename + char *newChildName = (char *) XP_ALLOC(XP_STRLEN(currentName) + XP_STRLEN(newParentMailboxName) + 1); + if (newChildName) + { + XP_STRCPY(newChildName, newParentMailboxName); + XP_STRCAT(newChildName, currentName + XP_STRLEN(oldParentMailboxName)); + RenameMailboxRespectingSubscriptions(currentName,newChildName, nonHierarchicalRename); // pass in xNonHierarchicalRename to + // determine if we should really reanme, + // or just move subscriptions + renameSucceeded = GetServerStateParser().LastCommandSuccessful(); + XP_FREE(newChildName); + } + FREEIF(currentName); + } + + XP_ListDestroy(fDeletableChildren); + fDeletableChildren = NULL; + } + return renameSucceeded; +} + +XP_Bool TNavigatorImapConnection::DeleteSubFolders(const char *selectedMailbox) +{ + XP_Bool deleteSucceeded = TRUE; + fDeletableChildren = XP_ListNew(); + + if (fDeletableChildren) + { + fHierarchyNameState = kDeleteSubFoldersInProgress; + char *pattern = PR_smprintf("%s%c*", selectedMailbox, fCurrentUrl->GetOnlineSubDirSeparator()); + if (pattern) + { + List(pattern); + XP_FREE(pattern); + } + fHierarchyNameState = kNoOperationInProgress; + + // this should be a short list so perform a sequential search for the + // longest name mailbox. Deleting the longest first will hopefully prevent the server + // from having problems about deleting parents + int numberToDelete = XP_ListCount(fDeletableChildren); + + deleteSucceeded = GetServerStateParser().LastCommandSuccessful(); + for (int outerIndex = 1; (outerIndex <= numberToDelete) && deleteSucceeded; outerIndex++) + { + char *longestName = NULL; + for (int innerIndex = 1; innerIndex <= XP_ListCount(fDeletableChildren); innerIndex++) + { + char *currentName = (char *) XP_ListGetObjectNum(fDeletableChildren, innerIndex); + if (!longestName || (XP_STRLEN(longestName) < XP_STRLEN(currentName) ) ) + longestName = currentName; + } + XP_ASSERT(longestName); + XP_ListRemoveObject(fDeletableChildren, longestName); + + // the imap parser has already converted to a non UTF7 string in the canonical + // format so convert it back + if (longestName) + { + char *serverName = fCurrentUrl->AllocateServerPath(longestName); + char *convertedName = serverName ? CreateUtf7ConvertedString(serverName, TRUE) : 0; + FREEIF(serverName); + XP_FREE(longestName); + longestName = convertedName; + } + + // some imap servers include the selectedMailbox in the list of + // subfolders of the selectedMailbox. Check for this so we don't delete + // the selectedMailbox (usually the trash and doing an empty trash) + + // The Cyrus imap server ignores the "INBOX.Trash" constraining string passed + // to the list command. Be defensive and make sure we only delete children of the trash + if (longestName && + XP_STRCMP(selectedMailbox, longestName) && + !XP_STRNCMP(selectedMailbox, longestName, XP_STRLEN(selectedMailbox))) + { + XP_Bool deleted = DeleteMailboxRespectingSubscriptions(longestName); + if (deleted) + FolderDeleted(longestName); + deleteSucceeded = deleted; + } + FREEIF(longestName); + } + + XP_ListDestroy(fDeletableChildren); + fDeletableChildren = NULL; + } + return deleteSucceeded; +} + + +void TNavigatorImapConnection::ProcessMailboxUpdate(XP_Bool handlePossibleUndo, XP_Bool /*fromBiffUpdate*/) +{ + if (DeathSignalReceived()) + return; + // fetch the flags and uids of all existing messages or new ones + if (!DeathSignalReceived() && GetServerStateParser().NumberOfMessages()) + { + if (handlePossibleUndo) + { + // undo any delete flags we may have asked to + char *undoIds = fCurrentUrl->CreateListOfMessageIdsString(); + if (undoIds && *undoIds) + { + // if this string started with a '-', then this is an undo of a delete + // if its a '+' its a redo + if (*undoIds == '-') + Store(undoIds+1, "-FLAGS (\\Deleted)", TRUE); // most servers will fail silently on a failure, deal with it? + else if (*undoIds == '+') + Store(undoIds+1, "+FLAGS (\\Deleted)", TRUE); // most servers will fail silently on a failure, deal with it? + else + XP_ASSERT(FALSE); + } + FREEIF(undoIds); + } + + if (!DeathSignalReceived()) // only expunge if not reading messages manually and before fetching new + { + if (fFlagState && (fFlagState->GetNumberOfDeletedMessages() >= 40) && fDeleteModelIsMoveToTrash) + Expunge(); // might be expensive, test for user cancel + } + + // make the parser record these flags + char fetchStr[100]; + uint32 added = 0, deleted = 0; + + if (fFlagState) + { + added = fFlagState->GetNumberOfMessages(); + deleted = fFlagState->GetNumberOfDeletedMessages(); + } + if (fFlagState && (!added || (added == deleted))) + FetchMessage("1:*", TIMAP4BlockingConnection::kFlags, TRUE); // id string shows uids + else { + sprintf(fetchStr, "%ld:*", GetServerStateParser().HighestRecordedUID() + 1); + FetchMessage(fetchStr, TIMAP4BlockingConnection::kFlags, TRUE); // only new messages please + } + } + else if (!DeathSignalReceived()) + GetServerStateParser().ResetFlagInfo(0); + + mailbox_spec *new_spec = GetServerStateParser().CreateCurrentMailboxSpec(); + if (new_spec && !DeathSignalReceived()) + { + MWContext *ct = NULL; + + LIBNET_LOCK(); + if (!DeathSignalReceived()) + { + // if this is an expunge url, libmsg will not ask for headers + if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kExpungeFolder) + new_spec->box_flags |= kJustExpunged; + + ct = new_spec->connection->fCurrentEntry->window_id; + ct->imapURLPane = MSG_FindPane(fCurrentEntry->window_id, MSG_ANYPANE); + } + LIBNET_UNLOCK(); + if (ct) + { + if (!ct->currentIMAPfolder) + ct->currentIMAPfolder = (MSG_IMAPFolderInfoMail *) MSG_FindImapFolder(ct->imapURLPane, fCurrentUrl->GetUrlHost(), "INBOX"); // use real folder name + /* MSG_IMAPFolderInfoMail *imapInbox; + if (urlFolder) + imapInbox = URL_s->msg_pane->GetMaster()->FindImapMailFolder(urlFolder->GetHostName(), "INBOX"); + else + imapInbox = URL_s->msg_pane->GetMaster()->FindImapMailFolder("INBOX"); + if (imapInbox) + imapInbox->NotifyFolderLoaded(URL_s->msg_pane); + */ + PR_EnterMonitor(fWaitForBodyIdsMonitor); + UpdatedMailboxSpec(new_spec); + } + } + else if (!new_spec) + HandleMemoryFailure(); + + // Block until libmsg decides whether to download headers or not. + uint32 *msgIdList; + uint32 msgCount = 0; + + if (!DeathSignalReceived()) + { + WaitForPotentialListOfMsgsToFetch(&msgIdList, msgCount); + + if (new_spec) + PR_ExitMonitor(fWaitForBodyIdsMonitor); + + if (msgIdList && !DeathSignalReceived() && GetServerStateParser().LastCommandSuccessful()) + { + FolderHeaderDump(msgIdList, msgCount); + FREEIF( msgIdList); + } + // this might be bogus, how are we going to do pane notification and stuff when we fetch bodies without + // headers! + } + // wait for a list of bodies to fetch. + if (!DeathSignalReceived() && GetServerStateParser().LastCommandSuccessful()) + { + WaitForPotentialListOfMsgsToFetch(&msgIdList, msgCount); + if ( msgCount && !DeathSignalReceived() && GetServerStateParser().LastCommandSuccessful()) + { + FolderMsgDump(msgIdList, msgCount, TIMAP4BlockingConnection::kEveryThingRFC822Peek); + FREEIF(msgIdList); + } + } + if (DeathSignalReceived()) + GetServerStateParser().ResetFlagInfo(0); +} + + +void TNavigatorImapConnection::ProcessSelectedStateURL() +{ + char *mailboxName = fCurrentUrl->CreateServerSourceFolderPathString(); + if (mailboxName) + { + char *convertedName = CreateUtf7ConvertedString(mailboxName, TRUE); + XP_FREE(mailboxName); + mailboxName = convertedName; + } + + if (mailboxName && !DeathSignalReceived()) + { + //char *inboxName = TIMAPHostInfo::GetOnlineInboxPathForHost(fCurrentUrl->GetUrlHost()); + char *inboxName = PR_smprintf("INBOX"); + TInboxReferenceCount inboxCounter(inboxName ? !XP_STRCASECMP(mailboxName, inboxName) : FALSE); + FREEIF(inboxName); + + if ( (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kBiff) && + (inboxCounter.GetInboxUsageCount() > 1)) + { + FREEIF(mailboxName); + return; + } + + XP_Bool selectIssued = FALSE; + if (GetServerStateParser().GetIMAPstate() == TImapServerState::kFolderSelected) + { + if (GetServerStateParser().GetSelectedMailboxName() && + XP_STRCMP(GetServerStateParser().GetSelectedMailboxName(), + mailboxName)) + { // we are selected in another folder + if (fCloseNeededBeforeSelect) + Close(); + if (GetServerStateParser().LastCommandSuccessful()) + { + selectIssued = TRUE; + AutoSubscribeToMailboxIfNecessary(mailboxName); + SelectMailbox(mailboxName); + } + } + else if (!GetServerStateParser().GetSelectedMailboxName()) + { // why are we in the selected state with no box name? + SelectMailbox(mailboxName); + selectIssued = TRUE; + } + else + { + // get new message counts, if any, from server + ProgressEventFunction_UsingId (MK_IMAP_STATUS_SELECTING_MAILBOX); + if (fNeedNoop) + { + Noop(); // I think this is needed when we're using a cached connection + fNeedNoop = FALSE; + } + } + } + else + { + // go to selected state + AutoSubscribeToMailboxIfNecessary(mailboxName); + SelectMailbox(mailboxName); + selectIssued = TRUE; + } + + if (selectIssued) + { + RefreshACLForFolderIfNecessary(mailboxName); + } + + XP_Bool uidValidityOk = TRUE; + if (GetServerStateParser().LastCommandSuccessful() && selectIssued && + (fCurrentUrl->GetIMAPurlType() != TIMAPUrl::kSelectFolder) && (fCurrentUrl->GetIMAPurlType() != TIMAPUrl::kLiteSelectFolder)) + { + uid_validity_info *uidStruct = (uid_validity_info *) XP_ALLOC(sizeof(uid_validity_info)); + if (uidStruct) + { + uidStruct->returnValidity = kUidUnknown; + uidStruct->hostName = fCurrentUrl->GetUrlHost(); + uidStruct->canonical_boxname = fCurrentUrl->CreateCanonicalSourceFolderPathString(); + TImapFEEvent *endEvent = + new TImapFEEvent(GetStoredUIDValidity, // function to call + this, // access to current entry/context + (void *) uidStruct, + FALSE); // retain storage ownership here + + if (endEvent) + { + fFEEventQueue->AdoptEventToEnd(endEvent); + WaitForFEEventCompletion(); + + // error on the side of caution, if the fe event fails to set uidStruct->returnValidity, then assume that UIDVALIDITY + // did not role. This is a common case event for attachments that are fetched within a browser context. + if (!DeathSignalReceived()) + uidValidityOk = (uidStruct->returnValidity == kUidUnknown) || (uidStruct->returnValidity == GetServerStateParser().FolderUID()); + } + else + HandleMemoryFailure(); + + FREEIF(uidStruct->canonical_boxname); + XP_FREE(uidStruct); + } + else + HandleMemoryFailure(); + } + + if (GetServerStateParser().LastCommandSuccessful() && !DeathSignalReceived() && (uidValidityOk || fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kDeleteAllMsgs)) + { + if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kLiteSelectFolder) + { + if (GetServerStateParser().LastCommandSuccessful()) + { + TImapFEEvent *liteselectEvent = + new TImapFEEvent(LiteSelectEvent, // function to call + (void *) this, + NULL, TRUE); + + if (liteselectEvent) + { + fFEEventQueue->AdoptEventToEnd(liteselectEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + // need to update the mailbox count - is this a good place? + ProcessMailboxUpdate(FALSE); // handle uidvalidity change + } + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kMsgFetch) + { + char *messageIdString = + fCurrentUrl->CreateListOfMessageIdsString(); + if (messageIdString) + { + // we dont want to send the flags back in a group + // GetServerStateParser().ResetFlagInfo(0); + if (HandlingMultipleMessages(messageIdString)) + { + // multiple messages, fetch them all + FetchMessage(messageIdString, + TIMAP4BlockingConnection::kEveryThingRFC822Peek, + fCurrentUrl->MessageIdsAreUids()); + } + else + { + // A single message ID + + // First, let's see if we're requesting a specific MIME part + char *imappart = fCurrentUrl->GetIMAPPartToFetch(); + if (imappart) + { + if (fCurrentUrl->MessageIdsAreUids()) + { + // We actually want a specific MIME part of the message. + // The Body Shell will generate it, even though we haven't downloaded it yet. + + TIMAPBodyShell *foundShell = TIMAPHostInfo::FindShellInCacheForHost(fCurrentUrl->GetUrlHost(), + GetServerStateParser().GetSelectedMailboxName(), messageIdString); + if (!foundShell) + { + // The shell wasn't in the cache. Deal with this case later. + Log("SHELL",NULL,"Loading part, shell not found in cache!"); + //PR_LOG(IMAP, out, ("BODYSHELL: Loading part, shell not found in cache!")); + // The parser will extract the part number from the current URL. + Bodystructure(messageIdString, fCurrentUrl->MessageIdsAreUids()); + } + else + { + Log("SHELL", NULL, "Loading Part, using cached shell."); + //PR_LOG(IMAP, out, ("BODYSHELL: Loading part, using cached shell.")); + foundShell->SetConnection(this); + GetServerStateParser().UseCachedShell(foundShell); + foundShell->Generate(imappart); + GetServerStateParser().UseCachedShell(NULL); + } + } + else + { + // Message IDs are not UIDs. + XP_ASSERT(FALSE); + } + XP_FREE(imappart); + } + else + { + // downloading a single message: try to do it by bodystructure, and/or do it by chunks + uint32 messageSize = GetMessageSize(messageIdString, + fCurrentUrl->MessageIdsAreUids()); + + // We need to check the format_out bits to see if we are allowed to leave out parts, + // or if we are required to get the whole thing. Some instances where we are allowed + // to do it by parts: when viewing a message, or its source + // Some times when we're NOT allowed: when forwarding a message, saving it, moving it, etc. + ActiveEntry *ce = GetActiveEntry(); + XP_Bool allowedToBreakApart = (ce && !DeathSignalReceived()) ? ce->URL_s->allow_content_change : FALSE; + + if (gMIMEOnDemand && + allowedToBreakApart && + !GetShouldFetchAllParts() && + GetServerStateParser().ServerHasIMAP4Rev1Capability() && + (messageSize > (uint32) gMIMEOnDemandThreshold) && + !fCurrentUrl->MimePartSelectorDetected()) // if a ?part=, don't do BS. + { + // OK, we're doing bodystructure + + // Before fetching the bodystructure, let's check our body shell cache to see if + // we already have it around. + TIMAPBodyShell *foundShell = NULL; + SetContentModified(TRUE); // This will be looked at by the cache + if (fCurrentUrl->MessageIdsAreUids()) + { + foundShell = TIMAPHostInfo::FindShellInCacheForHost(fCurrentUrl->GetUrlHost(), + GetServerStateParser().GetSelectedMailboxName(), messageIdString); + if (foundShell) + { + Log("SHELL",NULL,"Loading message, using cached shell."); + //PR_LOG(IMAP, out, ("BODYSHELL: Loading message, using cached shell.")); + foundShell->SetConnection(this); + GetServerStateParser().UseCachedShell(foundShell); + foundShell->Generate(NULL); + GetServerStateParser().UseCachedShell(NULL); + } + } + + if (!foundShell) + Bodystructure(messageIdString, fCurrentUrl->MessageIdsAreUids()); + } + else + { + // Not doing bodystructure. Fetch the whole thing, and try to do + // it by parts. + SetContentModified(FALSE); + FetchTryChunking(messageIdString, TIMAP4BlockingConnection::kEveryThingRFC822, + fCurrentUrl->MessageIdsAreUids(), NULL, messageSize); + } + } + + } + FREEIF( messageIdString); + } + else + HandleMemoryFailure(); + } + else if ((fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kSelectFolder) || (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kExpungeFolder)) + { + if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kExpungeFolder) + Expunge(); + ProcessMailboxUpdate(TRUE); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kMsgHeader) + { + char *messageIdString = + fCurrentUrl->CreateListOfMessageIdsString(); + if (messageIdString) + { + // we don't want to send the flags back in a group + // GetServerStateParser().ResetFlagInfo(0); + FetchMessage(messageIdString, + TIMAP4BlockingConnection::kHeadersRFC822andUid, + fCurrentUrl->MessageIdsAreUids()); + FREEIF( messageIdString); + } + else + HandleMemoryFailure(); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kSearch) + { + char *searchCriteriaString = + fCurrentUrl->CreateSearchCriteriaString(); + if (searchCriteriaString) + { + Search(searchCriteriaString,fCurrentUrl->MessageIdsAreUids()); + // drop the results on the floor for now + FREEIF( searchCriteriaString); + } + else + HandleMemoryFailure(); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kDeleteMsg) + { + char *messageIdString = + fCurrentUrl->CreateListOfMessageIdsString(); + + if (messageIdString) + { + if (HandlingMultipleMessages(messageIdString)) + ProgressEventFunction_UsingId (XP_IMAP_DELETING_MESSAGES); + else + ProgressEventFunction_UsingId(XP_IMAP_DELETING_MESSAGE); + Store(messageIdString, "+FLAGS (\\Deleted)", + fCurrentUrl->MessageIdsAreUids()); + + if (GetServerStateParser().LastCommandSuccessful()) + { + struct delete_message_struct *deleteMsg = (struct delete_message_struct *) XP_ALLOC (sizeof(struct delete_message_struct)); + + // convert name back from utf7 + utf_name_struct *nameStruct = (utf_name_struct *) XP_ALLOC(sizeof(utf_name_struct)); + char *convertedCanonicalName = NULL; + if (nameStruct) + { + nameStruct->toUtf7Imap = FALSE; + nameStruct->sourceString = (unsigned char *) GetServerStateParser().GetSelectedMailboxName(); + nameStruct->convertedString = NULL; + ConvertImapUtf7(nameStruct, NULL); + if (nameStruct->convertedString) + convertedCanonicalName = fCurrentUrl->AllocateCanonicalPath((char *) nameStruct->convertedString); + } + + deleteMsg->onlineFolderName = convertedCanonicalName; + deleteMsg->deleteAllMsgs = FALSE; + deleteMsg->msgIdString = messageIdString; // storage adopted, do not delete + messageIdString = nil; // deleting nil is ok + + TImapFEEvent *deleteEvent = + new TImapFEEvent(NotifyMessageDeletedEvent, // function to call + (void *) this, + (void *) deleteMsg, TRUE); + + if (deleteEvent) + fFEEventQueue->AdoptEventToEnd(deleteEvent); + else + HandleMemoryFailure(); + } + FREEIF( messageIdString); + } + else + HandleMemoryFailure(); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kDeleteAllMsgs) + { + uint32 numberOfMessages = GetServerStateParser().NumberOfMessages(); + if (numberOfMessages) + { + char messageIdString[100]; // enough for bazillion msgs + sprintf(messageIdString, "1:*"); + + Store(messageIdString, "+FLAGS (\\Deleted)", FALSE); // use sequence #'s + + if (GetServerStateParser().LastCommandSuccessful()) + Expunge(); // expunge messages with deleted flag + if (GetServerStateParser().LastCommandSuccessful()) + { + struct delete_message_struct *deleteMsg = (struct delete_message_struct *) XP_ALLOC (sizeof(struct delete_message_struct)); + + // convert name back from utf7 + utf_name_struct *nameStruct = (utf_name_struct *) XP_ALLOC(sizeof(utf_name_struct)); + char *convertedCanonicalName = NULL; + if (nameStruct) + { + nameStruct->toUtf7Imap = FALSE; + nameStruct->sourceString = (unsigned char *) GetServerStateParser().GetSelectedMailboxName(); + nameStruct->convertedString = NULL; + ConvertImapUtf7(nameStruct, NULL); + if (nameStruct->convertedString) + convertedCanonicalName = fCurrentUrl->AllocateCanonicalPath((char *) nameStruct->convertedString); + } + + deleteMsg->onlineFolderName = convertedCanonicalName; + deleteMsg->deleteAllMsgs = TRUE; + deleteMsg->msgIdString = nil; + + TImapFEEvent *deleteEvent = + new TImapFEEvent(NotifyMessageDeletedEvent, // function to call + (void *) this, + (void *) deleteMsg, TRUE); + + if (deleteEvent) + fFEEventQueue->AdoptEventToEnd(deleteEvent); + else + HandleMemoryFailure(); + } + + } + DeleteSubFolders(mailboxName); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kAppendMsgFromFile) + { + char *sourceMessageFile = + XP_STRDUP(GetActiveEntry()->URL_s->post_data); + char *mailboxName = + fCurrentUrl->CreateServerSourceFolderPathString(); + + if (mailboxName) + { + char *convertedName = CreateUtf7ConvertedString(mailboxName, TRUE); + XP_FREE(mailboxName); + mailboxName = convertedName; + } + if (mailboxName) + { + UploadMessageFromFile(sourceMessageFile, mailboxName, + kImapMsgSeenFlag); + } + else + HandleMemoryFailure(); + FREEIF( sourceMessageFile ); + FREEIF( mailboxName ); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kAddMsgFlags) + { + char *messageIdString = + fCurrentUrl->CreateListOfMessageIdsString(); + + if (messageIdString) + { + ProcessStoreFlags(messageIdString, fCurrentUrl->MessageIdsAreUids(), + fCurrentUrl->GetMsgFlags(), TRUE); + + FREEIF( messageIdString); + /* + if ( !DeathSignalReceived() && + GetServerStateParser().Connected() && + !GetServerStateParser().SyntaxError()) + { + //if (fCurrentUrl->GetMsgFlags() & kImapMsgDeletedFlag) + // Expunge(); // expunge messages with deleted flag + Check(); // flush servers flag state + } + */ + } + else + HandleMemoryFailure(); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kSubtractMsgFlags) + { + char *messageIdString = + fCurrentUrl->CreateListOfMessageIdsString(); + + if (messageIdString) + { + ProcessStoreFlags(messageIdString, fCurrentUrl->MessageIdsAreUids(), + fCurrentUrl->GetMsgFlags(), FALSE); + + FREEIF( messageIdString); + } + else + HandleMemoryFailure(); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kSetMsgFlags) + { + char *messageIdString = + fCurrentUrl->CreateListOfMessageIdsString(); + + if (messageIdString) + { + ProcessStoreFlags(messageIdString, fCurrentUrl->MessageIdsAreUids(), + fCurrentUrl->GetMsgFlags(), TRUE); + ProcessStoreFlags(messageIdString, fCurrentUrl->MessageIdsAreUids(), + ~fCurrentUrl->GetMsgFlags(), FALSE); + + FREEIF( messageIdString); + } + else + HandleMemoryFailure(); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kBiff) + { + PeriodicBiff(); + } + else if ((fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineCopy) || + (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineMove)) + { + char *messageIdString = + fCurrentUrl->CreateListOfMessageIdsString(); + char *destinationMailbox = + fCurrentUrl->CreateServerDestinationFolderPathString(); + if (destinationMailbox) + { + char *convertedName = CreateUtf7ConvertedString(destinationMailbox, TRUE); + XP_FREE(destinationMailbox); + destinationMailbox = convertedName; + } + + if (messageIdString && destinationMailbox) + { + if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineMove) { + if (HandlingMultipleMessages(messageIdString)) + ProgressEventFunction_UsingIdWithString (XP_IMAP_MOVING_MESSAGES_TO, destinationMailbox); + else + ProgressEventFunction_UsingIdWithString (XP_IMAP_MOVING_MESSAGE_TO, destinationMailbox); + } + else { + if (HandlingMultipleMessages(messageIdString)) + ProgressEventFunction_UsingIdWithString (XP_IMAP_COPYING_MESSAGES_TO, destinationMailbox); + else + ProgressEventFunction_UsingIdWithString (XP_IMAP_COPYING_MESSAGE_TO, destinationMailbox); + } + + Copy(messageIdString, destinationMailbox, + fCurrentUrl->MessageIdsAreUids()); + FREEIF( destinationMailbox); + ImapOnlineCopyState copyState; + if (DeathSignalReceived()) + copyState = kInterruptedState; + else + copyState = GetServerStateParser().LastCommandSuccessful() ? + kSuccessfulCopy : kFailedCopy; + OnlineCopyCompleted(copyState); + + if (GetServerStateParser().LastCommandSuccessful() && + (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineMove)) + { + Store(messageIdString, "+FLAGS (\\Deleted)", + fCurrentUrl->MessageIdsAreUids()); + + XP_Bool storeSuccessful = GetServerStateParser().LastCommandSuccessful(); + + OnlineCopyCompleted( storeSuccessful ? kSuccessfulDelete : kFailedDelete); + } + FREEIF( messageIdString); + } + else + HandleMemoryFailure(); + } + else if ((fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineToOfflineCopy) || + (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineToOfflineMove)) + { + char *messageIdString = + fCurrentUrl->CreateListOfMessageIdsString(); + if (messageIdString) + { + fProgressStringId = XP_FOLDER_RECEIVING_MESSAGE_OF; + fProgressIndex = 0; + fProgressCount = CountMessagesInIdString(messageIdString); + + FetchMessage(messageIdString, + TIMAP4BlockingConnection::kEveryThingRFC822Peek, + fCurrentUrl->MessageIdsAreUids()); + + fProgressStringId = 0; + OnlineCopyCompleted( + GetServerStateParser().LastCommandSuccessful() ? + kSuccessfulCopy : kFailedCopy); + + if (GetServerStateParser().LastCommandSuccessful() && + (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineToOfflineMove)) + { + Store(messageIdString, "+FLAGS (\\Deleted)", + fCurrentUrl->MessageIdsAreUids()); + XP_Bool storeSuccessful = GetServerStateParser().LastCommandSuccessful(); + + OnlineCopyCompleted( storeSuccessful ? kSuccessfulDelete : kFailedDelete); + } + + FREEIF( messageIdString); + } + else + HandleMemoryFailure(); + } + } + else if (GetServerStateParser().LastCommandSuccessful() && !uidValidityOk) + ProcessMailboxUpdate(FALSE); // handle uidvalidity change + + FREEIF( mailboxName); + } + else if (!DeathSignalReceived()) + HandleMemoryFailure(); + +} + +void TNavigatorImapConnection::AutoSubscribeToMailboxIfNecessary(const char *mailboxName) +{ + if (fFolderNeedsSubscribing) // we don't know about this folder - we need to subscribe to it / list it. + { + fHierarchyNameState = kListingForInfoOnly; + List(mailboxName); + fHierarchyNameState = kNoOperationInProgress; + + // removing and freeing it as we go. + TIMAPMailboxInfo *mb = NULL; + int total = XP_ListCount(fListedMailboxList); + do + { + mb = (TIMAPMailboxInfo *) XP_ListRemoveTopObject(fListedMailboxList); + delete mb; + } while (mb); + + // if the mailbox exists (it was returned from the LIST response) + if (total > 0) + { + // Subscribe to it, if the pref says so + if (TIMAPHostInfo::GetHostIsUsingSubscription(fCurrentUrl->GetUrlHost()) && fAutoSubscribeOnOpen) + { + Subscribe(mailboxName); + } + + // Always LIST it anyway, to get it into the folder lists, + // so that we can continue to perform operations on it, at least + // for this session. + fHierarchyNameState = kNoOperationInProgress; + List(mailboxName); + } + + // We should now be subscribed to it, and have it in our folder lists + // and panes. Even if something failed, we don't want to try this again. + fFolderNeedsSubscribing = FALSE; + + } +} + +void TNavigatorImapConnection::FindMailboxesIfNecessary() +{ + PR_EnterMonitor(fFindingMailboxesMonitor); + // biff should not discover mailboxes + XP_Bool foundMailboxesAlready = TIMAPHostInfo::GetHaveWeEverDiscoveredFoldersForHost(fCurrentUrl->GetUrlHost()); + if (!foundMailboxesAlready && + (fCurrentUrl->GetIMAPurlType() != TIMAPUrl::kBiff) && + (fCurrentUrl->GetIMAPurlType() != TIMAPUrl::kDiscoverAllBoxesUrl) && + (fCurrentUrl->GetIMAPurlType() != TIMAPUrl::kUpgradeToSubscription) && + !GetSubscribingNow()) + { + DiscoverMailboxList(); + + // If we decide to do it, here is where we should check to see if + // a namespace exists (personal namespace only?) and possibly + // create it if it doesn't exist. + } + PR_ExitMonitor(fFindingMailboxesMonitor); +} + + +// DiscoverMailboxList() is used to actually do the discovery of folders +// for a host. This is used both when we initially start up (and re-sync) +// and also when the user manually requests a re-sync, by collapsing and +// expanding a host in the folder pane. This is not used for the subscribe +// pane. +// DiscoverMailboxList() also gets the ACLs for each newly discovered folder +void TNavigatorImapConnection::DiscoverMailboxList() +{ + SetMailboxDiscoveryStatus(eContinue); + if (GetServerStateParser().ServerHasACLCapability()) + fHierarchyNameState = kListingForInfoAndDiscovery; + else + fHierarchyNameState = kNoOperationInProgress; + + // Pretend that the Trash folder doesn't exist, so we will rediscover it if we need to. + TIMAPHostInfo::SetOnlineTrashFolderExistsForHost(fCurrentUrl->GetUrlHost(), FALSE); + + // iterate through all namespaces and LSUB them. + for (int i = TIMAPHostInfo::GetNumberOfNamespacesForHost(fCurrentUrl->GetUrlHost()); i >= 1; i-- ) + { + TIMAPNamespace *ns = TIMAPHostInfo::GetNamespaceNumberForHost(fCurrentUrl->GetUrlHost(), i); + if (ns) + { + const char *prefix = ns->GetPrefix(); + if (prefix) + { + + if (*prefix) // only do it for non-empty namespace prefixes + { + // Explicitly discover each Namespace, so that we can create subfolders of them, + mailbox_spec *boxSpec = (mailbox_spec *) XP_CALLOC(1, sizeof(mailbox_spec) ); + if (boxSpec) + { + boxSpec->folderSelected = FALSE; + boxSpec->hostName = fCurrentUrl->GetUrlHost(); + boxSpec->connection = this; + boxSpec->flagState = NULL; + boxSpec->discoveredFromLsub = TRUE; + boxSpec->onlineVerified = TRUE; + boxSpec->box_flags = kNoselect; + boxSpec->hierarchySeparator = ns->GetDelimiter(); + boxSpec->allocatedPathName = fCurrentUrl->AllocateCanonicalPath(ns->GetPrefix()); + + switch (ns->GetType()) + { + case kPersonalNamespace: + boxSpec->box_flags |= kPersonalMailbox; + break; + case kPublicNamespace: + boxSpec->box_flags |= kPublicMailbox; + break; + case kOtherUsersNamespace: + boxSpec->box_flags |= kOtherUsersMailbox; + break; + default: // (kUnknownNamespace) + break; + } + + DiscoverMailboxSpec(boxSpec); + } + else + HandleMemoryFailure(); + } + + // now do the folders within this namespace + char *pattern = NULL, *pattern2 = NULL; + if (TIMAPHostInfo::GetHostIsUsingSubscription(fCurrentUrl->GetUrlHost())) + pattern = PR_smprintf("%s*", prefix); + else + { + pattern = PR_smprintf("%s%%", prefix); + char delimiter = ns->GetDelimiter(); + if (delimiter) + { + // delimiter might be NIL, in which case there's no hierarchy anyway + pattern2 = PR_smprintf("%s%%%c%%", prefix, delimiter); + } + } + if (pattern) + { + if (TIMAPHostInfo::GetHostIsUsingSubscription(fCurrentUrl->GetUrlHost())) // && !GetSubscribingNow()) should never get here from subscribe pane + Lsub(pattern); + else + { + List(pattern); + if (pattern2) + { + List(pattern2); + XP_FREE(pattern2); + } + } + XP_FREE(pattern); + } + } + } + } + + // explicitly LIST the INBOX if (a) we're not using subscription, or (b) we are using subscription and + // the user wants us to always show the INBOX. + if (GetServerStateParser().LastCommandSuccessful() && + (!TIMAPHostInfo::GetHostIsUsingSubscription(fCurrentUrl->GetUrlHost()) || + TIMAPHostInfo::GetShouldAlwaysListInboxForHost(fCurrentUrl->GetUrlHost()))) + { + List("INBOX", FALSE, TRUE); + } + fHierarchyNameState = kNoOperationInProgress; + + MailboxDiscoveryFinished(); + + // Get the ACLs for newly discovered folders + if (GetServerStateParser().ServerHasACLCapability()) + { + int total = XP_ListCount(fListedMailboxList), count = 0; + GetServerStateParser().SetReportingErrors(FALSE); + if (total) + { + ProgressEventFunction_UsingId(MK_IMAP_GETTING_ACL_FOR_FOLDER); + TIMAPMailboxInfo *mb = NULL; + do + { + mb = (TIMAPMailboxInfo *) XP_ListRemoveTopObject(fListedMailboxList); + if (mb) + { + if (FolderNeedsACLInitialized(mb->GetMailboxName())) + RefreshACLForFolder(mb->GetMailboxName()); + PercentProgressUpdateEvent(NULL, (count*100)/total); + delete mb; // this is the last time we're using the list, so delete the entries here + count++; + } + } while (mb && !DeathSignalReceived()); + } + } +} + + +void TNavigatorImapConnection::DiscoverAllAndSubscribedBoxes() +{ + // used for subscribe pane + // iterate through all namespaces + for (int i = TIMAPHostInfo::GetNumberOfNamespacesForHost(fCurrentUrl->GetUrlHost()); i >= 1; i-- ) + { + TIMAPNamespace *ns = TIMAPHostInfo::GetNamespaceNumberForHost(fCurrentUrl->GetUrlHost(), i); + if (ns) + { + const char *prefix = ns->GetPrefix(); + if (prefix) + { + // Fills in the hierarchy delimiters for some namespaces, for the case that they + // were filled in manually from the preferences. + if (ns->GetIsDelimiterFilledIn()) + { + fHierarchyNameState = kDiscoveringNamespacesOnly; + List(prefix); + fHierarchyNameState = kNoOperationInProgress; + } + + char *allPattern = PR_smprintf("%s*",prefix); + char *topLevelPattern = PR_smprintf("%s%%",prefix); + char *secondLevelPattern = NULL; + char delimiter = ns->GetDelimiter(); + if (delimiter) + { + // Hierarchy delimiter might be NIL, in which case there's no hierarchy anyway + secondLevelPattern = PR_smprintf("%s%%%c%%",prefix, delimiter); + } + if (allPattern) + { + Lsub(allPattern); // LSUB all the subscribed + XP_FREE(allPattern); + } + if (topLevelPattern) + { + List(topLevelPattern); // LIST the top level + XP_FREE(topLevelPattern); + } + if (secondLevelPattern) + { + List(secondLevelPattern); // LIST the second level + XP_FREE(secondLevelPattern); + } + } + } + } + SetFolderDiscoveryFinished(); +} + + +// report the success of the upgrade to IMAP subscription +static +void MOZTHREAD_SubscribeUpgradeFinished(void *blockingConnectionVoid, + void *endStateVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + EIMAPSubscriptionUpgradeState *endState = (EIMAPSubscriptionUpgradeState *) endStateVoid; + + MSG_ReportSuccessOfUpgradeToIMAPSubscription(imapConnection->GetActiveEntry()->window_id, endState); + imapConnection->NotifyEventCompletionMonitor(); +} + +void TNavigatorImapConnection::UpgradeToSubscriptionFinishedEvent(EIMAPSubscriptionUpgradeState endState) +{ + EIMAPSubscriptionUpgradeState *orphanedUpgradeState = (EIMAPSubscriptionUpgradeState *) XP_ALLOC(sizeof(EIMAPSubscriptionUpgradeState)); + if (orphanedUpgradeState) + *orphanedUpgradeState = endState; + + TImapFEEvent *upgradeEvent = + new TImapFEEvent(MOZTHREAD_SubscribeUpgradeFinished, // function to call + this, // access to current entry/context + orphanedUpgradeState, + FALSE); // storage passed + + + if (upgradeEvent) + { + fFEEventQueue->AdoptEventToEnd(upgradeEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); +} + +static void +MOZTHREAD_GetOldHostEvent(void *blockingConnectionVoid, + void *pOldHostNameVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + + char **pOldHostName = (char **) pOldHostNameVoid; + // fill in the value + PREF_CopyCharPref("network.hosts.pop_server", pOldHostName); + imapConnection->NotifyEventCompletionMonitor(); +} + +void TNavigatorImapConnection::GetOldIMAPHostNameEvent(char **oldHostName) +{ + TImapFEEvent *getHostEvent = + new TImapFEEvent(MOZTHREAD_GetOldHostEvent, // function to call + this, // access to current entry/context + oldHostName, + FALSE); + + if (getHostEvent) + { + fFEEventQueue->AdoptEventToEnd(getHostEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); +} + + +static void +MOZTHREAD_PromptUserForSubscriptionUpgradePath(void *blockingConnectionVoid, void *valueVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + XP_Bool *value = (XP_Bool *)valueVoid; + XP_Bool shouldWeUpgrade = FALSE; + + char *promptString = XP_GetString(MK_IMAP_UPGRADE_PROMPT_USER); + if (promptString) + { + char *promptString2 = XP_GetString(MK_IMAP_UPGRADE_PROMPT_USER_2); + if (promptString2) + { + char *finalPrompt = StrAllocCat(promptString, promptString2); + if (finalPrompt) + { + shouldWeUpgrade = FE_Confirm(ce->window_id, finalPrompt); + } + else + shouldWeUpgrade = FALSE; + } + else + shouldWeUpgrade = FALSE; + } + else + shouldWeUpgrade = FALSE; + + if (!shouldWeUpgrade) + FE_Alert(ce->window_id, XP_GetString(MK_IMAP_UPGRADE_CUSTOM)); + + *value = shouldWeUpgrade; + imapConnection->NotifyEventCompletionMonitor(); +} + +// Returns TRUE if we need to automatically upgrade them, and FALSE if +// they want to do it manually (through the Subscribe UI). +XP_Bool TNavigatorImapConnection::PromptUserForSubscribeUpgradePath() +{ + XP_Bool *value = (XP_Bool *)XP_ALLOC(sizeof(XP_Bool)); + XP_Bool rv = FALSE; + + if (!value) return FALSE; + + TImapFEEvent *getHostEvent = + new TImapFEEvent(MOZTHREAD_PromptUserForSubscriptionUpgradePath, // function to call + this, // access to current entry/context + value, + FALSE); + + if (getHostEvent) + { + fFEEventQueue->AdoptEventToEnd(getHostEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + + rv = *value; + XP_FREE(value); + return rv; +} + +// Should only use this for upgrading to subscribe model +void TNavigatorImapConnection::SubscribeToAllFolders() +{ + fHierarchyNameState = kListingForInfoOnly; + TIMAPMailboxInfo *mb = NULL; + + // This will fill in the list + if (!DeathSignalReceived()) + List("*"); + + int total = XP_ListCount(fListedMailboxList), count = 0; + GetServerStateParser().SetReportingErrors(FALSE); + if (!DeathSignalReceived()) do + { + mb = (TIMAPMailboxInfo *) XP_ListRemoveTopObject(fListedMailboxList); + if (mb) + { + Subscribe(mb->GetMailboxName()); + PercentProgressUpdateEvent(NULL, (count*100)/total); + delete mb; + count++; + } + } while (mb && !DeathSignalReceived()); + + PercentProgressUpdateEvent(NULL, 100); + + GetServerStateParser().SetReportingErrors(TRUE); + + fHierarchyNameState = kNoOperationInProgress; +} + +int TNavigatorImapConnection::GetNumberOfListedMailboxesWithUnlistedChildren() +{ + int count = 0, listSize = XP_ListCount(fListedMailboxList), i = 1; + for (i = 1; i <= listSize; i++) + { + TIMAPMailboxInfo *mb = (TIMAPMailboxInfo *)XP_ListGetObjectNum(fListedMailboxList, i); + if (!mb->GetChildrenListed()) + count++; + } + return count; +} + +void TNavigatorImapConnection::UpgradeToSubscription() +{ + EIMAPSubscriptionUpgradeState endState = kEverythingDone; + if (fCurrentUrl->GetShouldSubscribeToAll()) + { + SubscribeToAllFolders(); + } + else + { + TIMAPNamespace *ns = TIMAPHostInfo::GetDefaultNamespaceOfTypeForHost(fCurrentUrl->GetUrlHost(), kPersonalNamespace); + if (ns && ns->GetType()==kPersonalNamespace) + { + // This should handle the case of getting the personal namespace from either the + // prefs or the NAMESPACE extension. + // We will list all the personal folders, and subscribe to them all. + // We can do this, because we know they're personal, and the user said to try + // to subscribe to all. + + fHierarchyNameState = kDiscoveringNamespacesOnly; + List(ns->GetPrefix()); + + fHierarchyNameState = kListingForInfoOnly; + char *pattern = PR_smprintf("%s*",ns->GetPrefix()); + if (pattern && !DeathSignalReceived()) + List(pattern); + fHierarchyNameState = kNoOperationInProgress; + + // removing and freeing it as we go. + + GetServerStateParser().SetReportingErrors(FALSE); + int total = XP_ListCount(fListedMailboxList), count = 0; + TIMAPMailboxInfo *mb = 0; + if (!DeathSignalReceived()) do + { + mb = (TIMAPMailboxInfo *) XP_ListRemoveTopObject(fListedMailboxList); + if (mb) + { + Subscribe(mb->GetMailboxName()); + PercentProgressUpdateEvent(NULL, (count*100)/total); + delete mb; + count++; + } + } while (mb && !DeathSignalReceived()); + PercentProgressUpdateEvent(NULL, 100); + GetServerStateParser().SetReportingErrors(TRUE); + } + else if (!ns) + { + // There is no personal or default namespace. This means that the server + // supports the NAMESPACE extension, and has other namespaces, but explicitly + // has no personal namespace. I would be very surprised if we EVER encounter + // this situation, since the user must have been using Communicator 4.0 on + // a server that supported the NAMESPACE extension and only had, say, + // public folders. Not likely. All we can really do is alert the user + // and bring up the Subscribe UI. + AlertUserEvent_UsingId(MK_IMAP_UPGRADE_NO_PERSONAL_NAMESPACE); + endState = kBringUpSubscribeUI; + } + else if (!GetServerStateParser().ServerHasNamespaceCapability()) + { + // NAMESPACE extension not supported, and we only have a default namespace + // (That is, no explicit personal namespace is specified -- only "") + if ((GetServerStateParser().GetCapabilityFlag() & + kHasXNetscapeCapability) && + !DeathSignalReceived()) + { + Netscape(); + } + if (GetServerStateParser().ServerIsNetscape3xServer()) + { + // Server is a Netscape 3.0 Messaging Server. We know that all the + // folders are personal mail folders, and can subscribe to all (*). + SubscribeToAllFolders(); + } + else + { + // Worst case. It's not a Netscape 3.0 server, NAMESPACE is not supported, + // the user hasn't manually specified a personal namespace prefix, and + // Mission Control hasn't told us anything. Do the best we can. + // Progressively LIST % until either we run out of folders or we encounter + // 50 (MAX_NEW_IMAP_FOLDER_COUNT) + + const char *namespacePrefix = ns->GetPrefix(); // Has to be "", right? + + // List the prefix, to get the hierarchy delimiter. + // We probably shouldn't subscribe to the namespace itself. + fHierarchyNameState = kDiscoveringNamespacesOnly; + if (!DeathSignalReceived()) + List(namespacePrefix); + + // Now, generate the list + // Do it one level at a time, and stop either when nothing comes back or + // we pass the threshold mailbox count (50) + fHierarchyNameState = kListingForInfoOnly; + char *firstPattern = PR_smprintf("%s%%", namespacePrefix); + if (firstPattern && !DeathSignalReceived()) + { + List(firstPattern); + XP_FREE(firstPattern); + } + while ((XP_ListCount(fListedMailboxList) < MAX_NEW_IMAP_FOLDER_COUNT) && + (GetNumberOfListedMailboxesWithUnlistedChildren() > 0)) + { + int listSize = XP_ListCount(fListedMailboxList), i = 1; + for (i = 1; (i <= listSize) && !DeathSignalReceived(); i++) + { + TIMAPMailboxInfo *mb = (TIMAPMailboxInfo *)XP_ListGetObjectNum(fListedMailboxList, i); + if (!mb->GetChildrenListed()) + { + char delimiter = ns->GetDelimiter(); + // If there's a NIL hierarchy delimter, then we don't need to list children + if (delimiter) + { + char *pattern = PR_smprintf("%s%s%c%%",namespacePrefix,mb->GetMailboxName(), ns->GetDelimiter()); + if (pattern && !DeathSignalReceived()) + List(pattern); + } + mb->SetChildrenListed(TRUE); + } + } + } + fHierarchyNameState = kNoOperationInProgress; + + if (XP_ListCount(fListedMailboxList) < MAX_NEW_IMAP_FOLDER_COUNT) + { + // Less than the threshold (50?) + // Iterate through the list and subscribe to each mailbox, + // removing and freeing it as we go. + GetServerStateParser().SetReportingErrors(FALSE); + int total = XP_ListCount(fListedMailboxList), count = 0; + TIMAPMailboxInfo *mb = 0; + if (!DeathSignalReceived()) do + { + mb = (TIMAPMailboxInfo *)XP_ListRemoveTopObject(fListedMailboxList); + if (mb) + { + Subscribe(mb->GetMailboxName()); + PercentProgressUpdateEvent(NULL, (100*count)/total); + delete mb; + count++; + } + } while (mb && !DeathSignalReceived()); + PercentProgressUpdateEvent(NULL, 100); + GetServerStateParser().SetReportingErrors(TRUE); + } + else + { + // More than the threshold. Bring up the subscribe UI. + // We still must free the storage in the list + TIMAPMailboxInfo *mb = 0; + do + { + mb = (TIMAPMailboxInfo *) XP_ListRemoveTopObject(fListedMailboxList); + delete mb; + } while (mb); + AlertUserEvent_UsingId(MK_IMAP_UPGRADE_TOO_MANY_FOLDERS); + endState = kBringUpSubscribeUI; + } + } + } + } + if (!DeathSignalReceived()) // if not interrupted + UpgradeToSubscriptionFinishedEvent(endState); +} + + +void TNavigatorImapConnection::ProcessAuthenticatedStateURL() +{ + if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kLsubFolders) + { + //XP_ASSERT(FALSE); // now handled in FindMailboxesIfNecessary + // **** use to find out whether Drafts, Sent, & Templates folder + // exists or not even the user didn't subscribe to it + char *mailboxName = + fCurrentUrl->CreateServerDestinationFolderPathString(); + if (mailboxName) + { + char *convertedName = + CreateUtf7ConvertedString(mailboxName, TRUE); + if (convertedName) + { + FREEIF(mailboxName); + mailboxName = convertedName; + } + ProgressEventFunction_UsingId + (MK_IMAP_STATUS_LOOKING_FOR_MAILBOX); + IncrementCommandTagNumber(); + PR_snprintf(GetOutputBuffer(), kOutputBufferSize, + "%s list \"\" \"%s\"" CRLF, + GetServerCommandTag(), + mailboxName); + int ioStatus = WriteLineToSocket(GetOutputBuffer()); + TimeStampListNow(); + ParseIMAPandCheckForNewMail(); + // send list command to the server; modify + // DiscoverIMAPMailbox to check for the + // existence of the folder + + FREEIF(mailboxName); + } + else + { + HandleMemoryFailure(); + } + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kGetMailAccountUrl) { + if (GetServerStateParser().GetCapabilityFlag() & + kHasXNetscapeCapability) { + Netscape(); + if (GetServerStateParser().LastCommandSuccessful()) { + TImapFEEvent *alertEvent = + new TImapFEEvent(msgSetMailAccountURL, // function to call + this, // access to current entry + (void *) fCurrentUrl->GetUrlHost(), + TRUE); + if (alertEvent) + { + fFEEventQueue->AdoptEventToEnd(alertEvent); + // WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + } + } + } + else + { + // even though we don't have to to be legal protocol, Close any select mailbox + // so we don't miss any info these urls might change for the selected folder + // (e.g. msg count after append) + if (GetServerStateParser().GetIMAPstate() == TImapServerState::kFolderSelected) + { + // now we should be avoiding an implicit Close because it performs an implicit Expunge + // authenticated state urls should not use a cached connection that is in the selected state + XP_ASSERT(FALSE); + //Close(); + } + + if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOfflineToOnlineMove) + { + char *destinationMailbox = + fCurrentUrl->CreateServerDestinationFolderPathString(); + + if (destinationMailbox) + { + char *convertedName = CreateUtf7ConvertedString(destinationMailbox, TRUE); + XP_FREE(destinationMailbox); + destinationMailbox = convertedName; + } + if (destinationMailbox) + { + uint32 appendSize = 0; + do { + WaitForNextAppendMessageSize(); + appendSize = GetAppendSize(); + if (!DeathSignalReceived() && appendSize) + { + char messageSizeString[100]; + sprintf(messageSizeString, "%ld",(long) appendSize); + AppendMessage(destinationMailbox, messageSizeString, GetAppendFlags()); + } + } while (appendSize && GetServerStateParser().LastCommandSuccessful()); + FREEIF( destinationMailbox); + } + else + HandleMemoryFailure(); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kAppendMsgFromFile) + { + char *sourceMessageFile = + XP_STRDUP(GetActiveEntry()->URL_s->post_data); + char *mailboxName = + fCurrentUrl->CreateServerSourceFolderPathString(); + + if (mailboxName) + { + char *convertedName = CreateUtf7ConvertedString(mailboxName, TRUE); + XP_FREE(mailboxName); + mailboxName = convertedName; + } + if (mailboxName) + { + UploadMessageFromFile(sourceMessageFile, mailboxName, + kImapMsgSeenFlag); + } + else + HandleMemoryFailure(); + FREEIF( sourceMessageFile ); + FREEIF( mailboxName ); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kDiscoverAllBoxesUrl) + { + XP_ASSERT(!GetSubscribingNow()); // should not get here from subscribe UI + DiscoverMailboxList(); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kDiscoverAllAndSubscribedBoxesUrl) + { + XP_ASSERT(GetSubscribingNow()); + DiscoverAllAndSubscribedBoxes(); + } + else + { + char *sourceMailbox = fCurrentUrl->CreateServerSourceFolderPathString(); + if (sourceMailbox) + { + char *convertedName = CreateUtf7ConvertedString(sourceMailbox, TRUE); + XP_FREE(sourceMailbox); + sourceMailbox = convertedName; + } + + if (sourceMailbox) + { + if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kCreateFolder) + { + XP_Bool created = CreateMailboxRespectingSubscriptions(sourceMailbox); + if (created) + { + List(sourceMailbox); + } + else + FolderNotCreated(sourceMailbox); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kDiscoverChildrenUrl) + { + char *canonicalParent = fCurrentUrl->CreateCanonicalSourceFolderPathString(); + if (canonicalParent) + { + NthLevelChildList(canonicalParent, 2); + //CanonicalChildList(canonicalParent,FALSE); + XP_FREE(canonicalParent); + } + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kDiscoverLevelChildrenUrl) + { + char *canonicalParent = fCurrentUrl->CreateCanonicalSourceFolderPathString(); + int depth = fCurrentUrl->GetChildDiscoveryDepth(); + if (canonicalParent) + { + NthLevelChildList(canonicalParent, depth); + if (GetServerStateParser().LastCommandSuccessful()) + ChildDiscoverySucceeded(); + XP_FREE(canonicalParent); + } + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kSubscribe) + { + Subscribe(sourceMailbox); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kUnsubscribe) + { + Unsubscribe(sourceMailbox); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kRefreshACL) + { + RefreshACLForFolder(sourceMailbox); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kRefreshAllACLs) + { + RefreshAllACLs(); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kListFolder) + { + char *folderName = fCurrentUrl->CreateCanonicalSourceFolderPathString(); + List(folderName); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kUpgradeToSubscription) + { + UpgradeToSubscription(); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kFolderStatus) + { + StatusForFolder(sourceMailbox); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kRefreshFolderUrls) + { + XMailboxInfo(sourceMailbox); + if (GetServerStateParser().LastCommandSuccessful()) + { + InitializeFolderUrl(sourceMailbox); + } + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kDeleteFolder) + { + XP_Bool deleted = DeleteSubFolders(sourceMailbox); + if (deleted) + deleted = DeleteMailboxRespectingSubscriptions(sourceMailbox); + if (deleted) + FolderDeleted(sourceMailbox); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kRenameFolder) + { + char *destinationMailbox = + fCurrentUrl->CreateServerDestinationFolderPathString(); + if (destinationMailbox) + { + char *convertedName = CreateUtf7ConvertedString(destinationMailbox, TRUE); + XP_FREE(destinationMailbox); + destinationMailbox = convertedName; + } + if (destinationMailbox) + { + XP_Bool renamed = RenameHierarchyByHand(sourceMailbox, destinationMailbox); + if (renamed) + FolderRenamed(sourceMailbox, destinationMailbox); + + FREEIF( destinationMailbox); + } + else + HandleMemoryFailure(); + } + else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kMoveFolderHierarchy) + { + char *destinationMailbox = + fCurrentUrl->CreateServerDestinationFolderPathString(); + if (destinationMailbox) + { + // worst case length + char *newBoxName = (char *) XP_ALLOC(XP_STRLEN(destinationMailbox) + XP_STRLEN(sourceMailbox) + 2); + if (newBoxName) + { + char onlineDirSeparator = fCurrentUrl->GetOnlineSubDirSeparator(); + XP_STRCPY(newBoxName, destinationMailbox); + + char *leafSeparator = XP_STRRCHR(sourceMailbox, onlineDirSeparator); + if (!leafSeparator) + leafSeparator = sourceMailbox; // this is a root level box + else + leafSeparator++; + + if ( *newBoxName && ( *(newBoxName + XP_STRLEN(newBoxName) - 1) != onlineDirSeparator)) + { + char separatorStr[2]; + separatorStr[0] = onlineDirSeparator; + separatorStr[1] = 0; + XP_STRCAT(newBoxName, separatorStr); + } + XP_STRCAT(newBoxName, leafSeparator); + XP_Bool renamed = RenameHierarchyByHand(sourceMailbox, newBoxName); + if (renamed) + FolderRenamed(sourceMailbox, newBoxName); + } + } + } + + + FREEIF( sourceMailbox); + } + else + HandleMemoryFailure(); + } + } +} + +int32 TNavigatorImapConnection::GetMessageSizeForProgress() +{ + return GetServerStateParser().SizeOfMostRecentMessage(); +} + +int32 TNavigatorImapConnection::GetBytesMovedSoFarForProgress() +{ + return fBytesMovedSoFarForProgress; +} + +void TNavigatorImapConnection::SetBytesMovedSoFarForProgress(int32 bytesMoved) +{ + fBytesMovedSoFarForProgress = bytesMoved; +} + + +void TNavigatorImapConnection::AlertUserEvent_UsingId(uint32 msgId) +{ + TImapFEEvent *alertEvent = + new TImapFEEvent(AlertEventFunction_UsingId, // function to call + this, // access to current entry + (void *) msgId, + TRUE); // alert message + if (alertEvent) + { + fFEEventQueue->AdoptEventToEnd(alertEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); +} + +void TNavigatorImapConnection::AlertUserEvent(char *message) +{ + TImapFEEvent *alertEvent = + new TImapFEEvent(AlertEventFunction, // function to call + this, // access to current entry + message, + TRUE); // alert message + if (alertEvent) + { + fFEEventQueue->AdoptEventToEnd(alertEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); +} + +void TNavigatorImapConnection::AlertUserEventFromServer(char *serverSaid) +{ + TImapFEEvent *alertEvent = + new TImapFEEvent(AlertEventFunctionFromServer, // function to call + this, // access to current entry + serverSaid, + TRUE); // alert message + if (alertEvent) + { + fFEEventQueue->AdoptEventToEnd(alertEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); +} + +void TNavigatorImapConnection::ProgressUpdateEvent(char *message) +{ + TImapFEEvent *progressEvent = + new TImapFEEvent(ProgressEventFunction, // function to call + this, // access to current entry + message, + TRUE); // progress message + + if (progressEvent) + { + fFEEventQueue->AdoptEventToEnd(progressEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); +} + +void TNavigatorImapConnection::ProgressEventFunction_UsingId(uint32 msgId) +{ + if (msgId != (uint32) fLastProgressStringId) + { + TImapFEEvent *progressEvent = + new TImapFEEvent(ProgressStatusFunction_UsingId, // function to call + this, // access to current entry + (void *) msgId, + TRUE); // alert message id + if (progressEvent) + { + fFEEventQueue->AdoptEventToEnd(progressEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + fLastProgressStringId = msgId; + } +} + +void TNavigatorImapConnection::ProgressEventFunction_UsingIdWithString(uint32 msgId, const char* extraInfo) +{ + StatusMessageInfo *msgInfo = (StatusMessageInfo *) XP_ALLOC(sizeof(StatusMessageInfo)); // free in event handler + if (msgInfo) { + if (extraInfo) { + msgInfo->msgID = msgId; + msgInfo->extraInfo = XP_STRDUP (extraInfo); // free in event handler + TImapFEEvent *progressEvent = + new TImapFEEvent(ProgressStatusFunction_UsingIdWithString, // function to call + this, // access to current entry + (void *) msgInfo, + TRUE); // alert message info + if (progressEvent) + { + fFEEventQueue->AdoptEventToEnd(progressEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + } + } +} + +void TNavigatorImapConnection::PercentProgressUpdateEvent(char *message, int percent) +{ + int64 nowMS; + if (percent == fLastPercent) + return; // hasn't changed, right? So just return. Do we need to clear this anywhere? + + if (percent < 100) // always need to do 100% + { + int64 minIntervalBetweenProgress; + + LL_I2L(minIntervalBetweenProgress, 250); + int64 diffSinceLastProgress; + LL_I2L(nowMS, PR_IntervalToMilliseconds(PR_IntervalNow())); + LL_SUB(diffSinceLastProgress, nowMS, fLastProgressTime); // r = a - b + LL_SUB(diffSinceLastProgress, diffSinceLastProgress, minIntervalBetweenProgress); // r = a - b + if (!LL_GE_ZERO(diffSinceLastProgress)) + return; + } + + ProgressInfo *progress = (ProgressInfo *) XP_ALLOC(sizeof(ProgressInfo)); // free in event handler + if (progress) + { + // we can do the event without a message - it will set the percent bar + if (message) + progress->message = XP_STRDUP(message); // free in event handler + else + progress->message = 0; + progress->percent = percent; + fLastPercent = percent; + + TImapFEEvent *progressEvent = + new TImapFEEvent(PercentProgressEventFunction, // function to call + this, // access to current entry + progress, + TRUE); // progress message + + fLastProgressTime = nowMS; + if (progressEvent) + { + fFEEventQueue->AdoptEventToEnd(progressEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + } +} + +void TNavigatorImapConnection::PastPasswordCheckEvent() +{ + TImapFEEvent *pwCheckEvent = + new TImapFEEvent(PastPasswordCheckFunction, this, NULL, TRUE); + + if (pwCheckEvent) + { + fFEEventQueue->AdoptEventToEnd(pwCheckEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); +} + + +static void CommitNamespacesFunction(void *blockingConnectionVoid, void *hostNameVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + char *hostName = (char *)hostNameVoid; + + if (hostName) + TIMAPHostInfo::CommitNamespacesForHost(hostName, ce->window_id->mailMaster); + + imapConnection->NotifyEventCompletionMonitor(); +} + +void TNavigatorImapConnection::CommitNamespacesForHostEvent() +{ + TImapFEEvent *commitEvent = + new TImapFEEvent(CommitNamespacesFunction,this,(void *)(fCurrentUrl->GetUrlHost()), TRUE); + + if (commitEvent) + { + fFEEventQueue->AdoptEventToEnd(commitEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); +} + +static void CommitCapabilityFunction(void *blockingConnectionVoid, void *hostNameVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + char *hostName = (char *)hostNameVoid; + + if (hostName) + MSG_CommitCapabilityForHost(hostName, ce->window_id->mailMaster); + + imapConnection->NotifyEventCompletionMonitor(); +} + +void TNavigatorImapConnection::CommitCapabilityForHostEvent() +{ + TImapFEEvent *commitEvent = + new TImapFEEvent(CommitCapabilityFunction,this,(void *)(fCurrentUrl->GetUrlHost()), TRUE); + + if (commitEvent) + { + fFEEventQueue->AdoptEventToEnd(commitEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); +} + +typedef struct _MessageSizeInfo +{ + char *id; + char *folderName; + XP_Bool idIsUid; + uint32 size; +} MessageSizeInfo; + + +// Retrieves the message size given a message UID +// Retrieves the message size given a UID or message sequence number +static +void GetMessageSizeEvent(void *blockingConnectionVoid, + void *sizeInfoVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + MessageSizeInfo *sizeInfo = (MessageSizeInfo *)sizeInfoVoid; + MSG_Pane *masterPane = ce->URL_s->msg_pane; // we need some pane to get to the master + if (!masterPane) + masterPane = MSG_FindPane(ce->window_id, MSG_ANYPANE); // last ditch effort + + sizeInfo->size = MSG_GetIMAPMessageSizeFromDB(masterPane, imapConnection->GetHostName(), sizeInfo->folderName, sizeInfo->id, sizeInfo->idIsUid); + + imapConnection->NotifyEventCompletionMonitor(); +} + + +uint32 TNavigatorImapConnection::GetMessageSize(const char *messageId, XP_Bool idsAreUids) +{ + + MessageSizeInfo *sizeInfo = (MessageSizeInfo *)XP_ALLOC(sizeof(MessageSizeInfo)); + if (sizeInfo) + { + const char *folderFromParser = GetServerStateParser().GetSelectedMailboxName(); + if (folderFromParser) + { + sizeInfo->id = (char *)XP_ALLOC(XP_STRLEN(messageId) + 1); + sizeInfo->folderName = (char *)XP_ALLOC(XP_STRLEN(folderFromParser) + 1); + XP_STRCPY(sizeInfo->id, messageId); + sizeInfo->idIsUid = idsAreUids; + + if (sizeInfo->id && sizeInfo->folderName) + { + XP_STRCPY(sizeInfo->folderName, folderFromParser); + TImapFEEvent *getMessageSizeEvent = new TImapFEEvent(GetMessageSizeEvent, this, sizeInfo, FALSE); + + if (getMessageSizeEvent) + { + fFEEventQueue->AdoptEventToEnd(getMessageSizeEvent); + WaitForFEEventCompletion(); + } + else + { + HandleMemoryFailure(); + return 0; + } + XP_FREE(sizeInfo->id); + XP_FREE(sizeInfo->folderName); + } + else + { + HandleMemoryFailure(); + XP_FREE(sizeInfo); + return 0; + } + + int32 rv = 0; + if (!DeathSignalReceived()) + rv = sizeInfo->size; + XP_FREE(sizeInfo); + return rv; + } + else + { + HandleMemoryFailure(); + return 0; + } + } + else + { + HandleMemoryFailure(); + return 0; + } +} + + + +static void +MOZTHREAD_FolderIsNoSelectEvent(void *blockingConnectionVoid, void *valueVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + FolderQueryInfo *value = (FolderQueryInfo *)valueVoid; + + value->rv = MSG_IsFolderNoSelect(ce->window_id->mailMaster, value->name, value->hostName); + imapConnection->NotifyEventCompletionMonitor(); +} + + + +// Returns TRUE if the mailbox is a NoSelect mailbox. +// If we don't know about it, returns FALSE. +XP_Bool TNavigatorImapConnection::MailboxIsNoSelectMailbox(const char *mailboxName) +{ + FolderQueryInfo *value = (FolderQueryInfo *)XP_ALLOC(sizeof(FolderQueryInfo)); + XP_Bool rv = FALSE; + + if (!value) return FALSE; + + value->name = XP_STRDUP(mailboxName); + if (!value->name) + { + XP_FREE(value); + return FALSE; + } + + value->hostName = XP_STRDUP(fCurrentUrl->GetUrlHost()); + if (!value->hostName) + { + XP_FREE(value->name); + XP_FREE(value); + return FALSE; + } + + TImapFEEvent *theEvent = + new TImapFEEvent(MOZTHREAD_FolderIsNoSelectEvent, // function to call + this, // access to current entry/context + value, + FALSE); + + if (theEvent) + { + fFEEventQueue->AdoptEventToEnd(theEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + + rv = value->rv; + XP_FREE(value->hostName); + XP_FREE(value->name); + XP_FREE(value); + return rv; +} + +/////////////////// Begin Subscription Management Functions ///////////////////////////// + + +// returns TRUE is the create succeeded (regardless of subscription changes) +XP_Bool TNavigatorImapConnection::CreateMailboxRespectingSubscriptions(const char *mailboxName) +{ + TIMAP4BlockingConnection::CreateMailbox(mailboxName); + XP_Bool rv = GetServerStateParser().LastCommandSuccessful(); + if (rv) + { + if (fAutoSubscribe) // auto-subscribe is on + { + // create succeeded - let's subscribe to it + XP_Bool reportingErrors = GetServerStateParser().GetReportingErrors(); + GetServerStateParser().SetReportingErrors(FALSE); + Subscribe(mailboxName); + GetServerStateParser().SetReportingErrors(reportingErrors); + } + } + return (rv); +} + +// returns TRUE is the delete succeeded (regardless of subscription changes) +XP_Bool TNavigatorImapConnection::DeleteMailboxRespectingSubscriptions(const char *mailboxName) +{ + XP_Bool rv = TRUE; + if (!MailboxIsNoSelectMailbox(mailboxName)) + { + // Only try to delete it if it really exists + TIMAP4BlockingConnection::DeleteMailbox(mailboxName); + rv = GetServerStateParser().LastCommandSuccessful(); + } + + // We can unsubscribe even if the mailbox doesn't exist. + if (rv && fAutoUnsubscribe) // auto-unsubscribe is on + { + XP_Bool reportingErrors = GetServerStateParser().GetReportingErrors(); + GetServerStateParser().SetReportingErrors(FALSE); + Unsubscribe(mailboxName); + GetServerStateParser().SetReportingErrors(reportingErrors); + + } + return (rv); +} + +// returns TRUE is the rename succeeded (regardless of subscription changes) +// reallyRename tells us if we should really do the rename (TRUE) or if we should just move subscriptions (FALSE) +XP_Bool TNavigatorImapConnection::RenameMailboxRespectingSubscriptions(const char *existingName, const char *newName, XP_Bool reallyRename) +{ + XP_Bool rv = TRUE; + if (reallyRename && !MailboxIsNoSelectMailbox(existingName)) + { + TIMAP4BlockingConnection::RenameMailbox(existingName, newName); + rv = GetServerStateParser().LastCommandSuccessful(); + } + + if (rv) + { + if (fAutoSubscribe) // if auto-subscribe is on + { + XP_Bool reportingErrors = GetServerStateParser().GetReportingErrors(); + GetServerStateParser().SetReportingErrors(FALSE); + Subscribe(newName); + GetServerStateParser().SetReportingErrors(reportingErrors); + } + if (fAutoUnsubscribe) // if auto-unsubscribe is on + { + XP_Bool reportingErrors = GetServerStateParser().GetReportingErrors(); + GetServerStateParser().SetReportingErrors(FALSE); + Unsubscribe(existingName); + GetServerStateParser().SetReportingErrors(reportingErrors); + } + } + return (rv); +} + + +XP_Bool TNavigatorImapConnection::GetSubscribingNow() +{ + return (fCurrentEntry && fCurrentEntry->window_id && fCurrentEntry->window_id->imapURLPane && + (MSG_SUBSCRIBEPANE == MSG_GetPaneType(fCurrentEntry->window_id->imapURLPane))); +} + + +/////////////////// End Subscription Management Functions ///////////////////////////// + +/////////////////// Begin ACL Stuff /////////////////////////////////////////////////// + +void TNavigatorImapConnection::RefreshACLForFolderIfNecessary(const char *mailboxName) +{ + if (fFolderNeedsACLRefreshed && GetServerStateParser().ServerHasACLCapability()) + { + RefreshACLForFolder(mailboxName); + fFolderNeedsACLRefreshed = FALSE; + } +} + +void TNavigatorImapConnection::RefreshACLForFolder(const char *mailboxName) +{ + + TIMAPNamespace *ns = TIMAPHostInfo::GetNamespaceForMailboxForHost(fCurrentUrl->GetUrlHost(), mailboxName); + if (ns) + { + switch (ns->GetType()) + { + case kPersonalNamespace: + // It's a personal folder, most likely. + // I find it hard to imagine a server that supports ACL that doesn't support NAMESPACE, + // so most likely we KNOW that this is a personal, rather than the default, namespace. + + // First, clear what we have. + ClearAllFolderRights(mailboxName); + // Now, get the new one. + GetACLForFolder(mailboxName); + // We're all done, refresh the icon/flags for this folder + RefreshFolderACLView(mailboxName); + break; + default: + // We know it's a public folder or other user's folder. + // We only want our own rights + + // First, clear what we have + ClearAllFolderRights(mailboxName); + // Now, get the new one. + GetMyRightsForFolder(mailboxName); + // We're all done, refresh the icon/flags for this folder + RefreshFolderACLView(mailboxName); + break; + } + } + else + { + // no namespace, not even default... can this happen? + XP_ASSERT(FALSE); + } +} + +void TNavigatorImapConnection::RefreshAllACLs() +{ + fHierarchyNameState = kListingForInfoOnly; + TIMAPMailboxInfo *mb = NULL; + + // This will fill in the list + List("*"); + + int total = XP_ListCount(fListedMailboxList), count = 0; + GetServerStateParser().SetReportingErrors(FALSE); + do + { + mb = (TIMAPMailboxInfo *) XP_ListRemoveTopObject(fListedMailboxList); + if (mb) + { + RefreshACLForFolder(mb->GetMailboxName()); + PercentProgressUpdateEvent(NULL, (count*100)/total); + delete mb; + count++; + } + } while (mb); + + PercentProgressUpdateEvent(NULL, 100); + GetServerStateParser().SetReportingErrors(TRUE); + + fHierarchyNameState = kNoOperationInProgress; +} + +static void ClearFolderRightsEvent(void *blockingConnectionVoid, void *aclRightsInfoVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + TIMAPACLRightsInfo *aclRights = (TIMAPACLRightsInfo *)aclRightsInfoVoid; + + MSG_ClearFolderRightsForFolder(ce->window_id->mailMaster, aclRights->hostName, aclRights->mailboxName); + + imapConnection->NotifyEventCompletionMonitor(); +} + +void TNavigatorImapConnection::ClearAllFolderRights(const char *mailboxName) +{ + TIMAPACLRightsInfo *aclRightsInfo = new TIMAPACLRightsInfo(); + if (aclRightsInfo) + { + aclRightsInfo->hostName = XP_STRDUP(fCurrentUrl->GetUrlHost()); + aclRightsInfo->mailboxName = XP_STRDUP(mailboxName); + aclRightsInfo->rights = NULL; + aclRightsInfo->userName = NULL; + + if (aclRightsInfo->hostName && aclRightsInfo->mailboxName) + { + + TImapFEEvent *clearFolderRightsEvent = + new TImapFEEvent(ClearFolderRightsEvent, // function to call + this, // access to current entry/context + aclRightsInfo, + FALSE); + + + if (clearFolderRightsEvent) + { + fFEEventQueue->AdoptEventToEnd(clearFolderRightsEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + + XP_FREE(aclRightsInfo->hostName); + XP_FREE(aclRightsInfo->mailboxName); + } + else + HandleMemoryFailure(); + delete aclRightsInfo; + } + else + HandleMemoryFailure(); +} + + +static void AddFolderRightsEvent(void *blockingConnectionVoid, void *aclRightsInfoVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + TIMAPACLRightsInfo *aclRights = (TIMAPACLRightsInfo *)aclRightsInfoVoid; + + MSG_AddFolderRightsForUser(ce->window_id->mailMaster, aclRights->hostName, aclRights->mailboxName, aclRights->userName, aclRights->rights); + + imapConnection->NotifyEventCompletionMonitor(); +} + +void TNavigatorImapConnection::AddFolderRightsForUser(const char *mailboxName, const char *userName, const char *rights) +{ + TIMAPACLRightsInfo *aclRightsInfo = new TIMAPACLRightsInfo(); + if (aclRightsInfo) + { + aclRightsInfo->hostName = XP_STRDUP(fCurrentUrl->GetUrlHost()); + aclRightsInfo->mailboxName = XP_STRDUP(mailboxName); + if (userName) + aclRightsInfo->userName = XP_STRDUP(userName); + else + aclRightsInfo->userName = NULL; + aclRightsInfo->rights = XP_STRDUP(rights); + + + if (aclRightsInfo->hostName && aclRightsInfo->mailboxName && aclRightsInfo->rights && + userName ? (aclRightsInfo->userName != NULL) : TRUE) + { + + TImapFEEvent *addFolderRightsEvent = + new TImapFEEvent(AddFolderRightsEvent, // function to call + this, // access to current entry/context + aclRightsInfo, + FALSE); + + + if (addFolderRightsEvent) + { + fFEEventQueue->AdoptEventToEnd(addFolderRightsEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + + XP_FREE(aclRightsInfo->hostName); + XP_FREE(aclRightsInfo->mailboxName); + XP_FREE(aclRightsInfo->rights); + if (aclRightsInfo->userName) + XP_FREE(aclRightsInfo->userName); + } + else + HandleMemoryFailure(); + delete aclRightsInfo; + } + else + HandleMemoryFailure(); +} + +static void RefreshFolderRightsEvent(void *blockingConnectionVoid, void *aclRightsInfoVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + TIMAPACLRightsInfo *aclRights = (TIMAPACLRightsInfo *)aclRightsInfoVoid; + + MSG_RefreshFolderRightsViewForFolder(ce->window_id->mailMaster, aclRights->hostName, aclRights->mailboxName); + + imapConnection->NotifyEventCompletionMonitor(); +} + +void TNavigatorImapConnection::RefreshFolderACLView(const char *mailboxName) +{ + TIMAPACLRightsInfo *aclRightsInfo = new TIMAPACLRightsInfo(); + if (aclRightsInfo) + { + aclRightsInfo->hostName = XP_STRDUP(fCurrentUrl->GetUrlHost()); + aclRightsInfo->mailboxName = XP_STRDUP(mailboxName); + aclRightsInfo->rights = NULL; + aclRightsInfo->userName = NULL; + + if (aclRightsInfo->hostName && aclRightsInfo->mailboxName) + { + + TImapFEEvent *refreshFolderRightsEvent = + new TImapFEEvent(RefreshFolderRightsEvent, // function to call + this, // access to current entry/context + aclRightsInfo, + FALSE); + + + if (refreshFolderRightsEvent) + { + fFEEventQueue->AdoptEventToEnd(refreshFolderRightsEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + + XP_FREE(aclRightsInfo->hostName); + XP_FREE(aclRightsInfo->mailboxName); + } + else + HandleMemoryFailure(); + delete aclRightsInfo; + } + else + HandleMemoryFailure(); +} + + +static void +MOZTHREAD_FolderNeedsACLInitialized(void *blockingConnectionVoid, void *valueVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + FolderQueryInfo *value = (FolderQueryInfo *)valueVoid; + + value->rv = !MSG_IsFolderACLInitialized(ce->window_id->mailMaster, value->name, value->hostName); + imapConnection->NotifyEventCompletionMonitor(); +} + + +XP_Bool TNavigatorImapConnection::FolderNeedsACLInitialized(const char *folderName) +{ + FolderQueryInfo *value = (FolderQueryInfo *)XP_ALLOC(sizeof(FolderQueryInfo)); + XP_Bool rv = FALSE; + + if (!value) return FALSE; + + value->name = XP_STRDUP(folderName); + if (!value->name) + { + XP_FREE(value); + return FALSE; + } + + value->hostName = XP_STRDUP(fCurrentUrl->GetUrlHost()); + if (!value->hostName) + { + XP_FREE(value->name); + XP_FREE(value); + return FALSE; + } + + TImapFEEvent *folderACLInitEvent = + new TImapFEEvent(MOZTHREAD_FolderNeedsACLInitialized, // function to call + this, // access to current entry/context + value, FALSE); + + if (folderACLInitEvent) + { + fFEEventQueue->AdoptEventToEnd(folderACLInitEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + + rv = value->rv; + XP_FREE(value->hostName); + XP_FREE(value->name); + XP_FREE(value); + return rv; +} + +/////////////////// End ACL Stuff ///////////////////////////////////////////////////// + +XP_Bool TNavigatorImapConnection::InitializeFolderUrl(const char *folderName) +{ + FolderQueryInfo *value = (FolderQueryInfo *)XP_ALLOC(sizeof(FolderQueryInfo)); + XP_Bool rv = FALSE; + + if (!value) return FALSE; + + value->name = XP_STRDUP(folderName); + if (!value->name) + { + XP_FREE(value); + return FALSE; + } + + value->hostName = XP_STRDUP(fCurrentUrl->GetUrlHost()); + if (!value->hostName) + { + XP_FREE(value->name); + XP_FREE(value); + return FALSE; + } + + TImapFEEvent *folderURLInitEvent = + new TImapFEEvent(MOZ_THREADmsgSetFolderURL, // function to call + this, // access to current entry/context + value, TRUE); + + if (folderURLInitEvent) + { + fFEEventQueue->AdoptEventToEnd(folderURLInitEvent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + + rv = value->rv; + XP_FREE(value->hostName); + XP_FREE(value->name); + XP_FREE(value); + return rv; +} + + +static void +MOZTHREAD_GetShowAttachmentsInline(void *blockingConnectionVoid, void *valueVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + XP_Bool *value = (XP_Bool *)valueVoid; + + PREF_GetBoolPref("mail.inline_attachments", value); + + imapConnection->NotifyEventCompletionMonitor(); +} + +XP_Bool TNavigatorImapConnection::GetShowAttachmentsInline() +{ + XP_Bool *value = (XP_Bool *)XP_ALLOC(sizeof(XP_Bool)); + XP_Bool rv = FALSE; + + if (!value) return FALSE; + + TImapFEEvent *levent = + new TImapFEEvent(MOZTHREAD_GetShowAttachmentsInline, // function to call + this, // access to current entry/context + value, FALSE); + + if (levent) + { + fFEEventQueue->AdoptEventToEnd(levent); + WaitForFEEventCompletion(); + } + else + HandleMemoryFailure(); + + rv = *value; + XP_FREE(value); + return rv; +} + +void TNavigatorImapConnection::SetContentModified(XP_Bool modified) +{ + ActiveEntry *ce = GetActiveEntry(); + if (ce && !DeathSignalReceived()) + ce->URL_s->content_modified = modified; +} + +XP_Bool TNavigatorImapConnection::GetShouldFetchAllParts() +{ + ActiveEntry *ce = GetActiveEntry(); + if (ce && !DeathSignalReceived()) + return ce->URL_s->content_modified; + else + return TRUE; +} + +void TNavigatorImapConnection::Log(const char *logSubName, const char *extraInfo, const char *logData) +{ + static char *nonAuthStateName = "NA"; + static char *authStateName = "A"; + static char *selectedStateName = "S"; + static char *waitingStateName = "W"; + char *stateName = NULL; + + switch (GetServerStateParser().GetIMAPstate()) + { + case TImapServerState::kFolderSelected: + if (fCurrentUrl) + { + if (extraInfo) + PR_LOG(IMAP, out, ("%s:%s-%s:%s:%s: %s", fCurrentUrl->GetUrlHost(),selectedStateName, GetServerStateParser().GetSelectedMailboxName(), logSubName, extraInfo, logData)); + else + PR_LOG(IMAP, out, ("%s:%s-%s:%s: %s", fCurrentUrl->GetUrlHost(),selectedStateName, GetServerStateParser().GetSelectedMailboxName(), logSubName, logData)); + } + return; + break; + case TImapServerState::kNonAuthenticated: + stateName = nonAuthStateName; + break; + case TImapServerState::kAuthenticated: + stateName = authStateName; + break; + case TImapServerState::kWaitingForMoreClientInput: + stateName = waitingStateName; + break; + } + + if (fCurrentUrl) + { + if (extraInfo) + PR_LOG(IMAP, out, ("%s:%s:%s:%s: %s",fCurrentUrl->GetUrlHost(),stateName,logSubName,extraInfo,logData)); + else + PR_LOG(IMAP, out, ("%s:%s:%s: %s",fCurrentUrl->GetUrlHost(),stateName,logSubName,logData)); + } +} + + +// essentially, just a copy of parseadoptedmsgline, except it doesn't notify the eventcompletion monitor +static +void MOZTHREAD_TunnelOutStream(void *blockingConnectionVoid, void *adoptedmsg_line_info_Void) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + ActiveEntry *ce = imapConnection->GetActiveEntry(); + + msg_line_info *downloadLineDontDelete = (msg_line_info *) adoptedmsg_line_info_Void; + NET_StreamClass *outputStream = imapConnection->GetOutputStream(); + + unsigned int streamBytesMax = (*outputStream->is_write_ready) (outputStream); + char *stringToPut = downloadLineDontDelete->adoptedMessageLine; + XP_Bool allocatedString = FALSE; + + if ( streamBytesMax < (XP_STRLEN(downloadLineDontDelete->adoptedMessageLine) + 2)) + { + // dup the streamBytesMax number of chars, then put the rest of the string back into event queue + if (streamBytesMax != 0) + { + stringToPut = (char *) XP_ALLOC(streamBytesMax + 1); // 1 for \0 + if (stringToPut) + { + XP_MEMCPY(stringToPut, downloadLineDontDelete->adoptedMessageLine, streamBytesMax); + *(stringToPut + streamBytesMax) = 0; + allocatedString = TRUE; + } + + // shift buffer bytes + XP_MEMMOVE(downloadLineDontDelete->adoptedMessageLine, + downloadLineDontDelete->adoptedMessageLine + streamBytesMax, + (XP_STRLEN(downloadLineDontDelete->adoptedMessageLine) + 1) - streamBytesMax); + } + + // the output stream can't handle this event yet! put it back on the + // queue + TImapFEEvent *parseLineEvent = + new TImapFEEvent(MOZTHREAD_TunnelOutStream, // function to call + blockingConnectionVoid, // access to current entry + adoptedmsg_line_info_Void, + FALSE); // line to display + if (parseLineEvent) + imapConnection->GetFEEventQueue(). + AdoptEventToBeginning(parseLineEvent); + else + imapConnection->HandleMemoryFailure(); + } + + + if (streamBytesMax != 0) + { + imapConnection->SetBytesMovedSoFarForProgress(imapConnection->GetBytesMovedSoFarForProgress() + + XP_STRLEN(stringToPut)); + + if (!imapConnection->GetPseudoInterrupted()) + { + int bytesPut = 0; + + if (ce->window_id->currentIMAPfolder) + { + // the definition of put_block in net.h defines + // the 3rd parameter as the length of the block, + // but since we are always sending a null terminated + // string, I am able to sent the uid instead + bytesPut = (*outputStream->put_block) (outputStream, + stringToPut, + downloadLineDontDelete->uidOfMessage); + } + else // this is a download for display + { + bytesPut = (*outputStream->put_block) (outputStream, + stringToPut, + XP_STRLEN(stringToPut)); + } + + if (bytesPut < 0) + { + imapConnection->SetConnectionStatus(-1); // something bad happened, stop this url + imapConnection->GetServerStateParser().SetConnected(FALSE); + } + } + } + if (allocatedString) + XP_FREE(stringToPut); // event not done yet +// else if (streamBytesMax != 0) +// imapConnection->NotifyEventCompletionMonitor(); +} + +static void +MOZTHREAD_ProcessTunnel(void *blockingConnectionVoid, void *tunnelInfoVoid) +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) blockingConnectionVoid; + TunnelInfo *info = (TunnelInfo *)tunnelInfoVoid; + + if (info->maxSize <= 150) + { + // We want to read less than the size of a line. + // Close the tunnel, and switch back to the IMAP thread + // to do the protocol parsing, etc. + PR_LOG(IMAP, out, ("TUNNEL: Leaving Mozilla Thread with leftover size %ld",info->maxSize)); + imapConnection->NotifyTunnelCompletionMonitor(); + } + else + { + // We know there's enough data to read. So let's do it. + + // Read a line + char *newLine = NULL; + XP_Bool pauseForRead = TRUE; + int socketReadStatus = NET_BufferedReadLine(info->ioSocket, + &newLine, + info->inputSocketBuffer, + info->inputBufferSize, + &pauseForRead); + + if (socketReadStatus > 0) + { + // Set it up to handle reading what's left over + if (newLine) + { + char *lineToStream = PR_smprintf("%s%s",newLine, CRLF); + if (lineToStream) + { + info->maxSize -= XP_STRLEN(lineToStream); + XP_ASSERT(info->maxSize > 0); + TImapFEEvent *event = + new TImapFEEvent(MOZTHREAD_ProcessTunnel, // function to call + blockingConnectionVoid, // access to current entry + tunnelInfoVoid, + FALSE); + if (event) + imapConnection->GetFEEventQueue(). + AdoptEventToBeginning(event); + else + imapConnection->HandleMemoryFailure(); + + + // Finally, stream it out + + msg_line_info *outputStreamInfo = (msg_line_info *) XP_ALLOC(sizeof( msg_line_info)); + if (outputStreamInfo) + { + outputStreamInfo->adoptedMessageLine = lineToStream; + // Whoa, actually call one event from another. + // Let's let it worry about making sure everything is streamed out before we get called here again. + MOZTHREAD_TunnelOutStream(blockingConnectionVoid, outputStreamInfo); + XP_FREE(outputStreamInfo); + } + else + imapConnection->HandleMemoryFailure(); + XP_FREE(lineToStream); + } + else + imapConnection->HandleMemoryFailure(); + } + else + { + // no line yet, but there might have been data read (filling up the buffer) + // Put ourselves back on the queue + TImapFEEvent *event = + new TImapFEEvent(MOZTHREAD_ProcessTunnel, // function to call + blockingConnectionVoid, // access to current entry + tunnelInfoVoid, + FALSE); + if (event) + imapConnection->GetFEEventQueue(). + AdoptEventToBeginning(event); + else + imapConnection->HandleMemoryFailure(); + } + } + else + { + // socketReadStatus <= 0 + int socketError = PR_GetError(); + + // If wouldblock, just put this event back on the queue + if (socketError == XP_ERRNO_EWOULDBLOCK) + { + TImapFEEvent *event = + new TImapFEEvent(MOZTHREAD_ProcessTunnel, // function to call + blockingConnectionVoid, // access to current entry + tunnelInfoVoid, + FALSE); + if (event) + imapConnection->GetFEEventQueue(). + AdoptEventToBeginning(event); + else + imapConnection->HandleMemoryFailure(); + } + else + { + // Some socket error - handle this here + XP_ASSERT(FALSE); + } + } + } +} + +int32 TNavigatorImapConnection::OpenTunnel (int32 maxNumberOfBytesToRead) +{ + char *bytesToRead = PR_smprintf("Max bytes to read: %ld",maxNumberOfBytesToRead); + if (bytesToRead) + { + Log("TUNNEL","Opening Tunnel", bytesToRead); + XP_FREE(bytesToRead); + } + else + { + Log("TUNNEL",NULL,"Opening Tunnel"); + } + TunnelInfo *info = (TunnelInfo *)XP_ALLOC(sizeof(TunnelInfo)); + int32 originalMax = maxNumberOfBytesToRead, rv = 0; + if (info) + { + info->maxSize = maxNumberOfBytesToRead; + info->ioSocket = GetIOSocket(); + info->inputSocketBuffer = &fInputSocketBuffer; + info->inputBufferSize = &fInputBufferSize; + + TImapFEEvent *event = + new TImapFEEvent(MOZTHREAD_ProcessTunnel, // function to call + this, // access to current entry/context + info, // the tunneling info + FALSE); // Interrupts would be bad for this + + if (event) + { + fFEEventQueue->AdoptEventToEnd(event); + //WaitForFEEventCompletion(); + WaitForTunnelCompletion(); + } + else + HandleMemoryFailure(); + + rv = originalMax - info->maxSize; + + XP_FREE(info); + } + char *bytesSoFar = PR_smprintf("Bytes read so far: %ld", rv); + if (bytesSoFar) + { + Log("TUNNEL","Closing Tunnel", bytesSoFar); + XP_FREE(bytesSoFar); + } + else + { + Log("TUNNEL",NULL,"Closing Tunnel"); + } + return rv; +} + +int32 TNavigatorImapConnection::GetTunnellingThreshold() +{ + return gTunnellingThreshold; +} + +XP_Bool TNavigatorImapConnection::GetIOTunnellingEnabled() +{ + return gIOTunnelling; +} + + +PR_STATIC_CALLBACK(void) +#ifndef NSPR20 +imap_thread_main_function(void *imapConnectionVoid, void *) +#else +imap_thread_main_function(void *imapConnectionVoid) +#endif /* NSPR20 */ +{ + TNavigatorImapConnection *imapConnection = + (TNavigatorImapConnection *) imapConnectionVoid; + + imapConnection->StartProcessingActiveEntries(); + + delete imapConnection; +} + + +/* start a imap4 load + */ +extern int MK_POP3_PASSWORD_UNDEFINED; + +//#define KEVIN_DEBUG 1 +#if KEVIN_DEBUG +static int gNumberOfVisits; +static int gNumberOfVisitsProcessingEvents; +static int gNumberOfEventsForCurrentUrl; +#endif + +static +void NotifyOfflineFolderLoad(TIMAPUrl ¤tUrl, MWContext *window_id) +{ + mailbox_spec *notSelectedSpec = (mailbox_spec *) XP_CALLOC(1, sizeof( mailbox_spec)); + if (notSelectedSpec) + { + notSelectedSpec->allocatedPathName =currentUrl.CreateCanonicalSourceFolderPathString(); + notSelectedSpec->hostName =currentUrl.GetUrlHost(); + notSelectedSpec->folderSelected = FALSE; + notSelectedSpec->flagState = NULL; + notSelectedSpec->onlineVerified = FALSE; + UpdateIMAPMailboxInfo(notSelectedSpec, window_id); + } +} + +static XP_Bool gotPrefs = FALSE, useLibnetCacheForIMAP4Fetch; +static XP_Bool gotUpgradePrefs = FALSE; +static XP_Bool fTriedSavedPassword = FALSE; +static XP_Bool triedUpgradingToSubscription = FALSE; +static XP_Bool autoSubscribe = TRUE, autoUnsubscribe = TRUE, autoSubscribeOnOpen = TRUE, autoUnsubscribeFromNoSelect = TRUE; +static XP_Bool allowMultipleFolderConnections = TRUE; +static XP_Bool gGetHeaders = FALSE; +static int32 gTooFastTime = 2; +static int32 gIdealTime = 4; +static int32 gChunkAddSize = 2048; +static int32 gChunkSize = 10240; +static int32 gChunkThreshold = 10240 + 4096; +static XP_Bool gFetchByChunks = TRUE; +static int32 gMaxChunkSize = 40960; + +/* static */ int +TNavigatorImapConnection::SetupConnection(ActiveEntry * ce, MSG_Master *master, XP_Bool loadingURL) +{ + int32 returnValue = MK_WAITING_FOR_CONNECTION; + TIMAPUrl currentUrl(ce->URL_s->address, ce->URL_s->internal_url); + XP_Bool thisHostUsingSubscription = FALSE; + XP_Bool shouldUpgradeToSubscription = FALSE, upgradeLeaveAlone = FALSE, upgradeSubscribeAll = FALSE; + + if (!gotUpgradePrefs) + { + shouldUpgradeToSubscription = MSG_ShouldUpgradeToIMAPSubscription(master); + + PREF_GetBoolPref("mail.imap.upgrade.leave_subscriptions_alone", &upgradeLeaveAlone); + PREF_GetBoolPref("mail.imap.upgrade.auto_subscribe_to_all", &upgradeSubscribeAll); + + gotUpgradePrefs = TRUE; + } + + char *serverMailboxPath = (currentUrl.GetUrlIMAPstate() == + TIMAPUrl::kAuthenticatedStateURL) ? 0 : + currentUrl.CreateCanonicalSourceFolderPathString(); + + // if we are appending message even though it is in + // TIMAPUrl::kAuthenticatedStateURL we should try to reuse the cached + // connection. The reason for that is that the user might already have the + // folder opened using a new connection to append message can cause the + // thread window view out of sync. + if (!serverMailboxPath && currentUrl.GetIMAPurlType() == + TIMAPUrl::kAppendMsgFromFile) + serverMailboxPath = + currentUrl.CreateCanonicalSourceFolderPathString(); + + XP_Bool waitingForCachedConnection = FALSE; + XP_Bool newConnection = FALSE; + TNavigatorImapConnection *blockingConnection = NULL; + if (master) + { + blockingConnection = MSG_UnCacheImapConnection(master, currentUrl.GetUrlHost(), serverMailboxPath); + // if it's in the cache, by definition, it's not in use, I believe. + // this code doesn't work if you don't have remember password turned on, because we never make a connection! + if (!blockingConnection && IMAP_URLIsBiff(ce, currentUrl) + && TNavigatorImapConnection::IsConnectionActive(currentUrl.GetUrlHost(), serverMailboxPath)) + { + PR_LOG(IMAP, out, ("URL: Biff blocking on active connection %s", ce->URL_s->address)); + return -1; // we didn't have a cached connection but the inbox is busy, so biff should wait + } + } + + + if (blockingConnection && (currentUrl.GetIMAPurlType() == TIMAPUrl::kSelectFolder || currentUrl.GetIMAPurlType() == TIMAPUrl::kDeleteAllMsgs)) + blockingConnection->SetNeedsNoop(TRUE); + if (blockingConnection && + (blockingConnection->GetServerStateParser().GetIMAPstate() == TImapServerState::kFolderSelected) && + (currentUrl.GetUrlIMAPstate() == TIMAPUrl::kAuthenticatedStateURL) ) + { + // We dont want to be in the selected state in case we operate on the selected mailbox + // and we do not want an implicit Close() so shut this connection down + IMAP_TerminateConnection(blockingConnection); + blockingConnection = NULL; + } + ImapConData *newConnectionData = (ImapConData *) ce->con_data; + + if (blockingConnection) + { + MSG_SetFolderRunningIMAPUrl(ce->URL_s->msg_pane, currentUrl.GetUrlHost(), serverMailboxPath, MSG_RunningOnline); + // let's use the cached connection + newConnectionData->netConnection = blockingConnection; + ce->socket = blockingConnection->GetIOSocket(); + ce->con_sock = ce->socket; + + ce->local_file = FALSE; + NET_TotalNumberOfOpenConnections++; + NET_SetReadSelect(ce->window_id, ce->socket); + FE_SetProgressBarPercent(ce->window_id, -1); + // I don't think I need to call FE_SetConnectSelect since we are already connected + } + else + { + if (!allowMultipleFolderConnections) + { + XP_Bool folderActive = TNavigatorImapConnection::IsConnectionActive(currentUrl.GetUrlHost(), serverMailboxPath); + if (folderActive) + { + waitingForCachedConnection = TRUE; + if (loadingURL) + PR_LOG(IMAP, out, ("URL: Blocking on active connection %s", ce->URL_s->address)); + + ce->local_file = TRUE; + // Note: Chrisf: This needs to be a PRFileDesc, we're not sure how to do this so we'll leave it for you + // to fix when you get back... +// ce->socket = 1; + if (loadingURL) + NET_SetCallNetlibAllTheTime(ce->window_id, "mkimap4"); + } + } + // create the blocking connection object + if (!waitingForCachedConnection) + { + HG73299 + blockingConnection = TNavigatorImapConnection::Create(currentUrl.GetUrlHost()); + } + if (blockingConnection) + { + MSG_FolderInfo *folder = MSG_SetFolderRunningIMAPUrl(ce->URL_s->msg_pane, currentUrl.GetUrlHost(), serverMailboxPath, MSG_RunningOnline); + // Find out if delete is move-to-trash + XP_Bool deleteIsMoveToTrash = MSG_GetIMAPHostDeleteIsMoveToTrash(master, currentUrl.GetUrlHost()); + int threadPriority = 31; // medium priority was 15, but that may have slowed down thru-put +#ifdef XP_UNIX + threadPriority = 31; // highest priority for unix, especially SSL +#endif + + + newConnection = TRUE; + // start the imap thread +#ifdef NSPR20 + PRThread *imapThread = PR_CreateThread(PR_USER_THREAD, imap_thread_main_function, blockingConnection, + PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, + 0); // standard stack +#else + PRThread *imapThread = PR_CreateThread("imapIOthread", + threadPriority, + 0); // standard stack +#endif + // save the thread object so an IMAP interrupt will + // cause the TIMAP4BlockingConnection destructor to + // destroy it. + blockingConnection->SetBlockingThread(imapThread); + newConnectionData->netConnection = blockingConnection; +#ifndef NSPR20 + PR_Start(imapThread, // thread to start + imap_thread_main_function, // first function to call + blockingConnection, // function argument + nil); // function argument +#endif + + + XP_Bool folderNeedsSubscribing = FALSE; + XP_Bool folderNeedsACLRefreshed = FALSE; + if (folder) + { + folderNeedsSubscribing = MSG_GetFolderNeedsSubscribing(folder); + folderNeedsACLRefreshed = MSG_GetFolderNeedsACLListed(folder); + } + blockingConnection->SetFolderInfo(folder, folderNeedsSubscribing, folderNeedsACLRefreshed); + + blockingConnection->SetSubscribeParameters(autoSubscribe, autoUnsubscribe, + autoSubscribeOnOpen, autoUnsubscribeFromNoSelect, shouldUpgradeToSubscription, upgradeLeaveAlone, upgradeSubscribeAll); + blockingConnection->SetDeleteIsMoveToTrash(deleteIsMoveToTrash); + blockingConnection->Configure(gGetHeaders, gTooFastTime, gIdealTime, gChunkAddSize, gChunkSize, + gChunkThreshold, gFetchByChunks, gMaxChunkSize); + } + } + + if (blockingConnection) + { + blockingConnection->SetActive(TRUE); + if (ce->window_id && + ((ce->window_id->type == MWContextMail) || + (ce->window_id->type == MWContextNews))) + { + FE_UpdateStopState(ce->window_id); + } + PR_Sleep(PR_INTERVAL_NO_WAIT); + blockingConnection->SubmitActiveEntry(ce, newConnection); + + // force netlib to be called all the time, whether there is IO + // or not. This enables us to process the fe event queue. + // net_call_all_the_time_count is a global used by the existing netlib + //if(net_call_all_the_time_count < 1) + if (loadingURL) + NET_SetCallNetlibAllTheTime(ce->window_id, "mkimap4"); + blockingConnection->SetCallingNetlibAllTheTime(TRUE); + + // msg copy code has to use the TNavigatorImapConnection 'C' interface + if (ce->window_id->msgCopyInfo) + MSG_StoreNavigatorIMAPConnectionInMoveState(ce->window_id, blockingConnection); + ce->window_id->imapConnection = blockingConnection; + + +#if KEVIN_DEBUG + gNumberOfVisits=0; + gNumberOfVisitsProcessingEvents=0; + gNumberOfEventsForCurrentUrl=0; +#endif + returnValue = NET_ProcessIMAP4(ce); + } + else if (!waitingForCachedConnection) + { + FE_Alert(ce->window_id, XP_GetString(MK_OUT_OF_MEMORY)); + returnValue = -1; + } + FREEIF(serverMailboxPath); + return returnValue; +} + +MODULE_PRIVATE int32 +NET_IMAP4Load (ActiveEntry * ce) +{ + int returnValue=0; + + PR_LOG(IMAP, out, ("URL: Loading %s",ce->URL_s->address)); + + // Get the master and a master Pane. We will need them a few times later. + MSG_Pane *masterPane = ce->URL_s->msg_pane; // we need some pane to get to the master, libimg will run fetch urls to get mime parts + if (!masterPane) + masterPane = MSG_FindPane(ce->window_id, MSG_ANYPANE); // last ditch effort, GROSS! + MSG_Master *master = ce->window_id->mailMaster; + if (!master) + { + master = MSG_GetMaster(masterPane); + ce->window_id->mailMaster = master; + } + XP_ASSERT(master); + + // check to see if we have all the prefs we need for this load + if (!gotPrefs) + { + // cacheing pref + PREF_GetBoolPref("mail.imap.cache_fetch_responses",&useLibnetCacheForIMAP4Fetch); + + PREF_GetBoolPref("mail.imap.allow_multiple_folder_connections", &allowMultipleFolderConnections); + + // subscription prefs + PREF_GetBoolPref("mail.imap.auto_subscribe",&autoSubscribe); + PREF_GetBoolPref("mail.imap.auto_unsubscribe", &autoUnsubscribe); + PREF_GetBoolPref("mail.imap.auto_subscribe_on_open",&autoSubscribeOnOpen); + PREF_GetBoolPref("mail.imap.auto_unsubscribe_from_noselect_folders",&autoUnsubscribeFromNoSelect); + + // chunk size and header prefs + PREF_GetBoolPref("mail.imap.new_mail_get_headers", &gGetHeaders); + PREF_GetIntPref("mail.imap.chunk_fast", &gTooFastTime); // secs we read too little too fast + PREF_GetIntPref("mail.imap.chunk_ideal", &gIdealTime); // secs we read enough in good time + PREF_GetIntPref("mail.imap.chunk_add", &gChunkAddSize); // buffer size to add when wasting time + PREF_GetIntPref("mail.imap.chunk_size", &gChunkSize); + PREF_GetIntPref("mail.imap.min_chunk_size_threshold", &gChunkThreshold); + PREF_GetBoolPref("mail.imap.fetch_by_chunks", &gFetchByChunks); + PREF_GetIntPref("mail.imap.max_chunk_size", &gMaxChunkSize); + + // bodystructure prefs + PREF_GetBoolPref("mail.imap.mime_parts_on_demand", &gMIMEOnDemand); + PREF_GetIntPref("mail.imap.mime_parts_on_demand_threshold", &gMIMEOnDemandThreshold); + PREF_GetBoolPref("mail.imap.optimize_header_dl", &gOptimizedHeaders); + + PREF_GetIntPref("mail.imap.tunnelling_threshold", &gTunnellingThreshold); + PREF_GetBoolPref("mail.imap.io_tunnelling", &gIOTunnelling); + + gotPrefs = TRUE; + } + + ce->window_id->imapConnection = NULL; + // this might be simple note that the filtering is done + if (!XP_STRCMP(ce->URL_s->address, kImapFilteringCompleteURL)) + { + MSG_ImapInboxFilteringComplete(ce->URL_s->msg_pane); + return -1; + } + + // this might be simple note that the on/off synch is done + if (!XP_STRCMP(ce->URL_s->address, kImapOnOffSynchCompleteURL)) + { + MSG_ImapOnOffLineSynchComplete(ce->URL_s->msg_pane); + return -1; + } + + // if this url is bogus, be defensive and leave before we make a connection + TIMAPUrl currentUrl(ce->URL_s->address, ce->URL_s->internal_url); + if (!currentUrl.ValidIMAPUrl()) + return -1; + + const char *password = NULL; + + TIMAPHostInfo::AddHostToList(currentUrl.GetUrlHost()); + + // try cached password for this host; if this fails, ask libmsg. + XP_Bool savePassword = FALSE; + password = TIMAPHostInfo::GetPasswordForHost(currentUrl.GetUrlHost()) ; + if (!password) + { + savePassword = TRUE; + password = MSG_GetIMAPHostPassword(ce->window_id->mailMaster, currentUrl.GetUrlHost()); + } +/* if (!fTriedSavedPassword) */ { + // Try getting the password from the prefs, if it was saved + if (savePassword && password && (XP_STRLEN(password) > 0)) { // make sure we actually have one in the prefs + TIMAPHostInfo::SetPasswordForHost(currentUrl.GetUrlHost(), password); + } + fTriedSavedPassword = TRUE; + } + + // if this is a biff and we have no valid password, then bail before we make a connection + if (IMAP_URLIsBiff(ce, currentUrl)) + { + if (!MSG_GetIMAPHostIsSecure(master, currentUrl.GetUrlHost()) && !password) + return -1; + } + + // if we want to use the libnet cache: + // if this is not a message fetch, make sure this url is not cached. How can + // libnet cache a load mailbox command? duh. + if (!useLibnetCacheForIMAP4Fetch || + ((currentUrl.GetIMAPurlType() != TIMAPUrl::kMsgFetch) || + currentUrl.MimePartSelectorDetected())) + ce->format_out = CLEAR_CACHE_BIT(ce->format_out); + + // If this is a message fetch URL + // See if it is a force-reload-all-attachments URL. If so, strip off the + int whereStart = XP_STRLEN(ce->URL_s->address) - 9; // 9 == XP_STRLEN("&allparts") + if (whereStart > 0) + { + if (!XP_STRNCASECMP(ce->URL_s->address + whereStart, "&allparts", 9)) + { + // It is a force reload all - strip off the "&allparts" + *(ce->URL_s->address+whereStart) = 0; + ce->URL_s->content_modified = TRUE; // tiny hack. Set this bit to mean this is a + // forced reload. + } + } + + ImapConData *newConnectionData = (ImapConData *) XP_CALLOC(sizeof(ImapConData), 1); + if (!newConnectionData) + { + FE_Alert(ce->window_id, XP_GetString(MK_OUT_OF_MEMORY)); + return -1; + } + ce->con_data = newConnectionData; + newConnectionData->offLineRetrievalData = NULL; + newConnectionData->offlineDisplayStream = NULL; + + if ((currentUrl.GetIMAPurlType() == TIMAPUrl::kMsgFetch) && (currentUrl.MessageIdsAreUids()) ) + { + char *serverMailboxPath = currentUrl.CreateCanonicalSourceFolderPathString(); + char *msgIdString = currentUrl.CreateListOfMessageIdsString(); + + MSG_Pane *masterPane = ce->URL_s->msg_pane; // we need some pane to get to the master, libimg will run fetch urls to get mime parts + if (!masterPane) + masterPane = MSG_FindPane(ce->window_id, MSG_ANYPANE); // last ditch effort, GROSS! + + newConnectionData->offLineMsgKey = atoint32(msgIdString); + newConnectionData->offLineMsgFlags = MSG_GetImapMessageFlags(masterPane, currentUrl.GetUrlHost(), serverMailboxPath, newConnectionData->offLineMsgKey ); + + if ((newConnectionData->offLineMsgFlags & MSG_FLAG_OFFLINE) || NET_IsOffline()) + { + MSG_SetFolderRunningIMAPUrl(masterPane, currentUrl.GetUrlHost(), serverMailboxPath, MSG_RunningOffline); + ce->local_file = TRUE; + ce->socket = NULL; + NET_SetCallNetlibAllTheTime(ce->window_id, "mkimap4"); + /// do not cache this url since its already local + ce->format_out = CLEAR_CACHE_BIT(ce->format_out); + + MSG_StartOfflineImapRetrieval(masterPane, + currentUrl.GetUrlHost(), serverMailboxPath, + newConnectionData->offLineMsgKey, + &newConnectionData->offLineRetrievalData); + + returnValue = NET_ProcessIMAP4(ce); + } + + FREEIF(serverMailboxPath); + FREEIF(msgIdString); + } + + if (!newConnectionData->offLineRetrievalData && !NET_IsOffline()) + { + returnValue = TNavigatorImapConnection::SetupConnection(ce, master, TRUE); + } + else if (!newConnectionData->offLineRetrievalData && NET_IsOffline()) + { + // we handled offline fetch earlier, now handle offline select + if (currentUrl.GetIMAPurlType() == TIMAPUrl::kSelectFolder) + NotifyOfflineFolderLoad(currentUrl, ce->window_id); + returnValue = MK_OFFLINE; + } + + + return returnValue; +} + +// this is called from the memory cache to notify us that a cached imap message has finished loading +void IMAP_URLFinished(URL_Struct *URL_s) +{ + TIMAPUrl currentUrl(URL_s->address, URL_s->internal_url); + char *serverMailboxPath = currentUrl.CreateCanonicalSourceFolderPathString(); + MSG_FolderInfo *folder = MSG_SetFolderRunningIMAPUrl(URL_s->msg_pane, currentUrl.GetUrlHost(), serverMailboxPath, MSG_NotRunning); + FREEIF(serverMailboxPath); + MSG_IMAPUrlFinished(folder, URL_s); +} + +static MSG_FolderInfo *imap_clear_running_imap_url(ActiveEntry *ce) +{ + TIMAPUrl currentUrl(ce->URL_s->address, ce->URL_s->internal_url); + char *serverMailboxPath = currentUrl.CreateCanonicalSourceFolderPathString(); + MSG_FolderInfo *folder = MSG_SetFolderRunningIMAPUrl(ce->URL_s->msg_pane, currentUrl.GetUrlHost(), serverMailboxPath, MSG_NotRunning); + FREEIF(serverMailboxPath); + return folder; +} + + +/* NET_ProcessOfflineIMAP4 will control the state machine that + * loads messages from a imap db + * + * returns negative if the transfer is finished or error'd out + * + * returns zero or more if the transfer needs to be continued. + */ +static int +NET_ProcessOfflineIMAP4 (ActiveEntry *ce, ImapConData *cd) +{ + int keepCallingMe = 1; + + if (!cd->offlineDisplayStream) + { + if (ce->format_out == FO_PRESENT || ce->format_out == FO_CACHE_AND_PRESENT) + { + IMAP_InitializeImapFeData(ce); + } + + ce->URL_s->content_length = MSG_GetOfflineMessageSize(cd->offLineRetrievalData); + StrAllocCopy(ce->URL_s->content_type, ce->URL_s->content_length ? MESSAGE_RFC822 : TEXT_HTML); + cd->offlineDisplayStream = NET_StreamBuilder(ce->format_out, ce->URL_s, ce->window_id); + if (!cd->offlineDisplayStream) + ce->status = -1; + } + + if (ce->status >= 0) + { + int32 read_size = (*cd->offlineDisplayStream->is_write_ready) (cd->offlineDisplayStream); + if(!read_size) + return(0); /* wait until we are ready to write */ + else + read_size = MIN(read_size, NET_Socket_Buffer_Size); + + ce->status = MSG_ProcessOfflineImap(cd->offLineRetrievalData, NET_Socket_Buffer, read_size); + if(ce->status > 0) + { + ce->bytes_received += ce->status; + (*cd->offlineDisplayStream->put_block) (cd->offlineDisplayStream, NET_Socket_Buffer, ce->status); + } + + if (ce->status == 0) + { + if (cd->offlineDisplayStream) + (*cd->offlineDisplayStream->complete) (cd->offlineDisplayStream); + NET_ClearCallNetlibAllTheTime(ce->window_id, "mkimap4"); + + // clean up the offline info, in case we need to mark read online + ce->local_file = FALSE; + ce->socket = 0; + cd->offLineRetrievalData = NULL; + keepCallingMe = -1; + } + } + + if (ce->status < 0) + keepCallingMe = -1; + + // here we need to check if we have offline events saved up while we were running this url + // (and not plain old offline events getting played back). And we need to figure out how to run + // them from here...because we will interrupt current url if not careful. + if (keepCallingMe < 0) + { + // tell libmsg that we're not running an imap url for this folder anymore + MSG_FolderInfo *folder = imap_clear_running_imap_url(ce); + MSG_IMAPUrlFinished(folder, ce->URL_s); + } + + return keepCallingMe; +} + + + + +static void imap_interrupt_possible_libmsg_select(ActiveEntry *ce) +{ + // if this was a folder load then tell libmsg it was interrupted + TIMAPUrl interruptedURL(ce->URL_s->address, ce->URL_s->internal_url); + if (interruptedURL.GetIMAPurlType() == TIMAPUrl::kSelectFolder) + { + char *folderName = interruptedURL.CreateCanonicalSourceFolderPathString(); + MSG_InterruptImapFolderLoad( MSG_FindPaneOfContext(ce->window_id, MSG_ANYPANE), + interruptedURL.GetUrlHost(), folderName); + FREEIF(folderName); + } +} + +void TNavigatorImapConnection::ResetProgressInfo() +{ + LL_I2L(fLastProgressTime, 0); + fLastPercent = -1; + fLastProgressStringId = -1; +} + +/* NET_ProcessIMAP4 will control the state machine that + * loads messages from a imap4 server + * + * returns negative if the transfer is finished or error'd out + * + * returns zero or more if the transfer needs to be continued. + */ +MODULE_PRIVATE int32 +NET_ProcessIMAP4 (ActiveEntry *ce) +{ +#if KEVIN_DEBUG + gNumberOfVisits++; +#endif + MSG_Master *currentMaster = ce->window_id->mailMaster; + if (!currentMaster && ce->URL_s->msg_pane) // if there is no pane or master, then I can't find the master where the cache is + currentMaster = MSG_GetMaster(ce->URL_s->msg_pane); + + ImapConData *cd = (ImapConData *) ce->con_data; + XP_Bool shouldDieAtEnd = FALSE; + + if (cd->offLineRetrievalData) + { + return NET_ProcessOfflineIMAP4(ce, cd); + } + + TNavigatorImapConnection *imapConnection = cd->netConnection; + if (!imapConnection) + { + TNavigatorImapConnection::SetupConnection(ce, currentMaster, FALSE); + return MK_WAITING_FOR_CONNECTION; + } + assert(imapConnection); + + // if this is a move/copy operation, be sure to let libmsg do its thing. + if (imapConnection->CurrentConnectionIsMove() && ce->window_id->msgCopyInfo && + !MSG_IsMessageCopyFinished(ce->window_id->msgCopyInfo)) + { + int finishReturn = MSG_FinishCopyingMessages(ce->window_id); + + if (finishReturn < 0) + { + // handled in ReportSuccessOfOnlineCopy + + } + } + + // NotifyIOCompletionMonitor even if we end up here because we are calling netlib all of the time. + // an extra NotifyIOCompletionMonitor will cause one extra poll of the port. + // This can happen after we finish processing a bunch of fe events and go + // into a BlockedForIO state. So, we might randomly do one extra poll per + // blocked io read. After this poll, calling netlib off the time will + // be turned off if we are still blocked on io. + if (imapConnection->BlockedForIO()) + { + // we entered NET_ProcessIMAP4 because a net read is finished + imapConnection->NotifyIOCompletionMonitor(); // will give time to imap thread + } + else + PR_Sleep(PR_INTERVAL_NO_WAIT); // give the imap thread some time. + + int connectionStatus = imapConnection->GetConnectionStatus(); + + int eventLimitNotReached = 2; // if we don't limit the events, then huge downloads + // are effectively synchronous + +#if KEVIN_DEBUG + if (imapConnection->GetFEEventQueue().NumberOfEventsInQueue()) + gNumberOfVisitsProcessingEvents++; +#endif + + while (eventLimitNotReached-- && (connectionStatus >= 0) && + imapConnection->GetFEEventQueue().NumberOfEventsInQueue()) + { + if (imapConnection->GetFEEventQueue().NumberOfEventsInQueue()) + { + TImapFEEvent *event = + imapConnection->GetFEEventQueue().OrphanFirstEvent(); + event->DoEvent(); + delete event; +#if KEVIN_DEBUG + gNumberOfEventsForCurrentUrl++; +#endif + } + + // PR_Sleep(PR_INTERVAL_NO_WAIT); // when this yield was here, a huge message download, + // now that it is buffered, always caused an event + // in the queue but never tripped eventLimitNotReached. + // Since each buffered event is relatively slow, (e.g. + // html parsing 1500 bytes) the effect was to cause + // synchronous downloads + connectionStatus = imapConnection->GetConnectionStatus(); + } + + //if (eventLimitNotReached == -1) + // DebugStr("\pTouch the sky"); + + + if (connectionStatus < 0) + { + // we are done or errored out + ce->window_id->imapConnection = NULL; + XP_Bool diedWhileCallingNetlibSet = imapConnection->GetCallingNetlibAllTheTime(); + + // some events can fail and put another event in the queue + // this is rare and catastrophic anyway so only process the number + // of events we know about when we start this loop + int numberOfEventsToProcess = imapConnection->GetFEEventQueue().NumberOfEventsInQueue(); + while (numberOfEventsToProcess-- && imapConnection->GetFEEventQueue().NumberOfEventsInQueue()) + { + TImapFEEvent *event = + imapConnection->GetFEEventQueue().OrphanFirstEvent(); + event->DoEvent(); + delete event; +#if KEVIN_DEBUG + gNumberOfEventsForCurrentUrl++; +#endif + } + imapConnection->SignalEventQueueEmpty(); + + // tell libmsg that we're not running an imap url for this folder anymore + MSG_FolderInfo *folder = imap_clear_running_imap_url(ce); + + FREEIF(ce->con_data); + if (ce->socket != 0) + { + // stop calling me, i am finished + NET_ClearReadSelect(ce->window_id, ce->socket); + NET_ClearConnectSelect(ce->window_id, ce->socket); + NET_TotalNumberOfOpenConnections--; + + // maybe we can cache this connection + XP_Bool wasCached = FALSE; + + if (currentMaster && // do not cache a non-authenticated or disconnected connection. + (imapConnection->GetServerStateParser().GetIMAPstate() != TImapServerState::kNonAuthenticated) && + (imapConnection->GetServerStateParser().Connected()) && + !imapConnection->GetServerStateParser().SyntaxError()) + { + TIMAPUrl currentUrl(ce->URL_s->address, ce->URL_s->internal_url); + imapConnection->ResetProgressInfo(); + + wasCached = MSG_TryToCacheImapConnection(currentMaster, currentUrl.GetUrlHost(), + imapConnection->GetServerStateParser().GetSelectedMailboxName(), + imapConnection); + } + if (!wasCached) + { + char *logoutString = PR_smprintf("xxxx logout\r\n"); + + if (logoutString) + { + PR_LOG(IMAP, out, ("%s",logoutString)); + PR_LOG(IMAP, out, ("Logged out from NET_ProcessIMAP4")); + NET_BlockingWrite(ce->socket, logoutString, XP_STRLEN(logoutString)); + XP_FREEIF(logoutString); + } + + if (!imapConnection->GetServerStateParser().Connected()) + { + imap_interrupt_possible_libmsg_select(ce); + } + + shouldDieAtEnd = TRUE; + imapConnection->SetIOSocket(NULL); // delete connection + net_graceful_shutdown(ce->socket, HG87263); + PR_Close(ce->socket); + } + else + { + imapConnection->SetActive(FALSE); + } + + ce->socket = 0; + } + else + shouldDieAtEnd = TRUE; + + + // Whoa! + // net_call_all_the_time_count is a global used by the existing netlib + //net_call_all_the_time_count--; + //if(net_call_all_the_time_count < 1) + if (diedWhileCallingNetlibSet) + { + NET_ClearCallNetlibAllTheTime(ce->window_id, "mkimap4"); + imapConnection->SetCallingNetlibAllTheTime(FALSE); + } +#if KEVIN_DEBUG + char debugString[250]; + sprintf(debugString, "visits = %ld, usefull visits = %ld, # of events = %ld", + gNumberOfVisits, + gNumberOfVisitsProcessingEvents, + gNumberOfEventsForCurrentUrl); + DebugStr(c2pstr(debugString)); +#endif + // here we need to check if we have offline events saved up while we were running this url + // (and not plain old offline events getting played back). And we need to figure out how to run + // them from here...because we will interrupt current url if not careful. + MSG_IMAPUrlFinished(folder, ce->URL_s); + } + else + { + if (imapConnection->BlockedForIO() && !imapConnection->GetFEEventQueue().NumberOfEventsInQueue()) + { + // if we are blocked and there are no events to process + // then stop calling me until io completes + if (imapConnection->GetCallingNetlibAllTheTime()) + { + imapConnection->SetCallingNetlibAllTheTime(FALSE); + NET_ClearCallNetlibAllTheTime(ce->window_id, "mkimap4"); + } + } + else if (!imapConnection->GetCallingNetlibAllTheTime()) + { + // the thread might need to process events, keep entering + imapConnection->SetCallingNetlibAllTheTime(TRUE); + NET_SetCallNetlibAllTheTime(ce->window_id, "mkimap4"); + } + } + if (shouldDieAtEnd) + imapConnection->TellThreadToDie(); + // the imapConnection may now be deleted, so don't use it! + return connectionStatus; +} + + +/* abort the connection in progress + */ +MODULE_PRIVATE int32 +NET_InterruptIMAP4(ActiveEntry * ce) +{ + PR_LOG(IMAP, out, ("INTERRUPT Entered")); + ImapConData *cd = (ImapConData *) ce->con_data; + + TNavigatorImapConnection *imapConnection = cd->netConnection; + + if (!imapConnection || cd->offLineRetrievalData) + { + void *offLineRetrievalData = cd->offLineRetrievalData; + ce->status = MK_INTERRUPTED; + ce->window_id->imapConnection = NULL; + FREEIF(cd); + return MSG_InterruptOfflineImap(offLineRetrievalData); + } + + // Whoa! + // net_call_all_the_time_count is a global used by the existing netlib + if (imapConnection->GetCallingNetlibAllTheTime()) + { + imapConnection->SetCallingNetlibAllTheTime(FALSE); + NET_ClearCallNetlibAllTheTime(ce->window_id, "mkimap4"); + } + + // tell libmsg that we're not running an imap url for this folder anymore + imap_clear_running_imap_url(ce); + + // tell the imap thread that we are shutting down + // pass FALSE and call SetIsSafeToDie later + imapConnection->TellThreadToDie(FALSE); + + // some events can fail and put another event in the queue + // this is rare and catastrophic anyway so only process the number + // of events we know about when we start this loop + int numberOfEventsToProcess = imapConnection->GetFEEventQueue().NumberOfEventsInQueue(); + while (numberOfEventsToProcess-- && imapConnection->GetFEEventQueue().NumberOfEventsInQueue()) + { + TImapFEEvent *event = + imapConnection->GetFEEventQueue().OrphanFirstEvent(); + if (event->ShouldExecuteWhenInterrupted()) + { + if (event->GetEventFunction() == NormalEndMsgWriteStream) + event->SetEventFunction(AbortMsgWriteStream); + event->DoEvent(); + } + delete event; + } + imapConnection->SignalEventQueueEmpty(); + + imap_interrupt_possible_libmsg_select(ce); + + FREEIF(ce->con_data); + ce->status = MK_INTERRUPTED; + if (ce->socket != 0) + { + /* *** imapConnection->TellThreadToDie(FALSE) sends out the + * logout command string already + * + char *logoutString = PR_smprintf("xxxx logout\r\n"); + if (logoutString) + { + PR_LOG(IMAP, out, ("%s",logoutString)); + PR_LOG(IMAP, out, ("Logged out from NET_InterruptIMAP4")); + NET_BlockingWrite(ce->socket, logoutString, XP_STRLEN(logoutString)); + XP_FREEIF(logoutString); + } + * + */ + NET_ClearReadSelect(ce->window_id, ce->socket); + NET_ClearConnectSelect(ce->window_id, ce->socket); + TRACEMSG(("Closing and clearing socket ce->socket: %d", + ce->socket)); + imapConnection->SetIOSocket(NULL); + net_graceful_shutdown(ce->socket, HG83733); + PR_Close(ce->socket); + imapConnection->SetIOSocket(NULL); + NET_TotalNumberOfOpenConnections--; + ce->socket = 0; + } + + // tell it to stop + imapConnection->SetIsSafeToDie(); + // the imapConnection may now be deleted, so don't use it! + + ce->window_id->imapConnection = NULL; + + return MK_INTERRUPTED; +} + +/* called on shutdown to clean up */ +extern "C" void net_CleanupIMAP4(void); +extern "C" void +net_CleanupIMAP4(void) +{ +} + +extern "C" void +NET_InitIMAP4Protocol(void) +{ + static NET_ProtoImpl imap4_proto_impl; + + imap4_proto_impl.init = NET_IMAP4Load; + imap4_proto_impl.process = NET_ProcessIMAP4; + imap4_proto_impl.interrupt = NET_InterruptIMAP4; + imap4_proto_impl.cleanup = net_CleanupIMAP4; + + NET_RegisterProtocolImplementation(&imap4_proto_impl, IMAP_TYPE_URL); +} + +TLineDownloadCache::TLineDownloadCache() +{ + fLineInfo = (msg_line_info *) XP_ALLOC(sizeof( msg_line_info)); + fLineInfo->adoptedMessageLine = fLineCache; + fLineInfo->uidOfMessage = 0; + fBytesUsed = 0; +} + +TLineDownloadCache::~TLineDownloadCache() +{ + FREEIF( fLineInfo); +} + +uint32 TLineDownloadCache::CurrentUID() +{ + return fLineInfo->uidOfMessage; +} + +uint32 TLineDownloadCache::SpaceAvailable() +{ + return kDownLoadCacheSize - fBytesUsed; +} + +msg_line_info *TLineDownloadCache::GetCurrentLineInfo() +{ + return fLineInfo; +} + +void TLineDownloadCache::ResetCache() +{ + fBytesUsed = 0; +} + +XP_Bool TLineDownloadCache::CacheEmpty() +{ + return fBytesUsed == 0; +} + +void TLineDownloadCache::CacheLine(const char *line, uint32 uid) +{ + uint32 lineLength = XP_STRLEN(line); + XP_ASSERT((lineLength + 1) <= SpaceAvailable()); + + fLineInfo->uidOfMessage = uid; + + XP_STRCPY(fLineInfo->adoptedMessageLine + fBytesUsed, line); + fBytesUsed += lineLength; +} + +HG00374 + + +TIMAPSocketInfo::TIMAPSocketInfo() : +fIOSocket(0), +pInputSocketBuffer(nil), +pInputBufferSize(nil), +newLine(nil), +pauseForRead(FALSE), +readStatus(-1) +{ +} + +// TInboxReferenceCount functions + +PRMonitor *TInboxReferenceCount::fgDataSafeMonitor = nil; +int TInboxReferenceCount::fgInboxUsageCount = 0; + +TInboxReferenceCount::TInboxReferenceCount(XP_Bool bumpInboxCount) +{ + if (fgDataSafeMonitor) + { + PR_EnterMonitor(fgDataSafeMonitor); + fInboxCountWasBumped = bumpInboxCount; + if (fInboxCountWasBumped) + fgInboxUsageCount++; + PR_ExitMonitor(fgDataSafeMonitor); + } + else + fInboxCountWasBumped = FALSE; +} + +TInboxReferenceCount::~TInboxReferenceCount() +{ + if (fInboxCountWasBumped && fgDataSafeMonitor) + { + PR_EnterMonitor(fgDataSafeMonitor); + fgInboxUsageCount--; + PR_ExitMonitor(fgDataSafeMonitor); + } +} + +int TInboxReferenceCount::GetInboxUsageCount() +{ + int returnCount = 0; + + if (fgDataSafeMonitor) + { + PR_EnterMonitor(fgDataSafeMonitor); + returnCount = fgInboxUsageCount; + PR_ExitMonitor(fgDataSafeMonitor); + } + + return returnCount; +} + +//static +void TInboxReferenceCount::ImapStartup() +{ + if (!fgDataSafeMonitor) + fgDataSafeMonitor = PR_NewNamedMonitor("inbox-data-safe-monitor"); +} + +//static +void TInboxReferenceCount::ImapShutDown() +{ + if (fgDataSafeMonitor) + { + PR_DestroyMonitor(fgDataSafeMonitor); + fgDataSafeMonitor = NULL; + } +} + + +TIMAPMailboxInfo::TIMAPMailboxInfo(const char *name) +{ + m_mailboxName = XP_STRDUP(name); + m_childrenListed = FALSE; +} + +TIMAPMailboxInfo::~TIMAPMailboxInfo() +{ + FREEIF(m_mailboxName); +} + +void IMAP_StartupImap() +{ + TInboxReferenceCount::ImapStartup(); + TNavigatorImapConnection::ImapStartup(); +} + +void IMAP_ShutdownImap() +{ + TInboxReferenceCount::ImapShutDown(); + TNavigatorImapConnection::ImapShutDown(); +} + +XP_Bool IMAP_HaveWeBeenAuthenticated() +{ + return (gIMAPpassword || preAuthSucceeded); +} diff --git a/mozilla/network/protocol/imap4/mkimap4.h b/mozilla/network/protocol/imap4/mkimap4.h index e69de29bb2d..2f3c83197a9 100644 --- a/mozilla/network/protocol/imap4/mkimap4.h +++ b/mozilla/network/protocol/imap4/mkimap4.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#ifndef MKIMAP4_H +#define MKIMAP4_H + +#include "mkgeturl.h" + +XP_BEGIN_PROTOS + +extern int32 NET_IMAP4Load (ActiveEntry *ce); + +/* NET_ProcessIMAP4 will control the state machine that + * loads messages from a imap4 server + * + * returns negative if the transfer is finished or error'd out + * + * returns zero or more if the transfer needs to be continued. + */ +extern int32 NET_ProcessIMAP4 (ActiveEntry *ce); + +/* abort the connection in progress + */ +MODULE_PRIVATE int32 NET_InterruptIMAP4(ActiveEntry * ce); + +extern void NET_InitIMAP4Protocol(void); + +XP_END_PROTOS + + +#endif /* MKIMAP4_H */ diff --git a/mozilla/network/protocol/ldap/Makefile b/mozilla/network/protocol/ldap/Makefile index e69de29bb2d..fd205e943ed 100644 --- a/mozilla/network/protocol/ldap/Makefile +++ b/mozilla/network/protocol/ldap/Makefile @@ -0,0 +1,35 @@ +#!gmake +# +# The contents of this file are subject to the Netscape Public License +# Version 1.0 (the "NPL"); you may not use this file except in +# compliance with the NPL. You may obtain a copy of the NPL at +# http://www.mozilla.org/NPL/ +# +# Software distributed under the NPL is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL +# for the specific language governing rights and limitations under the +# NPL. +# +# The Initial Developer of this code under the NPL is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All Rights +# Reserved. + +DEPTH = ../../.. + +MODULE = ldapurl +LIBRARY_NAME = ldapurl + +CSRCS = \ + mkldap.c \ + $(NULL) + +EXPORTS= mkldap.h + +REQUIRES = ldap progress netcache network nspr2 dbm util pref js java fileurl \ + security layer img httpurl mimetype htmldlgs + + +include $(DEPTH)/config/rules.mk + +INCLUDES += -I$(DEPTH)/lib/libmsg diff --git a/mozilla/network/protocol/ldap/makefile.win b/mozilla/network/protocol/ldap/makefile.win index e69de29bb2d..9592933111a 100644 --- a/mozilla/network/protocol/ldap/makefile.win +++ b/mozilla/network/protocol/ldap/makefile.win @@ -0,0 +1,88 @@ +#!gmake +# +# The contents of this file are subject to the Netscape Public License +# Version 1.0 (the "NPL"); you may not use this file except in +# compliance with the NPL. You may obtain a copy of the NPL at +# http://www.mozilla.org/NPL/ +# +# Software distributed under the NPL is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL +# for the specific language governing rights and limitations under the +# NPL. +# +# The Initial Developer of this code under the NPL is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All Rights +# Reserved. + +IGNORE_MANIFEST=1 +# + +#------------------------------------------------------------------------ +# +# Makefile to build the Network file protocol LIB +# +#------------------------------------------------------------------------ + + +# +# Make sure we have MOZILLA_CLIENT defined so we get the +# proper JS includes +# +LCFLAGS = $(LCFLAGS) -DMOZILLA_CLIENT + +!ifdef BUILD_DEBUG_GC +LCFLAGS = $(LCFLAGS) -DDEBUG_GC +!endif + +LLIBS= \ + $(NULL) +MISCDEP=$(LLIBS) +OBJS= \ + .\$(OBJDIR)\mkldap.obj \ + $(NULL) + +CSRCS = \ + mkldap.c \ + $(NULL) + + +LIBRARY_NAME=ldapurl +MODULE=ldapurl +DEPTH=..\..\.. + +LOCAL_INCLUDES=-I. -I$(DEPTH)/dist/public/zlib -I$(DEPTH)/dist/public/parse +INCLUDES = $(LOCAL_INCLUDES) + + +EXTRA_LIBS= + +REQUIRES= progress network ldap +EXPORTS= mkldap.h + +# use LINCS on win32 for now since REQUIRES seems to be broken +#!if "$(MOZ_BITS)" != "16" +LINCS= \ + -I$(PUBLIC)\nspr2 \ + -I$(PUBLIC)\util \ + -I$(PUBLIC)\java \ + -I$(PUBLIC)\pref \ + -I$(PUBLIC)\htmldlgs \ + -I$(PUBLIC)\js \ + -I$(PUBLIC)\security \ + -I$(PUBLIC)\netcache \ + -I$(PUBLIC)\network \ + -I$(PUBLIC)\ldap \ + -I$(PUBLIC)\progress \ + -I$(DEPTH)\lib\libmsg \ + -I$(PUBLIC)\mimetype \ +#!endif + +include <$(DEPTH)\config\rules.mak> + +libs:: $(LIBRARY) + $(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib + +symbols:: + @echo "LIBRARY_NAME is $(LIBRARY_NAME)" + @echo "LIBRARY is $(LIBRARY)" diff --git a/mozilla/network/protocol/ldap/mkldap.cpp b/mozilla/network/protocol/ldap/mkldap.cpp index e69de29bb2d..a8791892432 100644 --- a/mozilla/network/protocol/ldap/mkldap.cpp +++ b/mozilla/network/protocol/ldap/mkldap.cpp @@ -0,0 +1,3309 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +// +// mkldap.cpp -- Handles LDAP-related URLs for the core Navigator, without +// requiring libmsg. mkldap is responsible for +// 1. basic async searching of LDAP servers, including authentication, +// capability negotiation with the server, and parsing results +// 2. Reading LDAP entries and converting them into HTML for display +// in the browsers +// 3. Reading an LDAP entry and converting it for use in the local +// Address Book +// 4. Replicating a large section of an LDAP directory into a local +// Address Book using changelog deltas +// + +#include "rosetta.h" +#include "mkutils.h" + +#include "mkgeturl.h" +#include "mkldap.h" +#include "xpgetstr.h" +#include "abcom.h" +#include "addrbook.h" +#include "msgcom.h" +#include "msgnet.h" +#include "msgpane.h" +#include "msgurlq.h" +#include "dirprefs.h" +#include "net.h" +#include "xp_str.h" +#include HG87373 +#include "secnav.h" +#include "libi18n.h" +#include "intl_csi.h" +#include "pw_public.h" + + +#ifdef MOZ_LDAP + #ifdef MOZ_LI + #include "LILDAPAllOps.h" + #endif + + #define NEEDPROTOS + #define LDAP_REFERRALS + #include "lber.h" + #include "ldap.h" +#endif + + +extern "C" +{ + extern int MK_OUT_OF_MEMORY; + extern int MK_MSG_SEARCH_FAILED; + + extern int MK_LDAP_COMMON_NAME; + extern int MK_LDAP_FAX_NUMBER; + extern int MK_LDAP_GIVEN_NAME; + extern int MK_LDAP_LOCALITY; + extern int MK_LDAP_PHOTOGRAPH; + extern int MK_LDAP_EMAIL_ADDRESS; + extern int MK_LDAP_MANAGER; + extern int MK_LDAP_ORGANIZATION; + extern int MK_LDAP_OBJECT_CLASS; + extern int MK_LDAP_ORG_UNIT; + extern int MK_LDAP_POSTAL_ADDRESS; + extern int MK_LDAP_SECRETARY; + extern int MK_LDAP_SURNAME; + extern int MK_LDAP_STREET; + extern int MK_LDAP_PHONE_NUMBER; + extern int MK_LDAP_CAR_LICENSE; + extern int MK_LDAP_BUSINESS_CAT; + extern int MK_LDAP_DEPT_NUMBER; + extern int MK_LDAP_DESCRIPTION; + extern int MK_LDAP_EMPLOYEE_TYPE; + extern int MK_LDAP_POSTAL_CODE; + extern int MK_LDAP_TITLE; + + extern int MK_LDAP_ADD_SERVER_TO_PREFS; + extern int MK_MALFORMED_URL_ERROR; + HG73226 + extern int MK_LDAP_HTML_TITLE; + + extern int MK_LDAP_AUTH_PROMPT; + + extern int XP_LDAP_OPEN_SERVER_FAILED; + extern int XP_LDAP_BIND_FAILED; + extern int XP_LDAP_SEARCH_FAILED; +} + + +#ifdef MOZ_LDAP + +//***************************************************************************** +// +// net_LdapConnectionData is the base class which knows how to run +// ldap URLs asynchronously and pull out search results. +// +// If you want to do something special with the results of an LDAP +// search, you probably want to derive from this class and use/make +// virtual functions. +// + +class net_LdapConnectionData +{ +public: + net_LdapConnectionData (ActiveEntry*, XP_Bool bInitLdap = TRUE); + virtual ~net_LdapConnectionData (); + + virtual int Load (ActiveEntry *ce); + virtual int Process (); + virtual int Interrupt (); + +protected: + typedef enum _LdapConnectionType + { + kBaseConnection, + kHtmlConnection, + kReplicatorConnection, + kAddressBookConnection, + kLIConnection + } LdapConnectionType; + + virtual int Unbind (); + virtual int OnBindCompleted () { return 0; } + virtual int OnEntryFound (LDAPMessage *) { return 0; } + virtual int OnSearchCompleted () { return MK_CONNECTED; } + virtual XP_Bool OnError(int err) { return TRUE; } + virtual int16 GetLocalCharSet (); + virtual LdapConnectionType GetConnectionType() { return kBaseConnection; } + + void AddLdapServerToPrefs (LDAPURLDesc* pDesc); + + char *ConvertFromServerCharSet (char *s); + char *ConvertToServerCharSet (char *s); + // sub classes may have strings prefixed to the url...We need to filter them out here. + virtual char * ExtractLDAPEncoding(char * s); + + XP_Bool ShouldAddUrlDescToPrefs (MWContext *context, XP_List *serverList, LDAPURLDesc *pDesc); + void ConvertUrlDescToDirServer (LDAPURLDesc *pDesc, DIR_Server **pServer); + + const char *GetHostDescription (); + char *GetAttributeFromDN (char *dn, const char *name); + const char *GetAttributeString (DIR_AttributeId id); + + void DisplayError (int templateId, const char *contextString, int ldapErrorId); + + int16 m_csid; + LDAP *m_ldap; + int m_messageId; + ActiveEntry *m_ce; + DIR_Server *m_server; +}; + +net_LdapConnectionData::net_LdapConnectionData (ActiveEntry *ce, XP_Bool bInitLdap) +{ + // We init LDAP with no server name and port since we expect all + // such information to be spedified by URLs in our case. + // + if (bInitLdap) + m_ldap = ldap_init(NULL,0); + else + m_ldap = NULL; + + m_messageId = -1; // Init to an illegal value so we can detect errors + + m_server = NULL; + + m_csid = -1; + m_ce = ce; + m_ce->socket = NULL; + + // Netlib magic so we get a timeslice without waiting for a read-ready socket. + // It would be nice to ditch this, but the LDAP SDK manages the sockets. + NET_SetCallNetlibAllTheTime(m_ce->window_id, "mkldap"); +} + + +net_LdapConnectionData::~net_LdapConnectionData () +{ + // This is the normal-termination case to let go of the connection + Unbind(); + + // Netlib magic so we won't get called anymore for this URL + // It would be nice to ditch this, but the LDAP SDK manages the sockets. + NET_ClearCallNetlibAllTheTime(m_ce->window_id, "mkldap"); +} + +int16 net_LdapConnectionData::GetLocalCharSet () +{ + if (m_csid == -1) + m_csid = INTL_DefaultDocCharSetID(0); + return m_csid; +} + +void net_LdapConnectionData::AddLdapServerToPrefs (LDAPURLDesc* pDesc) +{ + // Takes an ActiveEntry from the current operation, a url descriptor from ldap_url_parse, + // determines (by asking the user) if we should add it to our LDAP prefs, and adds it if necessary. + + // Do we want to add the server specified by the URL to our LDAP prefs? + XP_List *serverList = FE_GetDirServers(); + if (serverList && ShouldAddUrlDescToPrefs(m_ce->window_id, serverList, pDesc)) + { + ConvertUrlDescToDirServer (pDesc, &m_server); + if (m_server) + { + XP_ListAddObjectToEnd (serverList, m_server); + DIR_SaveServerPreferences (serverList); + } + } +} + + +int net_LdapConnectionData::Load (ActiveEntry *ce) +{ + int ldapErr = 0; + LDAPURLDesc *pDesc = NULL; + + XP_ASSERT(ce); + XP_ASSERT(ce->URL_s); + XP_ASSERT(ce->URL_s->address); + + if (m_ldap) + { + // addbook and html have prefixes in front of them (i.e. addbook-ldap). We now want + // to parse that out of the utf8 encoding string that will be sent to the server. + // In addition, we want to convert the resulting encoding to utf8 + + // we do not need to free encoding because it is a part of the url passed in.... + char * encoding = ExtractLDAPEncoding(ce->URL_s->address); + + // now take the ldap encoding and convert to the server char set. + char *utf8Encoding = utf8Encoding = ConvertToServerCharSet (encoding); + + if (utf8Encoding) + { + ldapErr = ldap_url_parse (utf8Encoding, &pDesc); + + if (!pDesc || ldapErr != LDAP_SUCCESS) + ldapErr = MK_MALFORMED_URL_ERROR; + + if (ldapErr == 0) + { + // RFC 1959 allows this syntax, but we don't really have a way + // to infer an LDAP server, and it's a pretty strange X.500-ism anyway + if (!pDesc->lud_host) + ldapErr = MK_MALFORMED_URL_ERROR; + + AddLdapServerToPrefs (pDesc); + + HG29237 + if (ldapErr == 0) + { + // Initiate the search on the server + int msgId = ldap_url_search (m_ldap, utf8Encoding, 0); + if (msgId != -1) + m_messageId = msgId; + else + ldapErr = -1; + } + } + else + ldapErr = MK_MALFORMED_URL_ERROR; + + XP_FREE(utf8Encoding); + } + else + ldapErr = -1; + } + else + XP_ASSERT(pDesc); + ldapErr = -1; + + if (pDesc) + ldap_free_urldesc (pDesc); + + return ldapErr; +} + + +// Interpret all data we get from the server as being UTF-8 +char *net_LdapConnectionData::ConvertFromServerCharSet (char *s) +{ + char *converted = NULL; + XP_ASSERT(s); + if (s) + { + // Use the win_csid for browsers but the GUI csid for the AB + int16 destCsid = GetLocalCharSet(); + converted = DIR_ConvertFromServerCharSet (m_server, s, destCsid); + } + return converted; +} + +char * net_LdapConnectionData::ExtractLDAPEncoding(char * url) +{ + // for the base class, we don't know about stripping any prefixes off... + return url; +} + +// Send all data to the server in UTF-8 +char *net_LdapConnectionData::ConvertToServerCharSet (char *s) +{ + char *converted = NULL; + XP_ASSERT(s); + if (s) + { + // Use the win_csid for browsers but the GUI csid for the AB + int16 srcCsid = GetLocalCharSet(); + converted = DIR_ConvertToServerCharSet (m_server, s, srcCsid); + } + return converted; +} + +const char *net_LdapConnectionData::GetHostDescription() +{ + // since the description is optional, it might be null, so pick up + // the server's DNS name if necessary + return m_server->description ? m_server->description : m_server->serverName; +} + +char *net_LdapConnectionData::GetAttributeFromDN (char *dn, const char *name) +{ + // Pull an attribute out of the distinguished name + // e.g. the cn attribute from cn=John Smith,o=Netscape Communications Corp.,c=US + + char *result = NULL; + char **explodedDn = ldap_explode_dn (dn, FALSE /* suppress types? */); + if (explodedDn) + { + int i = 0; + while (explodedDn[i] && !result) + { + const char *nameFromDn = XP_STRTOK (explodedDn[i], "="); + if (!XP_STRCASECMP(name, nameFromDn)) + { + char *tmpResult = XP_STRDUP(explodedDn[i] + XP_STRLEN(nameFromDn) + 1); + result = ConvertFromServerCharSet (tmpResult); + XP_FREE(tmpResult); + } + i++; + } + ldap_value_free (explodedDn); + } + return result; +} + + +const char *net_LdapConnectionData::GetAttributeString (DIR_AttributeId id) +{ + // This function is a bottleneck for DIR_GetAttributeName. I think most + // of the time we'll have a DIR_Server, but I think it can also be NULL + // if the user clicked on an ldap: URL for which there's no server in + // the preferences + + return DIR_GetFirstAttributeString (m_server, id); +} + +void net_LdapConnectionData::DisplayError (int templateId, const char *contextString, int ldapErrorId) +{ + // This builds up error dialogs which look like: + // "Failed to open connection to 'ldap.mcom.com' due to LDAP error 'bad DN name' (0x22)" + + char *templateString = XP_GetString (templateId); + char *ldapErrString = NULL; + + if (ldapErrorId != 0) + ldapErrString = ldap_err2string (ldapErrorId); + if (templateString && contextString && (ldapErrorId == 0 || ldapErrString)) + { + int len = XP_STRLEN(templateString) + XP_STRLEN(contextString) + 10; + if (ldapErrString) + len += XP_STRLEN(ldapErrString); + char *theBigString = (char*) XP_ALLOC (len); + if (theBigString) + { + if (ldapErrorId) + PR_snprintf (theBigString, len, templateString, contextString, ldapErrString, ldapErrorId); + else + PR_snprintf (theBigString, len, templateString, contextString); + FE_Alert (m_ce->window_id, theBigString); + XP_FREE(theBigString); + } + } +} + + +XP_Bool net_LdapConnectionData::ShouldAddUrlDescToPrefs (MWContext *context, XP_List *serverList, LDAPURLDesc *pDesc) +{ + XP_Bool shouldAdd = TRUE; + + // First look through the prefs to see if the server already exists there + for (int i = 1; i <= XP_ListCount(serverList) && shouldAdd; i++) + { + DIR_Server *server = (DIR_Server*) XP_ListGetObjectNum (serverList, i); + if (server && server->serverName && pDesc->lud_host) + { + if (!XP_STRCASECMP(pDesc->lud_host, server->serverName)) + { + // The server does exist -- don't ask to add it again + shouldAdd = FALSE; + if (!m_server) + m_server = server; + } + } + } + + // If the server doesn't exist, ask the user if they want to add it to their prefs + if (shouldAdd) + { + char *prompt = PR_smprintf (XP_GetString(MK_LDAP_ADD_SERVER_TO_PREFS), pDesc->lud_host); + if (prompt) + { + shouldAdd = FE_Confirm (context, prompt); + XP_FREE(prompt); + } + else + shouldAdd = FALSE; + } + + return shouldAdd; +} + + +void net_LdapConnectionData::ConvertUrlDescToDirServer (LDAPURLDesc *pDesc, DIR_Server **ppServer) +{ + *ppServer = (DIR_Server*) XP_ALLOC (sizeof(DIR_Server)); + if (*ppServer) + { + XP_BZERO(*ppServer, sizeof(DIR_Server)); + + // These fields just map right over + (*ppServer)->serverName = XP_STRDUP (pDesc->lud_host); + (*ppServer)->searchBase = XP_STRDUP (pDesc->lud_dn ? pDesc->lud_dn : ""); + (*ppServer)->port = pDesc->lud_port; + + // These could probably be in a generic DIR_Server initializer. Do we need that? + (*ppServer)->efficientWildcards = TRUE; + (*ppServer)->saveResults = TRUE; + (*ppServer)->maxHits = 100; + + // The address book needs a valid filename. Use the hostname to derive one. + char *fileName = XP_STRTOK(pDesc->lud_host, "."); + if (fileName) + (*ppServer)->fileName = WH_FileName (fileName, xpAddrBook); + else + (*ppServer)->fileName = WH_FileName (pDesc->lud_host, xpAddrBook); + + // Try to derive a reasonable name for DIR_Server.description by looking + // at the distinguished name and picking out the most specific RDN + // (i.e. the last OU, or any OU, or any O, or any C). + if (pDesc->lud_dn) + { + char **explodedDn = ldap_explode_dn (pDesc->lud_dn, TRUE /*suppress types? */); + if (explodedDn) + { + if (explodedDn[0]) + (*ppServer)->description = XP_STRDUP (explodedDn[0]); + + ldap_value_free (explodedDn); + } + } + + // If we couldn't find any reasonable-looking DN component, just use the host name + if (NULL == (*ppServer)->description) + (*ppServer)->description = XP_STRDUP ((*ppServer)->serverName); + } +} + + +int net_LdapConnectionData::Process () +{ + LDAPMessage *message = NULL; + struct timeval timeout; + XP_BZERO (&timeout, sizeof(timeout)); + int result = ldap_result (m_ldap, m_messageId, 0, &timeout, &message); + + switch (result) + { + case LDAP_RES_SEARCH_ENTRY: + result = OnEntryFound (message); + break; + case LDAP_RES_SEARCH_RESULT: + result = OnSearchCompleted (); + break; + case LDAP_RES_BIND: + result = OnBindCompleted (); + break; + case LDAP_TIMEOUT: + result = 0; + break; + case 0: + break; + default: + if (result > 0) + { + int err = ldap_result2error (m_ldap, message, 0); + result = ((err != 0) ? err : result); + } + + if (OnError(result)) + { + DisplayError (XP_LDAP_OPEN_SERVER_FAILED, GetHostDescription(), result); + result = -1; + } + break; + } + + if (message != NULL) + ldap_msgfree (message); + return result; +} + + +int net_LdapConnectionData::Interrupt () +{ + // The destructor will unbind. + int ldapErr = ldap_abandon (m_ldap, m_messageId); + return ldapErr; +} + + +int net_LdapConnectionData::Unbind () +{ + int ldapErr = 0; + if (m_ldap) + { + ldapErr = ldap_unbind(m_ldap); + m_ldap = NULL; + } + return ldapErr; +} + + +//***************************************************************************** +// +// net_LdapToHtml does the work of converting LDAP entries into HTML +// which can be displayed in a browser window +// + +const int kNumAttributeNames = 24; + +class net_LdapToHtml : public net_LdapConnectionData +{ +public: + net_LdapToHtml (ActiveEntry*); + + virtual int OnEntryFound (LDAPMessage *entry); + virtual int OnSearchCompleted(); + virtual int16 GetLocalCharSet (); + + typedef struct + { + char *attributeName; + int resourceId; + } AttributeName; + +protected: + virtual LdapConnectionType GetConnectionType() { return kHtmlConnection; } + + int OpenStream (); + int CloseStream (); + int WriteLineToStream (const char *line, XP_Bool bold, XP_Bool lineBreak, XP_Bool isMailto = FALSE, + XP_Bool isUri = FALSE, XP_Bool aligntop = FALSE, XP_Bool isCell = FALSE); + NET_StreamClass *m_stream; + + const char *LookupAttributeName (const char *attrib); + char *GetTableCaption (LDAPMessage*); + + int WriteEntryToStream (LDAPMessage*); + int WriteValue (const char *, XP_Bool isMailto = FALSE, XP_Bool isUri = FALSE); + int WriteAttribute (const char *); + int WritePlainLine (const char *); + + + XP_Bool IsMailtoAttribute (const char *); + XP_Bool IsUriAttribute (const char *); + + char *ConvertDnToUri (const char *); + char *EscapeSpaces (const char *); + char *InferUrlBase (); + + static AttributeName m_names[kNumAttributeNames]; +}; + + +// Note that these are sorted. Someday I'll match attribs by binary search +// +// These are initialized to zero instead of their real resource IDs because +// the HP/UX compiler seems not to be capable of doing that + +net_LdapToHtml::AttributeName net_LdapToHtml::m_names[kNumAttributeNames] = { + { "carlicense", 0 }, + { "cn", 0 }, + { "businesscategory", 0 }, + { "departmentnumber", 0 }, + { "description", 0 }, + { "employeetype", 0 }, + { "facsimiletelephonenumber", 0 }, + { "givenname", 0 }, + { "l", 0 }, + { "jpegPhoto", 0 }, + { "mail", 0 }, + { "manager", 0 }, + { "o", 0 }, + { "objectclass", 0 }, + { "ou", 0 }, + { "postaladdress", 0 }, + { "postalcode", 0 }, + { "secretary", 0 }, + { "sn", 0 }, + { "street", 0 }, + { "telephonenumber", 0 }, + { "title", 0 } + HG38731 +}; + + +net_LdapToHtml::net_LdapToHtml (ActiveEntry *ce) : net_LdapConnectionData (ce) +{ + m_stream = NULL; +} + + +int net_LdapToHtml::OnEntryFound (LDAPMessage *message) +{ + int status = 0; + if (!m_stream) + status = OpenStream(); + if (0 == status) + status = WriteEntryToStream (message); + return status; +} + +int net_LdapToHtml::OnSearchCompleted () +{ + CloseStream(); + return net_LdapConnectionData::OnSearchCompleted(); +} + + +int16 net_LdapToHtml::GetLocalCharSet () +{ + if (m_csid == -1) + { + INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_ce->window_id); + m_csid = INTL_GetCSIWinCSID(c); + if (CS_DEFAULT == m_csid) + m_csid = INTL_DefaultWinCharSetID (0); + } + return m_csid; +} + + +int net_LdapToHtml::OpenStream () +{ + int err = 0; + + if (NULL != m_stream) + return err; + + if (!m_ce->URL_s->content_type) + StrAllocCopy(m_ce->URL_s->content_type, TEXT_HTML); + m_stream = NET_StreamBuilder(m_ce->format_out, m_ce->URL_s, m_ce->window_id); + if (!m_stream) + return MK_OUT_OF_MEMORY; + + // Scribble HTML-starting stuff into the stream + char *htmlHeaders = PR_smprintf ("%s%s%s", XP_GetString(MK_LDAP_HTML_TITLE), LINEBREAK, LINEBREAK); + if (htmlHeaders) + err = (*(m_stream)->put_block)(m_stream, htmlHeaders, XP_STRLEN(htmlHeaders)); + else + err = MK_OUT_OF_MEMORY; + + if (1 == err) + err = 0; //### which is it: 1 for success or 0 for success? + + return err; +} + + +int net_LdapToHtml::CloseStream () +{ + int err = 0; + + // Never got any hits for this operation + if (!m_stream) + { + if (((err = OpenStream()) == 0) && m_stream) + { + char *noMatches = PR_smprintf ("

%s

", XP_GetString(MK_MSG_SEARCH_FAILED)); + if (noMatches) + { + err = (*(m_stream)->put_block)(m_stream, noMatches, XP_STRLEN(noMatches)); + XP_FREE(noMatches); + } + } + } + + // Scribble HTML-ending stuff into the stream + if (m_stream) + { + char htmlFooters[32]; + PR_snprintf (htmlFooters, sizeof(htmlFooters), "%s%s", LINEBREAK, LINEBREAK); + err = (*(m_stream)->put_block)(m_stream, htmlFooters, XP_STRLEN(htmlFooters)); + if (1 == err) + err = 0; //### which is it: 1 for success or 0 for success? + + // complete the stream + (*m_stream->complete) (m_stream); + XP_FREE(m_stream); + } + + return err; +} + + +const char *net_LdapToHtml::LookupAttributeName (const char *attrib) +{ + // If we know about this attribute (it has a DIR_AttributeId and we + // have a DIR_Server to ask, get the name from the DIR_Server + DIR_AttributeId id; + if (0 == DIR_AttributeNameToId (attrib, &id) && m_server) + return DIR_GetAttributeName (m_server, id); + + // If we didn't get the name from LDAP prefs, get it using our + // own hacky internal table. ### This should really move into the LDAP SDK. + + // Fill these resource IDs in here since the HP/UX compiler can't do it + // in the initializer for the static array. + if (m_names[0].resourceId == 0) + { + m_names[0].resourceId = MK_LDAP_CAR_LICENSE; + m_names[1].resourceId = MK_LDAP_COMMON_NAME; + m_names[2].resourceId = MK_LDAP_BUSINESS_CAT; + m_names[3].resourceId = MK_LDAP_DEPT_NUMBER; + m_names[4].resourceId = MK_LDAP_DESCRIPTION; + m_names[5].resourceId = MK_LDAP_EMPLOYEE_TYPE; + m_names[6].resourceId = MK_LDAP_FAX_NUMBER; + m_names[7].resourceId = MK_LDAP_GIVEN_NAME; + m_names[8].resourceId = MK_LDAP_LOCALITY; + m_names[9].resourceId = MK_LDAP_PHOTOGRAPH; + m_names[10].resourceId = MK_LDAP_EMAIL_ADDRESS; + m_names[11].resourceId = MK_LDAP_MANAGER; + m_names[12].resourceId = MK_LDAP_ORGANIZATION; + m_names[13].resourceId = MK_LDAP_OBJECT_CLASS; + m_names[14].resourceId = MK_LDAP_ORG_UNIT; + m_names[15].resourceId = MK_LDAP_POSTAL_ADDRESS; + m_names[16].resourceId = MK_LDAP_POSTAL_CODE; + m_names[17].resourceId = MK_LDAP_SECRETARY; + m_names[18].resourceId = MK_LDAP_SURNAME; + m_names[19].resourceId = MK_LDAP_STREET; + m_names[20].resourceId = MK_LDAP_PHONE_NUMBER; + m_names[21].resourceId = MK_LDAP_TITLE; + m_names[22].resourceId = HG87272; + m_names[23].resourceId = HG73272; + } + + for (int i = 0; i < kNumAttributeNames; i++) + { + if (!XP_STRCASECMP(attrib, m_names[i].attributeName)) + return XP_GetString (m_names[i].resourceId); + } + + // ### Boy, is this hacky. Maybe we can push this into default prefs now that we have excluded attributes + if (XP_STRCASECMP(attrib, "userpassword") && XP_STRCMP(attrib, "objectclass") && + XP_STRCASECMP(attrib, "subtreeaci")) + return attrib; + return NULL; +} + + +int net_LdapToHtml::WriteLineToStream (const char *line, XP_Bool bold, XP_Bool lineBreak, XP_Bool isMailto, XP_Bool isUri, XP_Bool aligntop, XP_Bool isCell) +{ +#define kMailtoTemplate "%s" +#define kUriTemplate "%s" + + int err = 0; + int htmlLen = XP_STRLEN(line) + XP_STRLEN(""); + if (bold) + htmlLen += XP_STRLEN (""); + if (lineBreak) + htmlLen += XP_STRLEN(LINEBREAK); + if (isMailto) + htmlLen += XP_STRLEN(kMailtoTemplate) + XP_STRLEN(line); + if (isUri) + htmlLen += XP_STRLEN(kUriTemplate) + XP_STRLEN(line); + + char *htmlLine = (char *) XP_ALLOC(htmlLen + 1); + if (htmlLine) + { + htmlLine[0] = '\0'; + if (isCell) + { + if (aligntop) + XP_STRCAT (htmlLine, ""); + else + XP_STRCAT (htmlLine, ""); + } + + if (bold) + XP_STRCAT (htmlLine, ""); + + if (isMailto) + { + char *mailtoHtml = PR_smprintf (kMailtoTemplate, line, line); + if (mailtoHtml) + { + XP_STRCAT(htmlLine, mailtoHtml); + XP_FREE(mailtoHtml); + } + } + else if (isUri) + { + // RFC 2079 sez the URL comes first, then zero or more spaces, then an optional label + char *label = NULL; + char *url = XP_STRDUP(line); + if (url) + { + int i = 0; + while (url[i] && url[i] != ' ') + i++; + if (url[i] == ' ') + { + url[i] = '\0'; + + int j = i + 1; + while (url[j] == ' ') + j++; + + label = &url[j]; + } + } + + char *uriHtml = PR_smprintf (kUriTemplate, url ? url : line, label ? label : line); + if (uriHtml) + { + XP_STRCAT(htmlLine, uriHtml); + XP_FREE(uriHtml); + } + XP_FREEIF(url); + } + else + XP_STRCAT (htmlLine, line); + + if (bold) + XP_STRCAT (htmlLine, " "); + if (isCell) + XP_STRCAT (htmlLine, ""); + if (lineBreak) + XP_STRCAT (htmlLine, LINEBREAK); + err = (*(m_stream)->put_block)(m_stream, htmlLine, XP_STRLEN(htmlLine)); + XP_FREE (htmlLine); + } + else + err = MK_OUT_OF_MEMORY; + return err; +} + + +int net_LdapToHtml::WriteAttribute (const char *attrib) +{ + return WriteLineToStream (attrib, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE); +} + + +int net_LdapToHtml::WriteValue (const char *value, XP_Bool isMailto, XP_Bool isUri) +{ + return WriteLineToStream (value, TRUE, TRUE, isMailto, isUri, FALSE, TRUE); +} + + +int net_LdapToHtml::WritePlainLine (const char *line) +{ + return WriteLineToStream (line, FALSE, TRUE); +} + + +XP_Bool net_LdapToHtml::IsMailtoAttribute (const char *attrib) +{ + if (m_server) + { + const char **strings = DIR_GetAttributeStrings (m_server, mail); + if (strings) + { + int i = 0; + while (strings[i]) + { + if (!XP_STRCASECMP(attrib, strings[i])) + return TRUE; + i++; + } + } + } + else + if (!XP_STRCASECMP(attrib, "mail")) + return TRUE; + + return FALSE; +} + + +XP_Bool net_LdapToHtml::IsUriAttribute (const char *attrib) +{ + switch (XP_TO_LOWER(attrib[0])) + { + case 'l': + if (!XP_STRCASECMP(attrib, "labeleduri") || // preferred + !XP_STRCASECMP(attrib, "labeledurl")) // old-style + return TRUE; + break; + case 'u': + if (!XP_STRCASECMP(attrib, "url")) // apparently some servers do this. be lenient in what we accept + return TRUE; + break; + } + return FALSE; +} + + +char *net_LdapToHtml::InferUrlBase () +{ + // Look in Netlib's data structures to find the URL we're running right now + // and assume that new URLs have the same host, port, etc + + // I'm guessing we can just look for everything up to the third slash. If this + // turns out to be not true, we can do the more expensive thing of running it + // through ldap_url_parse and then reforming it from the ldap_url_desc + + char *url = m_ce->URL_s->address; + + // Small memory wastage here, but this buf is so short-lived, it's probably + // not worth figuring out the real right size + char *urlBase = (char*) XP_ALLOC (XP_STRLEN(url)); + + if (urlBase) + { + char *tmpSrc = url; + char *tmpDst = urlBase; + int slashCount = 0; + + while (*tmpSrc) + { + if (*tmpSrc == '/') + slashCount++; + if (slashCount == 3) + { + *tmpDst = '\0'; + break; + } + else + *tmpDst++ = *tmpSrc; + tmpSrc++; + } + + } + return urlBase; + +} + + +char *net_LdapToHtml::EscapeSpaces (const char *string) +{ + int length = XP_STRLEN (string) + 1; // +1 for trailing null + const char *tmp = string; + while (*tmp) + { + if (*tmp == 0x20) + length += 2; // From " " to "%20" + tmp++; + } + + char *result = (char*) XP_ALLOC (length); + if (result) + { + tmp = string; + char *tmp2 = result; + + while (*tmp) + { + if (*tmp == 0x20) + { + *tmp2++ = '%'; + *tmp2++ = '2'; + *tmp2++ = '0'; + } + else + *tmp2++ = *tmp; + *tmp++; + } + *tmp2 = '\0'; + } + + return result; +} + + +char *net_LdapToHtml::ConvertDnToUri (const char *dn) +{ + char *uri = NULL; + char **explodedDn = ldap_explode_dn (dn, TRUE /* suppress types? */); + if (explodedDn) + { + char *urlBase = InferUrlBase(); + if (urlBase) + { + char *unescapedUrl = PR_smprintf ("%s/%s", urlBase, dn); + if (unescapedUrl) + { + char *escapedUrl = EscapeSpaces (unescapedUrl); + if (escapedUrl) + { + uri = PR_smprintf ("%s %s", escapedUrl, explodedDn[0]); + XP_FREE(escapedUrl); + } + XP_FREE(unescapedUrl); + } + XP_FREE(urlBase); + } + ldap_value_free (explodedDn); + } + + return uri; +} + + +char *net_LdapToHtml::GetTableCaption (LDAPMessage *message) +{ + // Here's a somewhat arbitrary way to decide what the table caption for an entry should be + + char *caption = NULL; + + // Look first for a "cn" attribute. + const char *cnAttrName = GetAttributeString (cn); + if (cnAttrName) + { + char **cnValues = ldap_get_values (m_ldap, message, cnAttrName); + if (cnValues) + { + if (cnValues[0]) + caption = ConvertFromServerCharSet (cnValues[0]); + ldap_value_free (cnValues); + } + } + + if (!caption) + { + // Use the left-most (most specific) component of the DN + char *dn = ldap_get_dn (m_ldap, message); + if (dn) + { + char **explodedDn = ldap_explode_dn (dn, TRUE /* suppress types? */); + if (explodedDn && explodedDn[0]) + caption = ConvertFromServerCharSet (explodedDn[0]); + ldap_value_free (explodedDn); + ldap_memfree(dn); + } + } + + return caption; +} + + +int net_LdapToHtml::WriteEntryToStream (LDAPMessage *message) +{ + // Write an LDAP directory entry to the HTML stream + + XP_ASSERT(message && m_stream); + if (!message || !m_stream) + return -1; + + WritePlainLine (""); + + char *caption = GetTableCaption (message); + if (caption) + { + char *captionHtml = PR_smprintf ("", caption); + if (captionHtml) + { + WritePlainLine (captionHtml); + XP_FREE(captionHtml); + } + XP_FREE(caption); + } + + BerElement *ber; + char *attrib = ldap_first_attribute (m_ldap, message, &ber); + while (attrib) + { + const char *attribName = LookupAttributeName (attrib); + if (attribName && !DIR_IsAttributeExcludedFromHtml (m_server, attrib)) + { + int valueIndex = 0; + + HG73211 + if (XP_STRCASECMP(attrib, "jpegPhoto") == 0 || XP_STRCASECMP(attrib, "audio") == 0) + { + struct berval **berImages = ldap_get_values_len (m_ldap, message, attrib); + while (berImages[valueIndex]) + { + WritePlainLine (""); + if (valueIndex == 0) + WriteAttribute (attribName); + else + WritePlainLine(""); + + // ### There are Win16 problems with this strategy. We could use libmime + // to stream the base64 bits one line at a time, but we'd still need to get the + // LDAP SDK to do something similar, or to use huge pointers. + + char *base64Encoding = NET_Base64Encode ( + berImages[valueIndex]->bv_val, + berImages[valueIndex]->bv_len); + if (base64Encoding) + { + char *htmlLine = NULL; + if (XP_STRCASECMP(attrib, "jpegPhoto") == 0) + htmlLine = PR_smprintf ("", base64Encoding); + else + htmlLine = PR_smprintf ("", base64Encoding); + if (htmlLine) + { + WritePlainLine (htmlLine); + XP_FREE (htmlLine); + } + XP_FREE(base64Encoding); + } + WritePlainLine (""); + valueIndex++; + } + ldap_value_free_len (berImages); + } + else + { + char **values = ldap_get_values (m_ldap, message, attrib); + if (values) + { + XP_Bool isMailtoAttribute = IsMailtoAttribute (attrib); + XP_Bool isUriAttribute = IsUriAttribute (attrib); + XP_Bool isDnAttribute = DIR_IsDnAttribute (m_server, attrib); + XP_Bool isEscapedAttribute = DIR_IsEscapedAttribute (m_server, attrib); + + while (values[valueIndex]) + { + char *decodedUtf8 = ConvertFromServerCharSet (values[valueIndex]); + if (decodedUtf8) + { + WritePlainLine (""); + if (valueIndex == 0) + WriteAttribute (attribName); + else + WritePlainLine(""); + + // This unescaping code is mandated by. DNs are one class of thing, and + // attributes which are "productions" of strings are another class of thing + // and plain old string attributes get left alone. + if (isDnAttribute) + { + // Here's some special code to make values which look like DNs into + // links to LDAP URLs. Cool, huh? + char *tmp = ConvertDnToUri (decodedUtf8); + if (tmp) + { + XP_FREE(decodedUtf8); + decodedUtf8 = tmp; + isUriAttribute = TRUE; + } + } + else if (isEscapedAttribute) + { + // Some attributes which are "productions" of a couple of strings have + // special encoding rules. Decode 'em into something useful here. + char *tmp = DIR_Unescape (decodedUtf8, TRUE /*makeHtml?*/); + if (tmp) + { + XP_FREE(decodedUtf8); + decodedUtf8 = tmp; + } + } + + WriteValue (decodedUtf8, isMailtoAttribute, isUriAttribute); + XP_FREE(decodedUtf8); + WritePlainLine (""); + } + valueIndex++; + } + ldap_value_free (values); + } + } + } + ldap_memfree (attrib); + attrib = ldap_next_attribute (m_ldap, message, ber); + } + + WritePlainLine ("
%s
"); + WritePlainLine ("
"); + + return 0; +} + + +//***************************************************************************** +// +// net_LdapReplicator is the class which knows how to read replication entries +// from the LDAP server to the local Address Book using the "change log" format +// specified in: +// http://ds.internic.net/internet-drafts/draft-good-ldap-changelog-00.txt +// + +#include "garray.h" + +extern "C" +{ + extern int MK_OUT_OF_MEMORY; + extern int MK_LDAP_REPL_CANT_SYNC_REPLICA; + extern int MK_LDAP_REPL_DSE_BOGUS; + extern int MK_LDAP_REPL_CHANGELOG_BOGUS; + extern int MK_LDAP_REPL_PROGRESS_TITLE; + extern int MK_LDAP_REPL_CONNECTING; + extern int MK_LDAP_REPL_CHANGE_ENTRY; + extern int MK_LDAP_REPL_NEW_ENTRY; + extern int MK_LDAP_AUTHDN_LOOKUP_FAILED; +} + +#define LDAP_REPL_CHUNK_SIZE 50 + + +static void CancelReplication(void *closure) +{ + MWContext *progressContext = (MWContext *)closure; + if (progressContext) + XP_InterruptContext(progressContext); +} + + +class net_LdapReplicator : public net_LdapConnectionData +{ +public: + net_LdapReplicator(ActiveEntry *ce); + virtual ~net_LdapReplicator(); + + virtual int Interrupt (); + + virtual int OnBindCompleted (); + virtual int OnEntryFound (LDAPMessage *message); + virtual int OnSearchCompleted (); + virtual XP_Bool OnError(int err); + +protected: + + typedef enum _LdapReplicationState + { + kInitializing, + kAuthorizing, + kBinding, + kParsingRootDSE, + kReplicatingChanges, + kReplicatingAdditions, + kReplicatingEntries + } LdapReplicationState; + + virtual LdapConnectionType GetConnectionType() { return kReplicatorConnection; } + + XP_Bool CollectUserCredentials (); + void SaveCredentialsToPrefs (); + void UpdateProgress (); + + int Load (ActiveEntry *); + int Process (); + int Initialize (); + int Authorize (); + int Bind (); + int SearchRootDSE (); + int Replicate (XP_Bool initial = FALSE); + int ProcessAdditions (); + + int ParseAuthorizationEntry (LDAPMessage *); + int ParseRootDSE (LDAPMessage *); + int ParseChangelogEntry (LDAPMessage *); + int ParseInitialEntry (LDAPMessage *); + void ParseReplicationUrl(const char *); + + XP_Bool m_initialReplicationOkay; + XP_Bool m_changesReturned; + char *m_authDn; + char *m_authPwd; + char *m_valueUsedToFindDn; + char *m_dnOfChangelog; // Root DSE's setting for the DN suffix of changelog entries + char *m_dataVersion; // Root DSE's setting to scope change numbers. The data version + int32 m_firstChangeNumber; // Root DSE's setting for oldest change it knows + int32 m_lastChangeNumber; // Root DSE's setting for newest change it knows + // gets reset whenever the server's DB gets reloaded from LDIF + + int32 m_progressEntry; + int32 m_progressTotal; + MWContext *m_progressContext; + pw_ptr m_progressWindow; + + LdapReplicationState m_replState; + CXP_GrowableArray m_dnsToAdd; + + AB_ContainerInfo *m_container; + + static char *m_rootReplicationAttribs[5]; + static char *m_entryReplicationAttribs[3]; +}; + +char *net_LdapReplicator::m_rootReplicationAttribs[5] = + { "changelog", "firstChangeNumber", "lastChangeNumber", "dataVersion", NULL }; +char *net_LdapReplicator::m_entryReplicationAttribs[3] = + { "targetdn", "changetype", NULL }; + + +net_LdapReplicator::net_LdapReplicator (ActiveEntry *ce) : net_LdapConnectionData (ce, FALSE) +{ + m_replState = kInitializing; + + m_initialReplicationOkay = FALSE; + m_changesReturned = FALSE; + m_authDn = NULL; + m_authPwd = NULL; + m_valueUsedToFindDn = NULL; + m_dnOfChangelog = NULL; + m_dataVersion = NULL; + m_firstChangeNumber = 0; + m_lastChangeNumber = 0; + + m_progressEntry = 0; + m_progressTotal = 0; + m_progressContext = NULL; + m_progressWindow = NULL; + + m_container = NULL; +} + +net_LdapReplicator::~net_LdapReplicator () +{ + int size; + + XP_FREEIF(m_authDn); + XP_FREEIF(m_authPwd); + XP_FREEIF(m_dnOfChangelog); + XP_FREEIF(m_dataVersion); + + size = m_dnsToAdd.Size(); + if (size > 0) + { + while (--size >= 0) + XP_FREE(m_dnsToAdd[size]); + } + + if (m_container) + { + AB_EndReplication(m_container); + m_container = NULL; + } + + if (m_progressWindow) + { + PW_Hide(m_progressWindow); + PW_Destroy(m_progressWindow); + + if (m_progressContext) + PW_DestroyProgressContext(m_progressContext); + } +} + + +int net_LdapReplicator::OnBindCompleted () +{ + return SearchRootDSE(); +} + +int net_LdapReplicator::OnEntryFound (LDAPMessage *message) +{ + int status = 0; + + switch (m_replState) + { + case kAuthorizing: + status = ParseAuthorizationEntry (message); + break; + case kParsingRootDSE: + status = ParseRootDSE (message); + break; + case kReplicatingChanges: + status = ParseChangelogEntry (message); + break; + case kReplicatingAdditions: + case kReplicatingEntries: + status = ParseInitialEntry (message); + break; + default: + XP_ASSERT(FALSE); + } + + return status; +} + +int net_LdapReplicator::OnSearchCompleted () +{ + int status = 0; + + switch (m_replState) + { + case kAuthorizing: + status = Bind(); + break; + case kParsingRootDSE: + status = Replicate (TRUE); + break; + case kReplicatingAdditions: + m_server->replInfo->lastChangeNumber++; + /* fall-through */ + case kReplicatingChanges: + status = ProcessAdditions (); + break; + case kReplicatingEntries: + if (m_initialReplicationOkay) + m_server->replInfo->lastChangeNumber = m_lastChangeNumber; + if (m_server->replInfo->lastChangeNumber >= 0) + { + SaveCredentialsToPrefs (); + DIR_SaveServerPreferences (FE_GetDirServers()); + } + status = MK_CONNECTED; + break; + default: + XP_ASSERT(FALSE); + } + + return status; +} + +XP_Bool net_LdapReplicator::OnError(int err) +{ + /* If we were performing the initial replication, destroy the subset of + * entries that we had replicated so far. + */ + if (m_server->replInfo->lastChangeNumber < 0) + AB_RemoveReplicaEntries(m_container); + + /* Because replication can be very expensive, we must try to save the data + * we have already replicated even in the event of an error. + */ + DIR_SaveServerPreferences (FE_GetDirServers()); + + return TRUE; +} + +XP_Bool net_LdapReplicator::CollectUserCredentials () +{ + /* First look in the DIR_Server we read out of the prefs. If it already + * knows the user's credentials, there's no need to ask again here. + */ + if (m_server->authDn && XP_STRLEN(m_server->authDn)) + m_authDn = XP_STRDUP (m_server->authDn); + if (m_server->savePassword && m_server->password && XP_STRLEN(m_server->password)) + m_authPwd = XP_STRDUP (m_server->password); + if (m_authDn && m_authPwd) + return TRUE; + + XP_Bool rc = FALSE; + char *prompt = PR_smprintf (XP_GetString(MK_LDAP_AUTH_PROMPT), + DIR_GetAttributeName (m_server, auth), + m_server->description); + if (prompt) + { + /* TBD: Only prompt for password if we already have the DN + */ + char *username = NULL, *password = NULL; + if (m_authDn) + { + password = FE_PromptPassword (m_ce->window_id, prompt); + rc = (password != NULL); + } + else + rc = FE_PromptUsernameAndPassword (m_ce->window_id, prompt, &username, &password); + + if (rc) + { + if (username) + m_valueUsedToFindDn = username; + if (password) + { + m_authPwd = SECNAV_MungeString (password); + XP_FREE(password); + } + } + + XP_FREE(prompt); + } + + return rc; +} + +void net_LdapReplicator::SaveCredentialsToPrefs () +{ + if (m_authDn) + DIR_SetAuthDN (m_server, m_authDn); + + if (m_server->savePassword && m_authPwd) + DIR_SetPassword (m_server, m_authPwd); +} + +void net_LdapReplicator::UpdateProgress () +{ + ++m_progressEntry; + + if (!m_progressContext) + return; + + if (m_replState == kReplicatingAdditions) + { + int percent = (m_progressEntry * 100) / m_progressTotal; + FE_SetProgressBarPercent (m_progressContext, percent); + } + + if ((m_progressEntry % MAX(1, m_progressEntry / 10)) == 0) + { + char *status; + + if (m_replState == kReplicatingAdditions) + status = PR_smprintf (XP_GetString(MK_LDAP_REPL_CHANGE_ENTRY), m_progressEntry); + else + status = PR_smprintf (XP_GetString(MK_LDAP_REPL_NEW_ENTRY), m_progressEntry); + + if (status) + { + FE_Progress (m_progressContext, status); + XP_FREE(status); + } + } +} + + +int net_LdapReplicator::Load (ActiveEntry *ce) +{ + XP_ASSERT(ce); + XP_ASSERT(ce->URL_s); + XP_ASSERT(ce->URL_s->address); + + /* If this URL was not started via NET_ReplicateDirectory, we need to + * create a progress window for it. + */ + if (!ce->URL_s->internal_url) + { + m_progressContext = PW_CreateProgressContext(); + if (m_progressContext) + { + m_progressWindow = PW_Create(NULL, pwStandard); + if (m_progressWindow) + { + PW_AssociateWindowWithContext(m_progressContext, m_progressWindow); + PW_SetWindowTitle(m_progressWindow, XP_GetString(MK_LDAP_REPL_PROGRESS_TITLE)); + PW_SetLine1(m_progressWindow, NULL); + PW_SetLine2(m_progressWindow, NULL); + PW_SetProgressRange(m_progressWindow, 0, 0); + PW_SetCancelCallback(m_progressWindow, CancelReplication, m_progressContext); + PW_Show(m_progressWindow); + } + else + { + PW_DestroyProgressContext(m_progressContext); + m_progressContext = NULL; + } + } + } + else + m_progressContext = ce->window_id; + + if (m_progressContext) + { + FE_Progress (m_progressContext, XP_GetString(MK_LDAP_REPL_CONNECTING)); + FE_SetProgressBarPercent (m_progressContext, 0); + } + + /* Get the DIR_Server that matches the hostname in the URL. + */ + ParseReplicationUrl (ce->URL_s->address); + if (m_server == NULL) + return -1; + + /* Initialize the LDAP structure + * + * TBD: need to account for the case where one host name corresponds to + * more than one machine (e.g. directory.netscape.com). + */ + m_ldap = ldap_init(m_server->serverName, m_server->port); + if (!m_ldap) + return MK_OUT_OF_MEMORY; + + return 0; +} + +int net_LdapReplicator::Process () +{ + int status = 0; + + if (m_replState == kInitializing) + status = Initialize (); + else + status = net_LdapConnectionData::Process (); + + return status; +} + +int net_LdapReplicator::Initialize () +{ + int status = 0; + + /* If we are supposed to authenticate with the server, attempt to bind to + * the server using authentication info from the prefs or the user. + */ + if (m_server->enableAuth) + { + if (CollectUserCredentials()) + { + /* If we don't have an authorization DN but we have an email id, + * then try to look up the authorization DN. + */ + if (!m_authDn && m_valueUsedToFindDn) + return Authorize(); + else + return Bind(); + } + } + + return SearchRootDSE(); +} + +int net_LdapReplicator::Authorize () +{ + int status = 0; + + char *attribs[2]; + attribs[0] = XP_STRDUP(DIR_GetFirstAttributeString (m_server, cn)); + attribs[1] = NULL; + + char *filter = PR_smprintf ("(%s=%s)", DIR_GetFirstAttributeString (m_server, auth), m_valueUsedToFindDn); + + int msgId = -1; + if (attribs[0] && filter) + msgId = ldap_search (m_ldap, m_server->searchBase, LDAP_SCOPE_SUBTREE, filter, &attribs[0], 0); + else + status = MK_OUT_OF_MEMORY; + + XP_FREEIF(attribs[0]); + XP_FREEIF(filter); + + if (msgId != -1) + { + m_replState = kAuthorizing; + m_messageId = msgId; + } + else if (status == 0) + { + DisplayError (XP_LDAP_SEARCH_FAILED, GetHostDescription(), ldap_get_lderrno(m_ldap,NULL,NULL)); + status = -1; + } + + return status; +} + +int net_LdapReplicator::Bind () +{ + char *upwd = SECNAV_UnMungeString (m_authPwd); + if (upwd) + { + int msgId = ldap_simple_bind(m_ldap, m_authDn, upwd); + XP_FREE(upwd); + + if (msgId != -1) + { + m_replState = kBinding; + m_messageId = msgId; + } + else + { + DisplayError (XP_LDAP_BIND_FAILED, GetHostDescription(), ldap_get_lderrno(m_ldap,NULL,NULL)); + return -1; + } + } + else + return MK_OUT_OF_MEMORY; + + return 0; +} + +int net_LdapReplicator::SearchRootDSE () +{ + /* Kick things off with a search of the Root DSE for the replication data. + */ + int msgId = ldap_search(m_ldap, "", LDAP_SCOPE_BASE, "objectclass=*", + m_rootReplicationAttribs, 0); + if (msgId != -1) + { + m_replState = kParsingRootDSE; + m_messageId = msgId; + } + else + { + DisplayError (XP_LDAP_SEARCH_FAILED, GetHostDescription(), ldap_get_lderrno(m_ldap,NULL,NULL)); + return -1; + } + + return 0; +} + +int net_LdapReplicator::Replicate (XP_Bool initial) +{ + int msgId = -1; + + /* If the last completed change is -1, we need to perform an initial + * replication. + */ + if (m_server->replInfo->lastChangeNumber == -1) + { + /* We're going to replicate every entry. + */ + m_replState = kReplicatingEntries; + + /* Delete the old replica database and start a fresh one. + */ + AB_RemoveReplicaEntries(m_container); + + /* Try to do a search which will return every entry (limited by the + * filter preference) on the server. + * + * TBD: This fails even on thehole unless Directory Manager is used. + * We must have a special replication dn or find some other way + * to initialize the database. + */ + msgId = ldap_search(m_ldap, m_server->searchBase, LDAP_SCOPE_SUBTREE, + DIR_GetReplicationFilter(m_server), AB_GetReplicaAttributeNames(m_container), 0); + } + else if (m_lastChangeNumber > m_server->replInfo->lastChangeNumber) + { + /* We're going to replicate only the changes. + */ + m_replState = kReplicatingChanges; + + if (initial) + m_progressTotal = m_lastChangeNumber - m_server->replInfo->lastChangeNumber; + + char *filter = PR_smprintf ("(&(changenumber>=%d)(changenumber<=%d))", + m_server->replInfo->lastChangeNumber + 1, + MIN(m_lastChangeNumber, + m_server->replInfo->lastChangeNumber + LDAP_REPL_CHUNK_SIZE)); + if (filter) + { + msgId = ldap_search(m_ldap, m_dnOfChangelog, LDAP_SCOPE_ONELEVEL, + filter, m_entryReplicationAttribs, 0); + XP_FREE(filter); + } + } + else + { + if (!initial) + { + SaveCredentialsToPrefs (); + DIR_SaveServerPreferences (FE_GetDirServers()); + } + return MK_CONNECTED; + } + + if (msgId != -1) + m_messageId = msgId; + else + { + DisplayError (XP_LDAP_SEARCH_FAILED, GetHostDescription(), ldap_get_lderrno(m_ldap,NULL,NULL)); + return -1; + } + + return 0; +} + + +int net_LdapReplicator::ProcessAdditions() +{ + int size = m_dnsToAdd.Size(); + if (size > 0) + { + int msgId = -1; + char *targetDn = (char *)(m_dnsToAdd[size - 1]); + + msgId = ldap_search(m_ldap, targetDn, LDAP_SCOPE_SUBTREE, + DIR_GetReplicationFilter(m_server), AB_GetReplicaAttributeNames(m_container), 0); + + XP_FREE(targetDn); + m_dnsToAdd.SetSize(size - 1); + + if (msgId != -1) + { + m_messageId = msgId; + m_replState = kReplicatingAdditions; + } + else + { + DisplayError (XP_LDAP_SEARCH_FAILED, GetHostDescription(), ldap_get_lderrno(m_ldap,NULL,NULL)); + return -1; + } + } + else + { + /* We must verify that the changelog search returned at least one + * entry. It may return none if the user has insufficient access. + * If so, we must abort the replication; otherwise, we will get stuck + * in an infinite loop. + */ + if (m_changesReturned) + { + return Replicate(); + } + else + { + DisplayError (XP_LDAP_SEARCH_FAILED, GetHostDescription(), LDAP_INSUFFICIENT_ACCESS); + return -1; + } + } + + return 0; +} + +int net_LdapReplicator::Interrupt () +{ + /* Put the last processed change number into prefs + */ + if (m_server->replInfo && m_server->replInfo->lastChangeNumber >= 0) + DIR_SaveServerPreferences (FE_GetDirServers()); + + /* If we were performing the initial replication, destroy the subset of + * entries that we had replicated so far. + */ + else + AB_RemoveReplicaEntries(m_container); + + if (m_ldap) + ldap_abandon (m_ldap, m_messageId); + + return net_LdapConnectionData::Interrupt(); // base class will unbind +} + + +void net_LdapReplicator::ParseReplicationUrl(const char *ldap_url) +{ + char *url = XP_STRDUP(ldap_url); + LDAPURLDesc *desc = (LDAPURLDesc *)XP_CALLOC(1, sizeof(LDAPURLDesc)); + if (desc && url) + { + char *next; + char *fullurl = url; + + /* Pull out the connection type (and fill in the default port numbers). + */ + if (XP_STRCASECMP(url, "repl-ldaps://") == 0) + { + url += 13; + desc->lud_options |= LDAP_URL_OPT_SECURE; + desc->lud_port = LDAPS_PORT; + } + else + { + url += 12; + desc->lud_port = LDAP_PORT; + } + + /* Pull out the authorized DN and password if they are present and + * requested. + */ + char *authDn=NULL, *authPwd=NULL; + next = XP_STRCHR(url, '@'); + if (next) + { + next[0] = '\0'; + + char *pwd = XP_STRCHR(url, ':'); + if (pwd) + { + pwd[0] = '\0'; + authPwd = pwd + 1; + } + authDn = url; + + url = next + 1; + } + + /* Pull out the host name, the port number and the base dn. + */ + next = XP_STRCHR(url, ':'); + if (!next) + next = XP_STRCHR(url, '/'); + if (!next) + desc->lud_host = url; + else + { + if (next[0] == ':') + desc->lud_port = 0; + next[0] = '\0'; + + desc->lud_host = url; + url = next + 1; + + if (desc->lud_port == 0) + { + next = XP_STRCHR(url, '/'); + if (next) + next[0] = '\0'; + desc->lud_port = XP_ATOI(url); + } + + if (next) + desc->lud_dn = next + 1; + } + + /* The host name, port number and base dn must be present and valid. + */ + if (desc->lud_host && desc->lud_port != 0 && desc->lud_dn) + { + AddLdapServerToPrefs(desc); + + if (m_server) + { + if (!m_server->authDn && authDn) + m_server->authDn = XP_STRDUP(authDn); + if (!m_server->password && authPwd) + m_server->password = XP_STRDUP(authPwd); + } + } + + url = fullurl; + } + + XP_FREEIF(desc); + XP_FREEIF(url); +} + + +int net_LdapReplicator::ParseAuthorizationEntry (LDAPMessage *message) +{ + if (!m_authDn) + { + char *authDn = ldap_get_dn (m_ldap, message); + m_authDn = XP_STRDUP(authDn); + ldap_memfree(authDn); + } + else + { + /* Better not have more than one hit for this search. + * We don't have a way for the user to choose which "phil" they are. + */ + DisplayError (MK_LDAP_AUTHDN_LOOKUP_FAILED, GetHostDescription(), 0); + return -1; + } + + return 0; +} + +int net_LdapReplicator::ParseRootDSE (LDAPMessage *message) +{ + int i; + char **values = NULL; + + for (i = 0; i < 4; i++) + { + values = ldap_get_values (m_ldap, message, m_rootReplicationAttribs[i]); + if (values && values[0]) + { + switch (i) { + case 0: + m_dnOfChangelog = XP_STRDUP (values[0]); + break; + case 1: + m_firstChangeNumber = XP_ATOI (values[0]); + break; + case 2: + m_lastChangeNumber = XP_ATOI (values[0]); + break; + case 3: + m_dataVersion = XP_STRDUP (values[0]); + break; + } + + ldap_value_free (values); + } + else + { + DisplayError (MK_LDAP_REPL_DSE_BOGUS, GetHostDescription(), 0); + return -1; + } + } + + /* Parse the root DSE. DIR_ValidateRootDSE can return three different + * types of values; + * <0 - There is no replication information available for this server; + * we should abort replication. + * 0 - Either our replication data matches what the server returned or + * we have never replicated. In the former case, perform an + * incremental update. In the later case, generate the replica + * from scratch. + * >0 - The server's replication data does not match ours; reinitialize + * our replica from scratch. + */ + int rc = DIR_ValidateRootDSE (m_server, m_dataVersion, m_firstChangeNumber, m_lastChangeNumber); + if (rc < 0) + { + DisplayError (MK_LDAP_REPL_DSE_BOGUS, GetHostDescription(), 0); + return -1; + } + + /* Get access to the container info that will manage the interaction + * between ldap server connection and the replica database. + * + * NOTE: This must be called after DIR_ValidateRootDSE, because the later + * is what sets up the replication information in the DIR_Server + * which in turn is required by the container to replicate. + */ + m_container = AB_BeginReplication(m_progressContext, m_server); + if (m_container == NULL) + { + DisplayError (MK_LDAP_REPL_CANT_SYNC_REPLICA, GetHostDescription(), 0); + return -1; + } + + return 0; +} + + +// Here we get an entry from the changelog and try to figure out what kind of +// change happened to the entry given by the targetdn. +int net_LdapReplicator::ParseChangelogEntry (LDAPMessage *message) +{ + char **values = NULL; + char *targetDn = NULL; + + values = ldap_get_values (m_ldap, message, "targetdn"); + if (values && values[0]) + { + targetDn = XP_STRDUP(values[0]); + ldap_value_free (values); + } + else + { + DisplayError (MK_LDAP_REPL_CHANGELOG_BOGUS, GetHostDescription(), 0); + return -1; + } + + values = ldap_get_values (m_ldap, message, "changetype"); + if (values && values[0]) + { + if (!XP_STRCASECMP(values[0], "add")) + { + m_dnsToAdd.Add (targetDn); + } + else if (!XP_STRCASECMP(values[0], "mod")) + { + AB_DeleteReplicaEntry (m_container,targetDn); + m_dnsToAdd.Add (targetDn); + } + else if (!XP_STRCASECMP(values[0], "del")) + { + AB_DeleteReplicaEntry (m_container,targetDn); + UpdateProgress(); + } + ldap_value_free (values); + } + else + { + DisplayError (MK_LDAP_REPL_CHANGELOG_BOGUS, GetHostDescription(), 0); + return -1; + } + + m_changesReturned = TRUE; + return 0; +} + +/* ParseInitialEntry + * + * Method for adding an entry to the replication database. + */ +int net_LdapReplicator::ParseInitialEntry (LDAPMessage *message) +{ + message = ldap_first_entry (m_ldap, message); + if (message) + { + int mapSize = AB_GetNumReplicaAttributes(m_container); + char **mapNames = AB_GetReplicaAttributeNames(m_container); + char **valueList = (char **)XP_ALLOC(sizeof(char *) * mapSize); + if (!valueList) + { + m_initialReplicationOkay = FALSE; + return MK_OUT_OF_MEMORY; + } + + /* Copy each of the attribute values from the LDAP result into the index + * in the value array that corresponds to the same index in the attribute + * mapping structure. + */ + int i, indexCn = -1, indexOrg = -1; + char **scratchArray = NULL; + for (i = 0; i < mapSize; i++) + { + scratchArray = ldap_get_values (m_ldap, message, mapNames[i]); + if (scratchArray && scratchArray[0]) + { + valueList[i] = DIR_ConvertFromServerCharSet (m_server, scratchArray[0], GetLocalCharSet()); + ldap_value_free (scratchArray); + } + else + { + if (AB_ReplicaAttributeMatchesId(m_container, i, cn)) + indexCn = i; + else if (AB_ReplicaAttributeMatchesId(m_container, i, o)) + indexOrg = i; + + valueList[i] = NULL; + } + } + + /* If we didn't get some attributes we wanted, try to pick them up from the DN + */ + if (indexOrg != -1 || indexCn != -1) + { + char *scratch; + if (scratch = ldap_get_dn (m_ldap, message)) + { + if ((scratchArray = ldap_explode_dn (scratch, FALSE)) != NULL) + { + char *dnComponent = NULL; + for (i = 0; (dnComponent = scratchArray[i]) != NULL; i++) + { + if (indexOrg != -1 && !XP_STRNCASECMP (dnComponent, "o=", 2)) + { + valueList[indexOrg] = DIR_ConvertFromServerCharSet (m_server, dnComponent+2, GetLocalCharSet()); + indexOrg = -1; + } + if (indexCn != -1 && !XP_STRNCASECMP (dnComponent, "cn=", 3)) + { + valueList[indexCn] = DIR_ConvertFromServerCharSet (m_server, dnComponent+3, GetLocalCharSet()); + indexCn = -1; + } + } + ldap_value_free (scratchArray); + } + ldap_memfree (scratch); + } + } + + /* If the entry is valid, add it to the replica database. Also set the + * initial replication okay flag to true in case we are performing an + * initial replication. Finally, update the progress window. + */ + if (indexCn == -1 && AB_AddReplicaEntry (m_container, valueList) == AB_SUCCESS) + { + m_initialReplicationOkay = TRUE; + UpdateProgress(); + } + + for (i = 0; i < mapSize; i++) + XP_FREEIF(valueList[i]); + XP_FREE(valueList); + } + + return 0; +} + + +//***************************************************************************** +// +// net_OfflineLdapReplicator is the class which knows which directory replicas +// should be updated and how to replicate them in succession. +// + +class net_OfflineLdapReplicator +{ +public: + net_OfflineLdapReplicator(MSG_Pane *, DIR_Server *server = NULL); + ~net_OfflineLdapReplicator(); + + XP_Bool ReplicateDirectory(); + + static void ReplicateNextDirectory(URL_Struct *, int, MWContext *); + +protected: + MSG_Pane *m_pane; + XP_List *m_serverList; + MWContext *m_progressContext; + pw_ptr m_progressWindow; +}; + + +net_OfflineLdapReplicator::net_OfflineLdapReplicator(MSG_Pane *pane, DIR_Server *server) +{ + m_pane = pane; + + m_progressContext = NULL; + m_progressWindow = NULL; + + m_serverList = XP_ListNew(); + if (m_serverList) + { + if (server) + { + if (DIR_TestFlag(server, DIR_REPLICATION_ENABLED)) + { + /* Create a progress window. + */ + m_progressContext = PW_CreateProgressContext(); + if (m_progressContext) + { + m_progressWindow = PW_Create(NULL, pwStandard); + if (m_progressWindow) + { + PW_AssociateWindowWithContext(m_progressContext, m_progressWindow); + PW_SetWindowTitle(m_progressWindow, XP_GetString(MK_LDAP_REPL_PROGRESS_TITLE)); + PW_SetLine1(m_progressWindow, NULL); + PW_SetLine2(m_progressWindow, NULL); + PW_SetProgressRange(m_progressWindow, 0, 0); + PW_SetCancelCallback(m_progressWindow, CancelReplication, m_progressContext); + PW_Show(m_progressWindow); + } + else + { + PW_DestroyProgressContext(m_progressContext); + } + } + + XP_ListAddObjectToEnd(m_serverList, server); + } + + } + else + { + XP_ASSERT(m_pane); + m_progressContext = m_pane->GetContext(); + + XP_List *serverList = FE_GetDirServers(); + for (int i = 1; i <= XP_ListCount(serverList); i++) + { + server = (DIR_Server *)XP_ListGetObjectNum(serverList, i); + if (DIR_TestFlag(server, DIR_REPLICATION_ENABLED)) + XP_ListAddObjectToEnd(m_serverList, server); + } + } + + if (XP_ListCount(m_serverList) == 0) + { + XP_ListDestroy(m_serverList); + m_serverList = NULL; + } + } +} + +net_OfflineLdapReplicator::~net_OfflineLdapReplicator() +{ + if (m_serverList) + XP_ListDestroy(m_serverList); + + /* We destroy the progress window, but the pane destroys the context? + * This seems kind of weird, but it's the way things work. + */ + if (m_progressWindow) + { + PW_Hide(m_progressWindow); + PW_Destroy(m_progressWindow); + } +} + + +XP_Bool net_OfflineLdapReplicator::ReplicateDirectory() +{ + /* Return FALSE iff there are no more servers to replicate. + */ + DIR_Server *server = (DIR_Server *)XP_ListRemoveTopObject(m_serverList); + if (!server) + return FALSE; + + char *auth = NULL; + if (server->enableAuth && server->authDn) + { + if (server->password) + auth = PR_smprintf("%s:%s@", server->authDn, server->password); + else + auth = PR_smprintf("%s@", server->authDn); + } + + char *url = PR_smprintf("repl-ldap%s://%s%s:%d/%s", server->isSecure ? "s" : "", + auth ? auth : "", server->serverName, server->port, server->searchBase); + if (url) + { + URL_Struct *URL_s = NET_CreateURLStruct(url, NET_DONT_RELOAD); + if(URL_s) + { + URL_s->owner_data = this; + URL_s->internal_url = TRUE; + URL_s->msg_pane = m_pane; + if (m_pane) + MSG_UrlQueue::AddUrlToPane(URL_s, net_OfflineLdapReplicator::ReplicateNextDirectory, m_pane, TRUE, FO_PRESENT); + else + NET_GetURL(URL_s, FO_PRESENT, m_progressContext, net_OfflineLdapReplicator::ReplicateNextDirectory); + } + XP_FREE(url); + } + XP_FREEIF(auth); + + return TRUE; +} + + +void net_OfflineLdapReplicator::ReplicateNextDirectory(URL_Struct *URL_s, int, MWContext *) +{ + net_OfflineLdapReplicator *replicator = (net_OfflineLdapReplicator *)(URL_s->owner_data); + if (replicator) + { + /* ReplicateDirectory returns FALSE if there are no more directories + * left to replicate. It is our job to clean up the replicator object + * when we're done. + */ + if (!replicator->ReplicateDirectory()) + delete replicator; + } +} + + +//***************************************************************************** +// +// net_LdapToAddressBook does the work of converting LDAP entries into a format +// that can be stored in the local Address Book database. +// +class net_LdapToAddressBook : public net_LdapConnectionData +{ +public: + net_LdapToAddressBook (ActiveEntry*); + virtual ~net_LdapToAddressBook(); + + // Base class overrides + virtual int OnEntryFound (LDAPMessage*); + + // Address Book specific stuff + void ConvertEntryToPerson (LDAPMessage *, PersonEntry &); + void ConvertEntryToPerson (LDAPMessage *message, AB_AttributeValue ** valuesArray, uint16 * numItems); // new version for new AB + int WriteEntryToAddressBook (LDAPMessage*); + virtual char * ExtractLDAPEncoding(char * url); + +protected: + virtual LdapConnectionType GetConnectionType() { return kAddressBookConnection; } + + // used to take an ldap attribute, value pair and conver it to one or more address book attribute + // values, adding those values to the abAttributes list. + int AddLDAPAttribute(const char * dirAttrib, const char * dirValue, XP_List * abAttributes); + + AB_AddressBookCopyInfo * m_abCopyInfo; // used for new address book... +}; + +// prototypes fo net_LdapToAddressBook helper functions... +AB_AttributeValue * findAttributeInList(AB_AttribID attrib, XP_List * abAttributes); + +net_LdapToAddressBook::net_LdapToAddressBook (ActiveEntry *ce) : net_LdapConnectionData(ce) +{ + if (ce && ce->URL_s) + m_abCopyInfo = (AB_AddressBookCopyInfo *) ce->URL_s->owner_data; + else + m_abCopyInfo = NULL; +} + +net_LdapToAddressBook::~net_LdapToAddressBook() +{ + // copy info freed by url exit function... +// if (m_abCopyInfo) +// AB_FreeAddressBookCopyInfo(m_abCopyInfo); // also destroys allocated memory.. + m_abCopyInfo = NULL; +} + +char * net_LdapToAddressBook::ExtractLDAPEncoding(char * url) +{ + // we want to strip off the addbook- part for the LDAP encoding... + // caller should not be freeing this return string because it is part of the url.. + return url+8; // 8 for "addbook-" +} + +// helper function used to find an attribute in the list +AB_AttributeValue * findAttributeInList(AB_AttribID attrib, XP_List * abAttributes) +{ + if (abAttributes) + { + for (int i = 1; i <= XP_ListCount(abAttributes); i++) + { + AB_AttributeValue *v = (AB_AttributeValue*) XP_ListGetObjectNum (abAttributes, i); + if (v && v->attrib == attrib) + return v; + } + } + + return NULL; +} + +int net_LdapToAddressBook::AddLDAPAttribute(const char * attrib, const char * dirValue, XP_List * abAttributes) +{ + int status = 0; + AB_AttributeValue * value = (AB_AttributeValue *) XP_ALLOC(sizeof(AB_AttributeValue)); + if (value) + { + status = -1; + // we can make a minor hack to optmize this...since all of + // them are string attributes, we only need to set the + // value->attrib field in the if statements...then if + // we found a match, set the value->u.string outside the + // if statements... + + if (!XP_STRCASECMP(attrib, GetAttributeString(cn))) + { + value->attrib = AB_attribDisplayName; + status = 0; + } + if (!XP_STRCASECMP(attrib, GetAttributeString (givenname))) + { + value->attrib = AB_attribGivenName; + status = 0; + } + else if (!XP_STRCASECMP(attrib, GetAttributeString(sn))) + { + value->attrib = AB_attribFamilyName; + status = 0; + } + else if (!XP_STRCASECMP(attrib, GetAttributeString(o))) + { + value->attrib = AB_attribCompanyName; + status = 0; + } + else if (!XP_STRCASECMP(attrib, GetAttributeString(l))) + { + value->attrib = AB_attribLocality; + status = 0; + } + else if (!XP_STRCASECMP(attrib, GetAttributeString(mail))) + { + value->attrib = AB_attribEmailAddress; + status = 0; + } + else if (!XP_STRCASECMP(attrib, "title")) + { + value->attrib = AB_attribTitle; + status = 0; + } + else if (!XP_STRCASECMP(attrib, "postaladdress")) + { + value->attrib = AB_attribPOAddress; + status = 0; + } + else if (!XP_STRCASECMP(attrib, "st")) + { + value->attrib = AB_attribRegion; + status = 0; + } + else if (!XP_STRCASECMP(attrib, "postalcode")) + { + value->attrib = AB_attribZipCode; + status = 0; + } + else if (!XP_STRCASECMP(attrib, GetAttributeString(telephonenumber))) + { + value->attrib = AB_attribWorkPhone; + status = 0; + } + else if (!XP_STRCASECMP(attrib, "facsimiletelephonenumber")) + { + value->attrib = AB_attribFaxPhone; + status = 0; + } + else if (!XP_STRCASECMP(attrib, "dlshost")) + { + value->attrib = AB_attribCoolAddress; + // we also need to set the use server... + AB_AttributeValue * nextValue = (AB_AttributeValue *) XP_ALLOC(sizeof(AB_AttributeValue)); + if (nextValue) + { + nextValue->attrib = AB_attribUseServer; + nextValue->u.shortValue = kSpecificDLS; + XP_ListAddObjectToEnd(abAttributes, (void *) nextValue); + } + } + else if (!XP_STRCASECMP(attrib, "host")) + { + value->attrib = AB_attribCoolAddress; + // we also need to set the use server... + AB_AttributeValue * nextValue = (AB_AttributeValue *) XP_ALLOC(sizeof(AB_AttributeValue)); + if (nextValue) + { + nextValue->attrib = AB_attribUseServer; + nextValue->u.shortValue = kHostOrIPAddress; + XP_ListAddObjectToEnd(abAttributes, (void *) nextValue); + } + } + + if (status == 0) // we found a match + { + value->u.string = XP_STRDUP(dirValue); + XP_ListAddObjectToEnd(abAttributes, (void *) value); + } + + else // we didn't find a match.... + XP_FREEIF(value); + } + else + status = MK_OUT_OF_MEMORY; + + return status; +} + + +void net_LdapToAddressBook::ConvertEntryToPerson (LDAPMessage *message, AB_AttributeValue ** valuesArray, uint16 * numItems) +{ + XP_List * abAttributes = XP_ListNew(); + AB_AttributeValue * values = NULL; + uint16 numAttributes = 0; + + if (abAttributes) + { + // loop over each attribute in the entry, and pick out the ones the address book + // is interested in. We are ignoring the case where the same attribute has multiple + // values. The AB doesn't have a way to deal with that. + + char *tmpCn = NULL; + BerElement *ber; + char *attrib = ldap_first_attribute (m_ldap, message, &ber); + while (attrib) + { + char ** values = ldap_get_values(m_ldap, message, attrib); + if (m_server && values && values[0]) + { + char *decodedUtf8 = ConvertFromServerCharSet (values[0]); + if (decodedUtf8) + { + AddLDAPAttribute(attrib, decodedUtf8, abAttributes); + if (!XP_STRCASECMP(attrib, GetAttributeString (cn))) + tmpCn = XP_STRDUP(decodedUtf8); // hang on to cn in case we need it later + } + + XP_FREEIF (decodedUtf8); + } + if (values) + ldap_value_free (values); + ldap_memfree (attrib); + attrib = ldap_next_attribute (m_ldap, message, ber); + } // add the next attribute.... + + // now we have some fixing up to do...adding CSID, + AB_AttributeValue * abValue = (AB_AttributeValue *) XP_ALLOC(sizeof(AB_AttributeValue)); + if (abValue) + { + abValue->attrib = AB_attribWinCSID; + abValue->u.shortValue = INTL_GetCSIWinCSID(LO_GetDocumentCharacterSetInfo(m_ce->window_id)); + XP_ListAddObjectToEnd(abAttributes, (void *) abValue); + } + + // If the entry doesn't have an 'o', get it from the DN + char *dn = NULL; + AB_AttributeValue * company = findAttributeInList(AB_attribCompanyName, abAttributes); + if (!company) + { + if (!dn) + dn = ldap_get_dn (m_ldap, message); + if (dn) + { + const char *oAttribName = GetAttributeString (o); + AB_AttributeValue * abValue = (AB_AttributeValue *) XP_ALLOC(sizeof(AB_AttributeValue)); + if (abValue) + { + abValue->attrib = AB_attribCompanyName; + abValue->u.string = GetAttributeFromDN (dn, oAttribName); + XP_ListAddObjectToEnd(abAttributes, (void *) abValue); + } + } + } + + // If the entry doesn't have 'givenname' or 'sn' try to use 'cn' + AB_AttributeValue * givenName = findAttributeInList(AB_attribGivenName, abAttributes); + AB_AttributeValue * familyName = findAttributeInList(AB_attribFamilyName, abAttributes); + if (!givenName /* || !familyName */) + { + char * useThisForGivenName = NULL; + // prefer CN as attribute to CN from dist name + if (tmpCn) + { + useThisForGivenName = tmpCn; + tmpCn = NULL; + } + else + { + if (!dn) + dn = ldap_get_dn (m_ldap, message); + if (dn) + { + const char *cnAttribName = GetAttributeString (cn); + useThisForGivenName = GetAttributeFromDN (dn, cnAttribName); + } + } + + if (useThisForGivenName) + { + AB_AttributeValue * abValue = (AB_AttributeValue *) XP_ALLOC(sizeof(AB_AttributeValue)); + if (abValue) + { + abValue->attrib = AB_attribGivenName; + abValue->u.string = useThisForGivenName; + XP_ListAddObjectToEnd(abAttributes, (void *) abValue); + } + } + } // !givenName + + if (dn) + ldap_memfree(dn); + if (tmpCn) + XP_FREE(tmpCn); + + // our last step is to turn the XP_List into an array of attribute values... + numAttributes = XP_ListCount(abAttributes); + if (numAttributes) + { + values = (AB_AttributeValue *) XP_ALLOC(sizeof(AB_AttributeValue) * numAttributes); + if (valuesArray) + { + // iterate through the list adding each attribute to our static array + for (uint16 index = 1; index <= numAttributes; index++) + { + AB_AttributeValue * value = (AB_AttributeValue *) XP_ListGetObjectNum(abAttributes, index); + AB_CopyEntryAttributeValue(value, &values[index-1]); + } + } + } + + // free the list attributes... + for (int i = 1; i <= XP_ListCount(abAttributes); i++) + { + AB_AttributeValue * value = (AB_AttributeValue *) XP_ListGetObjectNum(abAttributes, i); + AB_FreeEntryAttributeValue(value); + + } + + XP_ListDestroy (abAttributes); + + } // if abAttributes + + // set up the return values... + if (numItems) + *numItems = numAttributes; + if (valuesArray) + *valuesArray = values; + else + AB_FreeEntryAttributeValues(values, numAttributes); +} + + +#ifdef MOZ_NEWADDR +int net_LdapToAddressBook::WriteEntryToAddressBook (LDAPMessage *message) +{ + int status = 0; + if (m_abCopyInfo && m_abCopyInfo->destContainer) + { + AB_AttributeValue * valuesArray = NULL; + uint16 numItems = 0; + ConvertEntryToPerson(message, &valuesArray, &numItems); + if (valuesArray) + { + // If there is only one URL in the queue, we bring up the UI, but + // if there is more than one, we add them silently. There could be + // a ton of URLs, and we don't have a Yes/No/All dialog + status = AB_AddUserAB2(m_abCopyInfo->destContainer, valuesArray, numItems, NULL /* we don't care about the entry id...*/); + AB_FreeEntryAttributeValues(valuesArray, numItems); + } + } + else + status = ABError_NullPointer; + + return status; +} +#else +int net_LdapToAddressBook::WriteEntryToAddressBook (LDAPMessage *message) +{ + // Add an entry we found on an LDAP directory into the local AB DB + + int err = 0; + PersonEntry person; + person.Initialize (); + ConvertEntryToPerson (message, person); + + XP_List *dirServers = FE_GetDirServers (); + XP_ASSERT(dirServers); + if (dirServers) + { + DIR_Server *pab = NULL; + DIR_GetPersonalAddressBook (dirServers, &pab); + XP_ASSERT(pab); + if (pab) + { + ABook *ab = FE_GetAddressBook (NULL /* don't have a pane? */); + AB_BreakApartFirstName (ab, &person); + + // If there is only one URL in the queue, we bring up the UI, but + // if there is more than one, we add them silently. There could be + // a ton of URLs, and we don't have a Yes/No/All dialog + + if (MSG_GetUrlQueueSize (m_ce->URL_s->address, m_ce->window_id) <= 1) + err = AB_AddUserWithUI (m_ce->window_id, &person, pab, TRUE); + else + { + // Don't allow any external URLs to add batches of people to the AB + if (m_ce->URL_s->internal_url) + { + ABID unusedId; + err = AB_AddUser (pab, ab, &person, &unusedId); + } + } + } + } + + person.CleanUp(); + return err; +} +#endif + +int net_LdapToAddressBook::OnEntryFound (LDAPMessage *message) +{ + return WriteEntryToAddressBook (message); +} + +void net_LdapToAddressBook::ConvertEntryToPerson (LDAPMessage *message, PersonEntry &person) +{ + // Loop over each attribute in the entry, and pick out the ones the + // Address Book is interested in. Note that we're ignoring cases where + // the same attribute has multiple values. The AB doesn't have a way to + // deal with that. + + char *tmpCn = NULL; + BerElement *ber; + char *attrib = ldap_first_attribute (m_ldap, message, &ber); + while (attrib) + { + char **values = ldap_get_values (m_ldap, message, attrib); + + // We better have a DIR_Server for this, otherwise GetAttributeString + // will crash. Since the only way to get to this code is through the Search + // Directory dialog, I think this is an ok restriction. We could certainly + // add default names in dirprefs.c if necessary. + XP_ASSERT(m_server && values && values[0]); + if (!m_server || !values || !values[0]) + return; + + char *decodedUtf8 = ConvertFromServerCharSet (values[0]); + if (!decodedUtf8) + return; + + // ### maybe it would be nice to have a few more of these + // expressed as DIR_Prefs attributeIds??? + + if (!XP_STRCASECMP(attrib, GetAttributeString (cn))) + tmpCn = XP_STRDUP(decodedUtf8); // hang on to cn in case we need it later + if (!XP_STRCASECMP(attrib, GetAttributeString (givenname))) + person.pGivenName = XP_STRDUP(decodedUtf8); + else if (!XP_STRCASECMP(attrib, GetAttributeString(sn))) + person.pFamilyName = XP_STRDUP(decodedUtf8); + else if (!XP_STRCASECMP(attrib, GetAttributeString(o))) + person.pCompanyName = XP_STRDUP(decodedUtf8); + else if (!XP_STRCASECMP(attrib, GetAttributeString(l))) + person.pLocality = XP_STRDUP(decodedUtf8); + else if (!XP_STRCASECMP(attrib, GetAttributeString(mail))) + person.pEmailAddress = XP_STRDUP(decodedUtf8); + else if (!XP_STRCASECMP(attrib, "title")) + person.pTitle = XP_STRDUP(decodedUtf8); + else if (!XP_STRCASECMP(attrib, "postaladdress")) + person.pPOAddress = XP_STRDUP(decodedUtf8); + else if (!XP_STRCASECMP(attrib, "st")) + person.pRegion = XP_STRDUP(decodedUtf8); + else if (!XP_STRCASECMP(attrib, "postalcode")) + person.pZipCode = XP_STRDUP(decodedUtf8); + else if (!XP_STRCASECMP(attrib, GetAttributeString(telephonenumber))) + person.pWorkPhone = XP_STRDUP(decodedUtf8); + else if (!XP_STRCASECMP(attrib, "facsimiletelephonenumber")) + person.pFaxPhone = XP_STRDUP(decodedUtf8); + else if (!XP_STRCASECMP(attrib, "dlshost")) + { + person.UseServer = kSpecificDLS; + person.pCoolAddress = XP_STRDUP(decodedUtf8); + } + else if (!XP_STRCASECMP(attrib, "host")) + { + person.UseServer = kHostOrIPAddress; + person.pCoolAddress = XP_STRDUP(decodedUtf8); + } + + + ldap_value_free (values); + XP_FREE (decodedUtf8); + ldap_memfree (attrib); + + attrib = ldap_next_attribute (m_ldap, message, ber); + } + + // If the entry doesn't have an 'o', get it from the DN + char *dn = NULL; + if (!person.pCompanyName) + { + if (!dn) + dn = ldap_get_dn (m_ldap, message); + if (dn) + { + const char *oAttribName = GetAttributeString (o); + person.pCompanyName = GetAttributeFromDN (dn, oAttribName); + } + } + + // If the entry doesn't have 'givenname' or 'sn' try to use 'cn' + if (!person.pGivenName || !person.pFamilyName) + { + // prefer CN as attribute to CN from dist name + if (tmpCn) + { + person.pGivenName = tmpCn; + tmpCn = NULL; // PersonEntry now owns that memory, so don't free it + } + else + { + if (!dn) + dn = ldap_get_dn (m_ldap, message); + if (dn) + { + const char *cnAttribName = GetAttributeString (cn); + person.pGivenName = GetAttributeFromDN (dn, cnAttribName); + } + } + } + + if (dn) + ldap_memfree(dn); + if (tmpCn) + XP_FREE(tmpCn); + + person.WinCSID = INTL_GetCSIWinCSID(LO_GetDocumentCharacterSetInfo(m_ce->window_id)); +} + +#ifdef MOZ_LI + +/* + * Class net_LdapLIConnectionData + * ============================== + * This class is used for LI LDAP operations. It expects a LDAPOperation* to be + * passed in via the URL_s->owner_data. The operation to be performed is determined + * by URL_s->method. The results of the operations are returned in the LDAPOperation; + * the caller is responsible for creating and deleting the operation object. + */ +class net_LdapLIConnectionData : public net_LdapConnectionData +{ +public: + net_LdapLIConnectionData(ActiveEntry *ce); + virtual ~net_LdapLIConnectionData (); // The base class handles it correctly. + + virtual int Load (ActiveEntry *ce); + virtual int Process (); + virtual int Interrupt (); + + virtual int Unbind (); + + +protected: + virtual LdapConnectionType GetConnectionType() { return kLIConnection; } + +private: + XP_Bool m_bCallerProvidedConnection; // Did the caller provide the m_ldap connection? + LDAPOperation* m_pParams; // Parameters. +}; + +/* net_LdapLIConnectionData Constructor + * ==================================== + * Create a connection data object class for an LI operation; set up a connection if + * the caller didn't provide one. We tell the base class not to initialize m_ldap since + * a LDAP* may be passed in to us. + */ +net_LdapLIConnectionData::net_LdapLIConnectionData(ActiveEntry *ce) : net_LdapConnectionData(ce,FALSE) +{ + m_pParams = (LDAPOperation*)ce->URL_s->owner_data; + + if(m_pParams && (m_pParams->getConnection() || (m_pParams->getURLMethod() == LDAP_LI_BIND_METHOD))) + { + // Set the connection in the base class. + // Free the LDAP* struct if created by the base class. + m_ldap = m_pParams->getConnection(); + m_bCallerProvidedConnection = TRUE; + } + else + { + m_ldap = ldap_init(m_pParams->getURL()->getHost(),m_pParams->getURL()->getPort()); + m_bCallerProvidedConnection = FALSE; + } + +} + +/* net_LdapLIConnectionData Destructor + * ==================================== + * Remember not to unbind if the caller provided the connection. + */ +net_LdapLIConnectionData::~net_LdapLIConnectionData() +{ + // Base class destructor will try to unbind, so we have to prevent that if + // the caller provided the connection. + if(m_bCallerProvidedConnection) + m_ldap = NULL; +} + +/* Load() + * ====== + * Called by netlib when starting an LI operation. This function is responsible to starting + * the LDAP operations. + */ +int net_LdapLIConnectionData::Load(ActiveEntry *ce) +{ + int ldapErr = 0; + int msgId = -1; + char *attrs[2] = { "modifyTimeStamp" , NULL }; + const LDAPURL* url = m_pParams->getURL(); + char *utf8Encoding = NULL; + + if(url->getParseError() != LDAP_SUCCESS) + { + ldapErr = MK_MALFORMED_URL_ERROR; + goto net_LdapLIConnectionDataLoadExit; + } + + if(m_pParams->started()) + { + ldapErr = LI_LDAP_ALREADY_STARTED; + goto net_LdapLIConnectionDataLoadExit; + } + + XP_ASSERT(m_ce); + XP_ASSERT(m_ce->URL_s); + XP_ASSERT(m_ce->URL_s->address); + XP_ASSERT(m_ldap); + + /* Convert the URL to UTF8 */ + utf8Encoding = ConvertToServerCharSet (ce->URL_s->address); + + if(!utf8Encoding) + return LI_LDAP_MEMORY_ERROR; + + /* The URL is assumed to be valid (checked by + * LDAPOperation before calling NET_GetURL) + */ + + /* This is where the real work begins */ + + // let the world know what we are doing. + FE_GraphProgressInit(ce->window_id, NULL, 1); + + // Initiate the operation on the server + switch(m_pParams->getURLMethod()) + { + case LDAP_LI_BIND_METHOD: + // Bind operation is not really async yet, the initial IP stuff is still synchronous in the LDAP API. + msgId = ldap_simple_bind(m_ldap,m_pParams->getURL()->getDN(),((LDAPBindOperation*)m_pParams)->getPassword()); + break; + case LDAP_LI_SEARCH_METHOD: + msgId = ldap_search(m_ldap, + url->getDN(), + url->getScope(), + url->getFilter(), + url->getAttrs(), + ((LDAPSearchOperation*)m_pParams)->getAttrsOnly()); + break; + case LDAP_LI_ADD_METHOD: + case LDAP_LI_ADDGLM_METHOD: + // Note that since we take apart the URL, we end up ignoring everything but the DN. + msgId = ldap_add(m_ldap, url->getDN(), ((LDAPAddOperation*)m_pParams)->getLDAPModArray()->getLDAPModStarStar()); + break; + case LDAP_LI_MOD_METHOD: + case LDAP_LI_MODGLM_METHOD: + // Note that since we take apart the URL, we end up ignoring everything but the DN. + msgId = ldap_modify(m_ldap, url->getDN(), ((LDAPModOperation*)m_pParams)->getLDAPModArray()->getLDAPModStarStar()); + break; + case LDAP_LI_DEL_METHOD: + msgId = ldap_delete(m_ldap, url->getDN()); + break; + case LDAP_LI_GETLASTMOD_METHOD: + { + msgId = ldap_search(m_ldap, + url->getDN(), + url->getScope(), + url->getFilter(), + attrs, + ((LDAPSearchOperation*)m_pParams)->getAttrsOnly()); + } + break; + case LDAP_SUCCESS: + break; + } + + if (msgId != -1) + { + m_messageId = msgId; + } else { + m_pParams->setErrNo(ldap_get_lderrno(m_ldap,NULL,NULL)); + ldapErr = LI_LDAP_ERROR; + } + +net_LdapLIConnectionDataLoadExit: + + XP_FREEIF(utf8Encoding); + + return ldapErr; +} + +/* Process() + * ========= + * Process LI Gets/Puts, etc. Only one entry can be gotten/put at a time. + * The result of the process is a LDAPMessage* put into the LDAPOperation* + * in the URL_s->owner_data in the ActiveEntry. + * The caller of NET_GetURL is required to free this data via ldap_msgfree() + * if it is non-NULL in the LDAPOperation object. + */ +int net_LdapLIConnectionData::Process() +{ + LDAPMessage *message = NULL; + struct timeval timeout; + + // REMIND: put in the timeout stuff. + + XP_BZERO (&timeout, sizeof(timeout)); + int ldapErr = ldap_result (m_ldap, m_messageId, 1, &timeout, &message); + + XP_ASSERT(m_pParams); + + /* Check for network error */ + // We don't set result to NULL here because it may have already been set to + // something, e.g. by the LDAP_LI_ADDGLM or LDAP_LI_MODGLM methods. + if(ldapErr == -1) + { + m_pParams->setErrNo(ldap_get_lderrno(m_ldap,NULL,NULL)); + return LI_LDAP_ERROR; + } + + switch (ldapErr) + { + /* + * We can only get one entry at a time in the current implementation, so we + * terminate when we have at least one. + */ + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_RESULT: + case LDAP_RES_MODIFY: + case LDAP_RES_DELETE: + case LDAP_RES_ADD: + FE_GraphProgress(m_ce->window_id, NULL, 0, 1, -2); // -2 specifies that we want items not bytes + case LDAP_RES_BIND: + m_pParams->setResults(message); + ldapErr = LI_LDAP_DONE; /* Special value to indicate to netlib that we're done. */ + /* We don't use -1 here so we can differentiate between */ + /* a valid result and an ldap error */ + m_pParams->setErrNo(ldap_result2error(m_ldap,message,0)); + break; + case LDAP_SUCCESS: + break; + default: + if(message) + m_pParams->setResults(message); + m_pParams->setErrNo(ldap_result2error(m_ldap,message,0)); + ldapErr = LI_LDAP_ERROR; + break; + } + + /* If the ldapErr is -2, it means we're done and we've gotten something */ + if(ldapErr == LI_LDAP_DONE) { + + char *utf8Encoding = NULL; + + // the last modified time is given by the modifyTimeStamp attribute. + char *attrs[2] = { "modifyTimeStamp" , NULL }; + int msgID; + const LDAPURL* url = m_pParams->getURL(); + + // For LDAP_LI_MODGLM_METHOD and LDAP_LI_ADDGLM_METHOD, we have to + // do another search to get the last modified time if the first + // search completes successfully. To this, we simply start the search + // and don't return -1 so netlib will keep calling process(). We also + // change m_method to LDAP_LI_SEARCH_METHOD so that we won't end up in this + // if statement again. Note that we don't change it in m_ce->URL_s->method + // This is mainly to avoid netlib weirdness. + switch(m_pParams->getURLMethod()) { + case LDAP_LI_ADDGLM_METHOD: + case LDAP_LI_MODGLM_METHOD: + + // Determine if the ADD part succeeded. We don't free the result because + // it's the policy of the URL handler to always return the result when + // it's available. We'll have to free if it the ADD/MOD succeed so that + // the GLM result can be returned. + if(m_pParams-> getErrNo() != LDAP_SUCCESS) { + // m_pParams->result and param->status already set above. + ((LDAPModGLMOperation*)m_pParams)->setModSucceeded(FALSE); + break; // exit the ADDGLM and MODGLM stuff. + } + + // setResults will free message, since setResults(message) was called earlier. + m_pParams->setResults(NULL); + message = NULL; + ((LDAPModGLMOperation*)m_pParams)->setModSucceeded(TRUE); + + msgID = ldap_search(m_ldap,url->getDN(),url->getScope(),"(objectclass=*)",attrs,0); + + // each time we initiate a new ldap operation we need to increase the count + FE_GraphProgressInit(m_ce->window_id, NULL, 1); + + // If msgID == -1, then there was a network error; so we set the error condition and + // leave ldapErr = -1 so netlib will exit the url handler. + if(msgID == -1) { + m_pParams->setErrNo(ldap_get_lderrno(m_ldap,NULL,NULL)); + ldapErr = LI_LDAP_ERROR; + } else { + ldapErr = 1; // >0 indicates to netlib to continue processing + m_messageId = msgID; + ((LDAPModGLMOperation*)m_pParams)->changeModToGLM(); + } + break; + } // switch + FE_GraphProgressDestroy(m_ce->window_id, NULL, 1, 1); + } // if + + return ldapErr; +} + +/* Unbind() + * ======== + * Unbind from the server if the caller didn't provide a connection. + */ +int net_LdapLIConnectionData::Unbind() +{ + // If the caller did not provide the connection, we do the standard unbind. + // If the caller did provide the connection, we have a sticky situation + // because net_LdapConnectionData::Unbind() is called in the base class' + // destructor, and that destructor is guaranteed to be called. To avoid + // unbinding a caller provided connection, we just set m_ldap to NULL so that + // the base class' Unbind() won't do anything. + if(!m_bCallerProvidedConnection) + return net_LdapConnectionData::Unbind(); + else + m_ldap = NULL; + + return 0; // Don't know what else we can return. +} + +/* Interrupt() + * =========== + * netlib wants to interrupt this connect; we rely on the base class to do most of + * the work and just set the appropriate error. + */ +int net_LdapLIConnectionData::Interrupt() +{ + return net_LdapConnectionData::Interrupt(); +} + +#endif // MOZ_LI +#endif // MOZ_LDAP + + +//**************************************************************************** +// +// Callbacks from NET_GetURL +// + +extern "C" int32 net_LoadLdap (ActiveEntry *ce) +{ +#ifdef MOZ_LDAP + if (NET_IsOffline()) + return MK_OFFLINE; + + net_LdapConnectionData *cd = NULL; + switch (NET_URL_Type (ce->URL_s->address)) + { + case LDAP_TYPE_URL: + case SECURE_LDAP_TYPE_URL: +#ifdef MOZ_LI + // If we're doing an LDAP operation, we + // have to figure out if it's an LI operation + // and instantiate the right connection data object. + if((LDAP_SEARCH_METHOD < ce->URL_s->method) && (LDAP_LI_BIND_METHOD >= ce->URL_s->method)) + cd = new net_LdapLIConnectionData(ce); + else +#endif + cd = new net_LdapToHtml (ce); + break; + case LDAP_QUERY_DSE_TYPE_URL: // I think we need to query the DSE for any operation, so this is in the base class + cd = new net_LdapToHtml (ce); + break; + case ADDRESS_BOOK_LDAP_TYPE_URL: + cd = new net_LdapToAddressBook (ce); + break; + case LDAP_REPLICATION_TYPE_URL: + cd = new net_LdapReplicator (ce); + break; + default: + XP_ASSERT(FALSE); + return MK_MALFORMED_URL_ERROR; + } + if (!cd) + return MK_OUT_OF_MEMORY; + + ce->con_data = cd; + int err = cd->Load (ce); + if (err) + { + ce->status = err; + delete cd; + ce->con_data = NULL; + } + + return err; +#else + return MK_MALFORMED_URL_ERROR; +#endif +} + +extern "C" int32 net_ProcessLdap (ActiveEntry *ce) +{ +#ifdef MOZ_LDAP + net_LdapConnectionData *cd = (net_LdapConnectionData*) ce->con_data; + + int err = cd->Process(); + if (err < 0 || err == MK_CONNECTED) + { + delete cd; + ce->con_data = NULL; + } + + if(err == MK_CONNECTED) + err = -1; + + return err; +#else + return MK_MALFORMED_URL_ERROR; +#endif +} + + +extern "C" int32 net_InterruptLdap (ActiveEntry * ce) +{ +#ifdef MOZ_LDAP + net_LdapConnectionData *cd = (net_LdapConnectionData*) ce->con_data; + + int err = cd->Interrupt (); + delete cd; + ce->con_data = NULL; + ce->status = MK_INTERRUPTED; + + return err; +#else + return -1; +#endif +} + +extern "C" void +net_CleanupLdap(void) +{ +} + +extern "C" void +NET_InitLDAPProtocol(void) +{ + static NET_ProtoImpl ldap_proto_impl; + + ldap_proto_impl.init = net_LoadLdap; + ldap_proto_impl.process = net_ProcessLdap; + ldap_proto_impl.interrupt = net_InterruptLdap; + ldap_proto_impl.cleanup = net_CleanupLdap; + + NET_RegisterProtocolImplementation(&ldap_proto_impl, LDAP_TYPE_URL); + NET_RegisterProtocolImplementation(&ldap_proto_impl, SECURE_LDAP_TYPE_URL); + NET_RegisterProtocolImplementation(&ldap_proto_impl, ADDRESS_BOOK_LDAP_TYPE_URL); +} + + +//**************************************************************************** +// +// Helper APIs +// + +extern "C" XP_Bool NET_ReplicateDirectory (MSG_Pane *pane, DIR_Server *server) +{ +#ifdef MOZ_LDAP + net_OfflineLdapReplicator *replicator = new net_OfflineLdapReplicator(pane, server); + + if (replicator->ReplicateDirectory()) + return TRUE; + + delete replicator; +#endif + return FALSE; +} diff --git a/mozilla/network/protocol/ldap/mkldap.h b/mozilla/network/protocol/ldap/mkldap.h index e69de29bb2d..2540c12b0bd 100644 --- a/mozilla/network/protocol/ldap/mkldap.h +++ b/mozilla/network/protocol/ldap/mkldap.h @@ -0,0 +1,28 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#ifndef MKLDAP_H +#define MKLDAP_H + +PR_BEGIN_EXTERN_C + +void NET_InitLDAPProtocol(void); + +PR_END_EXTERN_C + +#endif diff --git a/mozilla/network/protocol/mailbox/Makefile b/mozilla/network/protocol/mailbox/Makefile new file mode 100644 index 00000000000..d6c331e4338 --- /dev/null +++ b/mozilla/network/protocol/mailbox/Makefile @@ -0,0 +1,33 @@ +#!gmake +# +# The contents of this file are subject to the Netscape Public License +# Version 1.0 (the "NPL"); you may not use this file except in +# compliance with the NPL. You may obtain a copy of the NPL at +# http://www.mozilla.org/NPL/ +# +# Software distributed under the NPL is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL +# for the specific language governing rights and limitations under the +# NPL. +# +# The Initial Developer of this code under the NPL is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All Rights +# Reserved. + +DEPTH = ../../.. + +MODULE = mailbxurl +LIBRARY_NAME = mailbxurl + +CSRCS = \ + mkmailbx.c \ + $(NULL) + +EXPORTS= mkmailbx.h + +REQUIRES = imap4url netcache network nspr2 dbm util pref js java fileurl \ + security layer img httpurl mimetype htmldlgs + +include $(DEPTH)/config/rules.mk + diff --git a/mozilla/network/protocol/mailbox/makefile.win b/mozilla/network/protocol/mailbox/makefile.win new file mode 100644 index 00000000000..facd21bf193 --- /dev/null +++ b/mozilla/network/protocol/mailbox/makefile.win @@ -0,0 +1,87 @@ +#!gmake +# +# The contents of this file are subject to the Netscape Public License +# Version 1.0 (the "NPL"); you may not use this file except in +# compliance with the NPL. You may obtain a copy of the NPL at +# http://www.mozilla.org/NPL/ +# +# Software distributed under the NPL is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL +# for the specific language governing rights and limitations under the +# NPL. +# +# The Initial Developer of this code under the NPL is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All Rights +# Reserved. + +IGNORE_MANIFEST=1 +# + +#------------------------------------------------------------------------ +# +# Makefile to build the Network file protocol LIB +# +#------------------------------------------------------------------------ + + +# +# Make sure we have MOZILLA_CLIENT defined so we get the +# proper JS includes +# +LCFLAGS = $(LCFLAGS) -DMOZILLA_CLIENT + +!ifdef BUILD_DEBUG_GC +LCFLAGS = $(LCFLAGS) -DDEBUG_GC +!endif + +LLIBS= \ + $(NULL) +MISCDEP=$(LLIBS) +OBJS= \ + .\$(OBJDIR)\mkmailbx.obj \ + $(NULL) + +CSRCS = \ + mkmailbx.c \ + $(NULL) + + +LIBRARY_NAME=mailbxurl +MODULE=mailbxurl +DEPTH=..\..\.. + +LOCAL_INCLUDES=-I. -I$(DEPTH)/dist/public/zlib -I$(DEPTH)/dist/public/parse +INCLUDES = $(LOCAL_INCLUDES) + + +EXTRA_LIBS= + +REQUIRES= network ldap imap4url +EXPORTS= mkmailbx.h + +# use LINCS on win32 for now since REQUIRES seems to be broken +#!if "$(MOZ_BITS)" != "16" +LINCS= \ + -I$(PUBLIC)\nspr2 \ + -I$(PUBLIC)\util \ + -I$(PUBLIC)\java \ + -I$(PUBLIC)\pref \ + -I$(PUBLIC)\htmldlgs \ + -I$(PUBLIC)\js \ + -I$(PUBLIC)\security \ + -I$(PUBLIC)\netcache \ + -I$(PUBLIC)\network \ + -I$(PUBLIC)\ldap \ + -I$(PUBLIC)\imap4url \ + -I$(PUBLIC)\mimetype \ +#!endif + +include <$(DEPTH)\config\rules.mak> + +libs:: $(LIBRARY) + $(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib + +symbols:: + @echo "LIBRARY_NAME is $(LIBRARY_NAME)" + @echo "LIBRARY is $(LIBRARY)" diff --git a/mozilla/network/protocol/mailbox/mkmailbx.c b/mozilla/network/protocol/mailbox/mkmailbx.c index 05d1d314c05..b51484e3b57 100644 --- a/mozilla/network/protocol/mailbox/mkmailbx.c +++ b/mozilla/network/protocol/mailbox/mkmailbx.c @@ -30,7 +30,9 @@ #include "libmime.h" #include "merrors.h" #include "mkimap4.h" - +#ifdef XP_MAC +#include "msg_srch.h" +#endif /* for XP_GetString() */ #include "xpgetstr.h" @@ -58,6 +60,8 @@ typedef enum _MailboxStates { MAILBOX_COMPRESS_FOLDER, MAILBOX_FINISH_COMPRESS_FOLDER, MAILBOX_BACKGROUND, + MAILBOX_NULL, + MAILBOX_NULL2, MAILBOX_DELIVER_QUEUED, MAILBOX_FINISH_DELIVER_QUEUED, MAILBOX_DONE, @@ -117,16 +121,16 @@ net_MailboxLoad (ActiveEntry * ce) char *wholeUrl; /* temp, until imap urls have their own identifier */ - if (!PL_strncasecmp(ce->URL_s->address, "mailbox://", 10) ) + if (!XP_STRNCASECMP(ce->URL_s->address, "IMAP://", 7) ) return NET_IMAP4Load(ce); - if (PL_strcasecmp(ce->URL_s->address, "mailbox:displayattachments") == 0) { + if (XP_STRCASECMP(ce->URL_s->address, "mailbox:displayattachments") == 0) { MIME_DisplayAttachmentPane(ce->window_id); return -1; } - cd = PR_NEW(MailboxConData); + cd = XP_NEW(MailboxConData); path = NET_ParseURL(ce->URL_s->address, GET_PATH_PART); search = NET_ParseURL(ce->URL_s->address, GET_SEARCH_PART); part = search; @@ -140,12 +144,12 @@ net_MailboxLoad (ActiveEntry * ce) } /* init */ - memset(cd, 0, sizeof(MailboxConData)); + XP_MEMSET(cd, 0, sizeof(MailboxConData)); cd->msgnum = -1; - wholeUrl = PL_strdup(ce->URL_s->address); + wholeUrl = XP_STRDUP(ce->URL_s->address); -#ifndef XP_MAC /* #### Giant Evil Mac Pathname Hack */ +#ifndef XP_MAC /* #### Fix Mac Pathname */ NET_UnEscape (path); NET_UnEscape (wholeUrl); #endif /* !XP_MAC */ @@ -154,12 +158,12 @@ net_MailboxLoad (ActiveEntry * ce) if (!cd->pane) { #ifdef DEBUG_phil - PR_LogPrint ("NET_MailboxLoad: url->msg_pane NULL for URL: %s\n", ce->URL_s->address); + XP_Trace ("NET_MailboxLoad: url->msg_pane NULL for URL: %s\n", ce->URL_s->address); #endif /* If we're displaying a message, there'll be a '?' in the url */ - if (PL_strchr(wholeUrl, '?')) + if (XP_STRCHR(wholeUrl, '?')) { - if (PL_strstr(wholeUrl, "?compress-folder") || PL_strstr(wholeUrl, "?deliver-queued")) + if (XP_STRSTR(wholeUrl, "?compress-folder") || XP_STRSTR(wholeUrl, "?deliver-queued")) cd->pane = MSG_FindPane(ce->window_id, MSG_FOLDERPANE); /* ###phil tar to the tarpit */ else { @@ -199,7 +203,7 @@ net_MailboxLoad (ActiveEntry * ce) if (cd->pane == NULL) cd->pane = MSG_FindPane(ce->window_id, MSG_THREADPANE); /* ###whs this isn't really true the way things are set up. */ - /* PR_ASSERT(cd->pane && MSG_GetContext(cd->pane) == ce->window_id); */ + /* XP_ASSERT(cd->pane && MSG_GetContext(cd->pane) == ce->window_id); */ } if (cd->pane == NULL) { @@ -207,7 +211,7 @@ net_MailboxLoad (ActiveEntry * ce) return -1; /* ### */ } - if (PL_strcasecmp(wholeUrl, "mailbox:copymessages") == 0) + if (XP_STRCASECMP(wholeUrl, "mailbox:copymessages") == 0) cd->next_state = MAILBOX_COPY_MESSAGES; else { @@ -217,27 +221,30 @@ net_MailboxLoad (ActiveEntry * ce) if (part && *part == '?') part++; while (part) { - char* amp = PL_strchr(part, '&'); + char* amp = XP_STRCHR(part, '&'); if (amp) *amp++ = '\0'; - if (PL_strncmp(part, "id=", 3) == 0) { - cd->msg_id = PL_strdup (NET_UnEscape (part+3)); - } else if (PL_strncmp(part, "number=", 7) == 0) { + if (XP_STRNCMP(part, "id=", 3) == 0) { + cd->msg_id = XP_STRDUP (NET_UnEscape (part+3)); + } else if (XP_STRNCMP(part, "number=", 7) == 0) { cd->msgnum = atol(part + 7); if (cd->msgnum == 0 && part[7] != '0') cd->msgnum = -1; - } else if (PL_strncmp(part, "uidl=", 5) == 0) { + } else if (XP_STRNCMP(part, "uidl=", 5) == 0) { /* ### Vile hack time. If a UIDL was specified, then tell libmsg about it, giving it a chance to arrange so that when this URL is all done, MSG_GetNewMail gets called. */ MSG_PrepareToIncUIDL(cd->pane, ce->URL_s, NET_UnEscape(part + 5)); } else if (ce->URL_s->internal_url && - PL_strncmp(part, "compress-folder", 15) == 0) { + XP_STRNCMP(part, "compress-folder", 15) == 0) { cd->next_state = MAILBOX_COMPRESS_FOLDER; } else if (ce->URL_s->internal_url && - PL_strncmp(part, "deliver-queued", 14) == 0) { + XP_STRNCMP(part, "deliver-queued", 14) == 0) { cd->next_state = MAILBOX_DELIVER_QUEUED; } else if (ce->URL_s->internal_url && - PL_strncmp(part, "background", 10) == 0) { + XP_STRNCMP(part, "background", 10) == 0) { cd->next_state = MAILBOX_BACKGROUND; + } else if (ce->URL_s->internal_url && + XP_STRNCMP(part, "null", 10) == 0) { + cd->next_state = MAILBOX_NULL; } part = amp; } @@ -284,7 +291,7 @@ mail_generate_html_footer_fn (const char *dest, void *closure, : 0); if (uidl) { - PR_Free(uidl); + XP_FREE(uidl); return MSG_GeneratePartialMessageBlurb (cd->pane, cur_entry->URL_s, closure, headers); @@ -299,41 +306,41 @@ mail_generate_reference_url_fn (const char *dest, void *closure, { ActiveEntry *cur_entry = (ActiveEntry *) closure; char *addr = cur_entry->URL_s->address; - char *search = (addr ? PL_strchr (addr, '?') : 0); + char *search = (addr ? XP_STRCHR (addr, '?') : 0); char *id2; char *new_dest; char *result; if (!dest || !*dest) return 0; - id2 = PL_strdup (dest); + id2 = XP_STRDUP (dest); if (!id2) return 0; - if (id2[PL_strlen (id2)-1] == '>') - id2[PL_strlen (id2)-1] = 0; + if (id2[XP_STRLEN (id2)-1] == '>') + id2[XP_STRLEN (id2)-1] = 0; if (id2[0] == '<') new_dest = NET_Escape (id2+1, URL_PATH); else new_dest = NET_Escape (id2, URL_PATH); FREEIF (id2); - result = (char *) PR_Malloc ((search ? search - addr : 0) + - (new_dest ? PL_strlen (new_dest) : 0) + + result = (char *) XP_ALLOC ((search ? search - addr : 0) + + (new_dest ? XP_STRLEN (new_dest) : 0) + 40); if (result && new_dest) { if (search) { - memcpy (result, addr, search - addr); + XP_MEMCPY (result, addr, search - addr); result[search - addr] = 0; } else if (addr) - PL_strcpy (result, addr); + XP_STRCPY (result, addr); else *result = 0; - PL_strcat (result, "?id="); - PL_strcat (result, new_dest); + XP_STRCAT (result, "?id="); + XP_STRCAT (result, new_dest); - if (search && PL_strstr (search, "&headers=all")) - PL_strcat (result, "&headers=all"); + if (search && XP_STRSTR (search, "&headers=all")) + XP_STRCAT (result, "&headers=all"); } FREEIF (new_dest); return result; @@ -349,9 +356,9 @@ net_make_mail_msg_stream (ActiveEntry *ce) if (ce->format_out == FO_PRESENT || ce->format_out == FO_CACHE_AND_PRESENT) { - MimeDisplayOptions *opt = PR_NEW (MimeDisplayOptions); + MimeDisplayOptions *opt = XP_NEW (MimeDisplayOptions); if (!opt) return MK_OUT_OF_MEMORY; - memset (opt, 0, sizeof(*opt)); + XP_MEMSET (opt, 0, sizeof(*opt)); opt->generate_reference_url_fn = mail_generate_reference_url_fn; opt->generate_header_html_fn = 0; @@ -379,8 +386,8 @@ net_ProcessMailbox (ActiveEntry *ce) MailboxConData * cd = (MailboxConData *)ce->con_data; /* temp, until imap urls have their own identifier */ - if ((!PL_strncasecmp(ce->URL_s->address, "mailbox://", 10) ) || - (!PL_strncasecmp(ce->URL_s->address, "view-source:mailbox://",22))) + if ((!XP_STRNCASECMP(ce->URL_s->address, "IMAP://", 7) ) || + (!XP_STRNCASECMP(ce->URL_s->address, "view-source:IMAP://",19))) return NET_ProcessIMAP4(ce); cd->pause_for_read = FALSE; /* already paused; reset */ @@ -388,7 +395,7 @@ net_ProcessMailbox (ActiveEntry *ce) while(!cd->pause_for_read) { #ifdef DEBUG_username - PR_LogPrint("NET_ProcessMailbox: at top of loop, state %d, status %d", cd->next_state, ce->status); + XP_Trace("NET_ProcessMailbox: at top of loop, state %d, status %d", cd->next_state, ce->status); #endif switch(cd->next_state) { @@ -397,15 +404,15 @@ net_ProcessMailbox (ActiveEntry *ce) if (!ce->URL_s->load_background) { char *fmt = XP_GetString( XP_MAIL_READING_FOLDER ); char *folder = cd->folder_name; - char *s = PL_strrchr (folder, '/'); + char *s = XP_STRRCHR (folder, '/'); if (s) folder = s+1; - s = (char *) PR_Malloc(PL_strlen(fmt) + PL_strlen(folder) + 20); + s = (char *) XP_ALLOC(XP_STRLEN(fmt) + XP_STRLEN(folder) + 20); if (s) { - sprintf (s, fmt, folder); + XP_SPRINTF (s, fmt, folder); NET_Progress(ce->window_id, s); - PR_Free(s); + XP_FREE(s); } } ce->status = MSG_BeginOpenFolderSock(cd->pane, @@ -417,20 +424,20 @@ net_ProcessMailbox (ActiveEntry *ce) if(ce->status == MK_CONNECTED) { #ifdef DEBUG_username - PR_LogPrint ("NET_ProcessMailBox: next state is MAILBOX_OPEN_MESSAGE"); + XP_Trace ("NET_ProcessMailBox: next state is MAILBOX_OPEN_MESSAGE"); #endif cd->next_state = MAILBOX_OPEN_MESSAGE; } else if(ce->status > -1) { #ifdef DEBUG_username - PR_LogPrint ("NET_ProcessMailBox: next state is MAILBOX_FINISH_OPEN_FOLDER"); + XP_Trace ("NET_ProcessMailBox: next state is MAILBOX_FINISH_OPEN_FOLDER"); #endif cd->pause_for_read = TRUE; cd->next_state = MAILBOX_FINISH_OPEN_FOLDER; } #ifdef DEBUG_username - PR_LogPrint ("NET_ProcessMailBox: MAILBOX_OPEN_FOLDER got error %d", ce->status); + XP_Trace ("NET_ProcessMailBox: MAILBOX_OPEN_FOLDER got error %d", ce->status); #endif break; @@ -458,6 +465,7 @@ net_ProcessMailbox (ActiveEntry *ce) * asking for a message */ cd->next_state = MAILBOX_DONE; + NET_Progress(ce->window_id, XP_GetString( XP_MAIL_READING_FOLDER_DONE ) ); } else { @@ -485,7 +493,7 @@ net_ProcessMailbox (ActiveEntry *ce) } else { - PR_ASSERT (cd->stream); + XP_ASSERT (cd->stream); cd->next_state = MAILBOX_READ_MESSAGE; } @@ -513,7 +521,7 @@ net_ProcessMailbox (ActiveEntry *ce) #endif while (cd->input_buffer == NULL) { cd->input_buffer = - (char*) PR_Malloc(cd->input_buffer_size); + (char*) XP_ALLOC(cd->input_buffer_size); if (!cd->input_buffer) { cd->input_buffer_size /= 2; if (cd->input_buffer_size < 512) { @@ -562,15 +570,15 @@ net_ProcessMailbox (ActiveEntry *ce) (!ce->URL_s->load_background)) { char *fmt= XP_GetString( XP_COMPRESSING_FOLDER ); char *folder = cd->folder_name; - char *s = PL_strrchr (folder, '/'); + char *s = XP_STRRCHR (folder, '/'); if (s) folder = s+1; - s = (char *)PR_Malloc (PL_strlen(fmt) + PL_strlen(folder) + 20); + s = (char *)XP_ALLOC (XP_STRLEN(fmt) + XP_STRLEN(folder) + 20); if (s) { - sprintf (s, fmt, folder); + XP_SPRINTF (s, fmt, folder); NET_Progress(ce->window_id, s); - PR_Free(s); + XP_FREE(s); } } ce->status = MSG_BeginCompressFolder(cd->pane, ce->URL_s, @@ -604,6 +612,16 @@ net_ProcessMailbox (ActiveEntry *ce) } break; + case MAILBOX_NULL: + ce->status = MK_WAITING_FOR_CONNECTION; + cd->next_state = MAILBOX_NULL2; + cd->pause_for_read = TRUE; + break; + case MAILBOX_NULL2: + ce->status = MK_CONNECTED; + cd->next_state = MAILBOX_DONE; + break; + case MAILBOX_DELIVER_QUEUED: if (!ce->URL_s->load_background) NET_Progress(ce->window_id, @@ -673,7 +691,7 @@ net_ProcessMailbox (ActiveEntry *ce) NET_ClearCallNetlibAllTheTime(ce->window_id, "mkmailbx"); if (cd->input_buffer) { - PR_Free(cd->input_buffer); + XP_FREE(cd->input_buffer); cd->input_buffer = NULL; } @@ -732,7 +750,7 @@ net_ProcessMailbox (ActiveEntry *ce) default: /* should never happen !!! */ TRACEMSG(("MAILBOX: BAD STATE!")); - PR_ASSERT(0); + XP_ASSERT(0); cd->next_state = MAILBOX_ERROR_DONE; break; } @@ -749,7 +767,7 @@ net_ProcessMailbox (ActiveEntry *ce) } /* while(!cd->pause_for_read) */ #ifdef DEBUG_username - PR_LogPrint ("Leaving NET_ProcessMailbox with status %d", ce->status); + XP_Trace ("Leaving NET_ProcessMailbox with status %d", ce->status); #endif return(ce->status); @@ -763,7 +781,7 @@ net_InterruptMailbox(ActiveEntry * ce) MailboxConData * cd = (MailboxConData *)ce->con_data; /* temp until imap urls have their own identifier */ - if (!PL_strncasecmp(ce->URL_s->address, "mailbox://", 10) ) + if (!XP_STRNCASECMP(ce->URL_s->address, "IMAP://", 7) ) return NET_InterruptIMAP4(ce); cd->next_state = MAILBOX_ERROR_DONE; diff --git a/mozilla/network/protocol/makefile.win b/mozilla/network/protocol/makefile.win index 8007b191092..28c7a9f34d3 100644 --- a/mozilla/network/protocol/makefile.win +++ b/mozilla/network/protocol/makefile.win @@ -26,6 +26,7 @@ IGNORE_MANIFEST=1 DEPTH=..\.. +include <$(DEPTH)\config\config.mak> DIRS= \ about \ file \ @@ -37,6 +38,17 @@ DIRS= \ dataurl \ js \ marimba \ +!endif +!ifdef MOZ_MAIL_NEWS + pop3 \ + smtp \ + nntp \ + mailbox \ + certld \ + imap4 \ +!endif +!ifdef MOZ_LDAP + ldap \ !endif $(NULL) diff --git a/mozilla/network/protocol/nntp/Makefile b/mozilla/network/protocol/nntp/Makefile new file mode 100644 index 00000000000..a18e8e18765 --- /dev/null +++ b/mozilla/network/protocol/nntp/Makefile @@ -0,0 +1,34 @@ +#!gmake +# +# The contents of this file are subject to the Netscape Public License +# Version 1.0 (the "NPL"); you may not use this file except in +# compliance with the NPL. You may obtain a copy of the NPL at +# http://www.mozilla.org/NPL/ +# +# Software distributed under the NPL is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL +# for the specific language governing rights and limitations under the +# NPL. +# +# The Initial Developer of this code under the NPL is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All Rights +# Reserved. + +DEPTH = ../../.. + +MODULE = nntpurl +LIBRARY_NAME = nntpurl + +CSRCS = \ + mknews.c \ + mknewsgr.c \ + $(NULL) + +EXPORTS= mknews.h mknewsgr.h + +REQUIRES = netcache network nspr2 dbm util pref js java fileurl \ + security layer img httpurl mimetype htmldlgs + +include $(DEPTH)/config/rules.mk + diff --git a/mozilla/network/protocol/nntp/makefile.win b/mozilla/network/protocol/nntp/makefile.win new file mode 100644 index 00000000000..55ec5c6ccd1 --- /dev/null +++ b/mozilla/network/protocol/nntp/makefile.win @@ -0,0 +1,89 @@ +#!gmake +# +# The contents of this file are subject to the Netscape Public License +# Version 1.0 (the "NPL"); you may not use this file except in +# compliance with the NPL. You may obtain a copy of the NPL at +# http://www.mozilla.org/NPL/ +# +# Software distributed under the NPL is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL +# for the specific language governing rights and limitations under the +# NPL. +# +# The Initial Developer of this code under the NPL is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All Rights +# Reserved. + +IGNORE_MANIFEST=1 +# + +#------------------------------------------------------------------------ +# +# Makefile to build the Network file protocol LIB +# +#------------------------------------------------------------------------ + + +# +# Make sure we have MOZILLA_CLIENT defined so we get the +# proper JS includes +# +LCFLAGS = $(LCFLAGS) -DMOZILLA_CLIENT + +!ifdef BUILD_DEBUG_GC +LCFLAGS = $(LCFLAGS) -DDEBUG_GC +!endif + +LLIBS= \ + $(NULL) +MISCDEP=$(LLIBS) +OBJS= \ + .\$(OBJDIR)\mknews.obj \ + .\$(OBJDIR)\mknewsgr.obj \ + $(NULL) + +CSRCS = \ + mknews.c \ + mknewsgr.c \ + $(NULL) + + +LIBRARY_NAME=nntpurl +MODULE=nntpurl +DEPTH=..\..\.. + +LOCAL_INCLUDES=-I. -I$(DEPTH)/dist/public/zlib -I$(DEPTH)/dist/public/parse +INCLUDES = $(LOCAL_INCLUDES) + + +EXTRA_LIBS= + +REQUIRES= ldap network httpurl +EXPORTS= mknews.h mknewsgr.h + +# use LINCS on win32 for now since REQUIRES seems to be broken +#!if "$(MOZ_BITS)" != "16" +LINCS= \ + -I$(PUBLIC)\nspr2 \ + -I$(PUBLIC)\util \ + -I$(PUBLIC)\java \ + -I$(PUBLIC)\pref \ + -I$(PUBLIC)\htmldlgs \ + -I$(PUBLIC)\js \ + -I$(PUBLIC)\security \ + -I$(PUBLIC)\netcache \ + -I$(PUBLIC)\network \ + -I$(PUBLIC)\ldap \ + -I$(PUBLIC)\httpurl \ + -I$(PUBLIC)\mimetype \ +#!endif + +include <$(DEPTH)\config\rules.mak> + +libs:: $(LIBRARY) + $(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib + +symbols:: + @echo "LIBRARY_NAME is $(LIBRARY_NAME)" + @echo "LIBRARY is $(LIBRARY)" diff --git a/mozilla/network/protocol/nntp/mknews.c b/mozilla/network/protocol/nntp/mknews.c index 66732a4a3f9..9d65d085bd9 100644 --- a/mozilla/network/protocol/nntp/mknews.c +++ b/mozilla/network/protocol/nntp/mknews.c @@ -24,6 +24,7 @@ /* Please leave outside of ifdef for windows precompiled headers */ #define FORCE_PR_LOG /* Allow logging in the release build (sorry this breaks the PCH) */ + #include "rosetta.h" #include "mkutils.h" @@ -34,6 +35,7 @@ #include "mime.h" #include "shist.h" #include "glhist.h" +#include "xp_reg.h" #include "mknews.h" #include "mktcp.h" #include "mkparse.h" @@ -41,12 +43,11 @@ #include "mkstream.h" #include "mkaccess.h" #include "mksort.h" -#include "mkcache.h" -#include "mkextcac.h" +#include "netcache.h" #include "mknewsgr.h" #include "libi18n.h" #include "gui.h" -#include "ssl.h" +#include HG10299 #include "mknews.h" #include "msgcom.h" @@ -57,8 +58,12 @@ #include "prtime.h" #include "prlog.h" +#include "xp_error.h" #include "secnav.h" #include "prefapi.h" +#include "xplocale.h" + +#include "xp_sock.h" /*#define CACHE_NEWSGRP_PASSWORD*/ @@ -177,8 +182,10 @@ PRIVATE XP_List * nntp_connection_list=0; PRIVATE XP_Bool net_news_last_username_probably_valid=FALSE; PRIVATE int32 net_NewsChunkSize=-1; /* default */ +PRIVATE int32 net_news_timeout = 170; /* seconds that an idle NNTP conn can live */ + #define PUTSTRING(s) (*cd->stream->put_block) \ - (cd->stream, s, PL_strlen(s)) + (cd->stream, s, XP_STRLEN(s)) #define COMPLETE_STREAM (*cd->stream->complete) \ (cd->stream) #define ABORT_STREAM(s) (*cd->stream->abort) \ @@ -359,9 +366,8 @@ typedef struct _NNTPConnection { XP_Bool default_host; XP_Bool no_xover; /* xover command is not supported here */ XP_Bool secure; /* is it a secure connection? */ - char *current_group; /* last GROUP command sent on this connection */ - + time_t last_used_time; /* last time this conn was used, for conn aging purposes */ } NNTPConnection; @@ -379,7 +385,7 @@ typedef struct _NewsConData { char *response_txt; /* text returned from NNTP server */ Bool pause_for_read; /* should we pause for the next read? */ - char *ssl_proxy_server; /* ssl proxy server hostname */ + HG82332 XP_Bool proxy_auth_required; /* is auth required */ XP_Bool sent_proxy_auth; /* have we sent a proxy auth? */ @@ -490,18 +496,30 @@ PUBLIC void NET_CleanTempXOVERCache(void) +PUBLIC void net_graceful_shutdown(PRFileDesc *sock, XP_Bool isSecure) +{ + static int32 int_pref = -1; + + if (int_pref == -1) + PREF_GetIntPref("network.socket_shutdown", &int_pref); + if (int_pref > 0 && int_pref < 4) + { + HG32949 + XP_SOCK_SHUTDOWN(sock, (int)(int_pref-1)); + } +} + static void net_nntp_close (PRFileDesc *sock, int status) { if (status != MK_INTERRUPTED) { const char *command = "QUIT" CRLF; - NET_BlockingWrite (sock, command, PL_strlen(command)); + NET_BlockingWrite (sock, command, XP_STRLEN(command)); NNTP_LOG_WRITE (command); } PR_Close(sock); } - /* user has removed a news host from the UI. * be sure it has also been removed from the * connection cache so we renegotiate news host @@ -514,7 +532,7 @@ PUBLIC void NET_OnNewsHostDeleted (const char *hostName) while ((conn = (NNTPConnection*) XP_ListNextObject(list)) != NULL) { - if (!(PL_strcasecmp(conn->hostname, hostName))) + if (!(XP_STRCASECMP(conn->hostname, hostName))) net_nntp_close (conn->csock, conn->busy ? MK_INTERRUPTED : 0); list = list->prev ? list->prev : nntp_connection_list; @@ -573,7 +591,7 @@ net_display_html_error_state (ActiveEntry *ce) if(cd->type_wanted == ARTICLE_WANTED || cd->type_wanted == CANCEL_WANTED) { - PL_strcpy(cd->output_buffer, + XP_STRCPY(cd->output_buffer, XP_GetString(XP_HTML_ARTICLE_EXPIRED)); if(ce->status > -1) PUTSTRING(cd->output_buffer); @@ -585,8 +603,8 @@ net_display_html_error_state (ActiveEntry *ce) "

<%.512s>", cd->path); if (cd->article_num > 0) { - PR_snprintf(cd->output_buffer+PL_strlen(cd->output_buffer), - OUTPUT_BUFFER_SIZE-PL_strlen(cd->output_buffer), + PR_snprintf(cd->output_buffer+XP_STRLEN(cd->output_buffer), + OUTPUT_BUFFER_SIZE-XP_STRLEN(cd->output_buffer), " (%lu)\n", (unsigned long) cd->article_num); if (ce->URL_s->msg_pane) MSG_MarkMessageKeyRead(ce->URL_s->msg_pane, cd->article_num, ""); @@ -620,7 +638,7 @@ net_display_html_error_state (ActiveEntry *ce) OUTPUT_BUFFER_SIZE, XP_GetString(MK_NEWS_ERROR_FMT), cd->response_txt); - ce->URL_s->error_msg = PL_strdup (cd->output_buffer); + ce->URL_s->error_msg = XP_STRDUP (cd->output_buffer); } /* if the server returned a 400 error then it is an expected @@ -670,7 +688,7 @@ net_news_response (ActiveEntry * ce) if(ce->status < 0) { ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, - PR_GetOSError()); + SOCKET_ERRNO); /* return TCP error */ @@ -685,15 +703,7 @@ net_news_response (ActiveEntry * ce) if(ce->bytes_received == 0) { - /* this is where kipp says that I can finally query - * for the security data. We can't do it after the - * connect since the handshake isn't done yet... - */ - /* clear existing data - */ - FREEIF(ce->URL_s->sec_info); - ce->URL_s->sec_info = SECNAV_SSLSocketStatus(ce->socket, - &ce->URL_s->security_on); + HG47352 } /* almost correct @@ -767,6 +777,7 @@ net_news_response (ActiveEntry * ce) HG40560 + /* interpret the server response after the connect * * returns negative if the server responds unexpectedly @@ -798,9 +809,9 @@ net_nntp_send_mode_reader (ActiveEntry * ce) { NewsConData * cd = (NewsConData *)ce->con_data; - PL_strcpy(cd->output_buffer, "MODE READER" CRLF); + XP_STRCPY(cd->output_buffer, "MODE READER" CRLF); - ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer)); + ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer)); NNTP_LOG_WRITE(cd->output_buffer); cd->next_state = NNTP_RESPONSE; @@ -834,8 +845,8 @@ net_nntp_send_mode_reader_response (ActiveEntry * ce) PRIVATE int net_nntp_send_list_extensions (ActiveEntry *ce) { NewsConData *cd = (NewsConData*) ce->con_data; - PL_strcpy(cd->output_buffer, "LIST EXTENSIONS" CRLF); - ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer)); + XP_STRCPY(cd->output_buffer, "LIST EXTENSIONS" CRLF); + ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer)); NNTP_LOG_WRITE(cd->output_buffer); cd->next_state = NNTP_RESPONSE; @@ -867,7 +878,7 @@ PRIVATE int net_nntp_send_list_extensions_response (ActiveEntry *ce) return ce->status; /* no line yet */ if (ce->status < 0) { - ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, PR_GetOSError()); + ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO); /* return TCP error */ return MK_TCP_READ_ERROR; } @@ -903,8 +914,8 @@ PRIVATE int net_nntp_send_list_searches (ActiveEntry *ce) if (MSG_QueryNewsExtension (cd->host, "SEARCH")) { - PL_strcpy(cd->output_buffer, "LIST SEARCHES" CRLF); - ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer)); + XP_STRCPY(cd->output_buffer, "LIST SEARCHES" CRLF); + ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer)); NNTP_LOG_WRITE(cd->output_buffer); cd->next_state = NNTP_RESPONSE; @@ -940,7 +951,7 @@ PRIVATE int net_nntp_send_list_searches_response (ActiveEntry *ce) return ce->status; /* no line yet */ if (ce->status < 0) { - ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, PR_GetOSError()); + ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO); /* return TCP error */ return MK_TCP_READ_ERROR; } @@ -964,8 +975,8 @@ PRIVATE int net_nntp_send_list_searches_response (ActiveEntry *ce) PRIVATE int net_send_list_search_headers (ActiveEntry *ce) { NewsConData *cd = (NewsConData*) ce->con_data; - PL_strcpy(cd->output_buffer, "LIST SRCHFIELDS" CRLF); - ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer)); + XP_STRCPY(cd->output_buffer, "LIST SRCHFIELDS" CRLF); + ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer)); NNTP_LOG_WRITE(cd->output_buffer); cd->next_state = NNTP_RESPONSE; @@ -995,7 +1006,7 @@ PRIVATE int net_send_list_search_headers_response (ActiveEntry *ce) return ce->status; /* no line yet */ if (ce->status < 0) { - ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, PR_GetOSError()); + ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO); /* return TCP error */ return MK_TCP_READ_ERROR; } @@ -1017,8 +1028,8 @@ PRIVATE int nntp_get_properties (ActiveEntry *ce) if (MSG_QueryNewsExtension (cd->host, "SETGET")) { - PL_strcpy(cd->output_buffer, "GET" CRLF); - ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer)); + XP_STRCPY(cd->output_buffer, "GET" CRLF); + ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer)); NNTP_LOG_WRITE(cd->output_buffer); cd->next_state = NNTP_RESPONSE; @@ -1053,17 +1064,17 @@ PRIVATE int nntp_get_properties_response (ActiveEntry *ce) return ce->status; /* no line yet */ if (ce->status < 0) { - ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, PR_GetOSError()); + ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO); /* return TCP error */ return MK_TCP_READ_ERROR; } if ('.' != line[0]) { - char *propertyName = PL_strdup(line); + char *propertyName = XP_STRDUP(line); if (propertyName) { - char *space = PL_strchr(propertyName, ' '); + char *space = XP_STRCHR(propertyName, ' '); if (space) { char *propertyValue = space + 1; @@ -1071,7 +1082,7 @@ PRIVATE int nntp_get_properties_response (ActiveEntry *ce) MSG_AddPropertyForGet (cd->host, propertyName, propertyValue); } - PR_Free(propertyName); + XP_FREE(propertyName); } } else @@ -1094,8 +1105,8 @@ PRIVATE int net_send_list_subscriptions (ActiveEntry *ce) if (0) #endif { - PL_strcpy(cd->output_buffer, "LIST SUBSCRIPTIONS" CRLF); - ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer)); + XP_STRCPY(cd->output_buffer, "LIST SUBSCRIPTIONS" CRLF); + ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer)); NNTP_LOG_WRITE(cd->output_buffer); cd->next_state = NNTP_RESPONSE; @@ -1131,7 +1142,7 @@ PRIVATE int net_send_list_subscriptions_response (ActiveEntry *ce) return ce->status; /* no line yet */ if (ce->status < 0) { - ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, PR_GetOSError()); + ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO); /* return TCP error */ return MK_TCP_READ_ERROR; } @@ -1177,7 +1188,7 @@ net_send_first_nntp_command (ActiveEntry *ce) cd->article_num = number; StrAllocCopy (cd->group_name, group); - if (cd->control_con->current_group && !PL_strcmp (cd->control_con->current_group, group)) + if (cd->control_con->current_group && !XP_STRCMP (cd->control_con->current_group, group)) cd->next_state = NNTP_SEND_ARTICLE_NUMBER; else cd->next_state = NNTP_SEND_GROUP_FOR_ARTICLE; @@ -1189,7 +1200,7 @@ net_send_first_nntp_command (ActiveEntry *ce) if(cd->type_wanted == NEWS_POST && !ce->URL_s->post_data) { - PR_ASSERT(0); + XP_ASSERT(0); return(-1); } else if(cd->type_wanted == NEWS_POST) @@ -1199,7 +1210,7 @@ net_send_first_nntp_command (ActiveEntry *ce) else if(cd->type_wanted == READ_NEWS_RC) { if(ce->URL_s->method == URL_POST_METHOD || - PL_strchr(ce->URL_s->address, '?')) + XP_STRCHR(ce->URL_s->address, '?')) cd->next_state = NEWS_NEWS_RC_POST; else cd->next_state = NEWS_DISPLAY_NEWS_RC; @@ -1284,7 +1295,7 @@ net_send_first_nntp_command (ActiveEntry *ce) StrAllocCopy(command, "GROUP "); - slash = PL_strchr(cd->group_name, '/'); /* look for a slash */ + slash = XP_STRCHR(cd->group_name, '/'); /* look for a slash */ cd->first_art = 0; cd->last_art = 0; if (slash) @@ -1305,14 +1316,14 @@ net_send_first_nntp_command (ActiveEntry *ce) if (MSG_QueryNewsExtension(cd->host, "SEARCH")) { /* use the SEARCH extension */ - char *slash = PL_strchr (cd->command_specific_data, '/'); + char *slash = XP_STRCHR (cd->command_specific_data, '/'); if (slash) { char *allocatedCommand = MSG_UnEscapeSearchUrl (slash + 1); if (allocatedCommand) { StrAllocCopy (command, allocatedCommand); - PR_Free(allocatedCommand); + XP_FREE(allocatedCommand); } } cd->next_state = NNTP_RESPONSE; @@ -1336,31 +1347,31 @@ net_send_first_nntp_command (ActiveEntry *ce) } else { - PR_ASSERT(FALSE); + XP_ASSERT(FALSE); cd->next_state = NNTP_ERROR; } } else if (cd->type_wanted == PROFILE_WANTED) { - char *slash = PL_strchr (cd->command_specific_data, '/'); + char *slash = XP_STRCHR (cd->command_specific_data, '/'); if (slash) { char *allocatedCommand = MSG_UnEscapeSearchUrl (slash + 1); if (allocatedCommand) { StrAllocCopy (command, allocatedCommand); - PR_Free(allocatedCommand); + XP_FREE(allocatedCommand); } } cd->next_state = NNTP_RESPONSE; - if (PL_strstr(ce->URL_s->address, "PROFILE NEW")) + if (XP_STRSTR(ce->URL_s->address, "PROFILE NEW")) cd->next_state_after_response = NNTP_PROFILE_ADD_RESPONSE; else cd->next_state_after_response = NNTP_PROFILE_DELETE_RESPONSE; } else if (cd->type_wanted == IDS_WANTED) { - char *slash = PL_strchr(cd->group_name, '/'); /* look for a slash */ + char *slash = XP_STRCHR(cd->group_name, '/'); /* look for a slash */ if (slash) *slash = '\0'; @@ -1376,14 +1387,14 @@ net_send_first_nntp_command (ActiveEntry *ce) if (*cd->path != '<') StrAllocCat(command,"<"); StrAllocCat(command, cd->path); - if (PL_strchr(command+8, '>')==0) + if (XP_STRCHR(command+8, '>')==0) StrAllocCat(command,">"); } StrAllocCat(command, CRLF); - ce->status = (int) NET_BlockingWrite(ce->socket, command, PL_strlen(command)); + ce->status = (int) NET_BlockingWrite(ce->socket, command, XP_STRLEN(command)); NNTP_LOG_WRITE(command); - PR_Free(command); + XP_FREE(command); cd->next_state = NNTP_RESPONSE; if (cd->type_wanted != SEARCH_WANTED && cd->type_wanted != PROFILE_WANTED) @@ -1443,7 +1454,7 @@ net_send_group_for_article (ActiveEntry * ce) cd->control_con->current_group); ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer, - PL_strlen(cd->output_buffer)); + XP_STRLEN(cd->output_buffer)); NNTP_LOG_WRITE(cd->output_buffer); cd->next_state = NNTP_RESPONSE; @@ -1477,7 +1488,7 @@ net_send_article_number (ActiveEntry * ce) cd->article_num); ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer, - PL_strlen(cd->output_buffer)); + XP_STRLEN(cd->output_buffer)); NNTP_LOG_WRITE(cd->output_buffer); cd->next_state = NNTP_RESPONSE; @@ -1500,16 +1511,16 @@ news_generate_news_url_fn (const char *dest, void *closure, char *prefix = NET_ParseURL(ce->URL_s->address, (GET_PROTOCOL_PART | GET_HOST_PART)); char *new_dest = NET_Escape (dest, URL_XALPHAS); - char *result = (char *) PR_Malloc (PL_strlen (prefix) + - (new_dest ? PL_strlen (new_dest) : 0) + char *result = (char *) XP_ALLOC (XP_STRLEN (prefix) + + (new_dest ? XP_STRLEN (new_dest) : 0) + 40); if (result && prefix) { - PL_strcpy (result, prefix); - if (prefix[PL_strlen(prefix) - 1] != ':') + XP_STRCPY (result, prefix); + if (prefix[XP_STRLEN(prefix) - 1] != ':') /* If there is a host, it must be terminated with a slash. */ - PL_strcat (result, "/"); - PL_strcat (result, new_dest); + XP_STRCAT (result, "/"); + XP_STRCAT (result, new_dest); } FREEIF (prefix); FREEIF (new_dest); @@ -1568,14 +1579,14 @@ net_setup_news_stream (ActiveEntry * ce) cd->next_state = NNTP_LIST_PRETTY_NAMES; else if (cd->type_wanted == PROFILE_WANTED) { - if (PL_strstr(ce->URL_s->address, "PROFILE NEW")) + if (XP_STRSTR(ce->URL_s->address, "PROFILE NEW")) cd->next_state = NNTP_PROFILE_ADD; else cd->next_state = NNTP_PROFILE_DELETE; } else { - PR_ASSERT(0); + XP_ASSERT(0); return -1; } @@ -1595,7 +1606,7 @@ news_generate_html_header_fn (const char *dest, void *closure, refer to a file in the disk cache rather than an NNTP connection. */ NewsConData *cd = 0; - PR_ASSERT(ce->protocol == NEWS_TYPE_URL || + XP_ASSERT(ce->protocol == NEWS_TYPE_URL || ce->protocol == FILE_CACHE_TYPE_URL || ce->protocol == MEMORY_CACHE_TYPE_URL); if (ce->protocol == NEWS_TYPE_URL) @@ -1621,7 +1632,7 @@ news_generate_html_header_fn (const char *dest, void *closure, NNTP_LOG_NOTE(("news_generate_html_header_fn: url->msg_pane NULL for URL: %s", ce->URL_s->address)); messagepane = MSG_FindPane(ce->window_id, MSG_MESSAGEPANE); } - PR_ASSERT (!cd || cd->type_wanted == ARTICLE_WANTED); + XP_ASSERT (!cd || cd->type_wanted == ARTICLE_WANTED); if (messagepane) MSG_ActivateReplyOptions (messagepane, headers); @@ -1640,19 +1651,19 @@ net_InitializeNewsFeData (ActiveEntry * ce) if (!NET_IsNewsMessageURL (ce->URL_s->address)) { - PR_ASSERT(0); + XP_ASSERT(0); return -1; } if (ce->URL_s->fe_data) { - PR_ASSERT(0); + XP_ASSERT(0); return -1; } - opt = PR_NEW (MimeDisplayOptions); + opt = XP_NEW (MimeDisplayOptions); if (!opt) return MK_OUT_OF_MEMORY; - memset (opt, 0, sizeof(*opt)); + XP_MEMSET (opt, 0, sizeof(*opt)); opt->generate_reference_url_fn = news_generate_reference_url_fn; opt->generate_news_url_fn = news_generate_news_url_fn; @@ -1676,7 +1687,7 @@ net_begin_article(ActiveEntry * ce) /* Set up the HTML stream */ FREEIF (ce->URL_s->content_type); - ce->URL_s->content_type = PL_strdup (MESSAGE_RFC822); + ce->URL_s->content_type = XP_STRDUP (MESSAGE_RFC822); #ifdef NO_ARTICLE_CACHEING ce->format_out = CLEAR_CACHE_BIT (ce->format_out); @@ -1684,7 +1695,7 @@ net_begin_article(ActiveEntry * ce) if (cd->type_wanted == CANCEL_WANTED) { - PR_ASSERT(ce->format_out == FO_PRESENT); + XP_ASSERT(ce->format_out == FO_PRESENT); ce->format_out = FO_PRESENT; } @@ -1702,7 +1713,7 @@ net_begin_article(ActiveEntry * ce) } cd->stream = NET_StreamBuilder(ce->format_out, ce->URL_s, ce->window_id); - PR_ASSERT (cd->stream); + XP_ASSERT (cd->stream); if (!cd->stream) return -1; cd->next_state = NNTP_READ_ARTICLE; @@ -1726,10 +1737,10 @@ net_news_begin_authorize(ActiveEntry * ce) if (cd->pane && (!net_news_last_username_probably_valid || (last_username_hostname && - PL_strcasecmp(last_username_hostname, cd->control_con->hostname)))) { + strcasecomp(last_username_hostname, cd->control_con->hostname)))) { username = SECNAV_UnMungeString(MSG_GetNewsgroupUsername(cd->pane)); if (username && last_username && - !PL_strcmp (username, last_username) && + !XP_STRCMP (username, last_username) && (cd->previous_response_code == 281 || cd->previous_response_code == 250 || cd->previous_response_code == 211)) { @@ -1765,7 +1776,7 @@ net_news_begin_authorize(ActiveEntry * ce) /* If the URL/cd->control_con->hostname contains @ this must be triggered * from the bookmark. Use the embed username if we could. */ - if ((cp = PL_strchr(cd->control_con->hostname, '@')) != NULL) + if ((cp = XP_STRCHR(cd->control_con->hostname, '@')) != NULL) { /* in this case the username and possibly * the password are in the URL @@ -1773,7 +1784,7 @@ net_news_begin_authorize(ActiveEntry * ce) char * colon; *cp = '\0'; - colon = PL_strchr(cd->control_con->hostname, ':'); + colon = XP_STRCHR(cd->control_con->hostname, ':'); if(colon) *colon = '\0'; @@ -1792,7 +1803,7 @@ net_news_begin_authorize(ActiveEntry * ce) if (!username && net_news_last_username_probably_valid) { if( last_username_hostname && - !PL_strcasecmp(last_username_hostname, cd->control_con->hostname) ) + !strcasecomp(last_username_hostname, cd->control_con->hostname) ) StrAllocCopy(username, last_username); else net_news_last_username_probably_valid = FALSE; @@ -1800,17 +1811,9 @@ net_news_begin_authorize(ActiveEntry * ce) if (!username) { -#if defined(SingleSignon) - username = SI_Prompt(ce->window_id, - XP_GetString(XP_PROMPT_ENTER_USERNAME), - "", - cd->control_con->hostname); - -#else username = FE_Prompt(ce->window_id, XP_GetString(XP_PROMPT_ENTER_USERNAME), username ? username : ""); -#endif /* reset net_news_last_username_probably_valid to false */ net_news_last_username_probably_valid = FALSE; @@ -1829,7 +1832,7 @@ net_news_begin_authorize(ActiveEntry * ce) if (cd->pane && !MSG_GetNewsgroupUsername(cd->pane)) { munged_username = SECNAV_MungeString(username); MSG_SetNewsgroupUsername(cd->pane, munged_username); - PR_FREEIF(munged_username); + XP_FREEIF(munged_username); } #endif @@ -1837,7 +1840,7 @@ net_news_begin_authorize(ActiveEntry * ce) StrAllocCat(command, username); StrAllocCat(command, CRLF); - ce->status = (int) NET_BlockingWrite(ce->socket, command, PL_strlen(command)); + ce->status = (int) NET_BlockingWrite(ce->socket, command, XP_STRLEN(command)); NNTP_LOG_WRITE(command); FREE(command); @@ -1897,7 +1900,7 @@ net_news_authorize_response(ActiveEntry * ce) if (net_news_last_username_probably_valid && last_password && last_password_hostname - && !PL_strcasecmp(last_password_hostname, cd->control_con->hostname)) + && !strcasecomp(last_password_hostname, cd->control_con->hostname)) { #ifdef CACHE_NEWSGRP_PASSWORD if (cd->pane) @@ -1906,7 +1909,7 @@ net_news_authorize_response(ActiveEntry * ce) StrAllocCopy(password, last_password); #endif } - else if ((cp = PL_strchr(cd->control_con->hostname, '@')) != NULL) + else if ((cp = XP_STRCHR(cd->control_con->hostname, '@')) != NULL) { /* in this case the username and possibly * the password are in the URL @@ -1914,7 +1917,7 @@ net_news_authorize_response(ActiveEntry * ce) char * colon; *cp = '\0'; - colon = PL_strchr(cd->control_con->hostname, ':'); + colon = XP_STRCHR(cd->control_con->hostname, ':'); if(colon) { *colon = '\0'; @@ -1930,18 +1933,9 @@ net_news_authorize_response(ActiveEntry * ce) } if (!password) { -#if defined(SingleSignon) - password = SI_PromptPassword - (ce->window_id, - XP_GetString - (XP_PLEASE_ENTER_A_PASSWORD_FOR_NEWS_SERVER_ACCESS), - cd->control_con->hostname, - TRUE, TRUE); -#else password = FE_PromptPassword(ce->window_id, XP_GetString( XP_PLEASE_ENTER_A_PASSWORD_FOR_NEWS_SERVER_ACCESS ) ); -#endif net_news_last_username_probably_valid = FALSE; } @@ -1959,7 +1953,7 @@ net_news_authorize_response(ActiveEntry * ce) if (cd->pane && !MSG_GetNewsgroupPassword(cd->pane)) { munged_password = SECNAV_MungeString(password); MSG_SetNewsgroupPassword(cd->pane, munged_password); - PR_FREEIF(munged_password); + XP_FREEIF(munged_password); } #endif @@ -1967,7 +1961,7 @@ net_news_authorize_response(ActiveEntry * ce) StrAllocCat(command, password); StrAllocCat(command, CRLF); - ce->status = (int) NET_BlockingWrite(ce->socket, command, PL_strlen(command)); + ce->status = (int) NET_BlockingWrite(ce->socket, command, XP_STRLEN(command)); NNTP_LOG_WRITE(command); FREE(command); @@ -1993,7 +1987,7 @@ net_news_authorize_response(ActiveEntry * ce) return(MK_NNTP_AUTH_FAILED); } - PR_ASSERT(0); /* should never get here */ + XP_ASSERT(0); /* should never get here */ return(-1); } @@ -2037,7 +2031,7 @@ net_news_password_response(ActiveEntry * ce) return(MK_NNTP_AUTH_FAILED); } - PR_ASSERT(0); /* should never get here */ + XP_ASSERT(0); /* should never get here */ return(-1); } @@ -2082,7 +2076,7 @@ PRIVATE int net_process_newgroups (ActiveEntry *ce) { NewsConData * cd = (NewsConData *)ce->con_data; - char *line, *s, *s1, *s2, *flag; + char *line, *s, *s1=NULL, *s2=NULL, *flag=NULL; int32 oldest, youngest; ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buf, @@ -2101,7 +2095,7 @@ net_process_newgroups (ActiveEntry *ce) if(ce->status<0) { - ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, PR_GetOSError()); + ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO); /* return TCP error */ @@ -2120,7 +2114,7 @@ net_process_newgroups (ActiveEntry *ce) { cd->next_state = NNTP_LIST_XACTIVE; #ifdef DEBUG_bienvenu - PR_LogPrint("listing xactive for %s\n", cd->group_name); + XP_Trace("listing xactive for %s\n", cd->group_name); #endif return 0; } @@ -2153,17 +2147,17 @@ net_process_newgroups (ActiveEntry *ce) /* format is "rec.arts.movies.past-films 7302 7119 y" */ - s = PL_strchr (line, ' '); + s = XP_STRCHR (line, ' '); if (s) { *s = 0; s1 = s+1; - s = PL_strchr (s1, ' '); + s = XP_STRCHR (s1, ' '); if (s) { *s = 0; s2 = s+1; - s = PL_strchr (s2, ' '); + s = XP_STRCHR (s2, ' '); if (s) { *s = 0; @@ -2233,7 +2227,7 @@ net_read_news_list (ActiveEntry *ce) if(ce->status<0) { - ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, PR_GetOSError()); + ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO); /* return TCP error */ @@ -2270,7 +2264,7 @@ net_read_news_list (ActiveEntry *ce) } /* find whitespace seperator if it exits */ - for(i=0; line[i] != '\0' && !NET_IS_SPACE(line[i]); i++) + for(i=0; line[i] != '\0' && !XP_IS_SPACE(line[i]); i++) ; /* null body */ if(line[i] == '\0') @@ -2437,7 +2431,7 @@ net_xover_send (ActiveEntry *ce) return((int) NET_BlockingWrite(ce->socket, cd->output_buffer, - PL_strlen(cd->output_buffer))); + XP_STRLEN(cd->output_buffer))); NNTP_LOG_WRITE(cd->output_buffer); } @@ -2467,7 +2461,7 @@ net_read_xover_response (ActiveEntry *ce) something went very wrong, since our servers do XOVER. Thus the assert. */ - /*PR_ASSERT (0);*/ + /*XP_ASSERT (0);*/ cd->next_state = NNTP_READ_GROUP; cd->control_con->no_xover = TRUE; } @@ -2509,7 +2503,7 @@ net_read_xover (ActiveEntry *ce) if(ce->status<0) { ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, - PR_GetOSError()); + SOCKET_ERRNO); /* return TCP error */ @@ -2531,9 +2525,9 @@ net_read_xover (ActiveEntry *ce) if(ce->status > 1) { ce->bytes_received += ce->status; -/* FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status, + FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status, ce->URL_s->content_length); -*/ + } ce->status = MSG_ProcessXOVER (cd->pane, line, &cd->xover_parse_state); @@ -2554,7 +2548,7 @@ net_process_xover (ActiveEntry *ce) /* if (cd->xover_parse_state) { ### dmb - we need a different check */ ce->status = MSG_FinishXOVER (cd->pane, &cd->xover_parse_state, 0); - PR_ASSERT (!cd->xover_parse_state); + XP_ASSERT (!cd->xover_parse_state); if (ce->status < 0) return ce->status; cd->next_state = NEWS_DONE; @@ -2567,12 +2561,12 @@ PRIVATE int net_profile_add (ActiveEntry *ce) { #if 0 NewsConData *cd = (NewsConData*) ce->con_data; - char *slash = PL_strrchr(ce->URL_s->address, '/'); + char *slash = XP_STRRCHR(ce->URL_s->address, '/'); if (slash) { - PL_strcpy (cd->output_buffer, slash + 1); - PL_strcat (cd->output_buffer, CRLF); - ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer)); + XP_STRCPY (cd->output_buffer, slash + 1); + XP_STRCAT (cd->output_buffer, CRLF); + ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer)); NNTP_LOG_WRITE(cd->output_buffer); cd->next_state = NNTP_RESPONSE; @@ -2587,7 +2581,7 @@ PRIVATE int net_profile_add (ActiveEntry *ce) return ce->status; #else - PR_ASSERT(FALSE); + XP_ASSERT(FALSE); return -1; #endif } @@ -2616,12 +2610,12 @@ PRIVATE int net_profile_delete (ActiveEntry *ce) { #if 0 NewsConData *cd = (NewsConData*) ce->con_data; - char *slash = PL_strrchr(ce->URL_s->address, '/'); + char *slash = XP_STRRCHR(ce->URL_s->address, '/'); if (slash) { - PL_strcpy (cd->output_buffer, slash + 1); - PL_strcat (cd->output_buffer, CRLF); - ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer)); + XP_STRCPY (cd->output_buffer, slash + 1); + XP_STRCAT (cd->output_buffer, CRLF); + ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer)); NNTP_LOG_WRITE(cd->output_buffer); cd->next_state = NNTP_RESPONSE; @@ -2636,7 +2630,7 @@ PRIVATE int net_profile_delete (ActiveEntry *ce) return ce->status; #else - PR_ASSERT(FALSE); + XP_ASSERT(FALSE); return -1; #endif } @@ -2683,7 +2677,7 @@ net_read_news_group (ActiveEntry *ce) cd->pause_for_read = TRUE; NNTP_LOG_WRITE(cd->output_buffer); - return((int) NET_BlockingWrite(ce->socket, cd->output_buffer, PL_strlen(cd->output_buffer))); + return((int) NET_BlockingWrite(ce->socket, cd->output_buffer, XP_STRLEN(cd->output_buffer))); } } @@ -2739,7 +2733,7 @@ net_read_news_group_body (ActiveEntry *ce) if(ce->status < 0) { - ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, PR_GetOSError()); + ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO); /* return TCP error */ @@ -2781,11 +2775,11 @@ net_send_news_post_data(ActiveEntry * ce) { /* normal done */ - PL_strcpy(cd->output_buffer, CRLF "." CRLF); + XP_STRCPY(cd->output_buffer, CRLF "." CRLF); NNTP_LOG_WRITE(cd->output_buffer); ce->status = (int) NET_BlockingWrite(ce->socket, cd->output_buffer, - PL_strlen(cd->output_buffer)); + XP_STRLEN(cd->output_buffer)); NNTP_LOG_WRITE(cd->output_buffer); NET_Progress(ce->window_id, @@ -2839,7 +2833,7 @@ net_send_news_post_data_response(ActiveEntry * ce) cd->next_state_after_response = NNTP_CHECK_FOR_MESSAGE; NNTP_LOG_WRITE(cd->output_buffer); return (int) NET_BlockingWrite(ce->socket, cd->output_buffer, - PL_strlen(cd->output_buffer)); + XP_STRLEN(cd->output_buffer)); } MSG_ClearCompositionMessageID(cd->pane); /* So that if the user tries @@ -2903,7 +2897,7 @@ net_DisplayNewsRC(ActiveEntry * ce) PR_snprintf(NET_Socket_Buffer, OUTPUT_BUFFER_SIZE, "GROUP %.512s" CRLF, cd->control_con->current_group); ce->status = (int) NET_BlockingWrite(ce->socket, NET_Socket_Buffer, - PL_strlen(NET_Socket_Buffer)); + XP_STRLEN(NET_Socket_Buffer)); NNTP_LOG_WRITE(NET_Socket_Buffer); percent = (int32) (100 * (((double) cd->newsrc_list_index) / @@ -2914,7 +2908,7 @@ net_DisplayNewsRC(ActiveEntry * ce) if (statusText) { FE_Progress (ce->window_id, statusText); - PR_Free(statusText); + XP_FREE(statusText); } cd->newsrc_list_index++; @@ -2969,18 +2963,18 @@ net_DisplayNewsRCResponse(ActiveEntry * ce) */ num_arts = cd->response_txt; - low = PL_strchr(num_arts, ' '); + low = XP_STRCHR(num_arts, ' '); if(low) { first_art = atol(low); *low++ = '\0'; - high= PL_strchr(low, ' '); + high= XP_STRCHR(low, ' '); } if(high) { *high++ = '\0'; - group = PL_strchr(high, ' '); + group = XP_STRCHR(high, ' '); } if(group) { @@ -2989,7 +2983,7 @@ net_DisplayNewsRCResponse(ActiveEntry * ce) the end. This will be space separated from the group name. If a space is found in the group name terminate at that point. */ - strtok(group, " "); + XP_STRTOK(group, " "); last_art = atol(high); } @@ -3041,8 +3035,8 @@ net_cancel_done_cb (MWContext *context, void *data, int status, ActiveEntry *ce = (ActiveEntry *) data; NewsConData *cd = (NewsConData *) ce->con_data; cd->cancel_status = status; - PR_ASSERT(status < 0 || file_name); - cd->cancel_msg_file = (status < 0 ? 0 : PL_strdup(file_name)); + XP_ASSERT(status < 0 || file_name); + cd->cancel_msg_file = (status < 0 ? 0 : XP_STRDUP(file_name)); } @@ -3052,7 +3046,7 @@ net_start_cancel (ActiveEntry *ce) NewsConData *cd = (NewsConData *) ce->con_data; char *command = "POST" CRLF; - ce->status = (int) NET_BlockingWrite(ce->socket, command, PL_strlen(command)); + ce->status = (int) NET_BlockingWrite(ce->socket, command, XP_STRLEN(command)); NNTP_LOG_WRITE(command); cd->next_state = NNTP_RESPONSE; @@ -3078,10 +3072,10 @@ net_do_cancel (ActiveEntry *ce) message at it... But the normal posting code doesn't do this check. Why? */ - PR_ASSERT (cd->response_code == 340); + XP_ASSERT (cd->response_code == 340); /* These shouldn't be set yet, since the headers haven't been "flushed" */ - PR_ASSERT (!cd->cancel_id && + XP_ASSERT (!cd->cancel_id && !cd->cancel_from && !cd->cancel_newsgroups && !cd->cancel_distribution); @@ -3090,7 +3084,7 @@ net_do_cancel (ActiveEntry *ce) are done, and it will call news_generate_html_header_fn which will notice the fields we're interested in. */ - PL_strcpy (cd->output_buffer, CRLF); /* CRLF used to be LINEBREAK. + XP_STRCPY (cd->output_buffer, CRLF); /* CRLF used to be LINEBREAK. LINEBREAK is platform dependent and is only on a mac. This CRLF is the protocol delimiter @@ -3105,7 +3099,7 @@ net_do_cancel (ActiveEntry *ce) newsgroups = cd->cancel_newsgroups; distribution = cd->cancel_distribution; - PR_ASSERT (id && newsgroups); + XP_ASSERT (id && newsgroups); if (!id || !newsgroups) return -1; /* "unknown error"... */ cd->cancel_newsgroups = 0; @@ -3113,12 +3107,12 @@ net_do_cancel (ActiveEntry *ce) cd->cancel_from = 0; cd->cancel_id = 0; - L = PL_strlen (id); + L = XP_STRLEN (id); from = MIME_MakeFromField (); - subject = (char *) PR_Malloc (L + 20); - other_random_headers = (char *) PR_Malloc (L + 20); - body = (char *) PR_Malloc (PL_strlen (XP_AppCodeName) + 100); + subject = (char *) XP_ALLOC (L + 20); + other_random_headers = (char *) XP_ALLOC (L + 20); + body = (char *) XP_ALLOC (XP_STRLEN (XP_AppCodeName) + 100); /* Make sure that this loser isn't cancelling someone else's posting. Yes, there are occasionally good reasons to do so. Those people @@ -3130,13 +3124,13 @@ net_do_cancel (ActiveEntry *ce) { char *us = MSG_ExtractRFC822AddressMailboxes (from); char *them = MSG_ExtractRFC822AddressMailboxes (old_from); - XP_Bool ok = (us && them && !PL_strcasecmp (us, them)); + XP_Bool ok = (us && them && !strcasecomp (us, them)); FREEIF(us); FREEIF(them); if (!ok) { status = MK_NNTP_CANCEL_DISALLOWED; - ce->URL_s->error_msg = PL_strdup (XP_GetString(status)); + ce->URL_s->error_msg = XP_STRDUP (XP_GetString(status)); cd->next_state = NEWS_ERROR; /* even though it worked */ cd->pause_for_read = FALSE; goto FAIL; @@ -3159,30 +3153,29 @@ net_do_cancel (ActiveEntry *ce) goto FAIL; } - PL_strcpy (subject, "cancel "); - PL_strcat (subject, id); + XP_STRCPY (subject, "cancel "); + XP_STRCAT (subject, id); - PL_strcpy (other_random_headers, "Control: cancel "); - PL_strcat (other_random_headers, id); - PL_strcat (other_random_headers, CRLF); + XP_STRCPY (other_random_headers, "Control: cancel "); + XP_STRCAT (other_random_headers, id); + XP_STRCAT (other_random_headers, CRLF); if (distribution) { - PL_strcat (other_random_headers, "Distribution: "); - PL_strcat (other_random_headers, distribution); - PL_strcat (other_random_headers, CRLF); + XP_STRCAT (other_random_headers, "Distribution: "); + XP_STRCAT (other_random_headers, distribution); + XP_STRCAT (other_random_headers, CRLF); } - PL_strcpy (body, "This message was cancelled from within "); - PL_strcat (body, XP_AppCodeName); - PL_strcat (body, "." CRLF); + XP_STRCPY (body, "This message was cancelled from within "); + XP_STRCAT (body, XP_AppCodeName); + XP_STRCAT (body, "." CRLF); /* #### Would it be a good idea to S/MIME-sign all "cancel" messages? */ fields = MSG_CreateCompositionFields(from, 0, 0, 0, 0, 0, newsgroups, 0, 0, subject, id, other_random_headers, - 0, 0, news_url, - FALSE, /* encrypt_p */ - FALSE /* sign_p */ + 0, 0, news_url + HG66664 ); if (!fields) { @@ -3197,7 +3190,7 @@ net_do_cancel (ActiveEntry *ce) fields, FALSE, /* digest_p */ TRUE, /* dont_deliver_p */ - TEXT_PLAIN, body, PL_strlen (body), + TEXT_PLAIN, body, XP_STRLEN (body), 0, /* other attachments */ NULL, /* multipart/related chunk */ net_cancel_done_cb); @@ -3208,13 +3201,13 @@ net_do_cancel (ActiveEntry *ce) if (!cd->cancel_msg_file) { status = cd->cancel_status; - PR_ASSERT (status < 0); + XP_ASSERT (status < 0); if (status >= 0) status = -1; goto FAIL; } /* Now send the data - do it blocking, who cares; the message is known - to be very small. First suck the whole file into memory. Then delete + to be very small. First read the whole file into memory. Then delete the file. Then do a blocking write of the data. (We could use file-posting, maybe, but I couldn't figure out how.) @@ -3231,7 +3224,7 @@ net_do_cancel (ActiveEntry *ce) data_fp = 0; data_size = st.st_size + 20; - data = (char *) PR_Malloc (data_size); + data = (char *) XP_ALLOC (data_size); if (! data) { status = MK_OUT_OF_MEMORY; @@ -3245,10 +3238,10 @@ net_do_cancel (ActiveEntry *ce) XP_FileClose (file); XP_FileRemove (cd->cancel_msg_file, xpFileToPost); - PL_strcat (data, CRLF "." CRLF CRLF); - status = NET_BlockingWrite(ce->socket, data, PL_strlen(data)); + XP_STRCAT (data, CRLF "." CRLF CRLF); + status = NET_BlockingWrite(ce->socket, data, XP_STRLEN(data)); NNTP_LOG_WRITE(data); - PR_Free (data); + XP_FREE (data); if (status < 0) { ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_WRITE_ERROR, @@ -3285,7 +3278,7 @@ static int net_xpat_send (ActiveEntry *ce) char *thisTerm = NULL; if (cd->current_search && - (thisTerm = PL_strchr(cd->current_search, '/')) != NULL) + (thisTerm = XP_STRCHR(cd->current_search, '/')) != NULL) { /* extract the XPAT encoding for one query term */ /* char *next_search = NULL; */ @@ -3293,7 +3286,7 @@ static int net_xpat_send (ActiveEntry *ce) char *unescapedCommand = NULL; char *endOfTerm = NULL; StrAllocCopy (command, ++thisTerm); - endOfTerm = PL_strchr(command, '/'); + endOfTerm = XP_STRCHR(command, '/'); if (endOfTerm) *endOfTerm = '\0'; StrAllocCat (command, CRLF); @@ -3302,15 +3295,15 @@ static int net_xpat_send (ActiveEntry *ce) /* send one term off to the server */ NNTP_LOG_WRITE(command); - status = NET_BlockingWrite(ce->socket, unescapedCommand, PL_strlen(unescapedCommand)); + status = NET_BlockingWrite(ce->socket, unescapedCommand, XP_STRLEN(unescapedCommand)); NNTP_LOG_WRITE(unescapedCommand); cd->next_state = NNTP_RESPONSE; cd->next_state_after_response = NNTP_XPAT_RESPONSE; cd->pause_for_read = TRUE; - PR_Free(command); - PR_Free(unescapedCommand); + XP_FREE(command); + XP_FREE(unescapedCommand); } else { @@ -3327,10 +3320,10 @@ static int net_list_pretty_names(ActiveEntry *ce) OUTPUT_BUFFER_SIZE, "LIST PRETTYNAMES %.512s" CRLF, cd->group_name ? cd->group_name : ""); - ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer)); + ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer)); NNTP_LOG_WRITE(cd->output_buffer); #ifdef DEBUG_bienvenu - PR_LogPrint(cd->output_buffer); + XP_Trace(cd->output_buffer); #endif cd->next_state = NNTP_RESPONSE; cd->next_state_after_response = NNTP_LIST_PRETTY_NAMES_RESPONSE; @@ -3373,7 +3366,7 @@ static int net_list_pretty_names_response(ActiveEntry *ce) { int i; /* find whitespace seperator if it exits */ - for (i=0; line[i] != '\0' && !NET_IS_SPACE(line[i]); i++) + for (i=0; line[i] != '\0' && !XP_IS_SPACE(line[i]); i++) ; /* null body */ if(line[i] == '\0') @@ -3385,7 +3378,7 @@ static int net_list_pretty_names_response(ActiveEntry *ce) if (i > 0) MSG_AddPrettyName(cd->host, line, prettyName); #ifdef DEBUG_bienvenu - PR_LogPrint("adding pretty name %s\n", prettyName); + XP_Trace("adding pretty name %s\n", prettyName); #endif } else @@ -3406,7 +3399,7 @@ static int net_list_xactive(ActiveEntry *ce) OUTPUT_BUFFER_SIZE, "LIST XACTIVE %.512s" CRLF, cd->group_name); - ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer)); + ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer)); NNTP_LOG_WRITE(cd->output_buffer); cd->next_state = NNTP_RESPONSE; @@ -3422,7 +3415,7 @@ static int net_list_xactive_response(ActiveEntry *ce) NewsConData * cd = (NewsConData *)ce->con_data; - PR_ASSERT(cd->response_code == 215); + XP_ASSERT(cd->response_code == 215); if (cd->response_code != 215) { cd->next_state = DISPLAY_NEWSGROUPS; @@ -3460,7 +3453,7 @@ static int net_list_xactive_response(ActiveEntry *ce) char *s = line; /* format is "rec.arts.movies.past-films 7302 7119 csp" */ - while (*s && !NET_IS_SPACE(*s)) + while (*s && !XP_IS_SPACE(*s)) s++; if (s) { @@ -3481,7 +3474,7 @@ static int net_list_xactive_response(ActiveEntry *ce) /* we're either going to list prettynames first, or list all prettynames every time, so we won't care so much if it gets interrupted. */ #ifdef DEBUG_bienvenu - PR_LogPrint("got xactive for %s of %s\n", line, flags); + XP_Trace("got xactive for %s of %s\n", line, flags); #endif MSG_SetGroupNeedsExtraInfo(cd->host, line, FALSE); } @@ -3493,11 +3486,11 @@ static int net_list_xactive_response(ActiveEntry *ce) char *old_group_name = cd->group_name; cd->group_name = MSG_GetFirstGroupNeedingExtraInfo(cd->host); /* make sure we're not stuck on the same group... */ - if (old_group_name && cd->group_name && PL_strcmp(old_group_name, cd->group_name)) + if (old_group_name && cd->group_name && XP_STRCMP(old_group_name, cd->group_name)) { - PR_Free(old_group_name); + XP_FREE(old_group_name); #ifdef DEBUG_bienvenu - PR_LogPrint("listing xactive for %s\n", cd->group_name); + XP_Trace("listing xactive for %s\n", cd->group_name); #endif cd->next_state = NNTP_LIST_XACTIVE; cd->pause_for_read = FALSE; @@ -3530,7 +3523,7 @@ static int net_list_group(ActiveEntry *ce) cd->group_name); MSG_InitAddArticleKeyToGroup(cd->pane, cd->host, cd->group_name, &cd->newsgroup_parse_state); - ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer)); + ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,XP_STRLEN(cd->output_buffer)); NNTP_LOG_WRITE(cd->output_buffer); cd->next_state = NNTP_RESPONSE; @@ -3545,7 +3538,7 @@ static int net_list_group_response(ActiveEntry *ce) NewsConData * cd = (NewsConData *)ce->con_data; - PR_ASSERT(cd->response_code == 211); + XP_ASSERT(cd->response_code == 211); if (cd->response_code != 211) { cd->next_state = NEWS_DONE; @@ -3623,7 +3616,7 @@ static int net_xpat_response (ActiveEntry *ce) else { /* set up the next term for next time around */ - char *nextTerm = PL_strchr(cd->current_search, '/'); + char *nextTerm = XP_STRCHR(cd->current_search, '/'); if (nextTerm) cd->current_search = ++nextTerm; else @@ -3640,7 +3633,7 @@ static int net_xpat_response (ActiveEntry *ce) PRIVATE int net_nntp_search(ActiveEntry *ce) { - PR_ASSERT(FALSE); + XP_ASSERT(FALSE); return 0; } @@ -3675,8 +3668,7 @@ PRIVATE int net_nntp_search_results (ActiveEntry *ce) return ce->status; /* no line yet */ if (ce->status < 0) { - ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, - PR_GetOSError()); + ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO); /* return TCP error */ return MK_TCP_READ_ERROR; } @@ -3767,7 +3759,7 @@ NET_parse_news_url (const char *url, FREEIF(host_and_port); PREF_CopyCharPref("network.hosts.nntp_server", &defhost); if (defhost && *defhost == '\0') { - PR_Free(defhost); + XP_FREE(defhost); defhost = NULL; } if (defhost) { @@ -3777,7 +3769,7 @@ NET_parse_news_url (const char *url, } if (defport) { host_and_port = PR_smprintf("%s:%ld", defhost, (long) defport); - PR_Free(defhost); + XP_FREE(defhost); } else { host_and_port = defhost; } @@ -3797,12 +3789,12 @@ NET_parse_news_url (const char *url, /* If a port was specified, but it was the default port, pretend it wasn't specified. */ - s = PL_strchr (host_and_port, ':'); + s = XP_STRCHR (host_and_port, ':'); if (s && sscanf (s+1, " %lu ", &port) == 1 && port == (secure_p ? SECURE_NEWS_PORT : NEWS_PORT)) *s = 0; - path_part = PL_strchr (url, ':'); - PR_ASSERT (path_part); + path_part = XP_STRCHR (url, ':'); + XP_ASSERT (path_part); if (!path_part) { status = -1; @@ -3812,14 +3804,14 @@ NET_parse_news_url (const char *url, if (path_part[0] == '/' && path_part[1] == '/') { /* Skip over host name. */ - path_part = PL_strchr (path_part + 2, '/'); + path_part = XP_STRCHR (path_part + 2, '/'); if (path_part) path_part++; } if (!path_part) path_part = ""; - group = PL_strdup (path_part); + group = XP_STRDUP (path_part); if (!group) { status = MK_OUT_OF_MEMORY; @@ -3835,7 +3827,7 @@ NET_parse_news_url (const char *url, Either way, there may be search data at the end. */ - s = PL_strchr (group, '@'); + s = XP_STRCHR (group, '@'); if (s) { message_id = group; @@ -3843,7 +3835,7 @@ NET_parse_news_url (const char *url, } else if (!*group) { - PR_Free (group); + XP_FREE (group); group = 0; } @@ -3861,7 +3853,7 @@ NET_parse_news_url (const char *url, if (message_id) { /* Move past the @. */ - PR_ASSERT (s && *s == '@'); + XP_ASSERT (s && *s == '@'); start = s; } else @@ -3875,7 +3867,7 @@ NET_parse_news_url (const char *url, break; if (*s) { - command_specific_data = PL_strdup (s); + command_specific_data = XP_STRDUP (s); *s = 0; if (!command_specific_data) { @@ -3887,18 +3879,18 @@ NET_parse_news_url (const char *url, /* Discard any now-empty strings. */ if (message_id && !*message_id) { - PR_Free (message_id); + XP_FREE (message_id); message_id = 0; } else if (group && !*group) { - PR_Free (group); + XP_FREE (group); group = 0; } } FAIL: - PR_ASSERT (!message_id || message_id != group); + XP_ASSERT (!message_id || message_id != group); if (status >= 0) { *host_and_portP = host_and_port; @@ -4080,6 +4072,26 @@ NET_IsNewsMessageURL (const char *url) return result; } +XP_Bool +NET_IsNewsServerURL( const char *url) +{ + char *host_and_port = 0; + XP_Bool secure_p = FALSE; + char *group = 0; + char *message_id = 0; + char *command_specific_data = 0; + int status = NET_parse_news_url (url, &host_and_port, &secure_p, + &group, &message_id, &command_specific_data); + XP_Bool result = FALSE; + if (status >= 0 && host_and_port && !group && !message_id) + result = TRUE; + FREEIF (host_and_port); + FREEIF (group); + FREEIF (message_id); + FREEIF (command_specific_data); + return result; +} + static XP_Bool nntp_are_connections_available (NNTPConnection *conn) { int connCount = 0; @@ -4091,7 +4103,7 @@ static XP_Bool nntp_are_connections_available (NNTPConnection *conn) { NNTP_LOG_NOTE(("nntp_are_connections_available: comparing %08lX %s", tmpConn, tmpConn->busy ? "busy" : "available")); - if (conn != tmpConn && tmpConn->busy && !PL_strcasecmp(tmpConn->hostname, conn->hostname)) + if (conn != tmpConn && tmpConn->busy && !strcasecomp(tmpConn->hostname, conn->hostname)) connCount++; } @@ -4107,12 +4119,12 @@ NET_OnlinePrefChangedFunc(const char *pref, void *data); MODULE_PRIVATE int PR_CALLBACK NET_OnlinePrefChangedFunc(const char *pref, void *data) { - int status; + int status=0; int32 port=0; char * socksHost = NULL; char text[MAXHOSTNAMELEN + 8]; - if (!PL_strcasecmp(pref,"network.online")) + if (!XP_STRCASECMP(pref,"network.online")) status = PREF_GetBoolPref("network.online", &isOnline); if ( isOnline ) { @@ -4144,8 +4156,8 @@ NET_NewsMaxArticlesChangedFunc(const char *pref, void *data); MODULE_PRIVATE int PR_CALLBACK NET_NewsMaxArticlesChangedFunc(const char *pref, void *data) { - int status; - if (!PL_strcasecmp(pref,"news.max_articles")) + int status=0; + if (!XP_STRCASECMP(pref,"news.max_articles")) { int32 maxArticles; @@ -4155,6 +4167,11 @@ MODULE_PRIVATE int PR_CALLBACK NET_NewsMaxArticlesChangedFunc(const char *pref, return status; } +MODULE_PRIVATE int PR_CALLBACK net_news_timeout_changed (const char *pref, void *data) +{ + return PREF_GetIntPref ("news.timeout", &net_news_timeout); +} + MODULE_PRIVATE XP_Bool NET_IsOffline() { @@ -4165,8 +4182,13 @@ NET_IsOffline() { /*int status =*/ PREF_GetBoolPref("network.online", &isOnline); PREF_RegisterCallback("network.online",NET_OnlinePrefChangedFunc, NULL); + /* because this routine gets called so often, we can register this callback here too. */ PREF_RegisterCallback("news.max_articles", NET_NewsMaxArticlesChangedFunc, NULL); + + PREF_GetIntPref ("news.timeout", &net_news_timeout); + PREF_RegisterCallback ("news.timeout", net_news_timeout_changed, NULL); + prefInitialized = TRUE; } return !isOnline; @@ -4176,7 +4198,7 @@ PRIVATE int32 net_NewsLoad (ActiveEntry *ce) { int status = 0; - NewsConData *cd = PR_NEW(NewsConData); + NewsConData *cd = XP_NEW(NewsConData); XP_Bool secure_p = FALSE; XP_Bool default_host = FALSE; char *url = ce->URL_s->address; @@ -4222,22 +4244,18 @@ net_NewsLoad (ActiveEntry *ce) goto FAIL; } - memset(cd, 0, sizeof(NewsConData)); + XP_MEMSET(cd, 0, sizeof(NewsConData)); ce->URL_s->content_length = 0; if(!ce->proxy_addr) { - /* look for an HTTPS proxy and use it if available, - * but the passed in one takes precedence - */ + /* look for an HTTPS proxy and use it if available, */ + /* but the passed in one takes precedence */ StrAllocCopy(ce->proxy_addr, NET_FindProxyHostForUrl(SECURE_HTTP_TYPE_URL, ce->URL_s->address)); } - else - { - StrAllocCopy(cd->ssl_proxy_server, ce->proxy_addr); - } + HG38737 cd->pane = ce->URL_s->msg_pane; if (!cd->pane) @@ -4245,17 +4263,23 @@ net_NewsLoad (ActiveEntry *ce) NNTP_LOG_NOTE(("NET_NewsLoad: url->msg_pane NULL for URL: %s\n", ce->URL_s->address)); cd->pane = MSG_FindPane(ce->window_id, MSG_ANYPANE); } - PR_ASSERT(cd->pane && MSG_GetContext(cd->pane) == ce->window_id); - if (!cd->pane) { + if (!cd->pane || MSG_GetContext(cd->pane) != ce->window_id) + { + XP_ASSERT(FALSE); status = -1; /* ### */ goto FAIL; } +#ifdef DEBUG + { + char urlDate[64]; + time_t now = XP_TIME(); + XP_StrfTime(ce->window_id, urlDate, sizeof(urlDate), XP_DATE_TIME_FORMAT, localtime(&now)); + NNTP_LOG_NOTE (("******** Begin loading news URL [ %s ] at %s", ce->URL_s->address, urlDate)); + } +#endif - - NNTP_LOG_NOTE (("NET_NewsLoad: %s", ce->URL_s->address)); - - cd->output_buffer = (char *) PR_Malloc(OUTPUT_BUFFER_SIZE); + cd->output_buffer = (char *) XP_ALLOC(OUTPUT_BUFFER_SIZE); if(!cd->output_buffer) { status = MK_OUT_OF_MEMORY; @@ -4266,7 +4290,7 @@ net_NewsLoad (ActiveEntry *ce) ce->socket = NULL; cd->article_num = -1; - PR_ASSERT (url); + XP_ASSERT (url); if (!url) { status = -1; @@ -4279,7 +4303,7 @@ net_NewsLoad (ActiveEntry *ce) if (status < 0) goto FAIL; - colon = PL_strchr (host_and_port, ':'); + colon = XP_STRCHR (host_and_port, ':'); if (colon) { unsigned long naturalLong; *colon = '\0'; @@ -4290,13 +4314,13 @@ net_NewsLoad (ActiveEntry *ce) secure_p, port); if (colon) *colon = ':'; - PR_ASSERT(cd->host); + XP_ASSERT(cd->host); if (!cd->host) { status = -1; goto FAIL; } - if (message_id && command_specific_data && !PL_strcmp (command_specific_data, "?cancel")) + if (message_id && command_specific_data && !XP_STRCMP (command_specific_data, "?cancel")) cancel_p = TRUE; StrAllocCopy(cd->path, message_id); @@ -4338,7 +4362,7 @@ net_NewsLoad (ActiveEntry *ce) status = -1; goto FAIL; } - PR_ASSERT (!group && !message_id && !command_specific_data); + XP_ASSERT (!group && !message_id && !command_specific_data); cd->type_wanted = NEWS_POST; StrAllocCopy(cd->path, ""); } @@ -4356,7 +4380,7 @@ net_NewsLoad (ActiveEntry *ce) } else if (command_specific_data) { - if (PL_strstr (command_specific_data, "?newgroups")) + if (XP_STRSTR (command_specific_data, "?newgroups")) /* news://HOST/?newsgroups news:/GROUP/?newsgroups (useless) news:?newsgroups (default host) @@ -4364,25 +4388,25 @@ net_NewsLoad (ActiveEntry *ce) cd->type_wanted = NEW_GROUPS; else { - if (PL_strstr(command_specific_data, "?list-pretty")) + if (XP_STRSTR(command_specific_data, "?list-pretty")) { cd->type_wanted = PRETTY_NAMES_WANTED; - cd->command_specific_data = PL_strdup(command_specific_data); + cd->command_specific_data = XP_STRDUP(command_specific_data); } - else if (PL_strstr(command_specific_data, "?profile")) + else if (XP_STRSTR(command_specific_data, "?profile")) { cd->type_wanted = PROFILE_WANTED; - cd->command_specific_data = PL_strdup(command_specific_data); + cd->command_specific_data = XP_STRDUP(command_specific_data); } - else if (PL_strstr(command_specific_data, "?list-ids")) + else if (XP_STRSTR(command_specific_data, "?list-ids")) { cd->type_wanted = IDS_WANTED; - cd->command_specific_data = PL_strdup(command_specific_data); + cd->command_specific_data = XP_STRDUP(command_specific_data); } else { cd->type_wanted = SEARCH_WANTED; - cd->command_specific_data = PL_strdup(command_specific_data); + cd->command_specific_data = XP_STRDUP(command_specific_data); cd->current_search = cd->command_specific_data; } } @@ -4394,12 +4418,13 @@ net_NewsLoad (ActiveEntry *ce) news://HOST/GROUP */ if (ce->window_id->type != MWContextNews && ce->window_id->type != MWContextNewsMsg + && ce->window_id->type != MWContextMailMsg && ce->window_id->type != MWContextMail && ce->window_id->type != MWContextMailNewsProgress) { status = -1; goto FAIL; } - if (PL_strchr (group, '*')) + if (XP_STRCHR (group, '*')) cd->type_wanted = LIST_WANTED; else cd->type_wanted = GROUP_WANTED; @@ -4461,19 +4486,36 @@ net_NewsLoad (ActiveEntry *ce) /* if the hostnames match up exactly and the connection * is not busy at the moment then reuse this connection. */ - if(!PL_strcmp(tmp_con->hostname, host_and_port) + if(!XP_STRCMP(tmp_con->hostname, host_and_port) && secure_p == tmp_con->secure &&!tmp_con->busy) { - cd->control_con = tmp_con; + /* check to see if this connection can be reused, or if it should be aged away */ + int32 con_age = XP_TIME() - tmp_con->last_used_time; + if (con_age <= net_news_timeout) + { + cd->control_con = tmp_con; - NNTP_LOG_NOTE(("Reusing control_con %08lX for %s", tmp_con, url)); + NNTP_LOG_NOTE(("Reusing control_con %08lX (age %ld secs) for %s", tmp_con, con_age, url)); - /* set select on the control socket */ - ce->socket = cd->control_con->csock; - NET_SetReadSelect(ce->window_id, cd->control_con->csock); - cd->control_con->prev_cache = TRUE; /* this was from the cache */ - break; + /* set select on the control socket */ + ce->socket = cd->control_con->csock; + NET_SetReadSelect(ce->window_id, cd->control_con->csock); + cd->control_con->prev_cache = TRUE; /* this was from the cache */ + break; + } + else + { + NNTP_LOG_NOTE(("Aging away control_con %08lX (age %ld secs)", tmp_con, con_age)); + + /* kill idle conn which has lived too long */ + list_entry = list_entry->prev ? list_entry->prev : nntp_connection_list; + XP_ListRemoveObject(nntp_connection_list, tmp_con); + net_nntp_close(tmp_con->csock, ce->status); + FREEIF(tmp_con->current_group); + FREEIF(tmp_con->hostname); + XP_FREE(tmp_con); + } } } } @@ -4498,14 +4540,14 @@ net_NewsLoad (ActiveEntry *ce) /* build a control connection structure so we * can store the data as we go along */ - cd->control_con = PR_NEW(NNTPConnection); + cd->control_con = XP_NEW(NNTPConnection); if(!cd->control_con) { status = MK_OUT_OF_MEMORY; goto FAIL; } NNTP_LOG_NOTE(("Created control_con %08lX for %s", cd->control_con, ce->URL_s->address)); - memset(cd->control_con, 0, sizeof(NNTPConnection)); + XP_MEMSET(cd->control_con, 0, sizeof(NNTPConnection)); cd->control_con->default_host = default_host; StrAllocCopy(cd->control_con->hostname, host_and_port); @@ -4517,6 +4559,8 @@ net_NewsLoad (ActiveEntry *ce) cd->control_con->csock = NULL; + cd->control_con->last_used_time = XP_TIME(); + /* define this to test support for older NNTP servers * that don't support the XOVER command */ @@ -4555,7 +4599,7 @@ net_NewsLoad (ActiveEntry *ce) net_nntp_close (con->csock, status); FREEIF(con->current_group); FREEIF(con->hostname); - PR_Free(con); + XP_FREE(con); break; } } @@ -4683,13 +4727,12 @@ net_ProcessNews (ActiveEntry *ce) if (nntp_are_connections_available(cd->control_con)) { ce->status = NET_BeginConnect( - (cd->ssl_proxy_server && cd->control_con->secure ? - cd->ssl_proxy_server :cd->control_con->hostname), + (HG29239 cd->control_con->hostname), NULL, "NNTP", - (cd->control_con->secure ? SECURE_NEWS_PORT : NEWS_PORT), + (HG28287 NEWS_PORT), &ce->socket, - (cd->ssl_proxy_server ? FALSE : cd->control_con->secure), + HG22999 &cd->tcp_con_data, ce->window_id, &ce->URL_s->error_msg, @@ -4738,10 +4781,9 @@ HG33086 case NNTP_CONNECT_WAIT: ce->status = NET_FinishConnect( - (cd->ssl_proxy_server && cd->control_con->secure ? - cd->ssl_proxy_server : cd->control_con->hostname), + (HG93230 cd->control_con->hostname), "NNTP", - (cd->control_con->secure ? SECURE_NEWS_PORT : NEWS_PORT), + (HG29399 NEWS_PORT), &ce->socket, &cd->tcp_con_data, ce->window_id, @@ -4937,9 +4979,9 @@ HG68092 else { if (line[0] == '.') - PL_strcpy (cd->output_buffer, line + 1); + XP_STRCPY (cd->output_buffer, line + 1); else - PL_strcpy (cd->output_buffer, line); + XP_STRCPY (cd->output_buffer, line); /* When we're sending this line to a converter (ie, it's a message/rfc822) use the local line termination @@ -4949,11 +4991,11 @@ HG68092 the local system will convert that to the local line terminator as it is read. */ - PL_strcat (cd->output_buffer, LINEBREAK); + XP_STRCAT (cd->output_buffer, LINEBREAK); /* Don't send content-type to mime parser if we're doing a cancel because it confuses mime parser into not parsing. */ - if (cd->type_wanted != CANCEL_WANTED || PL_strncmp(cd->output_buffer, "Content-Type:", 13)) + if (cd->type_wanted != CANCEL_WANTED || XP_STRNCMP(cd->output_buffer, "Content-Type:", 13)) ce->status = PUTSTRING(cd->output_buffer); } break; @@ -4982,7 +5024,7 @@ HG68092 case NEWS_PROCESS_XOVER: case NEWS_PROCESS_BODIES: - if (cd->group_name && PL_strstr (cd->group_name, ".emacs")) + if (cd->group_name && XP_STRSTR (cd->group_name, ".emacs")) /* This is a joke! Don't internationalize it... */ NET_Progress(ce->window_id, "Garbage collecting..."); else @@ -5165,6 +5207,7 @@ HG68092 { FREEIF(cd->control_con->hostname); FREE(cd->control_con); + cd->control_con = NULL; } } break; @@ -5172,18 +5215,19 @@ HG68092 case NEWS_FREE: /* do we need to know if we're parsing xover to call finish xover? */ + /* yes, I think we do! Why did I think we should??? */ /* If we've gotten to NEWS_FREE and there is still XOVER data, there was an error or we were interrupted or something. So, tell libmsg there was an abnormal exit so that it can free its data. */ -/* if (cd->xover_parse_state != NULL)*/ + if (cd->xover_parse_state != NULL) { int status; -/* PR_ASSERT (ce->status < 0);*/ +/* XP_ASSERT (ce->status < 0);*/ status = MSG_FinishXOVER (cd->pane, &cd->xover_parse_state, ce->status); - PR_ASSERT (!cd->xover_parse_state); + XP_ASSERT (!cd->xover_parse_state); if (ce->status >= 0 && status < 0) ce->status = status; } @@ -5195,7 +5239,7 @@ HG68092 FREEIF(cd->data_buf); FREEIF(cd->output_buffer); - FREEIF(cd->ssl_proxy_server); + HG20900 FREEIF(cd->stream); /* don't forget the stream */ if(cd->tcp_con_data) NET_FreeTCPConData(cd->tcp_con_data); @@ -5212,7 +5256,7 @@ HG68092 ce->URL_s, cd->original_content_length, ce->bytes_received); - PR_Free(cd); + XP_FREE(cd); /* gate the maximum number of cached connections * but don't delete busy ones. @@ -5234,7 +5278,7 @@ HG68092 net_nntp_close(con->csock, ce->status); FREEIF(con->current_group); FREEIF(con->hostname); - PR_Free(con); + XP_FREE(con); break; } } diff --git a/mozilla/network/protocol/nntp/mknewsgr.h b/mozilla/network/protocol/nntp/mknewsgr.h index 1dc9dfefb02..1473f044ac6 100644 --- a/mozilla/network/protocol/nntp/mknewsgr.h +++ b/mozilla/network/protocol/nntp/mknewsgr.h @@ -29,15 +29,15 @@ * will munge the newsgroup name passed in for efficiency * */ -extern void NET_StoreNewsGroup(char *hostname, PRBool is_secure, char * newsgroup); +extern void NET_StoreNewsGroup(char *hostname, XP_Bool is_secure, char * newsgroup); /* free up the list of newsgroups */ -extern void NET_FreeNewsgroups(char *hostname, PRBool is_secure); +extern void NET_FreeNewsgroups(char *hostname, XP_Bool is_secure); /* sort the newsgroups */ -extern void NET_SortNewsgroups(char *hostname, PRBool is_secure); +extern void NET_SortNewsgroups(char *hostname, XP_Bool is_secure); /* Search and display newsgroups */ @@ -48,15 +48,16 @@ extern int NET_DisplayNewsgroups(MWContext *context, /* Save them out to disk */ -extern void NET_SaveNewsgroupsToDisk(char *hostname, PRBool is_secure); +extern void NET_SaveNewsgroupsToDisk(char *hostname, XP_Bool is_secure); /* read them from disk */ -extern time_t NET_ReadNewsgroupsFromDisk(char *hostname, PRBool is_secure); +extern time_t NET_ReadNewsgroupsFromDisk(char *hostname, XP_Bool is_secure); /* Get the last access date, load the newsgroups from * disk if they are not loaded */ -extern time_t NET_NewsgroupsLastUpdatedTime(char *hostname, PRBool is_secure); +extern time_t NET_NewsgroupsLastUpdatedTime(char *hostname, XP_Bool is_secure); #endif + diff --git a/mozilla/network/protocol/pop3/Makefile b/mozilla/network/protocol/pop3/Makefile new file mode 100644 index 00000000000..4f8fa42f8a6 --- /dev/null +++ b/mozilla/network/protocol/pop3/Makefile @@ -0,0 +1,33 @@ +#!gmake +# +# The contents of this file are subject to the Netscape Public License +# Version 1.0 (the "NPL"); you may not use this file except in +# compliance with the NPL. You may obtain a copy of the NPL at +# http://www.mozilla.org/NPL/ +# +# Software distributed under the NPL is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL +# for the specific language governing rights and limitations under the +# NPL. +# +# The Initial Developer of this code under the NPL is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All Rights +# Reserved. + +DEPTH = ../../.. + +MODULE = pop3url +LIBRARY_NAME = pop3url + +CSRCS = \ + mkpop3.c \ + $(NULL) + +EXPORTS= mkpop3.h + +REQUIRES = netcache network nspr2 dbm util pref js java fileurl \ + security layer img httpurl mimetype htmldlgs + +include $(DEPTH)/config/rules.mk + diff --git a/mozilla/network/protocol/pop3/makefile.win b/mozilla/network/protocol/pop3/makefile.win new file mode 100644 index 00000000000..24715ba8fe5 --- /dev/null +++ b/mozilla/network/protocol/pop3/makefile.win @@ -0,0 +1,86 @@ +#!gmake +# +# The contents of this file are subject to the Netscape Public License +# Version 1.0 (the "NPL"); you may not use this file except in +# compliance with the NPL. You may obtain a copy of the NPL at +# http://www.mozilla.org/NPL/ +# +# Software distributed under the NPL is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL +# for the specific language governing rights and limitations under the +# NPL. +# +# The Initial Developer of this code under the NPL is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All Rights +# Reserved. + +IGNORE_MANIFEST=1 +# + +#------------------------------------------------------------------------ +# +# Makefile to build the Network file protocol LIB +# +#------------------------------------------------------------------------ + + +# +# Make sure we have MOZILLA_CLIENT defined so we get the +# proper JS includes +# +LCFLAGS = $(LCFLAGS) -DMOZILLA_CLIENT + +!ifdef BUILD_DEBUG_GC +LCFLAGS = $(LCFLAGS) -DDEBUG_GC +!endif + +LLIBS= \ + $(NULL) +MISCDEP=$(LLIBS) +OBJS= \ + .\$(OBJDIR)\mkpop3.obj \ + $(NULL) + +CSRCS = \ + mkpop3.c \ + $(NULL) + + +LIBRARY_NAME=pop3url +MODULE=pop3url +DEPTH=..\..\.. + +LOCAL_INCLUDES=-I. -I$(DEPTH)/dist/public/zlib -I$(DEPTH)/dist/public/parse +INCLUDES = $(LOCAL_INCLUDES) + + +EXTRA_LIBS= + +REQUIRES= network ldap +EXPORTS= mkpop3.h + +# use LINCS on win32 for now since REQUIRES seems to be broken +#!if "$(MOZ_BITS)" != "16" +LINCS= \ + -I$(PUBLIC)\nspr2 \ + -I$(PUBLIC)\util \ + -I$(PUBLIC)\java \ + -I$(PUBLIC)\pref \ + -I$(PUBLIC)\htmldlgs \ + -I$(PUBLIC)\js \ + -I$(PUBLIC)\security \ + -I$(PUBLIC)\netcache \ + -I$(PUBLIC)\network \ + -I$(PUBLIC)\ldap \ + -I$(PUBLIC)\mimetype \ +#!endif + +include <$(DEPTH)\config\rules.mak> + +libs:: $(LIBRARY) + $(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib + +symbols:: + @echo "LIBRARY_NAME is $(LIBRARY_NAME)" + @echo "LIBRARY is $(LIBRARY)" diff --git a/mozilla/network/protocol/pop3/mkpop3.c b/mozilla/network/protocol/pop3/mkpop3.c index 9b61c9f6659..c47a5fc27c5 100644 --- a/mozilla/network/protocol/pop3/mkpop3.c +++ b/mozilla/network/protocol/pop3/mkpop3.c @@ -23,7 +23,9 @@ */ /* Please leave outside of ifdef for windows precompiled headers */ +#include "rosetta.h" #include "mkutils.h" +#include "mktcp.h" /* A more guaranteed way of making sure that we never get duplicate messages is to always get each message's UIDL (if the server supports it) @@ -52,8 +54,9 @@ and change the POP3_QUIT_RESPONSE state to flush the newly committed deletes. */ #include "msgcom.h" #include "msgnet.h" #include "secnav.h" -#include "ssl.h" +#include HG09438 +#include "xp_error.h" #include "xpgetstr.h" #include "prefapi.h" @@ -81,7 +84,11 @@ extern int XP_THE_PREVIOUSLY_ENTERED_PASSWORD_IS_INVALID_ETC; extern int XP_CONNECT_HOST_CONTACTED_SENDING_LOGIN_INFORMATION; extern int XP_PASSWORD_FOR_POP3_USER; extern int MK_MSG_DOWNLOAD_COUNT; +extern int MK_UNABLE_TO_CONNECT; +extern int MK_CONNECTION_REFUSED; +extern int MK_CONNECTION_TIMED_OUT; +extern void net_graceful_shutdown(PRFileDesc * sock, XP_Bool isSecure); #define POP3_PORT 110 /* the iana port for pop3 */ #define OUTPUT_BUFFER_SIZE 512 /* max size of command string */ @@ -90,7 +97,7 @@ extern int MK_MSG_DOWNLOAD_COUNT; PRIVATE char *net_pop3_username=0; PRIVATE char *net_pop3_password=0; /* obfuscated pop3 password */ PRIVATE XP_Bool net_pop3_password_pending=FALSE; -PRIVATE XP_Bool net_pop3_block_biff = FALSE; +PRIVATE XP_Bool net_pop3_block = FALSE; /* We set this if we find that the UIDL command doesn't work. It is reset to FALSE if the user name is re-set (a cheap way @@ -173,6 +180,7 @@ typedef enum { #define KEEP 'k' /* If we want to keep this item on server. */ #define DELETE_CHAR 'd' /* If we want to delete this item. */ +#define TOO_BIG 'b' /* item left on server because it was too big */ typedef struct Pop3AllocedString { /* Need this silliness as a place to keep the strings that are allocated @@ -275,13 +283,10 @@ typedef struct _Pop3ConData { char *sender_info; } Pop3ConData; -/* forward decl */ -PRIVATE int32 net_ProcessPop3 (ActiveEntry *ce); - PUBLIC void NET_LeavePopMailOnServer(Bool do_it) { - /* PR_ASSERT(0);*/ /* This routine is obsolete. */ + /* XP_ASSERT(0);*/ /* This routine is obsolete. */ } /* Well, someone finally found a legitimate reason to put an @ in the @@ -322,7 +327,7 @@ NET_SetPopUsername(const char *username) */ if (!net_allow_at_sign_in_mail_user_name) { - if (net_pop3_username != NULL) at = PL_strchr(net_pop3_username, '@'); + if (net_pop3_username != NULL) at = XP_STRCHR(net_pop3_username, '@'); if (at != NULL) *at = '\0'; } } @@ -349,7 +354,7 @@ net_get_pop3_password(void) PUBLIC void NET_SetPopPassword(const char *password) { - if (password && PL_strlen(password)) + if (password && XP_STRLEN(password)) StrAllocCopy(net_pop3_password, password); else FREEIF(net_pop3_password); @@ -378,13 +383,13 @@ NET_SetPopPassword2(const char *password) PUBLIC void NET_SetPopMsgSizeLimit(int32 size) { - PR_ASSERT(0); /* This routine is obsolete */ + XP_ASSERT(0); /* This routine is obsolete */ } PUBLIC int32 NET_GetPopMsgSizeLimit(void) { - PR_ASSERT(0); /* This routine is obsolete */ + XP_ASSERT(0); /* This routine is obsolete */ return -1; } @@ -392,8 +397,8 @@ NET_GetPopMsgSizeLimit(void) static int uidl_cmp (const void *obj1, const void *obj2) { - PR_ASSERT (obj1 && obj2); - return PL_strcmp ((char *) obj1, (char *) obj2); + XP_ASSERT (obj1 && obj2); + return XP_STRCMP ((char *) obj1, (char *) obj2); } @@ -402,15 +407,16 @@ put_hash(Pop3UidlHost* host, XP_HashTable table, const char* key, char value) { Pop3AllocedString* tmp; int v = value; - tmp = PR_NEWZAP(Pop3AllocedString); + + tmp = XP_NEW_ZAP(Pop3AllocedString); if (tmp) { - tmp->str = PL_strdup(key); + tmp->str = XP_STRDUP(key); if (tmp->str) { tmp->next = host->strings; host->strings = tmp; XP_Puthash(table, tmp->str, (void*) v); } else { - PR_Free(tmp); + XP_FREE(tmp); } } } @@ -427,21 +433,22 @@ PRIVATE Pop3UidlHost* net_pop3_load_state(const char* searchhost, Pop3UidlHost* result = NULL; Pop3UidlHost* current = NULL; Pop3UidlHost* tmp; - result = PR_NEWZAP(Pop3UidlHost); + + result = XP_NEW_ZAP(Pop3UidlHost); if (!result) return NULL; - result->host = PL_strdup(searchhost); - result->user = PL_strdup(searchuser); + result->host = XP_STRDUP(searchhost); + result->user = XP_STRDUP(searchuser); result->hash = XP_HashTableNew(20, XP_StringHash, uidl_cmp); if (!result->host || !result->user || !result->hash) { FREEIF(result->host); FREEIF(result->user); if (result->hash) XP_HashTableDestroy(result->hash); - PR_Free(result); + XP_FREE(result); return NULL; } file = XP_FileOpen("", xpMailPopState, XP_FILE_READ); if (!file) return result; - buf = (char*)PR_Malloc(512); + buf = (char*)XP_ALLOC(512); if (buf) { while (XP_FileReadLine(buf, 512, file)) { if (*buf == '#' || *buf == CR || *buf == LF || *buf == 0) @@ -449,27 +456,27 @@ PRIVATE Pop3UidlHost* net_pop3_load_state(const char* searchhost, if (buf[0] == '*') { /* It's a host&user line. */ current = NULL; - host = strtok(buf + 1, " \t" LINEBREAK); - user = strtok(NULL, " \t" LINEBREAK); + host = XP_STRTOK(buf + 1, " \t" LINEBREAK); + user = XP_STRTOK(NULL, " \t" LINEBREAK); if (host == NULL || user == NULL) continue; for (tmp = result ; tmp ; tmp = tmp->next) { - if (PL_strcmp(host, tmp->host) == 0 && - PL_strcmp(user, tmp->user) == 0) { + if (XP_STRCMP(host, tmp->host) == 0 && + XP_STRCMP(user, tmp->user) == 0) { current = tmp; break; } } if (!current) { - current = PR_NEWZAP(Pop3UidlHost); + current = XP_NEW_ZAP(Pop3UidlHost); if (current) { - current->host = PL_strdup(host); - current->user = PL_strdup(user); + current->host = XP_STRDUP(host); + current->user = XP_STRDUP(user); current->hash = XP_HashTableNew(20, XP_StringHash, uidl_cmp); if (!current->host || !current->user || !current->hash) { FREEIF(current->host); FREEIF(current->user); if (current->hash) XP_HashTableDestroy(current->hash); - PR_Free(current); + XP_FREE(current); } else { current->next = result->next; result->next = current; @@ -479,18 +486,18 @@ PRIVATE Pop3UidlHost* net_pop3_load_state(const char* searchhost, } else { /* It's a line with a UIDL on it. */ if (current) { - flags = strtok(buf, " \t" LINEBREAK); - uidl = strtok(NULL, " \t" LINEBREAK); + flags = XP_STRTOK(buf, " \t" LINEBREAK); + uidl = XP_STRTOK(NULL, " \t" LINEBREAK); if (flags && uidl) { - PR_ASSERT(flags[0] == KEEP || flags[0] == DELETE_CHAR); - if (flags[0] == KEEP || flags[0] == DELETE_CHAR) { + XP_ASSERT((flags[0] == KEEP) || (flags[0] == DELETE_CHAR) || (flags[0] == TOO_BIG)); + if ((flags[0] == KEEP) || (flags[0] == DELETE_CHAR) || (flags[0] == TOO_BIG)) { put_hash(current, current->hash, uidl, flags[0]); } } } } } - PR_Free(buf); + XP_FREE(buf); } XP_FileClose(file); return result; @@ -520,8 +527,9 @@ net_pop3_write_mapper(XP_HashTable hash, const void* key, void* value, void* closure) { XP_File file = (XP_File) closure; - PR_ASSERT(value == ((void *) (int) KEEP) || - value == ((void *) (int) DELETE_CHAR)); + XP_ASSERT((value == ((void *) (int) KEEP)) || + (value == ((void *) (int) DELETE_CHAR)) || + (value == ((void *) (int) TOO_BIG))); XP_FilePrintf(file, "%c %s" LINEBREAK, (char)(long)value, (char*) key); return TRUE; } @@ -531,25 +539,85 @@ PRIVATE void net_pop3_write_state(Pop3UidlHost* host) { XP_File file; + int32 len = 0; + file = XP_FileOpen("", xpMailPopState, XP_FILE_WRITE_BIN); if (!file) return; - XP_FileWrite("# Netscape POP3 State File" LINEBREAK + len = XP_FileWrite("# Netscape POP3 State File" LINEBREAK "# This is a generated file! Do not edit." LINEBREAK LINEBREAK, -1, file); - for (; host ; host = host->next) { - if (!hash_empty(host->hash)) { - XP_FileWrite("*", 1, file); - XP_FileWrite(host->host, -1, file); - XP_FileWrite(" ", 1, file); - XP_FileWrite(host->user, -1, file); - XP_FileWrite(LINEBREAK, LINEBREAK_LEN, file); - XP_Maphash(host->hash, net_pop3_write_mapper, file); + for (; host && (len >= 0); host = host->next) + { + if (!hash_empty(host->hash)) + { + XP_FileWrite("*", 1, file); + XP_FileWrite(host->host, -1, file); + XP_FileWrite(" ", 1, file); + XP_FileWrite(host->user, -1, file); + len = XP_FileWrite(LINEBREAK, LINEBREAK_LEN, file); + XP_Maphash(host->hash, net_pop3_write_mapper, file); } } XP_FileClose(file); } +/* +Wrapper routines for POP data. The following routines are used from MSG_FolderInfoMail to +allow deleting of messages that have been kept on a POP3 server due to their size or +a preference to keep the messages on the server. When "deleting" messages we load +our state file, mark any messages we have for deletion and then re-save the state file. +*/ +extern char* ReadPopData(char *name); +extern void SavePopData(char *data); +extern void net_pop3_delete_if_in_server(char *data, char *uidl, XP_Bool *changed); +extern void KillPopData(char* data); + + +char* ReadPopData(char *name) +{ + Pop3UidlHost *uidlHost = NULL; + + if(!net_pop3_username || !*net_pop3_username) + return (char*) uidlHost; + + uidlHost = net_pop3_load_state(name, net_pop3_username); + return (char*) uidlHost; +} + +void SavePopData(char *data) +{ + Pop3UidlHost *host = (Pop3UidlHost*) data; + + if (!host) + return; + net_pop3_write_state(host); +} + + +/* +Look for a specific UIDL string in our hash tables, if we have it then we need +to mark the message for deletion so that it can be deleted later. If the uidl of the +message is not found, then the message was downloaded completly and already deleted +from the server. So this only applies to messages kept on the server or too big +for download. */ + +void net_pop3_delete_if_in_server(char *data, char *uidl, XP_Bool *changed) +{ + Pop3UidlHost *host = (Pop3UidlHost*) data; + + if (!host) + return; + if (XP_Gethash (host->hash, (const void*) uidl, 0)) + { + XP_Puthash(host->hash, uidl, (void*) DELETE_CHAR); + *changed = TRUE; + } +} + + + + PRIVATE void net_pop3_free_state(Pop3UidlHost* host) { @@ -558,22 +626,28 @@ net_pop3_free_state(Pop3UidlHost* host) Pop3AllocedString* next; while (host) { h = host->next; - PR_Free(host->host); - PR_Free(host->user); + XP_FREE(host->host); + XP_FREE(host->user); XP_HashTableDestroy(host->hash); tmp = host->strings; while (tmp) { next = tmp->next; - PR_Free(tmp->str); - PR_Free(tmp); + XP_FREE(tmp->str); + XP_FREE(tmp); tmp = next; } - PR_Free(host); + XP_FREE(host); host = h; } } +void KillPopData(char* data) +{ + if (!data) + return; + net_pop3_free_state((Pop3UidlHost*) data); +} PRIVATE void net_pop3_free_msg_info(ActiveEntry* ce) @@ -582,9 +656,11 @@ net_pop3_free_msg_info(ActiveEntry* ce) int i; if (cd->msg_info) { for (i=0 ; inumber_of_messages ; i++) { - if (cd->msg_info[i].uidl) PR_Free(cd->msg_info[i].uidl); + if (cd->msg_info[i].uidl) + XP_FREE(cd->msg_info[i].uidl); + cd->msg_info[i].uidl = NULL; } - PR_Free(cd->msg_info); + XP_FREE(cd->msg_info); cd->msg_info = NULL; } } @@ -640,7 +716,7 @@ net_pop3_wait_for_start_of_connection_response(ActiveEntry * ce) if(*line == '+') { cd->command_succeeded = TRUE; - if(PL_strlen(line) > 4) + if(XP_STRLEN(line) > 4) StrAllocCopy(cd->command_response, line+4); else StrAllocCopy(cd->command_response, line); @@ -698,7 +774,7 @@ net_pop3_wait_for_response(ActiveEntry * ce) if(*line == '+') { cd->command_succeeded = TRUE; - if(PL_strlen(line) > 4) + if(XP_STRLEN(line) > 4) StrAllocCopy(cd->command_response, line+4); else StrAllocCopy(cd->command_response, line); @@ -706,7 +782,7 @@ net_pop3_wait_for_response(ActiveEntry * ce) else { cd->command_succeeded = FALSE; - if(PL_strlen(line) > 5) + if(XP_STRLEN(line) > 5) StrAllocCopy(cd->command_response, line+5); else StrAllocCopy(cd->command_response, line); @@ -741,13 +817,13 @@ net_pop3_send_command(ActiveEntry *ce, const char * command) status = (int) NET_BlockingWrite(ce->socket, command, - PL_strlen(command)); + XP_STRLEN(command)); TRACEMSG(("Pop3 Tx: %s", cd->output_buffer)); if(status < 0) { - ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_WRITE_ERROR, PR_GetOSError()); + ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_WRITE_ERROR, SOCKET_ERRNO); cd->next_state = POP3_ERROR_DONE; return(MK_TCP_WRITE_ERROR); } @@ -811,7 +887,7 @@ net_pop3_auth_response(ActiveEntry *ce) } else if (ce->status < 0) { - int rv = PR_GetOSError(); + int rv = SOCKET_ERRNO; TRACEMSG(("TCP Error: %d", rv)); @@ -826,7 +902,7 @@ net_pop3_auth_response(ActiveEntry *ce) TRACEMSG((" Rx: %s", line)); - if (!PL_strcmp(line, ".")) + if (!XP_STRCMP(line, ".")) { /* now that we've read all the AUTH responses, decide which * state to go to next @@ -837,7 +913,7 @@ net_pop3_auth_response(ActiveEntry *ce) cd->next_state = POP3_SEND_USERNAME; cd->pause_for_read = FALSE; /* don't pause */ } - else if (!PL_strcasecmp (line, "LOGIN")) + else if (!XP_STRCASECMP (line, "LOGIN")) pop3CapabilityFlags |= POP3_HAS_AUTH_LOGIN; return 0; @@ -900,12 +976,12 @@ net_pop3_send_username(ActiveEntry *ce) if (POP3_HAS_AUTH_LOGIN & pop3CapabilityFlags) { char * str = NET_Base64Encode(net_pop3_username, - PL_strlen(net_pop3_username)); + XP_STRLEN(net_pop3_username)); if (str) { PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "%.256s" CRLF, str); - PR_FREEIF(str); + XP_FREEIF(str); } else { @@ -940,12 +1016,12 @@ net_pop3_send_password(ActiveEntry *ce) if (POP3_HAS_AUTH_LOGIN & pop3CapabilityFlags) { char * str = - NET_Base64Encode(password, PL_strlen(password)); + NET_Base64Encode(password, XP_STRLEN(password)); if (str) { PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "%.256s" CRLF, str); - PR_FREEIF(str); + XP_FREEIF(str); } else { @@ -956,8 +1032,8 @@ net_pop3_send_password(ActiveEntry *ce) PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "PASS %.256s" CRLF, password); } - memset(password, 0, PL_strlen(password)); - PR_Free(password); + XP_MEMSET(password, 0, XP_STRLEN(password)); + XP_FREE(password); if (cd->get_url) cd->next_state_after_response = POP3_SEND_GURL; @@ -999,7 +1075,7 @@ net_pop3_send_stat_or_gurl(ActiveEntry *ce, XP_Bool sendStat) /* clear the bogus password in case * we need to sync with auth smtp password */ - PR_FREEIF(net_pop3_password); + XP_FREEIF(net_pop3_password); return 0; } else if (net_pop3_password_pending) @@ -1015,11 +1091,11 @@ net_pop3_send_stat_or_gurl(ActiveEntry *ce, XP_Bool sendStat) } if (sendStat) { - PL_strcpy(cd->output_buffer, "STAT" CRLF); + XP_STRCPY(cd->output_buffer, "STAT" CRLF); cd->next_state_after_response = POP3_GET_STAT; } else { - PL_strcpy(cd->output_buffer, "GURL" CRLF); + XP_STRCPY(cd->output_buffer, "GURL" CRLF); cd->next_state_after_response = POP3_GURL_RESPONSE; } @@ -1051,11 +1127,11 @@ net_pop3_get_stat(ActiveEntry *ce) * * grab the first and second arg of stat response */ - num = strtok(cd->command_response, " "); + num = XP_STRTOK(cd->command_response, " "); cd->number_of_messages = atol(num); - num = strtok(NULL, " "); + num = XP_STRTOK(NULL, " "); cd->total_folder_size = (int32) atol(num); @@ -1089,20 +1165,20 @@ net_pop3_get_stat(ActiveEntry *ce) } } + /* #ifdef POP_ALWAYS_USE_UIDL_FOR_DUPLICATES if (net_uidl_command_unimplemented && net_xtnd_xlst_command_unimplemented && net_top_command_unimplemented) #else if ((net_uidl_command_unimplemented && net_xtnd_xlst_command_unimplemented && net_top_command_unimplemented) || (!cd->only_uidl && !cd->leave_on_server && (cd->size_limit < 0 || net_top_command_unimplemented))) -#endif +#endif */ /* We don't need message size or uidl info; go directly to getting messages. */ - { - cd->next_state = POP3_GET_MSG; - } else { - cd->next_state = POP3_SEND_LIST; - } + /* cd->next_state = POP3_GET_MSG; + else */ /* Fix bug 51262 where pop messages kept on server are re-downloaded after unchecking keep on server */ + + cd->next_state = POP3_SEND_LIST; return 0; } @@ -1144,11 +1220,12 @@ PRIVATE int net_pop3_send_list(ActiveEntry *ce) { Pop3ConData * cd = (Pop3ConData *)ce->con_data; - cd->msg_info = (Pop3MsgInfo *) PR_Malloc(sizeof(Pop3MsgInfo) * + cd->msg_info = (Pop3MsgInfo *) XP_ALLOC(sizeof(Pop3MsgInfo) * cd->number_of_messages); - if (!cd->msg_info) return(MK_OUT_OF_MEMORY); - memset(cd->msg_info, 0, sizeof(Pop3MsgInfo) * cd->number_of_messages); - PL_strcpy(cd->output_buffer, "LIST" CRLF); + if (!cd->msg_info) + return(MK_OUT_OF_MEMORY); + XP_MEMSET(cd->msg_info, 0, sizeof(Pop3MsgInfo) * cd->number_of_messages); + XP_STRCPY(cd->output_buffer, "LIST" CRLF); cd->next_state_after_response = POP3_GET_LIST; return(net_pop3_send_command(ce, cd->output_buffer)); } @@ -1212,17 +1289,17 @@ net_pop3_get_list(ActiveEntry *ce) * * list data is terminated by a ".CRLF" line */ - if(!PL_strcmp(line, ".")) + if(!XP_STRCMP(line, ".")) { cd->next_state = POP3_SEND_UIDL_LIST; cd->pause_for_read = FALSE; return(0); } - msg_num = atol(strtok(line, " ")); + msg_num = atol(XP_STRTOK(line, " ")); if(msg_num <= cd->number_of_messages && msg_num > 0) - cd->msg_info[msg_num-1].size = atol(strtok(NULL, " ")); + cd->msg_info[msg_num-1].size = atol(XP_STRTOK(NULL, " ")); return(0); } @@ -1374,7 +1451,7 @@ get_fake_uidl_top(ActiveEntry *ce) /* remove CRLF */ XP_StripLine(line); - if(!PL_strcmp(line, ".")) + if(!XP_STRCMP(line, ".")) { cd->current_msg_to_top--; if (!cd->current_msg_to_top || @@ -1401,12 +1478,15 @@ get_fake_uidl_top(ActiveEntry *ce) { /* we are looking for a string of the form Message-Id: <199602071806.KAA14787@neon.netscape.com> */ - char *firstToken = strtok(line, " "); - if (firstToken && !PL_strcasecmp(firstToken, "MESSAGE-ID:") ) + char *firstToken = XP_STRTOK(line, " "); + int state = 0; + + if (firstToken && !XP_STRCASECMP(firstToken, "MESSAGE-ID:") ) { - char *message_id_token = strtok(NULL, " "); - if ( !cd->only_uidl && message_id_token && - (((int) XP_Gethash(cd->uidlinfo->hash, message_id_token, 0) ) == 0) ) + char *message_id_token = XP_STRTOK(NULL, " "); + state = (int) XP_Gethash(cd->uidlinfo->hash, message_id_token, 0); + + if (!cd->only_uidl && message_id_token && (state == 0)) { /* we have not seen this message before */ /* if we are only doing a biff, stop here */ @@ -1418,24 +1498,27 @@ get_fake_uidl_top(ActiveEntry *ce) } else /* we will retrieve it and cache it in GET_MSG */ { - cd->number_of_messages_not_seen_before++; - cd->msg_info[cd->current_msg_to_top-1].uidl = PL_strdup(message_id_token); - if (!cd->msg_info[cd->current_msg_to_top-1].uidl) - return MK_OUT_OF_MEMORY; + cd->number_of_messages_not_seen_before++; + cd->msg_info[cd->current_msg_to_top-1].uidl = XP_STRDUP(message_id_token); + if (!cd->msg_info[cd->current_msg_to_top-1].uidl) + return MK_OUT_OF_MEMORY; } } else if (cd->only_uidl && message_id_token && - !PL_strcmp(cd->only_uidl, message_id_token)) + !XP_STRCMP(cd->only_uidl, message_id_token)) { - cd->last_accessed_msg = cd->current_msg_to_top; + cd->last_accessed_msg = cd->current_msg_to_top - 1; cd->found_new_message_boundary = TRUE; + cd->msg_info[cd->current_msg_to_top-1].uidl = XP_STRDUP(message_id_token); + if (!cd->msg_info[cd->current_msg_to_top-1].uidl) + return MK_OUT_OF_MEMORY; } else if (!cd->only_uidl) { /* we have seen this message and we care about the edge, stop looking for new ones */ if (cd->number_of_messages_not_seen_before != 0) { - cd->last_accessed_msg = cd->current_msg_to_top; + cd->last_accessed_msg = cd->current_msg_to_top; /* -1 ? */ cd->found_new_message_boundary = TRUE; /* we stay in this state so we can process the rest of the lines in the top message */ @@ -1483,7 +1566,7 @@ net_pop3_send_xtnd_xlst_msgid(ActiveEntry *ce) return(start_use_top_for_fake_uidl(ce)); - PL_strcpy(cd->output_buffer, "XTND XLST Message-Id" CRLF); + XP_STRCPY(cd->output_buffer, "XTND XLST Message-Id" CRLF); cd->next_state_after_response = POP3_GET_XTND_XLST_MSGID; cd->pause_for_read = TRUE; return(net_pop3_send_command(ce, cd->output_buffer)); @@ -1562,18 +1645,18 @@ net_pop3_get_xtnd_xlst_msgid(ActiveEntry *ce) * * list data is terminated by a ".CRLF" line */ - if(!PL_strcmp(line, ".")) + if(!XP_STRCMP(line, ".")) { cd->next_state = POP3_GET_MSG; cd->pause_for_read = FALSE; return(0); } - msg_num = atol(strtok(line, " ")); + msg_num = atol(XP_STRTOK(line, " ")); if(msg_num <= cd->number_of_messages && msg_num > 0) { -/* char *eatMessageIdToken = strtok(NULL, " "); */ - char *uidl = strtok(NULL, " "); /* not really a uidl but a unique token -km */ +/* char *eatMessageIdToken = XP_STRTOK(NULL, " "); */ + char *uidl = XP_STRTOK(NULL, " "); /* not really a uidl but a unique token -km */ if (!uidl) /* This is bad. The server didn't give us a UIDL for this message. @@ -1582,8 +1665,9 @@ net_pop3_get_xtnd_xlst_msgid(ActiveEntry *ce) there, I have no idea; must be a server bug. Or something. */ uidl = ""; - cd->msg_info[msg_num-1].uidl = PL_strdup(uidl); - if (!cd->msg_info[msg_num-1].uidl) return MK_OUT_OF_MEMORY; + cd->msg_info[msg_num-1].uidl = XP_STRDUP(uidl); + if (!cd->msg_info[msg_num-1].uidl) + return MK_OUT_OF_MEMORY; } return(0); @@ -1599,7 +1683,7 @@ net_pop3_send_uidl_list(ActiveEntry *ce) return(net_pop3_send_xtnd_xlst_msgid(ce)); - PL_strcpy(cd->output_buffer, "UIDL" CRLF); + XP_STRCPY(cd->output_buffer, "UIDL" CRLF); cd->next_state_after_response = POP3_GET_UIDL_LIST; cd->pause_for_read = TRUE; return(net_pop3_send_command(ce, cd->output_buffer)); @@ -1636,12 +1720,14 @@ net_pop3_get_uidl_list(ActiveEntry *ce) fmt = XP_GetString( XP_THE_POP3_SERVER_DOES_NOT_SUPPORT_UIDL_ETC ); host = NET_ParseURL(ce->URL_s->address, GET_HOST_PART); - PR_ASSERT(host); - if (!host) host = "(null)"; + XP_ASSERT(host); + if (!host) + host = "(null)"; buf = PR_smprintf (fmt, host); - if (!buf) return MK_OUT_OF_MEMORY; + if (!buf) + return MK_OUT_OF_MEMORY; FE_Alert (ce->window_id, buf); - PR_Free (buf); + XP_FREE (buf); /* Free up the msg_info structure, as we use its presence later to decide if we can do UIDL-based games. */ @@ -1695,17 +1781,17 @@ net_pop3_get_uidl_list(ActiveEntry *ce) * * list data is terminated by a ".CRLF" line */ - if(!PL_strcmp(line, ".")) + if(!XP_STRCMP(line, ".")) { cd->next_state = POP3_GET_MSG; cd->pause_for_read = FALSE; return(0); } - msg_num = atol(strtok(line, " ")); + msg_num = atol(XP_STRTOK(line, " ")); if(msg_num <= cd->number_of_messages && msg_num > 0) { - char *uidl = strtok(NULL, " "); + char *uidl = XP_STRTOK(NULL, " "); if (!uidl) /* This is bad. The server didn't give us a UIDL for this message. @@ -1714,8 +1800,9 @@ net_pop3_get_uidl_list(ActiveEntry *ce) there, I have no idea; must be a server bug. Or something. */ uidl = ""; - cd->msg_info[msg_num-1].uidl = PL_strdup(uidl); - if (!cd->msg_info[msg_num-1].uidl) return MK_OUT_OF_MEMORY; + cd->msg_info[msg_num-1].uidl = XP_STRDUP(uidl); + if (!cd->msg_info[msg_num-1].uidl) + return MK_OUT_OF_MEMORY; } return(0); @@ -1748,30 +1835,42 @@ net_pop3_get_msg(ActiveEntry *ce) everything, and it's easy. Otherwise, if we only want one uidl, than that's the only one we'll get. Otherwise, go through each message info, decide if we're going to get that - message, and add the number of bytes for it. */ + message, and add the number of bytes for it. When a message is too + large (per user's preferences) only add the size we are supposed + to get. */ if (cd->msg_info) { cd->total_download_size = 0; - for (i=0 ; inumber_of_messages ; i++) { + for (i=0 ; i < cd->number_of_messages ; i++) { c = 0; if (cd->only_uidl) { - if (cd->msg_info[i].uidl && PL_strcmp(cd->msg_info[i].uidl, + if (cd->msg_info[i].uidl && XP_STRCMP(cd->msg_info[i].uidl, cd->only_uidl) == 0) { - if (cd->msg_info[i].size > cd->size_limit) - cd->total_download_size = cd->size_limit; /* if more than max, only count max */ - else + /*if (cd->msg_info[i].size > cd->size_limit) + cd->total_download_size = cd->size_limit; */ /* if more than max, only count max */ + /*else*/ cd->total_download_size = cd->msg_info[i].size; break; } continue; } - if (cd->msg_info[i].uidl) { - c = (char) (int) XP_Gethash(cd->uidlinfo->hash, - cd->msg_info[i].uidl, 0); + if (cd->msg_info[i].uidl) + c = (char) (int) XP_Gethash(cd->uidlinfo->hash, cd->msg_info[i].uidl, 0); + if ((c == KEEP) && !cd->leave_on_server) + { /* This message has been downloaded but kept on server, we no longer want to keep it there */ + if (cd->newuidl == NULL) + { + cd->newuidl = XP_HashTableNew(20, XP_StringHash, uidl_cmp); + if (!cd->newuidl) + return MK_OUT_OF_MEMORY; + } + c = DELETE_CHAR; + put_hash(cd->uidlinfo, cd->newuidl, cd->msg_info[i].uidl, DELETE_CHAR); /* Mark message to be deleted in new table */ + put_hash(cd->uidlinfo, cd->uidlinfo->hash, cd->msg_info[i].uidl, DELETE_CHAR); /* and old one too */ } - if (c != KEEP && c != DELETE_CHAR) { - if (cd->msg_info[i].size > cd->size_limit) - cd->total_download_size += cd->size_limit; /* if more than max, only count max */ - else + if ((c != KEEP) && (c != DELETE_CHAR) && (c != TOO_BIG)) { /* mesage left on server */ + /*if (cd->msg_info[i].size > cd->size_limit) + cd->total_download_size += cd->size_limit; */ /* if more than max, only count max */ + /*else*/ cd->total_download_size += cd->msg_info[i].size; } } @@ -1813,34 +1912,52 @@ net_pop3_get_msg(ActiveEntry *ce) cd->next_state = POP3_SEND_XSENDER; else cd->next_state = POP3_SEND_RETR; - + cd->truncating_cur_msg = FALSE; cd->pause_for_read = FALSE; if (cd->msg_info) { - Pop3MsgInfo* info = cd->msg_info + - cd->last_accessed_msg; + Pop3MsgInfo* info = cd->msg_info + cd->last_accessed_msg; if (cd->only_uidl) { - if (info->uidl == NULL || PL_strcmp(info->uidl, cd->only_uidl) != 0) { - cd->next_state = POP3_GET_MSG; - } + if (info->uidl == NULL || XP_STRCMP(info->uidl, cd->only_uidl)) + cd->next_state = POP3_GET_MSG; + else + cd->next_state = POP3_SEND_RETR; } else { c = 0; + if (cd->newuidl == NULL) { + cd->newuidl = XP_HashTableNew(20, XP_StringHash, uidl_cmp); + if (!cd->newuidl) + return MK_OUT_OF_MEMORY; + } if (info->uidl) { c = (char) (int) XP_Gethash(cd->uidlinfo->hash, info->uidl, 0); } + cd->truncating_cur_msg = FALSE; if (c == DELETE_CHAR) { cd->next_state = POP3_SEND_DELE; } else if (c == KEEP) { cd->next_state = POP3_GET_MSG; - } else if (cd->size_limit >= 0 && - info->size > cd->size_limit && - !net_top_command_unimplemented && - cd->only_uidl == NULL) { - cd->next_state = POP3_SEND_TOP; + } else if ((c != TOO_BIG) && (cd->size_limit > 0) && (info->size > cd->size_limit) && + !net_top_command_unimplemented && (cd->only_uidl == NULL)) { + /* message is too big */ + cd->truncating_cur_msg = TRUE; + cd->next_state = POP3_SEND_TOP; + put_hash(cd->uidlinfo, cd->newuidl, info->uidl, TOO_BIG); + } else if (c == TOO_BIG) { /* message previously left on server, see if the + max download size has changed, because we may + want to download the message this time around. + Otherwise ignore the message, we have the header. */ + if ((cd->size_limit > 0) && (info->size <= cd->size_limit)) + XP_Remhash (cd->uidlinfo->hash, (void*) info->uidl); /* remove from our table, and download */ + else + { + cd->truncating_cur_msg = TRUE; + cd->next_state = POP3_GET_MSG; /* ignore this message and get next one */ + put_hash(cd->uidlinfo, cd->newuidl, info->uidl, TOO_BIG); + } } } if ((cd->leave_on_server && cd->next_state != POP3_SEND_DELE) || - cd->next_state == POP3_GET_MSG || - cd->next_state == POP3_SEND_TOP) { + cd->next_state == POP3_GET_MSG || cd->next_state == POP3_SEND_TOP) { /* This is a message we have decided to keep on the server. Notate that now for the future. (Don't change the popstate file at all @@ -1849,11 +1966,8 @@ net_pop3_get_msg(ActiveEntry *ce) leave them around until the user next does a GetNewMail.) */ if (info->uidl && cd->only_uidl == NULL) { - if (cd->newuidl == NULL) { - cd->newuidl = XP_HashTableNew(20, XP_StringHash, uidl_cmp); - if (!cd->newuidl) return MK_OUT_OF_MEMORY; - } - put_hash(cd->uidlinfo, cd->newuidl, info->uidl, KEEP); + if (!cd->truncating_cur_msg) /* message already marked as too_big */ + put_hash(cd->uidlinfo, cd->newuidl, info->uidl, KEEP); } } if (cd->next_state == POP3_GET_MSG) { @@ -1872,17 +1986,16 @@ net_pop3_send_top(ActiveEntry *ce) { Pop3ConData * cd = (Pop3ConData *)ce->con_data; - PR_ASSERT(!(net_uidl_command_unimplemented && + XP_ASSERT(!(net_uidl_command_unimplemented && net_xtnd_xlst_command_unimplemented && net_top_command_unimplemented) ); - PR_ASSERT(!net_top_command_unimplemented); + XP_ASSERT(!net_top_command_unimplemented); PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "TOP %ld 20" CRLF, cd->last_accessed_msg+1); - cd->truncating_cur_msg = TRUE; cd->next_state_after_response = POP3_TOP_RESPONSE; cd->cur_msg_size = -1; @@ -1923,7 +2036,7 @@ net_pop3_xsender_response(ActiveEntry *ce) pop3CapabilityFlags &= ~POP3_XSENDER_UNDEFINED; if (cd->command_succeeded) { - if (PL_strlen (cd->command_response) > 4) + if (XP_STRLEN (cd->command_response) > 4) { StrAllocCopy(cd->sender_info, cd->command_response); } @@ -1933,8 +2046,10 @@ net_pop3_xsender_response(ActiveEntry *ce) else { pop3CapabilityFlags &= ~POP3_HAS_XSENDER; } - - cd->next_state = POP3_SEND_RETR; + if (cd->truncating_cur_msg) + cd->next_state = POP3_SEND_TOP; + else + cd->next_state = POP3_SEND_RETR; return 0; } @@ -1946,7 +2061,6 @@ net_pop3_send_retr(ActiveEntry *ce) Pop3ConData * cd = (Pop3ConData *)ce->con_data; char buf[OUTPUT_BUFFER_SIZE]; - cd->truncating_cur_msg = FALSE; PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "RETR %ld" CRLF, @@ -1964,7 +2078,7 @@ net_pop3_send_retr(ActiveEntry *ce) if (cd->only_uidl) { /* Display bytes if we're only downloading one message. */ - PR_ASSERT(!cd->graph_progress_bytes_p); + XP_ASSERT(!cd->graph_progress_bytes_p); if (!cd->graph_progress_bytes_p) FE_GraphProgressInit(ce->window_id, ce->URL_s, cd->total_download_size); @@ -1995,6 +2109,29 @@ extern int msg_LineBuffer (const char *net_buffer, int32 net_buffer_size, PRIVATE int32 net_pop3_retr_handle_line(char *line, uint32 line_length, void *closure); +static int32 gPOP3parsed_bytes, gPOP3size; +static XP_Bool gPOP3dotFix, gPOP3AssumedEnd; + + +/* + To fix a bug where we think the message is over, check the alleged size of the message + before we assume that CRLF.CRLF is the end. + return TRUE if end of message is unlikely. parsed bytes is not accurate since we add + bytes every now and then. +*/ + +XP_Bool NET_POP3TooEarlyForEnd(int32 len) +{ + if (!gPOP3dotFix) + return FALSE; /* fix turned off */ + gPOP3parsed_bytes += len; + if (gPOP3parsed_bytes >= (gPOP3size - 3)) + return FALSE; + return TRUE; +} + + + /* digest the message */ PRIVATE int @@ -2003,54 +2140,65 @@ net_pop3_retr_response(ActiveEntry *ce) Pop3ConData * cd = (Pop3ConData *)ce->con_data; char *buffer; int32 buffer_size; + int32 flags = 0; + char *uidl = NULL; int32 old_bytes_received = ce->bytes_received; - + XP_Bool fix = FALSE; + if(cd->cur_msg_size == -1) - { + { /* this is the beginning of a message * get the response code and byte size */ if(!cd->command_succeeded) - { return net_pop3_error(ce, MK_POP3_RETR_FAILURE); - } - /* a successful retr response looks like: #num_bytes Junk + /* a successful RETR response looks like: #num_bytes Junk + from TOP we only get the +OK and data */ - cd->cur_msg_size = atol(strtok(cd->command_response, " ")); + if (cd->truncating_cur_msg) + { /* TOP, truncated message */ + cd->cur_msg_size = cd->size_limit; + flags |= MSG_FLAG_PARTIAL; + } + else + cd->cur_msg_size = atol(XP_STRTOK(cd->command_response, " ")); /* RETR complete message */ + + if (cd->sender_info) + flags |= MSG_FLAG_SENDER_AUTHED; if(cd->cur_msg_size < 0) cd->cur_msg_size = 0; + if (cd->msg_info && cd->msg_info[cd->last_accessed_msg].uidl) + uidl = cd->msg_info[cd->last_accessed_msg].uidl; + + gPOP3parsed_bytes = 0; + gPOP3size = cd->cur_msg_size; + gPOP3AssumedEnd = FALSE; + PREF_GetBoolPref("mail.dot_fix", &fix); + gPOP3dotFix = fix; + + TRACEMSG(("Opening message stream: MSG_IncorporateBegin")); /* open the message stream so we have someplace * to put the data */ - cd->msg_closure = - MSG_IncorporateBegin (cd->pane, - ce->format_out, - (cd->truncating_cur_msg ? - cd->msg_info[cd->last_accessed_msg].uidl : - NULL), - ce->URL_s, - (cd->sender_info ? - MSG_FLAG_SENDER_AUTHED : 0x0)); + cd->msg_closure = MSG_IncorporateBegin (cd->pane, ce->format_out, uidl, ce->URL_s, flags); TRACEMSG(("Done opening message stream!")); if(!cd->msg_closure) - { return(net_pop3_error(ce, MK_POP3_MESSAGE_WRITE_ERROR)); - } - } + } if (cd->data_buffer_size > 0) - { - PR_ASSERT(cd->obuffer_fp == 0); /* must be the first time */ + { + XP_ASSERT(cd->obuffer_fp == 0); /* must be the first time */ buffer = cd->data_buffer; buffer_size = cd->data_buffer_size; cd->data_buffer_size = 0; - } + } else { ce->status = PR_Read(ce->socket, @@ -2062,75 +2210,93 @@ net_pop3_retr_response(ActiveEntry *ce) cd->pause_for_read = TRUE; if(ce->status == 0) - { - /* this shouldn't happen - */ - return(net_pop3_error(ce, MK_POP3_SERVER_ERROR)); - } + { + /* should never happen */ + return(net_pop3_error(ce, MK_POP3_SERVER_ERROR)); + } else if(ce->status < 0) /* error */ { int err = PR_GetError(); - - TRACEMSG(("TCP Error: %d", err)); - + if (err == PR_WOULD_BLOCK_ERROR) - { - cd->pause_for_read = TRUE; - return (0); - } - - ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, err); - - /* return TCP error - */ - return MK_TCP_READ_ERROR; - } - } + { + /* + (rb) If we have the dot fix, and the server reported the wrong number of bytes there may + be nothing else to read, so now go ahead and close the message if we saw something + resembling the end of the message. + */ + if (gPOP3dotFix && gPOP3AssumedEnd) + { + ce->status = MSG_IncorporateComplete(cd->pane, cd->msg_closure); + cd->msg_closure = 0; + buffer_size = 0; + } + else + { + cd->pause_for_read = TRUE; + return (0); + } + } + else + { + TRACEMSG(("TCP Error: %d", err)); - ce->status = msg_LineBuffer(buffer, buffer_size, + ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, err); + + /* return TCP error + */ + return MK_TCP_READ_ERROR; + } + } + } + + if (cd->msg_closure) /* not done yet */ + ce->status = msg_LineBuffer(buffer, buffer_size, &cd->obuffer, &cd->obuffer_size, &cd->obuffer_fp, FALSE, net_pop3_retr_handle_line, (void *) ce); if (ce->status < 0) - { - if (cd->msg_closure) { - MSG_IncorporateAbort(cd->pane, cd->msg_closure, + { + if (cd->msg_closure) + { + MSG_IncorporateAbort(cd->pane, cd->msg_closure, MK_POP3_MESSAGE_WRITE_ERROR); - cd->msg_closure = NULL; + cd->msg_closure = NULL; } MSG_AbortMailDelivery(cd->pane); return(net_pop3_error(ce, MK_POP3_MESSAGE_WRITE_ERROR)); - } + } /* normal read. Yay! */ - if (cd->bytes_received_in_message + buffer_size > cd->cur_msg_size) { - buffer_size = cd->cur_msg_size - cd->bytes_received_in_message; - } + if (cd->bytes_received_in_message + buffer_size > cd->cur_msg_size) + buffer_size = cd->cur_msg_size - cd->bytes_received_in_message; + cd->bytes_received_in_message += buffer_size; ce->bytes_received += buffer_size; if (!cd->msg_closure) /* meaning _handle_line read ".\r\n" at end-of-msg */ - { + { cd->pause_for_read = FALSE; if (cd->truncating_cur_msg || (cd->leave_on_server && !(net_uidl_command_unimplemented && net_xtnd_xlst_command_unimplemented && - net_top_command_unimplemented) )) { - /* We've retreived all or part of this message, but we want to + net_top_command_unimplemented) )) + { + /* We've retrieved all or part of this message, but we want to keep it on the server. Go on to the next message. */ - cd->last_accessed_msg++; - cd->next_state = POP3_GET_MSG; - } else { - cd->next_state = POP3_SEND_DELE; + cd->last_accessed_msg++; + cd->next_state = POP3_GET_MSG; + } else + { + cd->next_state = POP3_SEND_DELE; } /* if we didn't get the whole message add the bytes that we didn't get to the bytes received part so that the progress percent stays sane. */ if(cd->bytes_received_in_message < cd->cur_msg_size) - ce->bytes_received += (cd->cur_msg_size - - cd->bytes_received_in_message); - } + ce->bytes_received += (cd->cur_msg_size - cd->bytes_received_in_message); + } if (cd->graph_progress_bytes_p) FE_GraphProgress(ce->window_id, ce->URL_s, @@ -2140,9 +2306,8 @@ net_pop3_retr_response(ActiveEntry *ce) /* set percent done to portion of total bytes of all messages that we're going to download. */ - FE_SetProgressBarPercent(ce->window_id, - ((ce->bytes_received*100) - / cd->total_download_size)); + if (cd->total_download_size) + FE_SetProgressBarPercent(ce->window_id, ((ce->bytes_received*100) / cd->total_download_size)); return(0); } @@ -2170,15 +2335,15 @@ net_pop3_top_response(ActiveEntry *ce) fmt = XP_GetString( XP_THE_POP3_SERVER_DOES_NOT_SUPPORT_THE_TOP_COMMAND ); host = NET_ParseURL(ce->URL_s->address, GET_HOST_PART); - size = PL_strlen(fmt) + PL_strlen(host ? host : "(null)") + 100; - buf = (char *) PR_Malloc (size); + size = XP_STRLEN(fmt) + XP_STRLEN(host ? host : "(null)") + 100; + buf = (char *) XP_ALLOC (size); if (!buf) { FREEIF(host); return MK_OUT_OF_MEMORY; } PR_snprintf (buf, size, fmt, host ? host : "(null)"); FE_Alert (ce->window_id, buf); - PR_Free (buf); + XP_FREE (buf); FREEIF(host); PREF_GetBoolPref ("mail.auth_login", &prefBool); @@ -2203,14 +2368,15 @@ net_pop3_retr_handle_line(char *line, uint32 line_length, void *closure) Pop3ConData * cd = (Pop3ConData *)ce->con_data; int status; - PR_ASSERT(cd->msg_closure); - if (!cd->msg_closure) return -1; + XP_ASSERT(cd->msg_closure); + if (!cd->msg_closure) + return -1; if (cd->sender_info && !cd->seenFromHeader) { if (line_length > 6 && !XP_MEMCMP("From: ", line, 6)) { - /* Zzzzz PL_strstr only works with NULL terminated string. Since, + /* Zzzzz XP_STRSTR only works with NULL terminated string. Since, * the last character of a line is either a carriage return * or a linefeed. Temporary setting the last character of the * line to NULL and later setting it back should be the right @@ -2219,7 +2385,7 @@ net_pop3_retr_handle_line(char *line, uint32 line_length, void *closure) char ch = line[line_length-1]; line[line_length-1] = 0; cd->seenFromHeader = TRUE; - if (PL_strstr(line, cd->sender_info) == NULL) + if (XP_STRSTR(line, cd->sender_info) == NULL) MSG_ClearSenderAuthedFlag(cd->pane, cd->msg_closure); line[line_length-1] = ch; } @@ -2227,11 +2393,16 @@ net_pop3_retr_handle_line(char *line, uint32 line_length, void *closure) status = MSG_IncorporateWrite(cd->pane, cd->msg_closure, line, line_length); - if (status >= 0 && - line[0] == '.' && - (line[1] == CR || line[1] == LF)) { - status = MSG_IncorporateComplete(cd->pane, cd->msg_closure); - cd->msg_closure = 0; + if ((status >= 0) && + (line[0] == '.') && + ((line[1] == CR) || (line[1] == LF))) + { + gPOP3AssumedEnd = TRUE; /* in case byte count from server is wrong, mark we may have had the end */ + if (!gPOP3dotFix || (gPOP3parsed_bytes >= (gPOP3size -3))) + { + status = MSG_IncorporateComplete(cd->pane, cd->msg_closure); + cd->msg_closure = 0; + } } return status; @@ -2261,9 +2432,10 @@ PRIVATE int net_pop3_dele_response(ActiveEntry *ce) { Pop3ConData * cd = (Pop3ConData *)ce->con_data; -#ifdef POP_ALWAYS_USE_UIDL_FOR_DUPLICATES - Pop3UidlHost *host = cd->uidlinfo; -#endif + Pop3UidlHost *host = NULL; + + if (cd) + host = cd->uidlinfo; /* the return from the delete will come here */ @@ -2271,8 +2443,7 @@ net_pop3_dele_response(ActiveEntry *ce) return(net_pop3_error(ce, MK_POP3_DELE_FAILURE)); -#ifdef POP_ALWAYS_USE_UIDL_FOR_DUPLICATES - /* + /* ###chrisf the delete succeeded. Write out state so that we keep track of all the deletes which have not yet been committed on the server. Flush this state upon successful @@ -2284,10 +2455,13 @@ net_pop3_dele_response(ActiveEntry *ce) */ if (host) { - if (cd->msg_info && cd->msg_info[cd->last_accessed_msg-1].uidl) - put_hash(host, host->hash, cd->msg_info[cd->last_accessed_msg-1].uidl, DELETE_CHAR); + if (cd->msg_info && cd->msg_info[cd->last_accessed_msg-1].uidl) { + if (cd->newuidl) + XP_Remhash(cd->newuidl, (void*) cd->msg_info[cd->last_accessed_msg-1].uidl); /* kill message in new hash table */ + else + XP_Remhash(host->hash, (void*) cd->msg_info[cd->last_accessed_msg-1].uidl); + } } -#endif cd->next_state = POP3_GET_MSG; @@ -2311,7 +2485,7 @@ net_pop3_commit_state(ActiveEntry *ce, XP_Bool remove_last_entry) Pop3MsgInfo* info = cd->msg_info + cd->last_accessed_msg; if (info && info->uidl && (cd->only_uidl == NULL) && cd->newuidl) { XP_Bool val = XP_Remhash (cd->newuidl, info->uidl); - PR_ASSERT(val); + XP_ASSERT(val); } } } @@ -2333,24 +2507,30 @@ net_pop3_commit_state(ActiveEntry *ce, XP_Bool remove_last_entry) PRIVATE int32 net_Pop3Load (ActiveEntry * ce) { - Pop3ConData * cd = PR_NEW(Pop3ConData); + Pop3ConData * cd = XP_NEW(Pop3ConData); char* host = NET_ParseURL(ce->URL_s->address, GET_HOST_PART); char* uidl; + int err = 0; + if (net_pop3_block) /* we already have a connection going */ + { + FREE(cd); + FREEIF(host); + return -1; /* avoid looping back in */ + } if(!cd || !host || !ce->URL_s->internal_url) { FREEIF(cd); FREEIF(host); return(MK_OUT_OF_MEMORY); } - memset(cd, 0, sizeof(Pop3ConData)); + XP_MEMSET(cd, 0, sizeof(Pop3ConData)); if(!net_pop3_username || !*net_pop3_username) { FREE(cd); FREEIF(host); - ce->URL_s->error_msg = NET_ExplainErrorDetails( - MK_POP3_USERNAME_UNDEFINED); + ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_POP3_USERNAME_UNDEFINED); ce->status = MK_POP3_USERNAME_UNDEFINED; #ifdef XP_MAC FE_Alert(ce->window_id, ce->URL_s->error_msg); /* BEFORE going to the prefs window */ @@ -2359,25 +2539,14 @@ net_Pop3Load (ActiveEntry * ce) return(MK_POP3_USERNAME_UNDEFINED); } - if(PL_strcasestr(ce->URL_s->address, "?check")) { + /* acquire the semaphore which prevents POP3 from making more connections */ + net_pop3_block = TRUE; - if (!net_pop3_block_biff) - cd->only_check_for_new_mail = TRUE; - else - { - /* don't allow a biff to open a second connection to the - * server because some servers will close the first connection - */ - PR_LogPrint("blocking biff due to avoid opening a second connection"); - FREEIF(cd); - FREEIF(host); - return -1; - } - } - - if(PL_strcasestr(ce->URL_s->address, "?gurl")) { + if(strcasestr(ce->URL_s->address, "?check")) + cd->only_check_for_new_mail = TRUE; + + if(strcasestr(ce->URL_s->address, "?gurl")) cd->get_url = TRUE; - } if (!cd->only_check_for_new_mail) { XP_Bool tmp = FALSE; @@ -2385,12 +2554,16 @@ net_Pop3Load (ActiveEntry * ce) if (!cd->pane) { #ifdef DEBUG_phil - PR_LogPrint ("NET_Pop3Load: url->msg_pane NULL for URL: %s\n", ce->URL_s->address); + XP_Trace ("NET_Pop3Load: url->msg_pane NULL for URL: %s\n", ce->URL_s->address); #endif cd->pane = MSG_FindPane(ce->window_id, MSG_FOLDERPANE); /* ###tw */ } - PR_ASSERT(cd->pane); - if (cd->pane == NULL) return -1; /* ### */ + XP_ASSERT(cd->pane); + if (cd->pane == NULL) + { + net_pop3_block = FALSE; + return -1; /* ### */ + } PREF_GetBoolPref("mail.leave_on_server", &(cd->leave_on_server)); PREF_GetBoolPref("mail.limit_message_size", &tmp); @@ -2403,9 +2576,10 @@ net_Pop3Load (ActiveEntry * ce) } cd->uidlinfo = net_pop3_load_state(host, net_pop3_username); - PR_Free(host); - cd->output_buffer = (char*)PR_Malloc(OUTPUT_BUFFER_SIZE); - if(!cd->uidlinfo || !cd->output_buffer) goto FAIL; + XP_FREE(host); + cd->output_buffer = (char*)XP_ALLOC(OUTPUT_BUFFER_SIZE); + if(!cd->uidlinfo || !cd->output_buffer) + goto FAIL; ce->con_data = cd; @@ -2413,11 +2587,12 @@ net_Pop3Load (ActiveEntry * ce) cd->biffstate = MSG_BIFF_NoMail; /* Return "no mail" unless proven otherwise. */ - uidl = PL_strcasestr(ce->URL_s->address, "?uidl="); + uidl = strcasestr(ce->URL_s->address, "?uidl="); if (uidl) { uidl += 6; - cd->only_uidl = PL_strdup(uidl); - if (!cd->only_uidl) goto FAIL; + cd->only_uidl = XP_STRDUP(uidl); + if (!cd->only_uidl) + goto FAIL; } ce->socket = NULL; @@ -2425,13 +2600,20 @@ net_Pop3Load (ActiveEntry * ce) cd->next_state = POP3_READ_PASSWORD; /* acquire the semaphore which prevents biff from interrupting connections */ - net_pop3_block_biff = TRUE; + net_pop3_block = TRUE; - return (net_ProcessPop3(ce)); + err = net_ProcessPop3(ce); + if (err < 0) + net_pop3_block = FALSE; + return (err); FAIL: - if (cd->uidlinfo) net_pop3_free_state(cd->uidlinfo); - if (cd->output_buffer) PR_Free(cd->output_buffer); + /* release the semaphore which prevents POP3 from making more connections */ + net_pop3_block = FALSE; + if (cd->uidlinfo) + net_pop3_free_state(cd->uidlinfo); + if (cd->output_buffer) + XP_FREE(cd->output_buffer); FREE(cd); return(MK_OUT_OF_MEMORY); } @@ -2450,7 +2632,7 @@ net_ProcessPop3 (ActiveEntry *ce) Pop3ConData * cd = (Pop3ConData *)ce->con_data; int oldStatus = 0; - TRACEMSG(("Entering NET_ProcessPop3")); + TRACEMSG(("Entering net_ProcessPop3")); cd->pause_for_read = FALSE; /* already paused; reset */ @@ -2476,18 +2658,18 @@ net_ProcessPop3 (ActiveEntry *ce) /* If we're just checking for new mail (biff) then don't prompt the user for a password; just tell him we don't know whether he has new mail. */ - if (cd->only_check_for_new_mail && + if ((cd->only_check_for_new_mail || MSG_Biff_Master_NikiCallingGetNewMail()) && (!net_pop3_password || !net_pop3_username)) { ce->status = MK_POP3_PASSWORD_UNDEFINED; cd->biffstate = MSG_BIFF_Unknown; - MSG_SetBiffStateAndUpdateFE(cd->biffstate); + MSG_SetBiffStateAndUpdateFE(cd->biffstate); /* update old style biff */ cd->next_state = POP3_FREE; cd->pause_for_read = FALSE; break; } - PR_ASSERT(net_pop3_username); + XP_ASSERT(net_pop3_username); if (cd->password_failed) fmt2 = @@ -2501,15 +2683,13 @@ net_ProcessPop3 (ActiveEntry *ce) char *password; char *host = NET_ParseURL(ce->URL_s->address, GET_HOST_PART); - size_t len = (PL_strlen(fmt1 ? fmt1 : fmt2) + - (host ? PL_strlen(host) : 0) + 300) + size_t len = (XP_STRLEN(fmt1 ? fmt1 : fmt2) + + (host ? XP_STRLEN(host) : 0) + 300) * sizeof(char); - char *prompt = (char *) PR_Malloc (len); -#if defined(SingleSignon) - char *usernameAndHost=0; -#endif + char *prompt = (char *) XP_ALLOC (len); if (!prompt) { FREEIF(host); + net_pop3_block = FALSE; return MK_OUT_OF_MEMORY; } if (fmt1) @@ -2520,40 +2700,31 @@ net_ProcessPop3 (ActiveEntry *ce) ? cd->command_response : XP_GetString(XP_NO_ANSWER)), net_pop3_username, host); -#if defined(SingleSignons) - StrAllocCopy(usernameAndHost, net_pop3_username); - StrAllocCat(usernameAndHost, "@"); - StrAllocCat(usernameAndHost, host); FREEIF (host); - password = SI_PromptPassword - (ce->window_id, - prompt, usernameAndHost, - FALSE, !cd->password_failed); cd->password_failed = FALSE; - XP_FREE(usernameAndHost); - -#else - FREEIF (host); - cd->password_failed = FALSE; - password = FE_PromptPassword - (ce->window_id, prompt); -#endif - PR_Free(prompt); + password = FE_PromptPassword(ce->window_id, prompt); + XP_FREE(prompt); if (password == NULL) - return MK_POP3_PASSWORD_UNDEFINED; + { + net_pop3_block = FALSE; + return MK_POP3_PASSWORD_UNDEFINED; + } net_set_pop3_password(password); - memset(password, 0, PL_strlen(password)); - PR_Free(password); + XP_MEMSET(password, 0, XP_STRLEN(password)); + XP_FREE(password); net_pop3_password_pending = TRUE; } - PR_ASSERT (net_pop3_username && net_pop3_password); + XP_ASSERT (net_pop3_username && net_pop3_password); if (!net_pop3_username || !net_pop3_password) - return -1; + { + net_pop3_block = FALSE; + return -1; + } cd->next_state = POP3_START_CONNECT; cd->pause_for_read = FALSE; @@ -2570,13 +2741,20 @@ net_ProcessPop3 (ActiveEntry *ce) "POP3", POP3_PORT, &ce->socket, - FALSE, + HG09439 &cd->tcp_con_data, ce->window_id, &ce->URL_s->error_msg, ce->socks_host, ce->socks_port); + if ((ce->status == MK_UNABLE_TO_CONNECT) || + (ce->status == MK_CONNECTION_TIMED_OUT) || + (ce->status == MK_CONNECTION_REFUSED)) + { + if (MSG_Biff_Master_NikiCallingGetNewMail()) + cd->next_state = POP3_FREE; /* calls from niki-biff should avoid dialogs */ + } if(ce->socket != NULL) NET_TotalNumberOfOpenConnections++; @@ -2609,7 +2787,7 @@ net_ProcessPop3 (ActiveEntry *ce) ce->con_sock = NULL; /* set con sock so we can select on it */ #endif } - else if(ce->status > -1) + else if(ce->status > -1) { cd->next_state = POP3_FINISH_CONNECT; ce->con_sock = ce->socket; /* set con sock so we can select on it */ @@ -2818,34 +2996,37 @@ net_ProcessPop3 (ActiveEntry *ce) status file and the FE's biff state. */ if (!cd->only_uidl) { - MSG_SetBiffStateAndUpdateFE(cd->biffstate); - if (!cd->only_check_for_new_mail) { + if (cd->only_check_for_new_mail) + MSG_SetBiffStateAndUpdateFE(cd->biffstate); /* update old style biff */ + else { /* We don't want to pop up a warning message any more (see bug 54116), so instead we put the "no new messages" or "retrieved x new messages" in the status line. Unfortunately, this tends to be running in a progress pane, so we try to get the real pane and show the message there. */ - MWContext* context = ce->window_id; - if (cd->pane) { - MSG_Pane* parentpane = MSG_GetParentPane(cd->pane); - if (parentpane) { - context = MSG_GetContext(parentpane); + MWContext* context = ce->window_id; + if (cd->pane) { + MSG_Pane* parentpane = MSG_GetParentPane(cd->pane); + if (parentpane) + context = MSG_GetContext(parentpane); + } + if (cd->total_download_size <= 0) { + /* There are no new messages. */ + if (context) + NET_Progress(context, XP_GetString(MK_POP3_NO_MESSAGES)); + } + else { + /* at least 1 message was queued to download */ + char *statusTemplate = XP_GetString (MK_MSG_DOWNLOAD_COUNT); + char *statusString = PR_smprintf (statusTemplate, cd->last_accessed_msg, cd->number_of_messages); + if (context) + NET_Progress(context, statusString); + FREEIF(statusString); + MSG_SetBiffStateAndUpdateFE(MSG_BIFF_NewMail); } - } - if (cd->total_download_size <= 0) { - /* There are no new messages. */ - if (context) FE_Progress(context, XP_GetString(MK_POP3_NO_MESSAGES)); - } - else { - /* at least 1 message was queued to download */ - char *statusTemplate = XP_GetString (MK_MSG_DOWNLOAD_COUNT); - char *statusString = PR_smprintf (statusTemplate, cd->last_accessed_msg, cd->number_of_messages); - if (context) FE_Progress(context, statusString); - FREEIF(statusString); - } } } - PL_strcpy(cd->output_buffer, "QUIT" CRLF); + XP_STRCPY(cd->output_buffer, "QUIT" CRLF); oldStatus = ce->status; ce->status = net_pop3_send_command(ce, cd->output_buffer); if (oldStatus == MK_INTERRUPTED) @@ -2903,9 +3084,9 @@ net_ProcessPop3 (ActiveEntry *ce) case POP3_INTERRUPTED: { - PL_strcpy(cd->output_buffer, "QUIT" CRLF); + XP_STRCPY(cd->output_buffer, "QUIT" CRLF); NET_BlockingWrite(ce->socket, cd->output_buffer, - PL_strlen(cd->output_buffer)); + XP_STRLEN(cd->output_buffer)); PR_Shutdown(ce->socket, PR_SHUTDOWN_SEND); /* make sure QUIT get send * before closing down the socket */ @@ -2931,7 +3112,7 @@ net_ProcessPop3 (ActiveEntry *ce) DELE command until the messages are known to be safely on disk. */ - PL_strcpy(cd->output_buffer, "RSET" CRLF); + XP_STRCPY(cd->output_buffer, "RSET" CRLF); net_pop3_send_command(ce, cd->output_buffer); #endif /* 0 */ @@ -2963,9 +3144,10 @@ net_ProcessPop3 (ActiveEntry *ce) context = MSG_GetContext(parentpane); } } - PR_ASSERT (!cd->password_failed); + XP_ASSERT (!cd->password_failed); MSG_AbortMailDelivery(cd->pane); - if (context) FE_Progress(context, statusString); + if (context) + NET_Progress(context, statusString); FREEIF(statusString); } @@ -2998,8 +3180,8 @@ net_ProcessPop3 (ActiveEntry *ce) FREEIF(cd->sender_info); FREE(ce->con_data); - /* release the semaphore which prevents biff from interrupting connections */ - net_pop3_block_biff = FALSE; + /* release the semaphore which prevents POP3 from creating more connections */ + net_pop3_block = FALSE; if (oldStatus == MK_INTERRUPTED) return MK_INTERRUPTED; /* Make sure everyone knows we got canceled */ @@ -3007,7 +3189,7 @@ net_ProcessPop3 (ActiveEntry *ce) break; default: - PR_ASSERT(0); + XP_ASSERT(0); } /* end switch */ @@ -3032,7 +3214,7 @@ net_InterruptPop3(ActiveEntry * ce) { Pop3ConData * cd = (Pop3ConData *)ce->con_data; - TRACEMSG(("NET_InterruptPop3 called")); + TRACEMSG(("net_InterruptPop3 called")); cd->next_state = POP3_SEND_QUIT; /* interrupt does not give enough time for the quit command to be executed on the server, leaving us diff --git a/mozilla/network/protocol/pop3/mkpop3.h b/mozilla/network/protocol/pop3/mkpop3.h index c0c84ac6e39..050400b978b 100644 --- a/mozilla/network/protocol/pop3/mkpop3.h +++ b/mozilla/network/protocol/pop3/mkpop3.h @@ -20,7 +20,8 @@ extern void NET_InitPop3Protocol(void); -MODULE_PRIVATE PRBool NET_GetAllowAtSignInMailUserName(); -MODULE_PRIVATE void NET_SetAllowAtSignInMailUserName(PRBool allow); +MODULE_PRIVATE XP_Bool NET_GetAllowAtSignInMailUserName(); +MODULE_PRIVATE int32 net_ProcessPop3 (ActiveEntry *ce); +MODULE_PRIVATE void NET_SetAllowAtSignInMailUserName(XP_Bool allow); #endif /* not MKPOP3_H */ diff --git a/mozilla/network/protocol/smtp/Makefile b/mozilla/network/protocol/smtp/Makefile new file mode 100644 index 00000000000..4ca18421c85 --- /dev/null +++ b/mozilla/network/protocol/smtp/Makefile @@ -0,0 +1,33 @@ +#!gmake +# +# The contents of this file are subject to the Netscape Public License +# Version 1.0 (the "NPL"); you may not use this file except in +# compliance with the NPL. You may obtain a copy of the NPL at +# http://www.mozilla.org/NPL/ +# +# Software distributed under the NPL is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL +# for the specific language governing rights and limitations under the +# NPL. +# +# The Initial Developer of this code under the NPL is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All Rights +# Reserved. + +DEPTH = ../../.. + +MODULE = smtpurl +LIBRARY_NAME = smtpurl + +CSRCS = \ + mksmtp.c \ + $(NULL) + +EXPORTS= mksmtp.h + +REQUIRES = netcache network nspr2 dbm util pref js java fileurl \ + security layer img httpurl mimetype htmldlgs + +include $(DEPTH)/config/rules.mk + diff --git a/mozilla/network/protocol/smtp/makefile.win b/mozilla/network/protocol/smtp/makefile.win new file mode 100644 index 00000000000..0605105e28c --- /dev/null +++ b/mozilla/network/protocol/smtp/makefile.win @@ -0,0 +1,86 @@ +#!gmake +# +# The contents of this file are subject to the Netscape Public License +# Version 1.0 (the "NPL"); you may not use this file except in +# compliance with the NPL. You may obtain a copy of the NPL at +# http://www.mozilla.org/NPL/ +# +# Software distributed under the NPL is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL +# for the specific language governing rights and limitations under the +# NPL. +# +# The Initial Developer of this code under the NPL is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All Rights +# Reserved. + +IGNORE_MANIFEST=1 +# + +#------------------------------------------------------------------------ +# +# Makefile to build the Network file protocol LIB +# +#------------------------------------------------------------------------ + + +# +# Make sure we have MOZILLA_CLIENT defined so we get the +# proper JS includes +# +LCFLAGS = $(LCFLAGS) -DMOZILLA_CLIENT + +!ifdef BUILD_DEBUG_GC +LCFLAGS = $(LCFLAGS) -DDEBUG_GC +!endif + +LLIBS= \ + $(NULL) +MISCDEP=$(LLIBS) +OBJS= \ + .\$(OBJDIR)\mksmtp.obj \ + $(NULL) + +CSRCS = \ + mksmtp.c \ + $(NULL) + + +LIBRARY_NAME=smtpurl +MODULE=smtpurl +DEPTH=..\..\.. + +LOCAL_INCLUDES=-I. -I$(DEPTH)/dist/public/zlib -I$(DEPTH)/dist/public/parse +INCLUDES = $(LOCAL_INCLUDES) + + +EXTRA_LIBS= + +REQUIRES= network ldap +EXPORTS= mksmtp.h + +# use LINCS on win32 for now since REQUIRES seems to be broken +#!if "$(MOZ_BITS)" != "16" +LINCS= \ + -I$(PUBLIC)\nspr2 \ + -I$(PUBLIC)\util \ + -I$(PUBLIC)\java \ + -I$(PUBLIC)\pref \ + -I$(PUBLIC)\htmldlgs \ + -I$(PUBLIC)\js \ + -I$(PUBLIC)\security \ + -I$(PUBLIC)\netcache \ + -I$(PUBLIC)\network \ + -I$(PUBLIC)\ldap \ + -I$(PUBLIC)\mimetype \ +#!endif + +include <$(DEPTH)\config\rules.mak> + +libs:: $(LIBRARY) + $(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib + +symbols:: + @echo "LIBRARY_NAME is $(LIBRARY_NAME)" + @echo "LIBRARY is $(LIBRARY)" diff --git a/mozilla/network/protocol/smtp/mksmtp.c b/mozilla/network/protocol/smtp/mksmtp.c index 7e5aff8a7cd..b58ca5857a9 100644 --- a/mozilla/network/protocol/smtp/mksmtp.c +++ b/mozilla/network/protocol/smtp/mksmtp.c @@ -37,16 +37,20 @@ #include "xp_time.h" #include "xp_thrmo.h" #include "merrors.h" -#include "ssl.h" +#include HG23535 #include "imap.h" +#include "xp_error.h" #include "prefapi.h" +#include "libi18n.h" #ifdef AUTH_SKEY_DEFINED extern int btoa8(char *out, char*in); #endif extern void NET_SetPopPassword2(const char *password); +extern void net_free_write_post_data_object(struct WritePostDataData *obj); +MODULE_PRIVATE char* NET_MailRelayHost(MWContext *context); /* for XP_GetString() */ #include "xpgetstr.h" @@ -69,7 +73,7 @@ extern int MK_POP3_USERNAME_UNDEFINED; extern int MK_POP3_PASSWORD_UNDEFINED; extern int XP_PASSWORD_FOR_POP3_USER; extern int XP_RETURN_RECEIPT_NOT_SUPPORT; -extern int XP_SENDMAIL_BAD_TLS; +extern int MK_SENDMAIL_BAD_TLS; #define SMTP_PORT 25 @@ -97,7 +101,6 @@ extern int XP_SENDMAIL_BAD_TLS; #define SMTP_AUTH_LOGIN_RESPONSE 18 #define SMTP_AUTH_RESPONSE 19 - HG08747 /* structure to hold data pertaining to the active state of @@ -176,10 +179,10 @@ NET_MailRelayHost(MWContext *context); MODULE_PRIVATE char * NET_MailRelayHost(MWContext *context) { - if(net_smtp_relay_host) - return(net_smtp_relay_host); + if (net_smtp_relay_host) + return net_smtp_relay_host; else - return(""); /* caller now checks for empty string and returns MK_MSG_NO_SMTP_HOST */ + return ""; /* caller now checks for empty string and returns MK_MSG_NO_SMTP_HOST */ } PUBLIC void @@ -193,7 +196,7 @@ NET_SetMailRelayHost(char * host) ** @ signs in their host names will be hosed. They also can't possibly ** be current happy internet users. */ - if (host) at = PL_strchr(host, '@'); + if (host) at = XP_STRCHR(host, '@'); if (at != NULL) host = at + 1; StrAllocCopy(net_smtp_relay_host, host); } @@ -206,7 +209,7 @@ net_smtp_get_user_domain_name() { const char *mail_addr, *at_sign = NULL; mail_addr = FE_UsersMailAddress(); - at_sign = PL_strchr(mail_addr, '@'); + at_sign = XP_STRCHR(mail_addr, '@'); return (at_sign ? at_sign+1 : mail_addr); } @@ -252,7 +255,7 @@ net_smtp_get_user_domain_name() PRIVATE char * esmtp_value_encode(char *addr) { - char *buffer = PR_Malloc(512); /* esmpt ORCPT allow up to 500 chars encoded addresses */ + char *buffer = XP_ALLOC(512); /* esmpt ORCPT allow up to 500 chars encoded addresses */ char *bp = buffer, *bpEnd = buffer+500; int len, i; @@ -262,7 +265,7 @@ esmtp_value_encode(char *addr) if (! addr || *addr == 0) /* this will never happen */ return buffer; - for (i=0, len=PL_strlen(addr); i < len && bp < bpEnd; i++) + for (i=0, len=XP_STRLEN(addr); i < len && bp < bpEnd; i++) { if (*addr >= 0x21 && *addr <= 0x7E && @@ -274,7 +277,7 @@ esmtp_value_encode(char *addr) else { PR_snprintf(bp, bpEnd-bp, "+%.2X", ((int)*addr++)); - bp += PL_strlen(bp); + bp += XP_STRLEN(bp); } } *bp=0; @@ -293,6 +296,7 @@ net_smtp_response (ActiveEntry * cur_entry) char * line; char cont_char; SMTPConData * cd = (SMTPConData *)cur_entry->con_data; + int err = 0; CE_STATUS = NET_BufferedReadLine(CE_SOCK, &line, &CD_DATA_BUFFER, &CD_DATA_BUFFER_SIZE, &CD_PAUSE_FOR_READ); @@ -311,8 +315,9 @@ net_smtp_response (ActiveEntry * cur_entry) */ if(CE_STATUS < 0) { + HG22864 CE_URL_S->error_msg = - NET_ExplainErrorDetails(MK_TCP_READ_ERROR, PR_GetOSError()); + NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO); /* return TCP error */ @@ -333,7 +338,7 @@ net_smtp_response (ActiveEntry * cur_entry) if (cont_char == '-') /* begin continuation */ CD_CONTINUATION_RESPONSE = CD_RESPONSE_CODE; - if(PL_strlen(line) > 3) + if(XP_STRLEN(line) > 3) StrAllocCopy(CD_RESPONSE_TXT, line+4); } else @@ -342,7 +347,7 @@ net_smtp_response (ActiveEntry * cur_entry) CD_CONTINUATION_RESPONSE = -1; /* ended */ StrAllocCat(CD_RESPONSE_TXT, "\n"); - if(PL_strlen(line) > 3) + if(XP_STRLEN(line) > 3) StrAllocCat(CD_RESPONSE_TXT, line+4); } @@ -373,7 +378,7 @@ net_smtp_login_response(ActiveEntry *cur_entry) TRACEMSG(("Tx: %s", buffer)); - CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, PL_strlen(buffer)); + CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, XP_STRLEN(buffer)); CD_NEXT_STATE = SMTP_RESPONSE; CD_NEXT_STATE_AFTER_RESPONSE = SMTP_SEND_HELO_RESPONSE; @@ -400,7 +405,7 @@ net_smtp_extension_login_response(ActiveEntry *cur_entry) TRACEMSG(("Tx: %s", buffer)); - CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, PL_strlen(buffer)); + CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, XP_STRLEN(buffer)); CD_NEXT_STATE = SMTP_RESPONSE; CD_NEXT_STATE_AFTER_RESPONSE = SMTP_SEND_EHLO_RESPONSE; @@ -462,11 +467,11 @@ net_smtp_send_helo_response(ActiveEntry *cur_entry) else { PR_snprintf(buffer, sizeof(buffer), "MAIL FROM:<%.256s>" CRLF, s); } - PR_Free (s); + XP_FREE (s); } TRACEMSG(("Tx: %s", buffer)); - CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, PL_strlen(buffer)); + CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, XP_STRLEN(buffer)); CD_NEXT_STATE = SMTP_RESPONSE; @@ -496,7 +501,7 @@ HG85890 TRACEMSG(("Tx: %s", buffer)); - CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, PL_strlen(buffer)); + CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, XP_STRLEN(buffer)); CD_NEXT_STATE = SMTP_RESPONSE; CD_NEXT_STATE_AFTER_RESPONSE = SMTP_SEND_HELO_RESPONSE; @@ -507,21 +512,23 @@ HG85890 char *ptr = NULL; HG09714 - ptr = PL_strcasestr(CD_RESPONSE_TXT, "DSN"); - CD_EHLO_DSN_ENABLED = (ptr && NET_TO_UPPER(*(ptr-1)) != 'X'); + ptr = strcasestr(CD_RESPONSE_TXT, "DSN"); + CD_EHLO_DSN_ENABLED = (ptr && XP_TO_UPPER(*(ptr-1)) != 'X'); /* should we use auth login */ PREF_GetBoolPref("mail.auth_login", &(CD_AUTH_LOGIN_ENABLED)); if (CD_AUTH_LOGIN_ENABLED) { /* okay user has set to use skey let's see does the server have the capability */ - CD_AUTH_LOGIN_ENABLED = (NULL != PL_strcasestr(CD_RESPONSE_TXT, "AUTH=LOGIN")); + CD_AUTH_LOGIN_ENABLED = (NULL != strcasestr(CD_RESPONSE_TXT, "AUTH LOGIN")); + if (!CD_AUTH_LOGIN_ENABLED) + CD_AUTH_LOGIN_ENABLED = (NULL != strcasestr(CD_RESPONSE_TXT, "AUTH=LOGIN")); /* check old style */ } - -HG90967 + HG90967 + CD_NEXT_STATE = CD_NEXT_STATE_AFTER_RESPONSE = SMTP_AUTH_RESPONSE; - -HG59852 + + HG59852 return (CE_STATUS); } } @@ -543,7 +550,7 @@ net_smtp_auth_login_response(ActiveEntry *cur_entry) if ( IMAP_GetPassword() == NULL ) IMAP_SetPassword(net_smtp_password); #endif /* MOZ_MAIL_NEWS */ - PR_FREEIF(pop_password); + XP_FREEIF(pop_password); } break; case 3: @@ -556,14 +563,14 @@ net_smtp_auth_login_response(ActiveEntry *cur_entry) /* NET_GetPopUsername () returns pointer to the cached * username. It did *NOT* alloc a new string */ - PR_FREEIF(net_smtp_password); + XP_FREEIF(net_smtp_password); if (FE_PromptUsernameAndPassword(cur_entry->window_id, NULL, &pop_username, &net_smtp_password)) { CD_NEXT_STATE = SMTP_SEND_AUTH_LOGIN_USERNAME; /* FE_PromptUsernameAndPassword() always alloc a new string * for pop_username. The caller has to free it. */ - PR_FREEIF(pop_username); + XP_FREEIF(pop_username); } else { /* User hit cancel, but since the client and server both say @@ -592,16 +599,16 @@ net_smtp_auth_login_username(ActiveEntry *cur_entry) #ifdef MOZ_MAIL_NEWS base64Str = NET_Base64Encode(pop_username, - PL_strlen(pop_username)); + XP_STRLEN(pop_username)); if (base64Str) { PR_snprintf(buffer, sizeof(buffer), "AUTH LOGIN %.256s" CRLF, base64Str); TRACEMSG(("Tx: %s", buffer)); - CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, PL_strlen(buffer)); + CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, XP_STRLEN(buffer)); CD_NEXT_STATE = SMTP_RESPONSE; CD_NEXT_STATE_AFTER_RESPONSE = SMTP_AUTH_LOGIN_RESPONSE; CD_PAUSE_FOR_READ = TRUE; - PR_FREEIF(base64Str); + XP_FREEIF(base64Str); return (CE_STATUS); } @@ -623,7 +630,7 @@ net_smtp_auth_login_password(ActiveEntry *cur_entry) */ if (!net_smtp_password || !*net_smtp_password) { - PR_FREEIF(net_smtp_password); /* in case its an empty string */ + XP_FREEIF(net_smtp_password); /* in case its an empty string */ net_smtp_password = (char *) NET_GetPopPassword(); } @@ -632,35 +639,35 @@ net_smtp_auth_login_password(ActiveEntry *cur_entry) char host[256]; int len = 256; - memset(host, 0, 256); + XP_MEMSET(host, 0, 256); PREF_GetCharPref("network.hosts.smtp_server", host, &len); PR_snprintf(buffer, sizeof (buffer), fmt, NET_GetPopUsername(), host); - PR_FREEIF(net_smtp_password); + XP_FREEIF(net_smtp_password); net_smtp_password = FE_PromptPassword(cur_entry->window_id, buffer); if (!net_smtp_password) return MK_POP3_PASSWORD_UNDEFINED; } - PR_ASSERT(net_smtp_password); + XP_ASSERT(net_smtp_password); if (net_smtp_password) { char *base64Str = NULL; #ifdef MOZ_MAIL_NEWS - base64Str = NET_Base64Encode(net_smtp_password, PL_strlen(net_smtp_password)); + base64Str = NET_Base64Encode(net_smtp_password, XP_STRLEN(net_smtp_password)); #endif /* MOZ_MAIL_NEWS */ if (base64Str) { PR_snprintf(buffer, sizeof(buffer), "%.256s" CRLF, base64Str); TRACEMSG(("Tx: %s", buffer)); - CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, PL_strlen(buffer)); + CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, XP_STRLEN(buffer)); CD_NEXT_STATE = SMTP_RESPONSE; CD_NEXT_STATE_AFTER_RESPONSE = SMTP_AUTH_LOGIN_RESPONSE; CD_PAUSE_FOR_READ = TRUE; - PR_FREEIF(base64Str); + XP_FREEIF(base64Str); return (CE_STATUS); } @@ -681,7 +688,7 @@ net_smtp_send_vrfy_response(ActiveEntry *cur_entry) else return(MK_USER_NOT_VERIFIED_BY_SMTP); #else - PR_ASSERT(0); + XP_ASSERT(0); return(-1); #endif } @@ -711,7 +718,7 @@ net_smtp_send_mail_response(ActiveEntry *cur_entry) PR_snprintf(buffer, sizeof(buffer), "RCPT TO:<%.256s> NOTIFY=SUCCESS,FAILURE ORCPT=rfc822;%.500s" CRLF, CD_MAIL_TO_ADDRESS_PTR, encodedAddress); - PR_FREEIF(encodedAddress); + XP_FREEIF(encodedAddress); } else { CE_STATUS = MK_OUT_OF_MEMORY; @@ -724,12 +731,12 @@ net_smtp_send_mail_response(ActiveEntry *cur_entry) } /* take the address we sent off the list (move the pointer to just past the terminating null.) */ - CD_MAIL_TO_ADDRESS_PTR += PL_strlen (CD_MAIL_TO_ADDRESS_PTR) + 1; + CD_MAIL_TO_ADDRESS_PTR += XP_STRLEN (CD_MAIL_TO_ADDRESS_PTR) + 1; CD_MAIL_TO_ADDRESSES_LEFT--; TRACEMSG(("Tx: %s", buffer)); - CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, PL_strlen(buffer)); + CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, XP_STRLEN(buffer)); CD_NEXT_STATE = SMTP_RESPONSE; CD_NEXT_STATE_AFTER_RESPONSE = SMTP_SEND_RCPT_RESPONSE; @@ -761,11 +768,11 @@ net_smtp_send_rcpt_response(ActiveEntry *cur_entry) } /* else send the RCPT TO: command */ - PL_strcpy(buffer, "DATA" CRLF); + XP_STRCPY(buffer, "DATA" CRLF); TRACEMSG(("Tx: %s", buffer)); - CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, PL_strlen(buffer)); + CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, XP_STRLEN(buffer)); CD_NEXT_STATE = SMTP_RESPONSE; CD_NEXT_STATE_AFTER_RESPONSE = SMTP_SEND_DATA_RESPONSE; @@ -791,32 +798,40 @@ net_smtp_send_data_response(ActiveEntry *cur_entry) #ifdef XP_UNIX { const char * FE_UsersRealMailAddress(void); /* definition */ - const char *real_name = FE_UsersRealMailAddress(); - char *s = (real_name ? MSG_MakeFullAddress (NULL, real_name) : 0); - if (real_name && !s) + const char *real_name; + char *s = 0; + XP_Bool suppress_sender_header = FALSE; + + PREF_GetBoolPref ("mail.suppress_sender_header", &suppress_sender_header); + if (!suppress_sender_header) + { + real_name = FE_UsersRealMailAddress(); + s = (real_name ? MSG_MakeFullAddress (NULL, real_name) : 0); + if (real_name && !s) { CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY); return(MK_OUT_OF_MEMORY); } - if(real_name) + if(real_name) { - char buffer[512]; + char buffer[512]; PR_snprintf(buffer, sizeof(buffer), "Sender: %.256s" CRLF, real_name); StrAllocCat(command, buffer); - if(!command) - { + if(!command) + { CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY); return(MK_OUT_OF_MEMORY); - } + } } - } - - TRACEMSG(("sending extra unix header: %s", command)); - - CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, command, PL_strlen(command)); - if(CE_STATUS < 0) - { - TRACEMSG(("Error sending message")); + + TRACEMSG(("sending extra unix header: %s", command)); + + CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, command, XP_STRLEN(command)); + if(CE_STATUS < 0) + { + TRACEMSG(("Error sending message")); + } + } } #endif /* XP_UNIX */ @@ -877,11 +892,11 @@ net_smtp_send_post_data(ActiveEntry *cur_entry) { /* normal done */ - PL_strcpy(cd->data_buffer, CRLF "." CRLF); + XP_STRCPY(cd->data_buffer, CRLF "." CRLF); TRACEMSG(("sending %s", cd->data_buffer)); CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, cd->data_buffer, - PL_strlen(cd->data_buffer)); + XP_STRLEN(cd->data_buffer)); NET_Progress(CE_WINDOW_ID, XP_GetString(XP_MESSAGE_SENT_WAITING_MAIL_REPLY)); @@ -952,7 +967,7 @@ PRIVATE int32 net_MailtoLoad (ActiveEntry * cur_entry) { /* get memory for Connection Data */ - SMTPConData * cd = PR_NEW(SMTPConData); + SMTPConData * cd = XP_NEW(SMTPConData); int32 pref = 0; cur_entry->con_data = cd; @@ -966,13 +981,14 @@ net_MailtoLoad (ActiveEntry * cur_entry) /* GH_UpdateGlobalHistory(cur_entry->URL_s); */ /* init */ - memset(cd, 0, sizeof(SMTPConData)); + XP_MEMSET(cd, 0, sizeof(SMTPConData)); CD_CONTINUATION_RESPONSE = -1; /* init */ CE_SOCK = NULL; HG61365 + /* make a copy of the address */ if(CE_URL_S->method == URL_POST_METHOD) @@ -987,7 +1003,7 @@ HG61365 This causes the address list to be parsed twice; this probably doesn't matter. */ - addrs1 = MSG_RemoveDuplicateAddresses (CE_URL_S->address+7, 0); + addrs1 = MSG_RemoveDuplicateAddresses (CE_URL_S->address+7, 0, FALSE /*removeAliasesToMe*/); /* Extract just the mailboxes from the full RFC822 address list. This means that people can post to mailto: URLs which contain @@ -1031,12 +1047,13 @@ HG61365 One additional parameter is allowed, which does not correspond to a visible field: "newshost". This is the NNTP host (and port) - to connect to if newsgroups are specified. If the value of this - field ends in "/secure", then SSL will be used. + to connect to if newsgroups are specified. Each parameter may appear only once, but the order doesn't matter. All values must be URL-encoded. */ + HG27655 + char *parms = NET_ParseURL (CE_URL_S->address, GET_SEARCH_PART); char *rest = parms; char *from = 0; /* internal only */ @@ -1069,12 +1086,12 @@ HG61365 { /* start past the '?' */ rest++; - rest = strtok (rest, "&"); + rest = XP_STRTOK (rest, "&"); while (rest && *rest) { char *token = rest; char *value = 0; - char *eq = PL_strchr (token, '='); + char *eq = XP_STRCHR (token, '='); if (eq) { value = eq+1; @@ -1083,12 +1100,12 @@ HG61365 switch (*token) { case 'A': case 'a': - if (!PL_strcasecmp (token, "attachment") && + if (!strcasecomp (token, "attachment") && CE_URL_S->internal_url) StrAllocCopy (attachment, value); break; case 'B': case 'b': - if (!PL_strcasecmp (token, "bcc")) + if (!strcasecomp (token, "bcc")) { if (bcc && *bcc) { @@ -1100,7 +1117,7 @@ HG61365 StrAllocCopy (bcc, value); } } - else if (!PL_strcasecmp (token, "body")) + else if (!strcasecomp (token, "body")) { if (body && *body) { @@ -1114,7 +1131,7 @@ HG61365 } break; case 'C': case 'c': - if (!PL_strcasecmp (token, "cc")) + if (!strcasecomp (token, "cc")) { if (cc && *cc) { @@ -1128,60 +1145,60 @@ HG61365 } break; case 'E': case 'e': - if (!PL_strcasecmp (token, "encrypt") || - !PL_strcasecmp (token, "encrypted")) - encrypt_p = (!PL_strcasecmp(value, "true") || - !PL_strcasecmp(value, "yes")); + if (!strcasecomp (token, "encrypt") || + !strcasecomp (token, "encrypted")) + encrypt_p = (!strcasecomp(value, "true") || + !strcasecomp(value, "yes")); break; case 'F': case 'f': - if (!PL_strcasecmp (token, "followup-to")) + if (!strcasecomp (token, "followup-to")) StrAllocCopy (followup_to, value); - else if (!PL_strcasecmp (token, "from") && + else if (!strcasecomp (token, "from") && CE_URL_S->internal_url) StrAllocCopy (from, value); - else if (!PL_strcasecmp (token, "force-plain-text") && + else if (!strcasecomp (token, "force-plain-text") && CE_URL_S->internal_url) force_plain_text = TRUE; break; case 'H': case 'h': - if (!PL_strcasecmp(token, "html-part") && + if (!strcasecomp(token, "html-part") && CE_URL_S->internal_url) { StrAllocCopy(html_part, value); } case 'N': case 'n': - if (!PL_strcasecmp (token, "newsgroups")) + if (!strcasecomp (token, "newsgroups")) StrAllocCopy (newsgroups, value); - else if (!PL_strcasecmp (token, "newshost") && + else if (!strcasecomp (token, "newshost") && CE_URL_S->internal_url) StrAllocCopy (newshost, value); break; case 'O': case 'o': - if (!PL_strcasecmp (token, "organization") && + if (!strcasecomp (token, "organization") && CE_URL_S->internal_url) StrAllocCopy (organization, value); break; case 'R': case 'r': - if (!PL_strcasecmp (token, "references")) + if (!strcasecomp (token, "references")) StrAllocCopy (references, value); - else if (!PL_strcasecmp (token, "reply-to") && + else if (!strcasecomp (token, "reply-to") && CE_URL_S->internal_url) StrAllocCopy (reply_to, value); break; case 'S': case 's': - if(!PL_strcasecmp (token, "subject")) + if(!strcasecomp (token, "subject")) StrAllocCopy (subject, value); - else if ((!PL_strcasecmp (token, "sign") || - !PL_strcasecmp (token, "signed")) && + else if ((!strcasecomp (token, "sign") || + !strcasecomp (token, "signed")) && CE_URL_S->internal_url) - sign_p = (!PL_strcasecmp(value, "true") || - !PL_strcasecmp(value, "yes")); + sign_p = (!strcasecomp(value, "true") || + !strcasecomp(value, "yes")); break; case 'P': case 'p': - if (!PL_strcasecmp (token, "priority")) + if (!strcasecomp (token, "priority")) StrAllocCopy (priority, value); break; case 'T': case 't': - if (!PL_strcasecmp (token, "to")) + if (!strcasecomp (token, "to")) { if (to && *to) { @@ -1197,7 +1214,7 @@ HG61365 } if (eq) *eq = '='; /* put it back */ - rest = strtok (0, "&"); + rest = XP_STRTOK (0, "&"); } } @@ -1222,30 +1239,21 @@ HG61365 if(newshost) { char *prefix = "news://"; - char *slash = PL_strrchr (newshost, '/'); - if (slash && !PL_strcasecmp (slash, "/secure")) - { - *slash = 0; - prefix = "snews://"; - } - newspost_url = (char *) PR_Malloc (PL_strlen (prefix) + - PL_strlen (newshost) + 10); + char *slash = XP_STRRCHR (newshost, '/'); + HG83763 + newspost_url = (char *) XP_ALLOC (XP_STRLEN (prefix) + + XP_STRLEN (newshost) + 10); if (newspost_url) { - PL_strcpy (newspost_url, prefix); - PL_strcat (newspost_url, newshost); - PL_strcat (newspost_url, "/"); + XP_STRCPY (newspost_url, prefix); + XP_STRCAT (newspost_url, newshost); + XP_STRCAT (newspost_url, "/"); } } else { - XP_Bool newsServerIsSecure = FALSE; - PREF_GetBoolPref("news.server_is_secure", &newsServerIsSecure); - - if (newsServerIsSecure) - newspost_url = PL_strdup("snews:"); - else - newspost_url = PL_strdup ("news:"); + HG35353 + newspost_url = XP_STRDUP ("news:"); } /* Tell the message library and front end to pop up an edit window. @@ -1283,7 +1291,7 @@ HG61365 FREEIF(newspost_url); CE_STATUS = MK_NO_DATA; - PR_Free(cd); /* no one else is gonna do it! */ + XP_FREE(cd); /* no one else is gonna do it! */ return(-1); } } @@ -1294,7 +1302,7 @@ HG61365 /* We have connected to the mail relay and the type of authorization/login required has been established. Before we actually send our name and password check - and see if we should do anything else for the selected auth mode. + and see if we should or must turn the connection to safer mode. */ PRIVATE int @@ -1302,8 +1310,7 @@ NET_CheckAuthResponse (ActiveEntry *cur_entry) { SMTPConData * cd = (SMTPConData *)cur_entry->con_data; int err = 0; - -HG54978 + HG54978 if (CD_AUTH_LOGIN_ENABLED) { @@ -1346,7 +1353,7 @@ net_ProcessMailto (ActiveEntry *cur_entry) case SMTP_START_CONNECT: mail_relay_host = NET_MailRelayHost(CE_WINDOW_ID); - if (PL_strlen(mail_relay_host) == 0) + if (XP_STRLEN(mail_relay_host) == 0) { CE_STATUS = MK_MSG_NO_SMTP_HOST; break; @@ -1566,4 +1573,74 @@ NET_InitMailtoProtocol(void) } #endif /* defined(MOZ_MAIL_NEWS) || defined(MOZ_MAIL_COMPOSE) */ + +static void +MessageSendingDone(URL_Struct* url, int status, MWContext* context) +{ + if (status < 0) { + char *error_msg = NET_ExplainErrorDetails(status, 0, 0, 0, 0); + if (error_msg) { + FE_Alert(context, error_msg); + } + XP_FREEIF(error_msg); + } + if (url->post_data) { + XP_FREE(url->post_data); + url->post_data = NULL; + } + if (url->post_headers) { + XP_FREE(url->post_headers); + url->post_headers = NULL; + } + NET_FreeURLStruct(url); +} + + + +int +NET_SendMessageUnattended(MWContext* context, char* to, char* subject, + char* otherheaders, char* body) +{ + char* urlstring; + URL_Struct* url; + int16 win_csid; + int16 csid; + char* convto; + char* convsub; + + win_csid = INTL_DefaultWinCharSetID(context); + csid = INTL_DefaultMailCharSetID(win_csid); + convto = IntlEncodeMimePartIIStr(to, csid, TRUE); + convsub = IntlEncodeMimePartIIStr(subject, csid, TRUE); + + urlstring = PR_smprintf("mailto:%s", convto ? convto : to); + + if (!urlstring) return MK_OUT_OF_MEMORY; + url = NET_CreateURLStruct(urlstring, NET_DONT_RELOAD); + XP_FREE(urlstring); + if (!url) return MK_OUT_OF_MEMORY; + + + + url->post_headers = PR_smprintf("To: %s\n\ +Subject: %s\n\ +%s\n", + convto ? convto : to, + convsub ? convsub : subject, + otherheaders); + if (convto) XP_FREE(convto); + if (convsub) XP_FREE(convsub); + if (!url->post_headers) return MK_OUT_OF_MEMORY; + url->post_data = XP_STRDUP(body); + if (!url->post_data) return MK_OUT_OF_MEMORY; + url->post_data_size = XP_STRLEN(url->post_data); + url->post_data_is_file = FALSE; + url->method = URL_POST_METHOD; + url->internal_url = TRUE; + return NET_GetURL(url, FO_PRESENT, context, MessageSendingDone); + +} + + + #endif /* defined(MOZILLA_CLIENT) || defined(LIBNET_SMTP) */ diff --git a/mozilla/nsprpub/pr/include/prerror.h b/mozilla/nsprpub/pr/include/prerror.h index e93211a198c..0f56f106bad 100644 --- a/mozilla/nsprpub/pr/include/prerror.h +++ b/mozilla/nsprpub/pr/include/prerror.h @@ -25,7 +25,7 @@ PR_BEGIN_EXTERN_C typedef PRInt32 PRErrorCode; -#define PR_NSPR_ERROR_BASE -6000 +#define PR_NSPR_ERROR_BASE -2600 #define PR_OUT_OF_MEMORY_ERROR PR_NSPR_ERROR_BASE + 0 /* Insufficient memory to perform request */