// distribution boxbackup-0.11.1 (svn version: 2821_2827)
// Box Backup, http://www.boxbackup.org/
// 
// Copyright (c) 2003-2010, Ben Summers and contributors.
// All rights reserved.
// 
// Note that this project uses mixed licensing. Any file with this license
// attached, or where the code LICENSE-GPL appears on the first line, falls
// under the "Box Backup GPL" license. See the file COPYING.txt for more
// information about this license.
// 
// ---------------------------------------------------------------------
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
// 
// [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html#SEC4]
// 
// As a special exception to the GPLv2, the Box Backup Project gives
// permission to link any code falling under this license (the Box Backup
// GPL) with any software that can be downloaded from
// the OpenSSL website [http://www.openssl.org] under either the
// "OpenSSL License" or the "Original SSLeay License", and to distribute
// the linked executables under the terms of the "Box Backup GPL" license.
// 
// As a special exception to the GPLv2, the Box Backup Project gives
// permission to link any code falling under this license (the Box Backup
// GPL) with any version of Microsoft's Volume Shadow Copy Service 7.2 SDK
// or Microsoft Windows Software Development Kit (SDK), including
// vssapi.lib, that can be downloaded from the Microsoft website
// [*.microsoft.com], and to distribute the linked executables under the
// terms of the "Box Backup GPL" license.
// --------------------------------------------------------------------------
//
// File
//		Name:    BackupStoreObjectDump.cpp
//		Purpose: Implementations of dumping objects to stdout/TRACE
//		Created: 3/5/04
//
// --------------------------------------------------------------------------

#include "Box.h"

#include <stdio.h>
#include <stdarg.h>
#include <map>

#include "BackupStoreDirectory.h"
#include "BackupStoreFile.h"
#include "BackupStoreFileWire.h"
#include "autogen_BackupStoreException.h"
#include "BackupStoreFilename.h"
#include "BackupClientFileAttributes.h"
#include "BackupStoreObjectMagic.h"

#include "MemLeakFindOn.h"


// --------------------------------------------------------------------------
//
// Function
//		Name:    static void OutputLine(FILE *, bool, const char *, ...)
//		Purpose: Output a line for the object dumping, to file and/or trace...
//		Created: 3/5/04
//
// --------------------------------------------------------------------------
static void OutputLine(FILE *file, bool ToTrace, const char *format, ...)
{
	char text[512];
	int r = 0;
	va_list ap;
	va_start(ap, format);
	r = vsnprintf(text, sizeof(text), format, ap);
	va_end(ap);

	if(file != 0)
	{
		::fprintf(file, "%s", text);		
	}
	if(ToTrace)
	{
		BOX_TRACE(text);
	}
}


// --------------------------------------------------------------------------
//
// Function
//		Name:    BackupStoreDirectory::Dump(void *clibFileHandle, bool ToTrace)
//		Purpose: (first arg is FILE *, but avoid including stdio.h everywhere)
//				 Dump the contents to a file, or trace.
//		Created: 3/5/04
//
// --------------------------------------------------------------------------
void BackupStoreDirectory::Dump(void *clibFileHandle, bool ToTrace)
{
	FILE *file = (FILE*)clibFileHandle;

	OutputLine(file, ToTrace, "Directory object.\nObject ID: %llx\nContainer ID: %llx\nNumber entries: %d\n"\
		"Attributes mod time: %llx\nAttributes size: %d\n", mObjectID, mContainerID, mEntries.size(),
		mAttributesModTime, mAttributes.GetSize());

	// So repeated filenames can be illustrated, even though they can't be decoded
	std::map<std::string, int> nameNum;
	int nameNumI = 0;

	// Dump items
	OutputLine(file, ToTrace, "Items:\nID     Size AttrHash         AtSz NSz NIdx Flags\n");
	for(std::vector<Entry*>::const_iterator i(mEntries.begin()); i != mEntries.end(); ++i)
	{
		// Choose file name index number for this file
		std::map<std::string, int>::iterator nn(nameNum.find((*i)->GetName().GetEncodedFilename()));
		int ni = nameNumI;
		if(nn != nameNum.end())
		{
			ni = nn->second;
		}
		else
		{
			nameNum[(*i)->GetName().GetEncodedFilename()] = nameNumI;
			++nameNumI;
		}
		
		// Do dependencies
		char depends[128];
		depends[0] = '\0';
		int depends_l = 0;
		if((*i)->GetDependsNewer() != 0)
		{
#ifdef _MSC_VER
			depends_l += ::sprintf(depends + depends_l, " depNew(%I64x)", (*i)->GetDependsNewer());
#else
			depends_l += ::sprintf(depends + depends_l, " depNew(%llx)", (long long)((*i)->GetDependsNewer()));
#endif
		}
		if((*i)->GetDependsOlder() != 0)
		{
#ifdef _MSC_VER
			depends_l += ::sprintf(depends + depends_l, " depOld(%I64x)", (*i)->GetDependsOlder());
#else
			depends_l += ::sprintf(depends + depends_l, " depOld(%llx)", (long long)((*i)->GetDependsOlder()));
#endif
		}

		// Output item
		int16_t f = (*i)->GetFlags();
#ifdef WIN32
		OutputLine(file, ToTrace, 
			"%06I64x %4I64d %016I64x %4d %3d %4d%s%s%s%s%s%s\n",
#else
		OutputLine(file, ToTrace, 
			"%06llx %4lld %016llx %4d %3d %4d%s%s%s%s%s%s\n",
#endif
			(*i)->GetObjectID(),
			(*i)->GetSizeInBlocks(),
			(*i)->GetAttributesHash(),
			(*i)->GetAttributes().GetSize(),
			(*i)->GetName().GetEncodedFilename().size(),
			ni,
			((f & BackupStoreDirectory::Entry::Flags_File)?" file":""),
			((f & BackupStoreDirectory::Entry::Flags_Dir)?" dir":""),
			((f & BackupStoreDirectory::Entry::Flags_Deleted)?" del":""),
			((f & BackupStoreDirectory::Entry::Flags_OldVersion)?" old":""),
			((f & BackupStoreDirectory::Entry::Flags_RemoveASAP)?" removeASAP":""),
			depends);
	}
}

// --------------------------------------------------------------------------
//
// Function
//		Name:    BackupStoreFile::DumpFile(void *, bool, IOStream &)
//		Purpose: (first arg is FILE *, but avoid including stdio.h everywhere)
//				 Dump the contents to a file, or trace.
//		Created: 4/5/04
//
// --------------------------------------------------------------------------
void BackupStoreFile::DumpFile(void *clibFileHandle, bool ToTrace, IOStream &rFile)
{
	FILE *file = (FILE*)clibFileHandle;

	// Read header
	file_StreamFormat hdr;
	if(!rFile.ReadFullBuffer(&hdr, sizeof(hdr),
		0 /* not interested in bytes read if this fails */, IOStream::TimeOutInfinite))
	{
		// Couldn't read header
		THROW_EXCEPTION(BackupStoreException, WhenDecodingExpectedToReadButCouldnt)
	}

	// Check and output header info
	if(hdr.mMagicValue != (int32_t)htonl(OBJECTMAGIC_FILE_MAGIC_VALUE_V1)
		&& hdr.mMagicValue != (int32_t)htonl(OBJECTMAGIC_FILE_MAGIC_VALUE_V0))
	{
		OutputLine(file, ToTrace, "File header doesn't have the correct magic, aborting dump\n");
		return;
	}

	OutputLine(file, ToTrace, "File object.\nContainer ID: %llx\nModification time: %llx\n"\
		"Max block clear size: %d\nOptions: %08x\nNum blocks: %d\n", box_ntoh64(hdr.mContainerID),
			box_ntoh64(hdr.mModificationTime), ntohl(hdr.mMaxBlockClearSize), ntohl(hdr.mOptions),
			box_ntoh64(hdr.mNumBlocks));

	// Read the next two objects
	BackupStoreFilename fn;
	fn.ReadFromStream(rFile, IOStream::TimeOutInfinite);
	OutputLine(file, ToTrace, "Filename size: %d\n",
		fn.GetEncodedFilename().size());
	
	BackupClientFileAttributes attr;
	attr.ReadFromStream(rFile, IOStream::TimeOutInfinite);
	OutputLine(file, ToTrace, "Attributes size: %d\n", attr.GetSize());
	
	// Dump the blocks
	rFile.Seek(0, IOStream::SeekType_Absolute);
	BackupStoreFile::MoveStreamPositionToBlockIndex(rFile);

	// Read in header
	file_BlockIndexHeader bhdr;
	rFile.ReadFullBuffer(&bhdr, sizeof(bhdr), 0);
	if(bhdr.mMagicValue != (int32_t)htonl(OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V1)
		&& bhdr.mMagicValue != (int32_t)htonl(OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V0))
	{
		OutputLine(file, ToTrace, "WARNING: Block header doesn't have the correct magic\n");
	}
	// number of blocks
	int64_t nblocks = box_ntoh64(bhdr.mNumBlocks);
	OutputLine(file, ToTrace, "Other file ID (for block refs): %llx\nNum blocks (in blk hdr): %lld\n",
		box_ntoh64(bhdr.mOtherFileID), nblocks);

	// Dump info about each block
	OutputLine(file, ToTrace, "======== ===== ==========\n   Index Where  EncSz/Idx\n");
	int64_t nnew = 0, nold = 0;
	for(int64_t b = 0; b < nblocks; ++b)
	{
		file_BlockIndexEntry en;
		if(!rFile.ReadFullBuffer(&en, sizeof(en), 0))
		{
			OutputLine(file, ToTrace, "Didn't manage to read block %lld from file\n", b);
			continue;
		}
		int64_t s = box_ntoh64(en.mEncodedSize);
		if(s > 0)
		{
			nnew++;
			BOX_TRACE(std::setw(8) << b << " this  s=" <<
				std::setw(8) << s);
		}
		else
		{
			nold++;
			BOX_TRACE(std::setw(8) << b << " other i=" <<
				std::setw(8) << 0 - s);
		}
	}
	BOX_TRACE("======== ===== ==========");
}

