/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Mike McCabe * John Bandhauer * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* Implementation of xptiManifest. */ #include "xptiprivate.h" #include "nsManifestLineReader.h" #include "nsString.h" static const char g_Disclaimer[] = "# Generated file. ** DO NOT EDIT! **"; static const char g_TOKEN_Files[] = "Files"; static const char g_TOKEN_ArchiveItems[] = "ArchiveItems"; static const char g_TOKEN_Interfaces[] = "Interfaces"; static const char g_TOKEN_Header[] = "Header"; static const char g_TOKEN_Version[] = "Version"; static const char g_TOKEN_AppDir[] = "AppDir"; static const char g_TOKEN_Directories[] = "Directories"; static const int g_VERSION_MAJOR = 2; static const int g_VERSION_MINOR = 0; /***************************************************************************/ static PRBool GetCurrentAppDirString(xptiInterfaceInfoManager* aMgr, nsACString &aStr) { nsCOMPtr appDir; aMgr->GetApplicationDir(getter_AddRefs(appDir)); if(appDir) return NS_SUCCEEDED(appDir->GetPersistentDescriptor(aStr)); return PR_FALSE; } static PRBool CurrentAppDirMatchesPersistentDescriptor(xptiInterfaceInfoManager* aMgr, const char *inStr) { nsCOMPtr appDir; aMgr->GetApplicationDir(getter_AddRefs(appDir)); nsCOMPtr descDir; nsresult rv = NS_NewNativeLocalFile(EmptyCString(), PR_FALSE, getter_AddRefs(descDir)); if(NS_FAILED(rv)) return PR_FALSE; rv = descDir->SetPersistentDescriptor(nsDependentCString(inStr)); if(NS_FAILED(rv)) return PR_FALSE; PRBool matches; rv = appDir->Equals(descDir, &matches); return NS_SUCCEEDED(rv) && matches; } PR_STATIC_CALLBACK(PLDHashOperator) xpti_InterfaceWriter(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number, void *arg) { xptiInterfaceEntry* entry = ((xptiHashEntry*)hdr)->value; PRFileDesc* fd = (PRFileDesc*) arg; char iidStr[NSID_LENGTH]; entry->GetTheIID()->ToProvidedString(iidStr); const xptiTypelib& typelib = entry->GetTypelibRecord(); PRBool success = !!PR_fprintf(fd, "%d,%s,%s,%d,%d,%d\n", (int) number, entry->GetTheName(), iidStr, (int) typelib.GetFileIndex(), (int) (typelib.IsZip() ? typelib.GetZipItemIndex() : -1), (int) entry->GetScriptableFlag()); return success ? PL_DHASH_NEXT : PL_DHASH_STOP; } // static PRBool xptiManifest::Write(xptiInterfaceInfoManager* aMgr, xptiWorkingSet* aWorkingSet) { PRBool succeeded = PR_FALSE; PRFileDesc* fd = nsnull; PRUint32 i; PRUint32 size32; PRIntn interfaceCount = 0; nsCAutoString appDirString; nsCOMPtr tempFile; if(!aMgr->GetCloneOfManifestLocation(getter_AddRefs(tempFile)) || !tempFile) return PR_FALSE; nsCAutoString originalLeafName; tempFile->GetNativeLeafName(originalLeafName); nsCAutoString leafName; leafName.Assign(originalLeafName + NS_LITERAL_CSTRING(".tmp")); tempFile->SetNativeLeafName(leafName); // All exits via "goto out;" from here on... if(NS_FAILED(tempFile-> OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0666, &fd)) || !fd) { goto out; } // write file header comments if(!PR_fprintf(fd, "%s\n", g_Disclaimer)) goto out; // write the [Header] block, version number, and appdir. if(!PR_fprintf(fd, "\n[%s,%d]\n", g_TOKEN_Header, 2)) goto out; if(!PR_fprintf(fd, "%d,%s,%d,%d\n", 0, g_TOKEN_Version, g_VERSION_MAJOR, g_VERSION_MINOR)) goto out; GetCurrentAppDirString(aMgr, appDirString); if(appDirString.IsEmpty()) goto out; if(!PR_fprintf(fd, "%d,%s,%s\n", 1, g_TOKEN_AppDir, appDirString.get())) goto out; // write Directories list if(!PR_fprintf(fd, "\n[%s,%d]\n", g_TOKEN_Directories, (int) aWorkingSet->GetDirectoryCount())) goto out; for(i = 0; i < aWorkingSet->GetDirectoryCount(); i++) { nsCOMPtr dir; nsCAutoString str; aWorkingSet->GetDirectoryAt(i, getter_AddRefs(dir)); if(!dir) goto out; dir->GetPersistentDescriptor(str); if(str.IsEmpty()) goto out; if(!PR_fprintf(fd, "%d,%s\n", (int) i, str.get())) goto out; } // write Files list if(!PR_fprintf(fd, "\n[%s,%d]\n", g_TOKEN_Files, (int) aWorkingSet->GetFileCount())) goto out; for(i = 0; i < aWorkingSet->GetFileCount(); i++) { const xptiFile& file = aWorkingSet->GetFileAt(i); LL_L2UI(size32, file.GetSize()); if(!PR_fprintf(fd, "%d,%s,%d,%u,%lld\n", (int) i, file.GetName(), (int) file.GetDirectory(), size32, PRInt64(file.GetDate()))) goto out; } // write ArchiveItems list if(!PR_fprintf(fd, "\n[%s,%d]\n", g_TOKEN_ArchiveItems, (int) aWorkingSet->GetZipItemCount())) goto out; for(i = 0; i < aWorkingSet->GetZipItemCount(); i++) { if(!PR_fprintf(fd, "%d,%s\n", (int) i, aWorkingSet->GetZipItemAt(i).GetName())) goto out; } // write the Interfaces list interfaceCount = aWorkingSet->mNameTable->entryCount; if(!PR_fprintf(fd, "\n[%s,%d]\n", g_TOKEN_Interfaces, (int) interfaceCount)) goto out; if(interfaceCount != (PRIntn) PL_DHashTableEnumerate(aWorkingSet->mNameTable, xpti_InterfaceWriter, fd)) goto out; if(PR_SUCCESS == PR_Close(fd)) { succeeded = PR_TRUE; } fd = nsnull; out: if(fd) PR_Close(fd); if(succeeded) { // delete the old file and rename this nsCOMPtr mainFile; if(!aMgr->GetCloneOfManifestLocation(getter_AddRefs(mainFile)) || !mainFile) return PR_FALSE; PRBool exists; if(NS_FAILED(mainFile->Exists(&exists))) return PR_FALSE; if(exists && NS_FAILED(mainFile->Remove(PR_FALSE))) return PR_FALSE; nsCOMPtr parent; mainFile->GetParent(getter_AddRefs(parent)); // MoveTo means rename. if(NS_FAILED(tempFile->MoveToNative(parent, originalLeafName))) return PR_FALSE; } return succeeded; } /***************************************************************************/ /***************************************************************************/ static char* ReadManifestIntoMemory(xptiInterfaceInfoManager* aMgr, PRUint32* pLength) { PRFileDesc* fd = nsnull; PRInt32 flen; PRInt64 fileSize; char* whole = nsnull; PRBool success = PR_FALSE; nsCOMPtr aFile; if(!aMgr->GetCloneOfManifestLocation(getter_AddRefs(aFile)) || !aFile) return nsnull; if(NS_FAILED(aFile->GetFileSize(&fileSize)) || !(flen = nsInt64(fileSize))) return nsnull; whole = new char[flen]; if (!whole) return nsnull; // All exits from on here should be via 'goto out' if(NS_FAILED(aFile->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd)) || !fd) goto out; if(flen > PR_Read(fd, whole, flen)) goto out; success = PR_TRUE; out: if(fd) PR_Close(fd); if(!success) { delete [] whole; return nsnull; } *pLength = flen; return whole; } static PRBool ReadSectionHeader(nsManifestLineReader& reader, const char *token, int minCount, int* count) { while(1) { if(!reader.NextLine()) break; if(*reader.LinePtr() == '[') { char* p = reader.LinePtr() + (reader.LineLength() - 1); if(*p != ']') break; *p = 0; char* values[2]; int lengths[2]; if(2 != reader.ParseLine(values, lengths, 2)) break; // ignore the leading '[' if(0 != PL_strcmp(values[0]+1, token)) break; if((*count = atoi(values[1])) < minCount) break; return PR_TRUE; } } return PR_FALSE; } // static PRBool xptiManifest::Read(xptiInterfaceInfoManager* aMgr, xptiWorkingSet* aWorkingSet) { int i; char* whole = nsnull; PRBool succeeded = PR_FALSE; PRUint32 flen; nsManifestLineReader reader; xptiHashEntry* hashEntry; int headerCount = 0; int dirCount = 0; int fileCount = 0; int zipItemCount = -1; int interfaceCount = 0; int dir; int flags; char* values[6]; // 6 is currently the max items we need to parse int lengths[6]; PRUint32 size32; PRInt64 size; PRInt64 date; whole = ReadManifestIntoMemory(aMgr, &flen); if(!whole) return PR_FALSE; reader.Init(whole, flen); // All exits from here on should be via 'goto out' // Look for "Header" section // This version accepts only version 1,0. We also freak if the header // has more than one entry. The rationale is that we want to force an // autoreg if the xpti.dat file was written by *any* other version of // the software. Future versions may wish to support updating older // manifests in some interesting way. if(!ReadSectionHeader(reader, g_TOKEN_Header, 2, &headerCount)) goto out; if(headerCount != 2) goto out; // Verify the version number if(!reader.NextLine()) goto out; // index,VersionLiteral,major,minor if(4 != reader.ParseLine(values, lengths, 4)) goto out; // index if(0 != atoi(values[0])) goto out; // VersionLiteral if(0 != PL_strcmp(values[1], g_TOKEN_Version)) goto out; // major if(g_VERSION_MAJOR != atoi(values[2])) goto out; // minor if(g_VERSION_MINOR != atoi(values[3])) goto out; // Verify the application directory if(!reader.NextLine()) goto out; // index,AppDirLiteral,directoryname if(3 != reader.ParseLine(values, lengths, 3)) goto out; // index if(1 != atoi(values[0])) goto out; // AppDirLiteral if(0 != PL_strcmp(values[1], g_TOKEN_AppDir)) goto out; if(!CurrentAppDirMatchesPersistentDescriptor(aMgr, values[2])) goto out; // Look for "Directories" section if(!ReadSectionHeader(reader, g_TOKEN_Directories, 1, &dirCount)) goto out; else { // To validate that the directory list matches the current search path // we first confirm that the list lengths match. nsCOMPtr searchPath; aMgr->GetSearchPath(getter_AddRefs(searchPath)); PRUint32 searchPathCount; searchPath->Count(&searchPathCount); if(dirCount != (int) searchPathCount) goto out; } // Read the directory records for(i = 0; i < dirCount; ++i) { if(!reader.NextLine()) goto out; // index,directoryname if(2 != reader.ParseLine(values, lengths, 2)) goto out; // index if(i != atoi(values[0])) goto out; // directoryname if(!aWorkingSet->DirectoryAtMatchesPersistentDescriptor(i, values[1])) goto out; } // Look for "Files" section if(!ReadSectionHeader(reader, g_TOKEN_Files, 1, &fileCount)) goto out; // Alloc room in the WorkingSet for the filearray. if(!aWorkingSet->NewFileArray(fileCount)) goto out; // Read the file records for(i = 0; i < fileCount; ++i) { if(!reader.NextLine()) goto out; // index,filename,dirIndex,dilesSize,filesDate if(5 != reader.ParseLine(values, lengths, 5)) goto out; // index if(i != atoi(values[0])) goto out; // filename if(!*values[1]) goto out; // dirIndex dir = atoi(values[2]); if(dir < 0 || dir > dirCount) goto out; // fileSize size32 = atoi(values[3]); if(size32 <= 0) goto out; LL_UI2L(size, size32); // fileDate date = nsCRT::atoll(values[4]); if(LL_IS_ZERO(date)) goto out; // Append a new file record to the array. aWorkingSet->AppendFile( xptiFile(nsInt64(size), nsInt64(date), dir, values[1], aWorkingSet)); } // Look for "ZipItems" section if(!ReadSectionHeader(reader, g_TOKEN_ArchiveItems, 0, &zipItemCount)) goto out; // Alloc room in the WorkingSet for the zipItemarray. if(zipItemCount) if(!aWorkingSet->NewZipItemArray(zipItemCount)) goto out; // Read the zipItem records for(i = 0; i < zipItemCount; ++i) { if(!reader.NextLine()) goto out; // index,filename if(2 != reader.ParseLine(values, lengths, 2)) goto out; // index if(i != atoi(values[0])) goto out; // filename if(!*values[1]) goto out; // Append a new zipItem record to the array. aWorkingSet->AppendZipItem(xptiZipItem(values[1], aWorkingSet)); } // Look for "Interfaces" section if(!ReadSectionHeader(reader, g_TOKEN_Interfaces, 1, &interfaceCount)) goto out; // Read the interface records for(i = 0; i < interfaceCount; ++i) { int fileIndex; int zipItemIndex; nsIID iid; xptiInterfaceEntry* entry; xptiTypelib typelibRecord; if(!reader.NextLine()) goto out; // index,interfaceName,iid,fileIndex,zipIndex,flags if(6 != reader.ParseLine(values, lengths, 6)) goto out; // index if(i != atoi(values[0])) goto out; // interfaceName if(!*values[1]) goto out; // iid if(!iid.Parse(values[2])) goto out; // fileIndex fileIndex = atoi(values[3]); if(fileIndex < 0 || fileIndex >= fileCount) goto out; // zipIndex (NOTE: -1 is a valid value) zipItemIndex = atoi(values[4]); if(zipItemIndex < -1 || zipItemIndex >= zipItemCount) goto out; // flags flags = atoi(values[5]); if(flags != 0 && flags != 1) goto out; // Build an InterfaceInfo and hook it in. if(zipItemIndex == -1) typelibRecord.Init(fileIndex); else typelibRecord.Init(fileIndex, zipItemIndex); entry = xptiInterfaceEntry::NewEntry(values[1], lengths[1], iid, typelibRecord, aWorkingSet); if(!entry) goto out; entry->SetScriptableFlag(flags==1); // Add our entry to the iid hashtable. hashEntry = (xptiHashEntry*) PL_DHashTableOperate(aWorkingSet->mNameTable, entry->GetTheName(), PL_DHASH_ADD); if(hashEntry) hashEntry->value = entry; // Add our entry to the name hashtable. hashEntry = (xptiHashEntry*) PL_DHashTableOperate(aWorkingSet->mIIDTable, entry->GetTheIID(), PL_DHASH_ADD); if(hashEntry) hashEntry->value = entry; } // success! succeeded = PR_TRUE; out: if(whole) delete [] whole; if(!succeeded) { // Cleanup the WorkingSet on failure. aWorkingSet->InvalidateInterfaceInfos(); aWorkingSet->ClearHashTables(); aWorkingSet->ClearFiles(); } return succeeded; } // static PRBool xptiManifest::Delete(xptiInterfaceInfoManager* aMgr) { nsCOMPtr aFile; if(!aMgr->GetCloneOfManifestLocation(getter_AddRefs(aFile)) || !aFile) return PR_FALSE; PRBool exists; if(NS_FAILED(aFile->Exists(&exists))) return PR_FALSE; if(exists && NS_FAILED(aFile->Remove(PR_FALSE))) return PR_FALSE; return PR_TRUE; }