<commit_before>/*
 -----------------------------------------------------------------------------
 This source file is part of OGRE
 (Object-oriented Graphics Rendering Engine)
 For the latest info, see http://www.ogre3d.org/

 Copyright (c) 2000-2012 Torus Knot Software Ltd

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 -----------------------------------------------------------------------------
 */
#include "OgreStableHeaders.h"
#include "OgreDeflate.h"
#include "OgreException.h"

#include <zlib.h>

namespace Ogre
{
	// memory implementations
	void* OgreZalloc(void* opaque, unsigned int items, unsigned int size)
	{
		return OGRE_MALLOC(items * size, MEMCATEGORY_GENERAL);
	}
	void OgreZfree(void* opaque, void* address)
	{
		OGRE_FREE(address, MEMCATEGORY_GENERAL);
	}
	#define OGRE_DEFLATE_TMP_SIZE 16384
    //---------------------------------------------------------------------
	DeflateStream::DeflateStream(const DataStreamPtr& compressedStream, const String& tmpFileName)
	: DataStream(compressedStream->getAccessMode())
	, mCompressedStream(compressedStream)
    , mTempFileName(tmpFileName)
	, mZStream(0)
	, mCurrentPos(0)
	, mTmp(0)
	, mIsCompressedValid(true)
	{
		init();
	}
    //---------------------------------------------------------------------
	DeflateStream::DeflateStream(const String& name, const DataStreamPtr& compressedStream, const String& tmpFileName)
	: DataStream(name, compressedStream->getAccessMode())
	, mCompressedStream(compressedStream)
    , mTempFileName(tmpFileName)
	, mZStream(0)
	, mCurrentPos(0)
	, mTmp(0)
	, mIsCompressedValid(true)
	{
		init();
	}
    //---------------------------------------------------------------------
	void DeflateStream::init()
	{
		mZStream = OGRE_ALLOC_T(z_stream, 1, MEMCATEGORY_GENERAL);
		mZStream->zalloc = OgreZalloc;
		mZStream->zfree = OgreZfree;

		if (getAccessMode() == READ)
		{
			mTmp = (unsigned char*)OGRE_MALLOC(OGRE_DEFLATE_TMP_SIZE, MEMCATEGORY_GENERAL);
			size_t restorePoint = mCompressedStream->tell();
			// read early chunk
			mZStream->next_in = mTmp;
			mZStream->avail_in = mCompressedStream->read(mTmp, OGRE_DEFLATE_TMP_SIZE);

			if (inflateInit(mZStream) != Z_OK)
			{
				mIsCompressedValid = false;
			}
			else
				mIsCompressedValid = true;

			if (mIsCompressedValid)
			{
				// in fact, inflateInit on some implementations doesn't try to read
				// anything. We need to at least read something to test
				Bytef testOut[4];
				size_t savedIn = mZStream->avail_in;
				mZStream->avail_out = 4;
				mZStream->next_out = testOut;
				if (inflate(mZStream, Z_SYNC_FLUSH) != Z_OK)
					mIsCompressedValid = false;
				// restore for reading
				mZStream->avail_in = savedIn;
				mZStream->next_in = mTmp;

				inflateReset(mZStream);
			}

			if (!mIsCompressedValid)
			{
				// Not compressed data!
				// Fail gracefully, fall back on reading the underlying stream direct
				destroy();
				mCompressedStream->seek(restorePoint);
			}
		}
		else
		{
            if(mTempFileName.empty())
            {
                // Write to temp file
                char tmpname[L_tmpnam];
                tmpnam(tmpname);
                mTempFileName = tmpname;
            }

			std::fstream *f = OGRE_NEW_T(std::fstream, MEMCATEGORY_GENERAL)();
			f->open(mTempFileName.c_str(), std::ios::binary | std::ios::out);
			mTmpWriteStream = DataStreamPtr(OGRE_NEW FileStreamDataStream(f));

		}

	}
    //---------------------------------------------------------------------
	void DeflateStream::destroy()
	{
		if (getAccessMode() == READ)
			inflateEnd(mZStream);

		OGRE_FREE(mZStream, MEMCATEGORY_GENERAL);
		mZStream = 0;
		OGRE_FREE(mTmp, MEMCATEGORY_GENERAL);
		mTmp = 0;
	}
	//---------------------------------------------------------------------
	DeflateStream::~DeflateStream()
	{
		close();
		destroy();
	}
    //---------------------------------------------------------------------
	size_t DeflateStream::read(void* buf, size_t count)
	{
		if (!mIsCompressedValid)
		{
			return mCompressedStream->read(buf, count);
		}

		if (getAccessMode() & WRITE)
		{
			return mTmpWriteStream->read(buf, count);
		}
		else
		{

			size_t restorePoint = mCompressedStream->tell();
			// read from cache first
			size_t cachereads = mReadCache.read(buf, count);

			size_t newReadUncompressed = 0;

			if (cachereads < count)
			{
				mZStream->avail_out = count - cachereads;
				mZStream->next_out = (Bytef*)buf + cachereads;

				while (mZStream->avail_out)
				{
					// Pull next chunk of compressed data from the underlying stream
					if (!mZStream->avail_in && !mCompressedStream->eof())
					{
						mZStream->avail_in = mCompressedStream->read(mTmp, OGRE_DEFLATE_TMP_SIZE);
						mZStream->next_in = mTmp;
					}

					if (mZStream->avail_in)
					{
						int availpre = mZStream->avail_out;
						int status = inflate(mZStream, Z_SYNC_FLUSH);
						size_t readUncompressed = availpre - mZStream->avail_out;
						newReadUncompressed += readUncompressed;
						if (status != Z_OK)
						{
							// End of data, or error
							if (status != Z_STREAM_END)
							{
								mCompressedStream->seek(restorePoint);
								OGRE_EXCEPT(Exception::ERR_INVALID_STATE,
											"Error in compressed stream",
											"DeflateStrea::read");
							}
							else
							{
								// back up the stream so that it can be used from the end onwards
								long unusedCompressed = mZStream->avail_in;
								mCompressedStream->skip(-unusedCompressed);
							}

							break;
						}
					}
				}
			}

			// Cache the last bytes read
			mReadCache.cacheData((char*)buf + cachereads, newReadUncompressed);

			mCurrentPos += newReadUncompressed + cachereads;

			return newReadUncompressed + cachereads;
		}
	}
    //---------------------------------------------------------------------
	size_t DeflateStream::write(const void* buf, size_t count)
	{
		if ((getAccessMode() & WRITE) == 0)
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
						"Not a writable stream", "DeflateStream::write");

		return mTmpWriteStream->write(buf, count);
	}
    //---------------------------------------------------------------------
	void DeflateStream::compressFinal()
	{
		// Close temp stream
		mTmpWriteStream->close();

		// Copy & compress
		// We do this rather than compress directly because some code seeks
		// around while writing (e.g. to update size blocks) which is not
		// possible when compressing on the fly

		int ret, flush;
		char in[OGRE_DEFLATE_TMP_SIZE];
		char out[OGRE_DEFLATE_TMP_SIZE];

		if (deflateInit(mZStream, Z_DEFAULT_COMPRESSION) != Z_OK)
		{
			destroy();
			OGRE_EXCEPT(Exception::ERR_INVALID_STATE,
						"Error initialising deflate compressed stream!",
						"DeflateStream::init");
		}

		std::ifstream inFile;
		inFile.open(mTempFileName.c_str(), std::ios::in | std::ios::binary);

		do
		{
			inFile.read(in, OGRE_DEFLATE_TMP_SIZE);
			mZStream->avail_in = (uInt)inFile.gcount();
			if (inFile.bad())
			{
				deflateEnd(mZStream);
				OGRE_EXCEPT(Exception::ERR_INVALID_STATE,
							"Error reading temp uncompressed stream!",
							"DeflateStream::init");
			}
			flush = inFile.eof() ? Z_FINISH : Z_NO_FLUSH;
			mZStream->next_in = (Bytef*)in;

			/* run deflate() on input until output buffer not full, finish
			 compression if all of source has been read in */
			do
			{
				mZStream->avail_out = OGRE_DEFLATE_TMP_SIZE;
				mZStream->next_out = (Bytef*)out;
				ret = deflate(mZStream, flush);    /* no bad return value */
				assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
				size_t compressed = OGRE_DEFLATE_TMP_SIZE - mZStream->avail_out;
				mCompressedStream->write(out, compressed);
			} while (mZStream->avail_out == 0);
			assert(mZStream->avail_in == 0);     /* all input will be used */

			/* done when last data in file processed */
		} while (flush != Z_FINISH);
		assert(ret == Z_STREAM_END);        /* stream will be complete */

		deflateEnd(mZStream);

        inFile.close();
		remove(mTempFileName.c_str());

	}
    //---------------------------------------------------------------------
	void DeflateStream::skip(long count)
	{
		if (!mIsCompressedValid)
		{
			mCompressedStream->skip(count);
			return;
		}

		if (getAccessMode() & WRITE)
		{
			mTmpWriteStream->skip(count);
		}
		else
		{
			if (count > 0)
			{
				if (!mReadCache.ff(count))
				{
					OGRE_EXCEPT(Exception::ERR_INVALID_STATE,
								"You can only skip within the cache range in a deflate stream.",
								"DeflateStream::skip");
				}
			}
			else if (count < 0)
			{
				if (!mReadCache.rewind((size_t)(-count)))
				{
					OGRE_EXCEPT(Exception::ERR_INVALID_STATE,
								"You can only skip within the cache range in a deflate stream.",
								"DeflateStream::skip");
				}
			}
		}
		mCurrentPos = static_cast<size_t>(static_cast<long>(mCurrentPos) + count);

	}
    //---------------------------------------------------------------------
	void DeflateStream::seek( size_t pos )
	{
		if (!mIsCompressedValid)
		{
			mCompressedStream->seek(pos);
			return;
		}
		if (getAccessMode() & WRITE)
		{
			mTmpWriteStream->seek(pos);
		}
		else
		{
			if (pos == 0)
			{
				mCurrentPos = 0;
				mZStream->next_in = mTmp;
				mCompressedStream->seek(0);
				mZStream->avail_in = mCompressedStream->read(mTmp, OGRE_DEFLATE_TMP_SIZE);
				inflateReset(mZStream);
			}
			else
			{
				skip(pos - tell());
			}
		}
	}
    //---------------------------------------------------------------------
	size_t DeflateStream::tell(void) const
	{
		if (!mIsCompressedValid)
		{
			return mCompressedStream->tell();
		}
		else if(getAccessMode() & WRITE)
		{
			return mTmpWriteStream->tell();
		}
		else
		{
			return mCurrentPos;
		}

	}
    //---------------------------------------------------------------------
	bool DeflateStream::eof(void) const
	{
		if (getAccessMode() & WRITE)
			return mTmpWriteStream->eof();
		else
		{
			if (!mIsCompressedValid)
				return mCompressedStream->eof();
			else
				return mCompressedStream->eof() && mZStream->avail_in == 0;
		}
	}
    //---------------------------------------------------------------------
	void DeflateStream::close(void)
	{
		if (getAccessMode() & WRITE)
		{
			compressFinal();
		}

		// don't close underlying compressed stream in case used for something else
	}
    //---------------------------------------------------------------------

}

<commit_msg>Fix [3538259]: Use _tempnam on Windows to generate temporary file name in OgreDeflate.cpp<commit_after>/*
 -----------------------------------------------------------------------------
 This source file is part of OGRE
 (Object-oriented Graphics Rendering Engine)
 For the latest info, see http://www.ogre3d.org/

 Copyright (c) 2000-2012 Torus Knot Software Ltd

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 -----------------------------------------------------------------------------
 */
#include "OgreStableHeaders.h"
#include "OgreDeflate.h"
#include "OgreException.h"

#include <zlib.h>

namespace Ogre
{
	// memory implementations
	void* OgreZalloc(void* opaque, unsigned int items, unsigned int size)
	{
		return OGRE_MALLOC(items * size, MEMCATEGORY_GENERAL);
	}
	void OgreZfree(void* opaque, void* address)
	{
		OGRE_FREE(address, MEMCATEGORY_GENERAL);
	}
	#define OGRE_DEFLATE_TMP_SIZE 16384
    //---------------------------------------------------------------------
	DeflateStream::DeflateStream(const DataStreamPtr& compressedStream, const String& tmpFileName)
	: DataStream(compressedStream->getAccessMode())
	, mCompressedStream(compressedStream)
    , mTempFileName(tmpFileName)
	, mZStream(0)
	, mCurrentPos(0)
	, mTmp(0)
	, mIsCompressedValid(true)
	{
		init();
	}
    //---------------------------------------------------------------------
	DeflateStream::DeflateStream(const String& name, const DataStreamPtr& compressedStream, const String& tmpFileName)
	: DataStream(name, compressedStream->getAccessMode())
	, mCompressedStream(compressedStream)
    , mTempFileName(tmpFileName)
	, mZStream(0)
	, mCurrentPos(0)
	, mTmp(0)
	, mIsCompressedValid(true)
	{
		init();
	}
    //---------------------------------------------------------------------
	void DeflateStream::init()
	{
		mZStream = OGRE_ALLOC_T(z_stream, 1, MEMCATEGORY_GENERAL);
		mZStream->zalloc = OgreZalloc;
		mZStream->zfree = OgreZfree;

		if (getAccessMode() == READ)
		{
			mTmp = (unsigned char*)OGRE_MALLOC(OGRE_DEFLATE_TMP_SIZE, MEMCATEGORY_GENERAL);
			size_t restorePoint = mCompressedStream->tell();
			// read early chunk
			mZStream->next_in = mTmp;
			mZStream->avail_in = mCompressedStream->read(mTmp, OGRE_DEFLATE_TMP_SIZE);

			if (inflateInit(mZStream) != Z_OK)
			{
				mIsCompressedValid = false;
			}
			else
				mIsCompressedValid = true;

			if (mIsCompressedValid)
			{
				// in fact, inflateInit on some implementations doesn't try to read
				// anything. We need to at least read something to test
				Bytef testOut[4];
				size_t savedIn = mZStream->avail_in;
				mZStream->avail_out = 4;
				mZStream->next_out = testOut;
				if (inflate(mZStream, Z_SYNC_FLUSH) != Z_OK)
					mIsCompressedValid = false;
				// restore for reading
				mZStream->avail_in = savedIn;
				mZStream->next_in = mTmp;

				inflateReset(mZStream);
			}

			if (!mIsCompressedValid)
			{
				// Not compressed data!
				// Fail gracefully, fall back on reading the underlying stream direct
				destroy();
				mCompressedStream->seek(restorePoint);
			}
		}
		else
		{
            if(mTempFileName.empty())
            {
                // Write to temp file
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
                char* tmpname = _tempnam(".", "ogre");
                if (!tmpname)
                {
                    // Having no file name here will cause various problems later.
                    OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Temporary file name generation failed.", "DeflateStream::init");
                }
                else
                {
                    mTempFileName = tmpname;
                    free(tmpname);
                }
#else
                char tmpname[L_tmpnam];
                tmpnam(tmpname);
                mTempFileName = tmpname;
#endif
            }

			std::fstream *f = OGRE_NEW_T(std::fstream, MEMCATEGORY_GENERAL)();
			f->open(mTempFileName.c_str(), std::ios::binary | std::ios::out);
			mTmpWriteStream = DataStreamPtr(OGRE_NEW FileStreamDataStream(f));

		}

	}
    //---------------------------------------------------------------------
	void DeflateStream::destroy()
	{
		if (getAccessMode() == READ)
			inflateEnd(mZStream);

		OGRE_FREE(mZStream, MEMCATEGORY_GENERAL);
		mZStream = 0;
		OGRE_FREE(mTmp, MEMCATEGORY_GENERAL);
		mTmp = 0;
	}
	//---------------------------------------------------------------------
	DeflateStream::~DeflateStream()
	{
		close();
		destroy();
	}
    //---------------------------------------------------------------------
	size_t DeflateStream::read(void* buf, size_t count)
	{
		if (!mIsCompressedValid)
		{
			return mCompressedStream->read(buf, count);
		}

		if (getAccessMode() & WRITE)
		{
			return mTmpWriteStream->read(buf, count);
		}
		else
		{

			size_t restorePoint = mCompressedStream->tell();
			// read from cache first
			size_t cachereads = mReadCache.read(buf, count);

			size_t newReadUncompressed = 0;

			if (cachereads < count)
			{
				mZStream->avail_out = count - cachereads;
				mZStream->next_out = (Bytef*)buf + cachereads;

				while (mZStream->avail_out)
				{
					// Pull next chunk of compressed data from the underlying stream
					if (!mZStream->avail_in && !mCompressedStream->eof())
					{
						mZStream->avail_in = mCompressedStream->read(mTmp, OGRE_DEFLATE_TMP_SIZE);
						mZStream->next_in = mTmp;
					}

					if (mZStream->avail_in)
					{
						int availpre = mZStream->avail_out;
						int status = inflate(mZStream, Z_SYNC_FLUSH);
						size_t readUncompressed = availpre - mZStream->avail_out;
						newReadUncompressed += readUncompressed;
						if (status != Z_OK)
						{
							// End of data, or error
							if (status != Z_STREAM_END)
							{
								mCompressedStream->seek(restorePoint);
								OGRE_EXCEPT(Exception::ERR_INVALID_STATE,
											"Error in compressed stream",
											"DeflateStrea::read");
							}
							else
							{
								// back up the stream so that it can be used from the end onwards
								long unusedCompressed = mZStream->avail_in;
								mCompressedStream->skip(-unusedCompressed);
							}

							break;
						}
					}
				}
			}

			// Cache the last bytes read
			mReadCache.cacheData((char*)buf + cachereads, newReadUncompressed);

			mCurrentPos += newReadUncompressed + cachereads;

			return newReadUncompressed + cachereads;
		}
	}
    //---------------------------------------------------------------------
	size_t DeflateStream::write(const void* buf, size_t count)
	{
		if ((getAccessMode() & WRITE) == 0)
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
						"Not a writable stream", "DeflateStream::write");

		return mTmpWriteStream->write(buf, count);
	}
    //---------------------------------------------------------------------
	void DeflateStream::compressFinal()
	{
		// Close temp stream
		mTmpWriteStream->close();

		// Copy & compress
		// We do this rather than compress directly because some code seeks
		// around while writing (e.g. to update size blocks) which is not
		// possible when compressing on the fly

		int ret, flush;
		char in[OGRE_DEFLATE_TMP_SIZE];
		char out[OGRE_DEFLATE_TMP_SIZE];

		if (deflateInit(mZStream, Z_DEFAULT_COMPRESSION) != Z_OK)
		{
			destroy();
			OGRE_EXCEPT(Exception::ERR_INVALID_STATE,
						"Error initialising deflate compressed stream!",
						"DeflateStream::init");
		}

		std::ifstream inFile;
		inFile.open(mTempFileName.c_str(), std::ios::in | std::ios::binary);

		do
		{
			inFile.read(in, OGRE_DEFLATE_TMP_SIZE);
			mZStream->avail_in = (uInt)inFile.gcount();
			if (inFile.bad())
			{
				deflateEnd(mZStream);
				OGRE_EXCEPT(Exception::ERR_INVALID_STATE,
							"Error reading temp uncompressed stream!",
							"DeflateStream::init");
			}
			flush = inFile.eof() ? Z_FINISH : Z_NO_FLUSH;
			mZStream->next_in = (Bytef*)in;

			/* run deflate() on input until output buffer not full, finish
			 compression if all of source has been read in */
			do
			{
				mZStream->avail_out = OGRE_DEFLATE_TMP_SIZE;
				mZStream->next_out = (Bytef*)out;
				ret = deflate(mZStream, flush);    /* no bad return value */
				assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
				size_t compressed = OGRE_DEFLATE_TMP_SIZE - mZStream->avail_out;
				mCompressedStream->write(out, compressed);
			} while (mZStream->avail_out == 0);
			assert(mZStream->avail_in == 0);     /* all input will be used */

			/* done when last data in file processed */
		} while (flush != Z_FINISH);
		assert(ret == Z_STREAM_END);        /* stream will be complete */

		deflateEnd(mZStream);

        inFile.close();
		remove(mTempFileName.c_str());

	}
    //---------------------------------------------------------------------
	void DeflateStream::skip(long count)
	{
		if (!mIsCompressedValid)
		{
			mCompressedStream->skip(count);
			return;
		}

		if (getAccessMode() & WRITE)
		{
			mTmpWriteStream->skip(count);
		}
		else
		{
			if (count > 0)
			{
				if (!mReadCache.ff(count))
				{
					OGRE_EXCEPT(Exception::ERR_INVALID_STATE,
								"You can only skip within the cache range in a deflate stream.",
								"DeflateStream::skip");
				}
			}
			else if (count < 0)
			{
				if (!mReadCache.rewind((size_t)(-count)))
				{
					OGRE_EXCEPT(Exception::ERR_INVALID_STATE,
								"You can only skip within the cache range in a deflate stream.",
								"DeflateStream::skip");
				}
			}
		}
		mCurrentPos = static_cast<size_t>(static_cast<long>(mCurrentPos) + count);

	}
    //---------------------------------------------------------------------
	void DeflateStream::seek( size_t pos )
	{
		if (!mIsCompressedValid)
		{
			mCompressedStream->seek(pos);
			return;
		}
		if (getAccessMode() & WRITE)
		{
			mTmpWriteStream->seek(pos);
		}
		else
		{
			if (pos == 0)
			{
				mCurrentPos = 0;
				mZStream->next_in = mTmp;
				mCompressedStream->seek(0);
				mZStream->avail_in = mCompressedStream->read(mTmp, OGRE_DEFLATE_TMP_SIZE);
				inflateReset(mZStream);
			}
			else
			{
				skip(pos - tell());
			}
		}
	}
    //---------------------------------------------------------------------
	size_t DeflateStream::tell(void) const
	{
		if (!mIsCompressedValid)
		{
			return mCompressedStream->tell();
		}
		else if(getAccessMode() & WRITE)
		{
			return mTmpWriteStream->tell();
		}
		else
		{
			return mCurrentPos;
		}

	}
    //---------------------------------------------------------------------
	bool DeflateStream::eof(void) const
	{
		if (getAccessMode() & WRITE)
			return mTmpWriteStream->eof();
		else
		{
			if (!mIsCompressedValid)
				return mCompressedStream->eof();
			else
				return mCompressedStream->eof() && mZStream->avail_in == 0;
		}
	}
    //---------------------------------------------------------------------
	void DeflateStream::close(void)
	{
		if (getAccessMode() & WRITE)
		{
			compressFinal();
		}

		// don't close underlying compressed stream in case used for something else
	}
    //---------------------------------------------------------------------

}

<|endoftext|>


<commit_before>/*
    Copyright (C) 2013  Martin Klapetek <mklapetek@kde.org>
    Copyright (C) 2014  David Edmundson <davidedmundson@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "im-persons-data-source.h"

#include <TelepathyQt/AccountManager>
#include <TelepathyQt/AccountFactory>
#include <TelepathyQt/ContactManager>
#include <TelepathyQt/PendingOperation>
#include <TelepathyQt/PendingReady>
#include <TelepathyQt/Presence>

#include "KTp/contact-factory.h"
#include "KTp/global-contact-manager.h"
#include "KTp/types.h"
#include "debug.h"

#include <KPeopleBackend/AllContactsMonitor>

#include <KPluginFactory>
#include <KPluginLoader>

#include <QSqlDatabase>
#include <QSqlQuery>
#include <QPixmap>

using namespace KPeople;

class KTpAllContacts : public AllContactsMonitor
{
    Q_OBJECT
public:
    KTpAllContacts();
    ~KTpAllContacts();
    virtual QMap<QString, AbstractContact::Ptr> contacts() Q_DECL_OVERRIDE;

private Q_SLOTS:
    void loadCache();
    void onAccountManagerReady(Tp::PendingOperation *op);
    void onContactChanged();
    void onContactInvalidated();
    void onAllKnownContactsChanged(const Tp::Contacts &contactsAdded, const Tp::Contacts &contactsRemoved);

private:
    QString createUri(const KTp::ContactPtr &contact) const;

    //presence names indexed by ConnectionPresenceType
    QHash<QString, KTp::ContactPtr> m_contacts;
    QMap<QString, AbstractContact::Ptr> m_contactVCards;
};

static const QString S_KPEOPLE_PROPERTY_ACCOUNT_PATH = QString::fromLatin1("telepathy-accountPath");
static const QString S_KPEOPLE_PROPERTY_CONTACT_ID = QString::fromLatin1("telepathy-contactId");
static const QString S_KPEOPLE_PROPERTY_PRESENCE = QString::fromLatin1("telepathy-presence");
const QHash<Tp::ConnectionPresenceType, QString> s_presenceStrings = {
        { Tp::ConnectionPresenceTypeUnset, QString() },
        { Tp::ConnectionPresenceTypeOffline, QString::fromLatin1("offline") },
        { Tp::ConnectionPresenceTypeAvailable, QString::fromLatin1("available") },
        { Tp::ConnectionPresenceTypeAway, QString::fromLatin1("away") },
        { Tp::ConnectionPresenceTypeExtendedAway, QString::fromLatin1("xa") },
        { Tp::ConnectionPresenceTypeHidden, QString::fromLatin1("hidden") }, //of 'offline' ?
        { Tp::ConnectionPresenceTypeBusy, QString::fromLatin1("busy") },
        { Tp::ConnectionPresenceTypeUnknown, QString() },
        { Tp::ConnectionPresenceTypeError, QString() }
};

class TelepathyContact : public KPeople::AbstractContact
{
public:
    virtual QVariant customProperty(const QString &key) const Q_DECL_OVERRIDE
    {
        if (m_contact && m_account) {
            if (key == AbstractContact::NameProperty)
                return m_contact->alias();
            else if(key == AbstractContact::GroupsProperty)
                return m_contact->groups();
            else if(key == S_KPEOPLE_PROPERTY_CONTACT_ID)
                return m_contact->id();
            else if(key == S_KPEOPLE_PROPERTY_ACCOUNT_PATH)
                return m_account->objectPath();
            else if(key == S_KPEOPLE_PROPERTY_PRESENCE)
                return s_presenceStrings.value(m_contact->presence().type());
            else if (key == AbstractContact::PictureProperty)
                return m_contact->avatarPixmap();
        }
        return m_properties[key];
    }

    void insertProperty(const QString &key, const QVariant &value)
    {
        m_properties[key] = value;
    }

    void setContact(const KTp::ContactPtr &contact)
    {
        m_contact = contact;
    }

    void setAccount(const Tp::AccountPtr &account)
    {
        m_account = account;
    }

private:
    KTp::ContactPtr m_contact;
    Tp::AccountPtr m_account;
    QVariantMap m_properties;
};

KTpAllContacts::KTpAllContacts()
{
    Tp::registerTypes();

    loadCache();
}

KTpAllContacts::~KTpAllContacts()
{
}

void KTpAllContacts::loadCache()
{
    QSqlDatabase db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), QLatin1String("ktpCache"));
    QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/ktp");
    QDir().mkpath(path);
    db.setDatabaseName(path+QStringLiteral("/cache.db"));
    if (!db.open()) {
        qWarning() << "couldn't open database" << db.databaseName();
    }

    QSqlQuery query(db);
    query.exec(QLatin1String("SELECT groupName FROM groups ORDER BY groupId;"));

    QStringList groupsList;
    while (query.next()) {
        groupsList.append(query.value(0).toString());
    }

    if (!groupsList.isEmpty()) {
        query.exec(QLatin1String("SELECT accountId, contactId, alias, avatarFileName, groupsIds FROM contacts;"));
    } else {
        query.exec(QLatin1String("SELECT accountId, contactId, alias, avatarFileName FROM contacts;"));
    }

    while (query.next()) {
        QExplicitlySharedDataPointer<TelepathyContact> addressee(new TelepathyContact);

        const QString accountId =  query.value(0).toString();
        const QString contactId =  query.value(1).toString();
        addressee->insertProperty(AbstractContact::NameProperty, query.value(2).toString());
        addressee->insertProperty(AbstractContact::PictureProperty, QUrl::fromLocalFile(query.value(3).toString()));

        if (!groupsList.isEmpty()) {
            QVariantList contactGroups;

            Q_FOREACH (const QString &groupIdStr, query.value(4).toString().split(QLatin1String(","))) {
                bool convSuccess;
                int groupId = groupIdStr.toInt(&convSuccess);
                if ((!convSuccess) || (groupId >= groupsList.count()))
                    continue;

                contactGroups.append(groupsList.at(groupId));
            }

            addressee->insertProperty(QStringLiteral("groups"), contactGroups);
        }

        addressee->insertProperty(S_KPEOPLE_PROPERTY_CONTACT_ID, contactId);
        addressee->insertProperty(S_KPEOPLE_PROPERTY_ACCOUNT_PATH, TP_QT_ACCOUNT_OBJECT_PATH_BASE + QLatin1Char('/') + accountId);
        addressee->insertProperty(S_KPEOPLE_PROPERTY_PRESENCE, s_presenceStrings[Tp::ConnectionPresenceTypeOffline]);

        const QString uri = QLatin1String("ktp://") + accountId + QLatin1Char('?') + contactId;

        m_contactVCards[uri] = addressee;
        Q_EMIT contactAdded(uri, addressee);
    }

    //now start fetching the up-to-date information
    connect(KTp::accountManager()->becomeReady(), SIGNAL(finished(Tp::PendingOperation*)),
        this, SLOT(onAccountManagerReady(Tp::PendingOperation*)));

    emitInitialFetchComplete(true);
}

QString KTpAllContacts::createUri(const KTp::ContactPtr &contact) const
{
    // so real ID will look like
    // ktp://gabble/jabber/blah/asdfjwer?foo@bar.com
    // ? is used as it is not a valid character in the dbus path that makes up the account UID
    return QLatin1String("ktp://") + contact->accountUniqueIdentifier() + QLatin1Char('?') + contact->id();
}

void KTpAllContacts::onAccountManagerReady(Tp::PendingOperation *op)
{
    if (op->isError()) {
        qCWarning(KTP_KPEOPLE) << "Failed to initialize AccountManager:" << op->errorName();
        qCWarning(KTP_KPEOPLE) << op->errorMessage();

        return;
    }

    qCDebug(KTP_KPEOPLE) << "Account manager ready";

    connect(KTp::contactManager(), SIGNAL(allKnownContactsChanged(Tp::Contacts,Tp::Contacts)),
            this, SLOT(onAllKnownContactsChanged(Tp::Contacts,Tp::Contacts)));

    onAllKnownContactsChanged(KTp::contactManager()->allKnownContacts(), Tp::Contacts());
}

void KTpAllContacts::onAllKnownContactsChanged(const Tp::Contacts &contactsAdded, const Tp::Contacts &contactsRemoved)
{
    if (!m_contacts.isEmpty()) {
        Q_FOREACH (const Tp::ContactPtr &c, contactsRemoved) {
            const KTp::ContactPtr &contact = KTp::ContactPtr::qObjectCast(c);
            const QString uri = createUri(contact);
            m_contacts.remove(uri);
            m_contactVCards.remove(uri);
            Q_EMIT contactRemoved(uri);
        }
    }

    Q_FOREACH (const Tp::ContactPtr &contact, contactsAdded) {
        KTp::ContactPtr ktpContact = KTp::ContactPtr::qObjectCast(contact);
        const QString uri = createUri(ktpContact);

        AbstractContact::Ptr vcard = m_contactVCards.value(uri);
        bool added = false;
        if (!vcard) {
            vcard = AbstractContact::Ptr(new TelepathyContact);
            m_contactVCards[uri] = vcard;
            added = true;
        }
        static_cast<TelepathyContact*>(vcard.data())->setContact(ktpContact);
        static_cast<TelepathyContact*>(vcard.data())->setAccount(KTp::contactManager()->accountForContact(ktpContact));

        m_contacts.insert(uri, ktpContact);

        if (added) {
            Q_EMIT contactAdded(uri, vcard);
        } else {
            Q_EMIT contactChanged(uri, vcard);
        }

        connect(ktpContact.data(), SIGNAL(presenceChanged(Tp::Presence)),
                this, SLOT(onContactChanged()));

        connect(ktpContact.data(), SIGNAL(capabilitiesChanged(Tp::ContactCapabilities)),
                this, SLOT(onContactChanged()));

        connect(ktpContact.data(), SIGNAL(invalidated()),
                this, SLOT(onContactInvalidated()));

        connect(ktpContact.data(), SIGNAL(avatarDataChanged(Tp::AvatarData)),
                this, SLOT(onContactChanged()));

        connect(ktpContact.data(), SIGNAL(addedToGroup(QString)),
                this, SLOT(onContactChanged()));

        connect(ktpContact.data(), SIGNAL(removedFromGroup(QString)),
                this, SLOT(onContactChanged()));
    }
}

void KTpAllContacts::onContactChanged()
{
    const KTp::ContactPtr contact(qobject_cast<KTp::Contact*>(sender()));
    const QString uri = createUri(contact);
    Q_EMIT contactChanged(uri, m_contactVCards.value(uri));
}

void KTpAllContacts::onContactInvalidated()
{
    const KTp::ContactPtr contact(qobject_cast<KTp::Contact*>(sender()));
    const QString uri = createUri(contact);

    m_contacts.remove(uri);

    //set to offline and emit changed
    AbstractContact::Ptr vcard = m_contactVCards[uri];
    TelepathyContact *tpContact = static_cast<TelepathyContact*>(vcard.data());
    tpContact->insertProperty(S_KPEOPLE_PROPERTY_PRESENCE, QStringLiteral("offline"));
    Q_EMIT contactChanged(uri, vcard);
}

QMap<QString, AbstractContact::Ptr> KTpAllContacts::contacts()
{
    return m_contactVCards;
}

IMPersonsDataSource::IMPersonsDataSource(QObject *parent, const QVariantList &args)
    : BasePersonsDataSource(parent)
{
    Q_UNUSED(args);
}

IMPersonsDataSource::~IMPersonsDataSource()
{
}

QString IMPersonsDataSource::sourcePluginId() const
{
    return QStringLiteral("ktp");
}

AllContactsMonitor* IMPersonsDataSource::createAllContactsMonitor()
{
    return new KTpAllContacts();
}

K_PLUGIN_FACTORY( IMPersonsDataSourceFactory, registerPlugin<IMPersonsDataSource>(); )
K_EXPORT_PLUGIN( IMPersonsDataSourceFactory("im_persons_data_source_plugin") )

#include "im-persons-data-source.moc"
<commit_msg>Check for contact actually being valid before using it<commit_after>/*
    Copyright (C) 2013  Martin Klapetek <mklapetek@kde.org>
    Copyright (C) 2014  David Edmundson <davidedmundson@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "im-persons-data-source.h"

#include <TelepathyQt/AccountManager>
#include <TelepathyQt/AccountFactory>
#include <TelepathyQt/ContactManager>
#include <TelepathyQt/PendingOperation>
#include <TelepathyQt/PendingReady>
#include <TelepathyQt/Presence>

#include "KTp/contact-factory.h"
#include "KTp/global-contact-manager.h"
#include "KTp/types.h"
#include "debug.h"

#include <KPeopleBackend/AllContactsMonitor>

#include <KPluginFactory>
#include <KPluginLoader>

#include <QSqlDatabase>
#include <QSqlQuery>
#include <QPixmap>

using namespace KPeople;

class KTpAllContacts : public AllContactsMonitor
{
    Q_OBJECT
public:
    KTpAllContacts();
    ~KTpAllContacts();
    virtual QMap<QString, AbstractContact::Ptr> contacts() Q_DECL_OVERRIDE;

private Q_SLOTS:
    void loadCache();
    void onAccountManagerReady(Tp::PendingOperation *op);
    void onContactChanged();
    void onContactInvalidated();
    void onAllKnownContactsChanged(const Tp::Contacts &contactsAdded, const Tp::Contacts &contactsRemoved);

private:
    QString createUri(const KTp::ContactPtr &contact) const;

    //presence names indexed by ConnectionPresenceType
    QHash<QString, KTp::ContactPtr> m_contacts;
    QMap<QString, AbstractContact::Ptr> m_contactVCards;
};

static const QString S_KPEOPLE_PROPERTY_ACCOUNT_PATH = QString::fromLatin1("telepathy-accountPath");
static const QString S_KPEOPLE_PROPERTY_CONTACT_ID = QString::fromLatin1("telepathy-contactId");
static const QString S_KPEOPLE_PROPERTY_PRESENCE = QString::fromLatin1("telepathy-presence");
const QHash<Tp::ConnectionPresenceType, QString> s_presenceStrings = {
        { Tp::ConnectionPresenceTypeUnset, QString() },
        { Tp::ConnectionPresenceTypeOffline, QString::fromLatin1("offline") },
        { Tp::ConnectionPresenceTypeAvailable, QString::fromLatin1("available") },
        { Tp::ConnectionPresenceTypeAway, QString::fromLatin1("away") },
        { Tp::ConnectionPresenceTypeExtendedAway, QString::fromLatin1("xa") },
        { Tp::ConnectionPresenceTypeHidden, QString::fromLatin1("hidden") }, //of 'offline' ?
        { Tp::ConnectionPresenceTypeBusy, QString::fromLatin1("busy") },
        { Tp::ConnectionPresenceTypeUnknown, QString() },
        { Tp::ConnectionPresenceTypeError, QString() }
};

class TelepathyContact : public KPeople::AbstractContact
{
public:
    virtual QVariant customProperty(const QString &key) const Q_DECL_OVERRIDE
    {
        // Check if the contact is valid first
        if (m_contact && m_contact->manager() && m_contact->manager()->connection() && m_account) {
            if (key == AbstractContact::NameProperty)
                return m_contact->alias();
            else if(key == AbstractContact::GroupsProperty)
                return m_contact->groups();
            else if(key == S_KPEOPLE_PROPERTY_CONTACT_ID)
                return m_contact->id();
            else if(key == S_KPEOPLE_PROPERTY_ACCOUNT_PATH)
                return m_account->objectPath();
            else if(key == S_KPEOPLE_PROPERTY_PRESENCE)
                return s_presenceStrings.value(m_contact->presence().type());
            else if (key == AbstractContact::PictureProperty)
                return m_contact->avatarPixmap();
        }
        return m_properties[key];
    }

    void insertProperty(const QString &key, const QVariant &value)
    {
        m_properties[key] = value;
    }

    void setContact(const KTp::ContactPtr &contact)
    {
        m_contact = contact;
    }

    void setAccount(const Tp::AccountPtr &account)
    {
        m_account = account;
    }

private:
    KTp::ContactPtr m_contact;
    Tp::AccountPtr m_account;
    QVariantMap m_properties;
};

KTpAllContacts::KTpAllContacts()
{
    Tp::registerTypes();

    loadCache();
}

KTpAllContacts::~KTpAllContacts()
{
}

void KTpAllContacts::loadCache()
{
    QSqlDatabase db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), QLatin1String("ktpCache"));
    QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/ktp");
    QDir().mkpath(path);
    db.setDatabaseName(path+QStringLiteral("/cache.db"));
    if (!db.open()) {
        qWarning() << "couldn't open database" << db.databaseName();
    }

    QSqlQuery query(db);
    query.exec(QLatin1String("SELECT groupName FROM groups ORDER BY groupId;"));

    QStringList groupsList;
    while (query.next()) {
        groupsList.append(query.value(0).toString());
    }

    if (!groupsList.isEmpty()) {
        query.exec(QLatin1String("SELECT accountId, contactId, alias, avatarFileName, groupsIds FROM contacts;"));
    } else {
        query.exec(QLatin1String("SELECT accountId, contactId, alias, avatarFileName FROM contacts;"));
    }

    while (query.next()) {
        QExplicitlySharedDataPointer<TelepathyContact> addressee(new TelepathyContact);

        const QString accountId =  query.value(0).toString();
        const QString contactId =  query.value(1).toString();
        addressee->insertProperty(AbstractContact::NameProperty, query.value(2).toString());
        addressee->insertProperty(AbstractContact::PictureProperty, QUrl::fromLocalFile(query.value(3).toString()));

        if (!groupsList.isEmpty()) {
            QVariantList contactGroups;

            Q_FOREACH (const QString &groupIdStr, query.value(4).toString().split(QLatin1String(","))) {
                bool convSuccess;
                int groupId = groupIdStr.toInt(&convSuccess);
                if ((!convSuccess) || (groupId >= groupsList.count()))
                    continue;

                contactGroups.append(groupsList.at(groupId));
            }

            addressee->insertProperty(QStringLiteral("groups"), contactGroups);
        }

        addressee->insertProperty(S_KPEOPLE_PROPERTY_CONTACT_ID, contactId);
        addressee->insertProperty(S_KPEOPLE_PROPERTY_ACCOUNT_PATH, TP_QT_ACCOUNT_OBJECT_PATH_BASE + QLatin1Char('/') + accountId);
        addressee->insertProperty(S_KPEOPLE_PROPERTY_PRESENCE, s_presenceStrings[Tp::ConnectionPresenceTypeOffline]);

        const QString uri = QLatin1String("ktp://") + accountId + QLatin1Char('?') + contactId;

        m_contactVCards[uri] = addressee;
        Q_EMIT contactAdded(uri, addressee);
    }

    //now start fetching the up-to-date information
    connect(KTp::accountManager()->becomeReady(), SIGNAL(finished(Tp::PendingOperation*)),
        this, SLOT(onAccountManagerReady(Tp::PendingOperation*)));

    emitInitialFetchComplete(true);
}

QString KTpAllContacts::createUri(const KTp::ContactPtr &contact) const
{
    // so real ID will look like
    // ktp://gabble/jabber/blah/asdfjwer?foo@bar.com
    // ? is used as it is not a valid character in the dbus path that makes up the account UID
    return QLatin1String("ktp://") + contact->accountUniqueIdentifier() + QLatin1Char('?') + contact->id();
}

void KTpAllContacts::onAccountManagerReady(Tp::PendingOperation *op)
{
    if (op->isError()) {
        qCWarning(KTP_KPEOPLE) << "Failed to initialize AccountManager:" << op->errorName();
        qCWarning(KTP_KPEOPLE) << op->errorMessage();

        return;
    }

    qCDebug(KTP_KPEOPLE) << "Account manager ready";

    connect(KTp::contactManager(), SIGNAL(allKnownContactsChanged(Tp::Contacts,Tp::Contacts)),
            this, SLOT(onAllKnownContactsChanged(Tp::Contacts,Tp::Contacts)));

    onAllKnownContactsChanged(KTp::contactManager()->allKnownContacts(), Tp::Contacts());
}

void KTpAllContacts::onAllKnownContactsChanged(const Tp::Contacts &contactsAdded, const Tp::Contacts &contactsRemoved)
{
    if (!m_contacts.isEmpty()) {
        Q_FOREACH (const Tp::ContactPtr &c, contactsRemoved) {
            const KTp::ContactPtr &contact = KTp::ContactPtr::qObjectCast(c);
            const QString uri = createUri(contact);
            m_contacts.remove(uri);
            m_contactVCards.remove(uri);
            Q_EMIT contactRemoved(uri);
        }
    }

    Q_FOREACH (const Tp::ContactPtr &contact, contactsAdded) {
        KTp::ContactPtr ktpContact = KTp::ContactPtr::qObjectCast(contact);
        const QString uri = createUri(ktpContact);

        AbstractContact::Ptr vcard = m_contactVCards.value(uri);
        bool added = false;
        if (!vcard) {
            vcard = AbstractContact::Ptr(new TelepathyContact);
            m_contactVCards[uri] = vcard;
            added = true;
        }
        static_cast<TelepathyContact*>(vcard.data())->setContact(ktpContact);
        static_cast<TelepathyContact*>(vcard.data())->setAccount(KTp::contactManager()->accountForContact(ktpContact));

        m_contacts.insert(uri, ktpContact);

        if (added) {
            Q_EMIT contactAdded(uri, vcard);
        } else {
            Q_EMIT contactChanged(uri, vcard);
        }

        connect(ktpContact.data(), SIGNAL(presenceChanged(Tp::Presence)),
                this, SLOT(onContactChanged()));

        connect(ktpContact.data(), SIGNAL(capabilitiesChanged(Tp::ContactCapabilities)),
                this, SLOT(onContactChanged()));

        connect(ktpContact.data(), SIGNAL(invalidated()),
                this, SLOT(onContactInvalidated()));

        connect(ktpContact.data(), SIGNAL(avatarDataChanged(Tp::AvatarData)),
                this, SLOT(onContactChanged()));

        connect(ktpContact.data(), SIGNAL(addedToGroup(QString)),
                this, SLOT(onContactChanged()));

        connect(ktpContact.data(), SIGNAL(removedFromGroup(QString)),
                this, SLOT(onContactChanged()));
    }
}

void KTpAllContacts::onContactChanged()
{
    const KTp::ContactPtr contact(qobject_cast<KTp::Contact*>(sender()));
    const QString uri = createUri(contact);
    Q_EMIT contactChanged(uri, m_contactVCards.value(uri));
}

void KTpAllContacts::onContactInvalidated()
{
    const KTp::ContactPtr contact(qobject_cast<KTp::Contact*>(sender()));
    const QString uri = createUri(contact);

    m_contacts.remove(uri);

    //set to offline and emit changed
    AbstractContact::Ptr vcard = m_contactVCards[uri];
    TelepathyContact *tpContact = static_cast<TelepathyContact*>(vcard.data());
    tpContact->insertProperty(S_KPEOPLE_PROPERTY_PRESENCE, QStringLiteral("offline"));
    Q_EMIT contactChanged(uri, vcard);
}

QMap<QString, AbstractContact::Ptr> KTpAllContacts::contacts()
{
    return m_contactVCards;
}

IMPersonsDataSource::IMPersonsDataSource(QObject *parent, const QVariantList &args)
    : BasePersonsDataSource(parent)
{
    Q_UNUSED(args);
}

IMPersonsDataSource::~IMPersonsDataSource()
{
}

QString IMPersonsDataSource::sourcePluginId() const
{
    return QStringLiteral("ktp");
}

AllContactsMonitor* IMPersonsDataSource::createAllContactsMonitor()
{
    return new KTpAllContacts();
}

K_PLUGIN_FACTORY( IMPersonsDataSourceFactory, registerPlugin<IMPersonsDataSource>(); )
K_EXPORT_PLUGIN( IMPersonsDataSourceFactory("im_persons_data_source_plugin") )

#include "im-persons-data-source.moc"
<|endoftext|>


<commit_before>#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <iostream>
#include <fstream>
#include <boost/lexical_cast.hpp>
#include "packet.h"

#define USAGE "Usage:\r\nc \r\n[tux machine number] \r\n[probability of packet corruption in int form] \r\n[probability of packet loss in int form] \r\n[probability of packet delay] \r\n[length of delay in ms] \r\n"
#define PORT 10038
#define PAKSIZE 512
#define ACK 0
#define NAK 1
#define BUFSIZE 505
#define FILENAME "Testfile"
#define TIMEOUT 15 //in ms
#define WIN_SIZE 16

using namespace std;

bool isvpack(unsigned char * p);
bool init(int argc, char** argv);
bool loadFile();
bool getFile();
bool sendFile();
bool isAck();
void handleAck();
void handleNak(int& x);
bool* gremlin(Packet * pack, int corruptProb, int lossProb, int delayProb);
Packet createPacket(int index);
void loadWindow();
bool sendPacket();
bool getGet();

struct sockaddr_in a;
struct sockaddr_in ca;
socklen_t calen;
int rlen;
int s;
bool ack;
string fstr;
char * file;
int probCorrupt;
int probLoss;
int probDelay;
int delayT;
Packet p;
Packet window[16];
int length;
bool dropPck;
bool delayPck;
int toms;
unsigned char b[BUFSIZE];
int base;

int main(int argc, char** argv) {

  if(!init(argc, argv)) return -1;

  if(sendFile()) cout << "GET Testfile complete." << endl;

  return 0;
}

bool init(int argc, char** argv){

  if(argc != 6) {
    cout << USAGE << endl;
    return false;
  }

  char * probCorruptStr = argv[2];
  probCorrupt = boost::lexical_cast<int>(probCorruptStr);
  char * probLossStr = argv[3];
  probLoss = boost::lexical_cast<int>(probLossStr);
  char * probDelayStr = argv[4];
  probDelay = boost::lexical_cast<int>(probDelayStr);
  char* delayTStr = argv[5];
  delayT = boost::lexical_cast<int>(delayTStr);

  struct timeval timeout;
  timeout.tv_usec = TIMEOUT * 1000;
  toms = TIMEOUT;

  /* Create our socket. */
  if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    cout << "Socket creation failed. (socket s)" << endl;
    return 0;
  }

  setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));

  /*
   * Bind our socket to an IP (whatever the computer decides)
   * and a specified port.
   *
   */

  if (!loadFile()) {
    cout << "Loading file failed. (filename FILENAME)" << endl;
    return false;
  }

  memset((char *)&a, 0, sizeof(a));
  a.sin_family = AF_INET;
  a.sin_addr.s_addr = htonl(INADDR_ANY);
  a.sin_port = htons(PORT);

  if (bind(s, (struct sockaddr *)&a, sizeof(a)) < 0) {
    cout << "Socket binding failed. (socket s, address a)" << endl;
    return 0;
  }

  fstr = string(file);
  cout << "File: " << endl << fstr << endl;

  base = 0;
  dropPck = false;
  calen = sizeof(ca);

  getGet();

  return true;
}

bool getGet(){
	unsigned char packet[PAKSIZE + 1];
	rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&ca, &calen);
	return (rlen > 0) ? true : false;
}

bool isvpack(unsigned char * p) {
  cout << endl << "=== IS VALID PACKET TESTING" << endl;

  char * sns = new char[2];
  memcpy(sns, &p[0], 1);
  sns[1] = '\0';

  char * css = new char[7];
  memcpy(css, &p[1], 6);
  css[6] = '\0';

  char * db = new char[121 + 1];
  memcpy(db, &p[2], 121);
  db[121] = '\0';

  cout << "Seq. num: " << sns << endl;
  cout << "Checksum: " << css << endl;
  cout << "Message: " << db << endl;

  int sn = boost::lexical_cast<int>(sns);
  int cs = boost::lexical_cast<int>(css);

  Packet pk (0, db);
  pk.setSequenceNum(sn);

  // change to validate based on checksum and sequence number

  if(sn == 0) return false; //doesn't matter, only for the old version (netwark)
  if(cs != pk.generateCheckSum()) return false;
  return true;
}

bool getFile(){

  /* Loop forever, waiting for messages from a client. */
  cout << "Waiting on port " << PORT << "..." << endl;

  ofstream file("Dumpfile");

  for (;;) {
    unsigned char packet[PAKSIZE + 1];
    unsigned char dataPull[PAKSIZE - 7 + 1];
    rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&ca, &calen);

    for(int x = 0; x < PAKSIZE - 7; x++) {
      dataPull[x] = packet[x + 7];
    }
    dataPull[PAKSIZE - 7] = '\0';
    packet[PAKSIZE] = '\0';
    if (rlen > 0) {
      char * css = new char[6];
      memcpy(css, &packet[1], 5);
      css[5] = '\0';
      cout << endl << endl << "=== RECEIPT" << endl;
      cout << "Seq. num: " << packet[0] << endl;
      cout << "Checksum: " << css << endl;
      cout << "Received message: " << dataPull << endl;
      cout << "Sent response: ";
      if(isvpack(packet)) {
        ack = ACK;
        //seqNum = (seqNum) ? false : true;
        file << dataPull;
      } else {
        ack = NAK;
      }
      cout << ((ack == ACK) ? "ACK" : "NAK") << endl;
      Packet p (false, reinterpret_cast<const char *>(dataPull));
      p.setCheckSum(boost::lexical_cast<int>(css));
      p.setAckNack(ack);

      if(sendto(s, p.str(), PAKSIZE, 0, (struct sockaddr *)&ca, calen) < 0) {
        cout << "Acknowledgement failed. (socket s, acknowledgement message ack, client address ca, client address length calen)" << endl;
        return 0;
      }
      delete css;
    }
  }
  file.close();
  return true;
}

bool loadFile() {

  ifstream is (FILENAME, ifstream::binary);

  if(is) {
    is.seekg(0, is.end);
    length = is.tellg();
    is.seekg(0, is.beg);

    file = new char[length];

    cout << "Reading " << length << " characters..." << endl;
    is.read(file, length);

    if(!is) { cout << "File reading failed. (filename " << FILENAME << "). Only " << is.gcount() << " could be read."; return false; }
    is.close();
  }
  return true;
}

void loadWindow(){
	for(int i = base; i < base + WIN_SIZE; i++) {
		window[i-base] = createPacket(i);
		if(strlen(window[i-base].getDataBuffer()) < BUFSIZE && window[i-base].chksm()) {
			cout << "In loadWindow's secret base, index is: " << i-base << endl;
			for(++i; i < base + WIN_SIZE; i++){
				window[i-base].loadDataBuffer("\0");
			}

		}
		/*cout << "window[i-base] seq. num: " << window[i-base].getSequenceNum() << endl;*/
	}

		/*cout << "packet " << base + 1 << ": " << window[1].str() << endl;
		cout << "packet " << base + 2 << ": " << window[2].str() << endl;*/
}

bool sendFile() {
	/*Currently causes the program to only send the first 16 packets of file out
		requires additional code later to sendFile again with updated window*/
	fd_set stReadFDS;

	struct timeval stTimeOut;

	FD_ZERO(&stReadFDS);
	stTimeOut.tv_sec = 0;
	stTimeOut.tv_usec = 1000 * TIMEOUT;
	FD_SET(s, &stReadFDS);

	base = 0;
	int desc_ready;
	int finale = -1;
	int max_sd;
	bool hasRead = false;
	while(base * BUFSIZE < length) {
		loadWindow();

		if(p.str()[0] == '\0') finale = p.getSequenceNum();
		for(int x = 0; x < WIN_SIZE; x++) {
			p = window[x];
			if(!sendPacket()) continue;
		}
		for(int x = 0; x < WIN_SIZE; x++) {
			cout << endl << "beginning of loop " << x << endl;
			FD_ZERO(&stReadFDS);
			stTimeOut.tv_sec = 0;
			stTimeOut.tv_usec = 1000 * TIMEOUT;
			FD_SET(s, &stReadFDS);

			max_sd = s;
			cout << endl << "before select" << endl;
			int t = select(max_sd + 1, &stReadFDS, NULL, NULL, &stTimeOut);
			if (t == -1){
				perror("select()");
			}
			if (t == 0) {
				cout << "=== ACK TIMEOUT (select)" << endl;

			}
			desc_ready = t;
			for(int u = 0; u <= max_sd  &&  desc_ready > 0; u++){
				if(recvfrom(s, b, BUFSIZE + 7, 0, (struct sockaddr *)&ca, &calen) < 0) {
					cout << "=== ACK TIMEOUT (recvfrom)" << endl;
				} else hasRead = true;
				if(!hasRead) continue;
				if(isAck()) {
					handleAck();
				} else {
					handleAck();
					//handleNak(x);
				}
			}
			cout << "end of loop " << x << endl;
			if(finale > 0 && base == finale) {cout << "Finale: " << finale << endl; break;}
			memset(b, 0, BUFSIZE);
		}

	}

	if(sendto(s, "\0", BUFSIZE, 0, (struct sockaddr *)&ca, sizeof(ca)) < 0) {
		cout << "Final package sending failed. (socket s, server address sa, message m)" << endl;
		return false;
	}
	return true;
}

Packet createPacket(int index){
    string mstr = fstr.substr(index * BUFSIZE, BUFSIZE);

	if(mstr.length() < BUFSIZE) {
		cout << "Null terminated mstr." << endl;
		mstr[length - (index * BUFSIZE)] = '\0';
    }

	cout << "index: " << index << endl;
    return Packet (index, mstr.c_str());
}

bool sendPacket(){
	cout << endl;
    cout << "=== TRANSMISSION START" << endl;
	cout << "Sequence number before gremlin function: " << p.getSequenceNum() << endl;
    int pc = probCorrupt; int pl = probLoss; int pd = probDelay;
	bool* pckStatus = gremlin(&p, pc, pl, pd);

	dropPck = pckStatus[0];
	delayPck = pckStatus[1];

	if (dropPck == true) return false;
	if (delayPck == true) p.setAckNack(1);

    if(sendto(s, p.str(), BUFSIZE + 8, 0, (struct sockaddr *)&ca, sizeof(ca)) < 0) {
		cout << "Package sending failed. (socket s, server address sa, message m)" << endl;
		return false;
    }
    return true;
}
bool isAck() {
    cout << endl << "=== SERVER RESPONSE TEST" << endl;
    cout << "Data: " << b << endl;
    if(b[6] == '0') return true;
    else return false;
}
void handleAck() {
	int ack = boost::lexical_cast<int>(b);
	if(base < ack) base = ack;
	cout << "Window base: " << base << endl;
}
void handleNak(int& x) {

      char * sns = new char[2];
      memcpy(sns, &b[0], 1);
      sns[1] = '\0';

      char * css = new char[5];
      memcpy(css, &b[1], 5);

      char * db = new char[BUFSIZE + 1];
      memcpy(db, &b[2], BUFSIZE);
      db[BUFSIZE] = '\0';

      cout << "Sequence number: " << sns << endl;
      cout << "Checksum: " << css << endl;

      Packet pk (0, db);
      pk.setSequenceNum(boost::lexical_cast<int>(sns));
      pk.setCheckSum(boost::lexical_cast<int>(css));

      if(!pk.chksm()) x--;
      else x = (x - 2 > 0) ? x - 2 : 0;
}
bool* gremlin(Packet * pack, int corruptProb, int lossProb, int delayProb){

  bool* packStatus = new bool[2];
  int r = rand() % 100;

  cout << "Corruption probability: " << corruptProb << endl;
  cout << "Random number: " << r << endl;

  packStatus[0] = false;
  packStatus[1] = false;

  if(r <= (lossProb)){
	packStatus[0] = true;
    cout << "Dropped!" << endl;
  }
  else if(r <= (delayProb)){
	  packStatus[1] = true;
	  cout << "Delayed!" << endl;
  }
  else if(r <= (corruptProb)){
    cout << "Corrupted!" << endl;
	cout << "Seq. num (pre-gremlin): " << pack->getSequenceNum() << endl;
    pack->loadDataBuffer((char*)"GREMLIN LOL");
  }
  cout << "Seq. num: " << pack->getSequenceNum() << endl;
  cout << "Checksum: " << pack->getCheckSum() << endl;
  //cout << "Message: "  << pack->getDataBuffer() << endl;

  return packStatus;
}<commit_msg>added test line<commit_after>#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <iostream>
#include <fstream>
#include <boost/lexical_cast.hpp>
#include "packet.h"

#define USAGE "Usage:\r\nc \r\n[tux machine number] \r\n[probability of packet corruption in int form] \r\n[probability of packet loss in int form] \r\n[probability of packet delay] \r\n[length of delay in ms] \r\n"
#define PORT 10038
#define PAKSIZE 512
#define ACK 0
#define NAK 1
#define BUFSIZE 505
#define FILENAME "Testfile"
#define TIMEOUT 15 //in ms
#define WIN_SIZE 16

using namespace std;

bool isvpack(unsigned char * p);
bool init(int argc, char** argv);
bool loadFile();
bool getFile();
bool sendFile();
bool isAck();
void handleAck();
void handleNak(int& x);
bool* gremlin(Packet * pack, int corruptProb, int lossProb, int delayProb);
Packet createPacket(int index);
void loadWindow();
bool sendPacket();
bool getGet();

struct sockaddr_in a;
struct sockaddr_in ca;
socklen_t calen;
int rlen;
int s;
bool ack;
string fstr;
char * file;
int probCorrupt;
int probLoss;
int probDelay;
int delayT;
Packet p;
Packet window[16];
int length;
bool dropPck;
bool delayPck;
int toms;
unsigned char b[BUFSIZE];
int base;

int main(int argc, char** argv) {

  if(!init(argc, argv)) return -1;

  if(sendFile()) cout << "GET Testfile complete." << endl;

  return 0;
}

bool init(int argc, char** argv){

  if(argc != 6) {
    cout << USAGE << endl;
    return false;
  }

  char * probCorruptStr = argv[2];
  probCorrupt = boost::lexical_cast<int>(probCorruptStr);
  char * probLossStr = argv[3];
  probLoss = boost::lexical_cast<int>(probLossStr);
  char * probDelayStr = argv[4];
  probDelay = boost::lexical_cast<int>(probDelayStr);
  char* delayTStr = argv[5];
  delayT = boost::lexical_cast<int>(delayTStr);

  struct timeval timeout;
  timeout.tv_usec = TIMEOUT * 1000;
  toms = TIMEOUT;

  /* Create our socket. */
  if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    cout << "Socket creation failed. (socket s)" << endl;
    return 0;
  }

  setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));

  /*
   * Bind our socket to an IP (whatever the computer decides)
   * and a specified port.
   *
   */

  if (!loadFile()) {
    cout << "Loading file failed. (filename FILENAME)" << endl;
    return false;
  }

  memset((char *)&a, 0, sizeof(a));
  a.sin_family = AF_INET;
  a.sin_addr.s_addr = htonl(INADDR_ANY);
  a.sin_port = htons(PORT);

  if (bind(s, (struct sockaddr *)&a, sizeof(a)) < 0) {
    cout << "Socket binding failed. (socket s, address a)" << endl;
    return 0;
  }

  fstr = string(file);
  cout << "File: " << endl << fstr << endl;

  base = 0;
  dropPck = false;
  calen = sizeof(ca);

  getGet();

  return true;
}

bool getGet(){
	unsigned char packet[PAKSIZE + 1];
	rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&ca, &calen);
	return (rlen > 0) ? true : false;
}

bool isvpack(unsigned char * p) {
  cout << endl << "=== IS VALID PACKET TESTING" << endl;

  char * sns = new char[2];
  memcpy(sns, &p[0], 1);
  sns[1] = '\0';

  char * css = new char[7];
  memcpy(css, &p[1], 6);
  css[6] = '\0';

  char * db = new char[121 + 1];
  memcpy(db, &p[2], 121);
  db[121] = '\0';

  cout << "Seq. num: " << sns << endl;
  cout << "Checksum: " << css << endl;
  cout << "Message: " << db << endl;

  int sn = boost::lexical_cast<int>(sns);
  int cs = boost::lexical_cast<int>(css);

  Packet pk (0, db);
  pk.setSequenceNum(sn);

  // change to validate based on checksum and sequence number

  if(sn == 0) return false; //doesn't matter, only for the old version (netwark)
  if(cs != pk.generateCheckSum()) return false;
  return true;
}

bool getFile(){

  /* Loop forever, waiting for messages from a client. */
  cout << "Waiting on port " << PORT << "..." << endl;

  ofstream file("Dumpfile");

  for (;;) {
    unsigned char packet[PAKSIZE + 1];
    unsigned char dataPull[PAKSIZE - 7 + 1];
    rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&ca, &calen);

    for(int x = 0; x < PAKSIZE - 7; x++) {
      dataPull[x] = packet[x + 7];
    }
    dataPull[PAKSIZE - 7] = '\0';
    packet[PAKSIZE] = '\0';
    if (rlen > 0) {
      char * css = new char[6];
      memcpy(css, &packet[1], 5);
      css[5] = '\0';
      cout << endl << endl << "=== RECEIPT" << endl;
      cout << "Seq. num: " << packet[0] << endl;
      cout << "Checksum: " << css << endl;
      cout << "Received message: " << dataPull << endl;
      cout << "Sent response: ";
      if(isvpack(packet)) {
        ack = ACK;
        //seqNum = (seqNum) ? false : true;
        file << dataPull;
      } else {
        ack = NAK;
      }
      cout << ((ack == ACK) ? "ACK" : "NAK") << endl;
      Packet p (false, reinterpret_cast<const char *>(dataPull));
      p.setCheckSum(boost::lexical_cast<int>(css));
      p.setAckNack(ack);

      if(sendto(s, p.str(), PAKSIZE, 0, (struct sockaddr *)&ca, calen) < 0) {
        cout << "Acknowledgement failed. (socket s, acknowledgement message ack, client address ca, client address length calen)" << endl;
        return 0;
      }
      delete css;
    }
  }
  file.close();
  return true;
}

bool loadFile() {

  ifstream is (FILENAME, ifstream::binary);

  if(is) {
    is.seekg(0, is.end);
    length = is.tellg();
    is.seekg(0, is.beg);

    file = new char[length];

    cout << "Reading " << length << " characters..." << endl;
    is.read(file, length);

    if(!is) { cout << "File reading failed. (filename " << FILENAME << "). Only " << is.gcount() << " could be read."; return false; }
    is.close();
  }
  return true;
}

void loadWindow(){
	for(int i = base; i < base + WIN_SIZE; i++) {
		window[i-base] = createPacket(i);
		if(strlen(window[i-base].getDataBuffer()) < BUFSIZE && window[i-base].chksm()) {
			cout << "In loadWindow's secret base, index is: " << i-base << endl;
			for(++i; i < base + WIN_SIZE; i++){
				window[i-base].loadDataBuffer("\0");
			}

		}
		/*cout << "window[i-base] seq. num: " << window[i-base].getSequenceNum() << endl;*/
	}

		/*cout << "packet " << base + 1 << ": " << window[1].str() << endl;
		cout << "packet " << base + 2 << ": " << window[2].str() << endl;*/
}

bool sendFile() {
	/*Currently causes the program to only send the first 16 packets of file out
		requires additional code later to sendFile again with updated window*/
	fd_set stReadFDS;

	struct timeval stTimeOut;

	FD_ZERO(&stReadFDS);
	stTimeOut.tv_sec = 0;
	stTimeOut.tv_usec = 1000 * TIMEOUT;
	FD_SET(s, &stReadFDS);

	base = 0;
	int desc_ready;
	int finale = -1;
	int max_sd;
	bool hasRead = false;
	while(base * BUFSIZE < length) {
		loadWindow();

		if(p.str()[0] == '\0') finale = p.getSequenceNum();
		for(int x = 0; x < WIN_SIZE; x++) {
			p = window[x];
			if(!sendPacket()) continue;
		}
		for(int x = 0; x < WIN_SIZE; x++) {
			cout << endl << "beginning of loop " << x << endl;
			FD_ZERO(&stReadFDS);
			stTimeOut.tv_sec = 0;
			stTimeOut.tv_usec = 1000 * TIMEOUT;
			FD_SET(s, &stReadFDS);

			max_sd = s;
			cout << endl << "before select" << endl;
			int t = select(max_sd + 1, &stReadFDS, NULL, NULL, &stTimeOut);
			cout << "after select" << endl;
			if (t == -1){
				perror("select()");
			}
			if (t == 0) {
				cout << "=== ACK TIMEOUT (select)" << endl;

			}
			desc_ready = t;
			for(int u = 0; u <= max_sd  &&  desc_ready > 0; u++){
				if(recvfrom(s, b, BUFSIZE + 7, 0, (struct sockaddr *)&ca, &calen) < 0) {
					cout << "=== ACK TIMEOUT (recvfrom)" << endl;
				} else hasRead = true;
				if(!hasRead) continue;
				if(isAck()) {
					handleAck();
				} else {
					handleAck();
					//handleNak(x);
				}
			}
			cout << "end of loop " << x << endl;
			if(finale > 0 && base == finale) {cout << "Finale: " << finale << endl; break;}
			memset(b, 0, BUFSIZE);
		}

	}

	if(sendto(s, "\0", BUFSIZE, 0, (struct sockaddr *)&ca, sizeof(ca)) < 0) {
		cout << "Final package sending failed. (socket s, server address sa, message m)" << endl;
		return false;
	}
	return true;
}

Packet createPacket(int index){
    string mstr = fstr.substr(index * BUFSIZE, BUFSIZE);

	if(mstr.length() < BUFSIZE) {
		cout << "Null terminated mstr." << endl;
		mstr[length - (index * BUFSIZE)] = '\0';
    }

	cout << "index: " << index << endl;
    return Packet (index, mstr.c_str());
}

bool sendPacket(){
	cout << endl;
    cout << "=== TRANSMISSION START" << endl;
	cout << "Sequence number before gremlin function: " << p.getSequenceNum() << endl;
    int pc = probCorrupt; int pl = probLoss; int pd = probDelay;
	bool* pckStatus = gremlin(&p, pc, pl, pd);

	dropPck = pckStatus[0];
	delayPck = pckStatus[1];

	if (dropPck == true) return false;
	if (delayPck == true) p.setAckNack(1);

    if(sendto(s, p.str(), BUFSIZE + 8, 0, (struct sockaddr *)&ca, sizeof(ca)) < 0) {
		cout << "Package sending failed. (socket s, server address sa, message m)" << endl;
		return false;
    }
    return true;
}
bool isAck() {
    cout << endl << "=== SERVER RESPONSE TEST" << endl;
    cout << "Data: " << b << endl;
    if(b[6] == '0') return true;
    else return false;
}
void handleAck() {
	int ack = boost::lexical_cast<int>(b);
	if(base < ack) base = ack;
	cout << "Window base: " << base << endl;
}
void handleNak(int& x) {

      char * sns = new char[2];
      memcpy(sns, &b[0], 1);
      sns[1] = '\0';

      char * css = new char[5];
      memcpy(css, &b[1], 5);

      char * db = new char[BUFSIZE + 1];
      memcpy(db, &b[2], BUFSIZE);
      db[BUFSIZE] = '\0';

      cout << "Sequence number: " << sns << endl;
      cout << "Checksum: " << css << endl;

      Packet pk (0, db);
      pk.setSequenceNum(boost::lexical_cast<int>(sns));
      pk.setCheckSum(boost::lexical_cast<int>(css));

      if(!pk.chksm()) x--;
      else x = (x - 2 > 0) ? x - 2 : 0;
}
bool* gremlin(Packet * pack, int corruptProb, int lossProb, int delayProb){

  bool* packStatus = new bool[2];
  int r = rand() % 100;

  cout << "Corruption probability: " << corruptProb << endl;
  cout << "Random number: " << r << endl;

  packStatus[0] = false;
  packStatus[1] = false;

  if(r <= (lossProb)){
	packStatus[0] = true;
    cout << "Dropped!" << endl;
  }
  else if(r <= (delayProb)){
	  packStatus[1] = true;
	  cout << "Delayed!" << endl;
  }
  else if(r <= (corruptProb)){
    cout << "Corrupted!" << endl;
	cout << "Seq. num (pre-gremlin): " << pack->getSequenceNum() << endl;
    pack->loadDataBuffer((char*)"GREMLIN LOL");
  }
  cout << "Seq. num: " << pack->getSequenceNum() << endl;
  cout << "Checksum: " << pack->getCheckSum() << endl;
  //cout << "Message: "  << pack->getDataBuffer() << endl;

  return packStatus;
}<|endoftext|>


<commit_before>#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <boost/lexical_cast.hpp>
#include "packet.h"

#define USAGE "Usage:\r\nc \r\n[tux machine number] \r\n[probability of packet corruption in int form] \r\n[probability of packet loss in int form] \r\n[probability of packet delay] \r\n[length of delay in ms] \r\n"
#define PORT 10038
#define PAKSIZE 128
#define ACK 0
#define NAK 1
#define BUFSIZE 121
#define FILENAME "Testfile"
#define TIMEOUT 100 //in ms

using namespace std;

bool isvpack(unsigned char * p);
bool init(int argc, char** argv);
bool loadFile();
bool getFile();
bool sendFile();
bool isAck();
void handleAck();
void handleNak(int& x);
bool* gremlin(Packet * pack, int corruptProb, int lossProb, int delayProb);
Packet createPacket(int index);
bool sendPacket();

bool seqNum = true;
struct sockaddr_in a;
struct sockaddr_in ca;
socklen_t calen;
int rlen;
int s;
bool ack;
string fstr;
char * file;
int probCorrupt;
int probLoss;
int probDelay;
int delayT;
Packet p;
int length;
bool dropPck;
bool delayPck;
unsigned char b[BUFSIZE];

int main(int argc, char** argv) {

  if(!init(argc, argv)) return -1;

  calen = sizeof(ca);

  unsigned char packet[PAKSIZE + 1];
  rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&ca, &calen);

  sendFile();

  return 0;
}

bool init(int argc, char** argv){

  if(argc != 6) {
    cout << USAGE << endl;
    return false;
  }

  char * probCorruptStr = argv[2];
  probCorrupt = boost::lexical_cast<int>(probCorruptStr);
  char * probLossStr = argv[3];
  probLoss = boost::lexical_cast<int>(probLossStr);
  char * probDelayStr = argv[4];
  probDelay = boost::lexical_cast<int>(probDelayStr);
  char* delayTStr = argv[5];
  delayT = boost::lexical_cast<int>(delayTStr);

  static int timeout = TIMEOUT;

  /* Create our socket. */
  if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    cout << "Socket creation failed. (socket s)" << endl;
    return 0;
  }

  setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));

  /*
   * Bind our socket to an IP (whatever the computer decides)
   * and a specified port.
   *
   */

  if (!loadFile()) {
    cout << "Loading file failed. (filename FILENAME)" << endl;
    return false;
  }

  memset((char *)&a, 0, sizeof(a));
  a.sin_family = AF_INET;
  a.sin_addr.s_addr = htonl(INADDR_ANY);
  a.sin_port = htons(PORT);

  if (bind(s, (struct sockaddr *)&a, sizeof(a)) < 0) {
    cout << "Socket binding failed. (socket s, address a)" << endl;
    return 0;
  }

  fstr = string(file);
  cout << "File: " << endl << fstr << endl;

  seqNum = true;
  dropPck = false;

  return true;
}

bool isvpack(unsigned char * p) {
  cout << endl << "=== IS VALID PACKET TESTING" << endl;

  char * sns = new char[2];
  memcpy(sns, &p[0], 1);
  sns[1] = '\0';

  char * css = new char[7];
  memcpy(css, &p[1], 6);
  css[6] = '\0';

  char * db = new char[121 + 1];
  memcpy(db, &p[2], 121);
  db[121] = '\0';

  cout << "Seq. num: " << sns << endl;
  cout << "Checksum: " << css << endl;
  cout << "Message: " << db << endl;

  int sn = boost::lexical_cast<int>(sns);
  int cs = boost::lexical_cast<int>(css);

  Packet pk (0, db);
  pk.setSequenceNum(sn);

  // change to validate based on checksum and sequence number

  if(sn == seqNum) return false;
  if(cs != pk.generateCheckSum()) return false;
  return true;
}

bool getFile(){

  /* Loop forever, waiting for messages from a client. */
  cout << "Waiting on port " << PORT << "..." << endl;

  ofstream file("Dumpfile");

  bool isSeqNumSet = false;
  for (;;) {
    unsigned char packet[PAKSIZE + 1];
    unsigned char dataPull[PAKSIZE - 7 + 1];
    rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&ca, &calen);
    if(!isSeqNumSet) {
      isSeqNumSet = true;
      char * str = new char[1];
      memcpy(str, &packet[0], 1);
      seqNum = boost::lexical_cast<int>(str);
    }
    for(int x = 0; x < PAKSIZE - 7; x++) {
      dataPull[x] = packet[x + 7];
    }
    dataPull[PAKSIZE - 7] = '\0';
    packet[PAKSIZE] = '\0';
    if (rlen > 0) {
      char * css = new char[6];
      memcpy(css, &packet[1], 5);
      css[5] = '\0';
      cout << endl << endl << "=== RECEIPT" << endl;
      cout << "Seq. num: " << packet[0] << endl;
      cout << "Checksum: " << css << endl;
      cout << "Received message: " << dataPull << endl;
      cout << "Sent response: ";
      if(isvpack(packet)) {
        ack = ACK;
        seqNum = (seqNum) ? false : true;
        file << dataPull;
      } else {
        ack = NAK;
      }
      cout << ((ack == ACK) ? "ACK" : "NAK") << endl;
      Packet p ((seqNum) ? false : true, reinterpret_cast<const char *>(dataPull));
      p.setCheckSum(boost::lexical_cast<int>(css));
      p.setAckNack(ack);

      if(sendto(s, p.str(), PAKSIZE, 0, (struct sockaddr *)&ca, calen) < 0) {
        cout << "Acknowledgement failed. (socket s, acknowledgement message ack, client address ca, client address length calen)" << endl;
        return 0;
      }
      delete css;
    }
  }
  file.close();
  return true;
}

bool loadFile() {

  ifstream is (FILENAME, ifstream::binary);

  if(is) {
    is.seekg(0, is.end);
    length = is.tellg();
    is.seekg(0, is.beg);

    file = new char[length];

    cout << "Reading " << length << " characters..." << endl;
    is.read(file, length);

    if(!is) { cout << "File reading failed. (filename " << FILENAME << "). Only " << is.gcount() << " could be read."; return false; }
    is.close();
  }
  return true;
}

bool sendFile() {

  for(int x = 0; x <= (length / BUFSIZE) + 1; x++) {
    p = createPacket(x);
    if(!sendPacket()) continue;

    while(recvfrom(s, b, BUFSIZE + 7, 0, (struct sockaddr *)&ca, &calen) < 0) {
		cout << "Timed out. Resending..." << endl;
		sendPacket();
	}

    if(isAck()) {
      handleAck();
    } else {
      handleNak(x);
    }

    memset(b, 0, BUFSIZE);
  }
  return true;
}

Packet createPacket(int index){
    cout << endl;
    cout << "=== TRANSMISSION START" << endl;
    string mstr = fstr.substr(index * BUFSIZE, BUFSIZE);

	if(mstr.length() < BUFSIZE) {
      mstr[length - (index * BUFSIZE)] = '\0';
    }
    return Packet (seqNum, mstr.c_str());
}

bool sendPacket(){
    int pc = probCorrupt; int pl = probLoss; int pd = probDelay;
	bool* pckStatus = gremlin(&p, pc, pl, pd);

	dropPck = pckStatus[0];
	delayPck = pckStatus[1];

	cout << "dropPck: " << dropPck << endl;
	cout << "delayPck: " << delayPck << endl;

	if (dropPck == 1) return false;
	if (delayPck == 1) sleep(delayT);
    if(sendto(s, p.str(), BUFSIZE + 7, 0, (struct sockaddr *)&ca, sizeof(ca)) < 0) {
		cout << "Package sending failed. (socket s, server address sa, message m)" << endl;
		return false;
    }
    return true;
}

bool isAck() {
    cout << endl << "=== SERVER RESPONSE TEST" << endl;
    cout << "Data: " << b << endl;
    if(b[6] == '0') return true;
    else return false;
}
void handleAck() {

}
void handleNak(int& x) {

      char * sns = new char[2];
      memcpy(sns, &b[0], 1);
      sns[1] = '\0';

      char * css = new char[5];
      memcpy(css, &b[1], 5);

      char * db = new char[BUFSIZE + 1];
      memcpy(db, &b[2], BUFSIZE);
      db[BUFSIZE] = '\0';

      cout << "Sequence number: " << sns << endl;
      cout << "Checksum: " << css << endl;

      Packet pk (0, db);
      pk.setSequenceNum(boost::lexical_cast<int>(sns));
      pk.setCheckSum(boost::lexical_cast<int>(css));

      if(!pk.chksm()) x--;
      else x = (x - 2 > 0) ? x - 2 : 0;
}

bool* gremlin(Packet * pack, int corruptProb, int lossProb, int delayProb){
  bool dropPacket = false;
  bool delayPacket = false;
  bool* packStatus = new bool[2];
  int r = rand() % 100;

  cout << "Corruption probability: " << corruptProb << endl;
  cout << "Random number: " << r << endl;

  if(r <= (lossProb)){
    dropPacket = true;
	packStatus[0] = dropPacket;
    cout << "Dropped!" << endl;
  }
  else if(r <= (delayProb)){
	  delayPacket = true;
	  packStatus[1] = delayPacket;
	  cout << "Delayed!" << endl;
  }
  else if(r <= (corruptProb)){
    cout << "Corrupted!" << endl;
    pack->loadDataBuffer((char*)"GREMLIN LOL");
  }
  else seqNum = (seqNum) ? false : true;
  cout << "Seq. num: " << pack->getSequenceNum() << endl;
  cout << "Checksum: " << pack->getCheckSum() << endl;
  cout << "Message: "  << pack->getDataBuffer() << endl;

  return packStatus;
}<commit_msg>Testing for the fix's fix's fix (again)<commit_after>#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <boost/lexical_cast.hpp>
#include "packet.h"

#define USAGE "Usage:\r\nc \r\n[tux machine number] \r\n[probability of packet corruption in int form] \r\n[probability of packet loss in int form] \r\n[probability of packet delay] \r\n[length of delay in ms] \r\n"
#define PORT 10038
#define PAKSIZE 128
#define ACK 0
#define NAK 1
#define BUFSIZE 121
#define FILENAME "Testfile"
#define TIMEOUT 100 //in ms

using namespace std;

bool isvpack(unsigned char * p);
bool init(int argc, char** argv);
bool loadFile();
bool getFile();
bool sendFile();
bool isAck();
void handleAck();
void handleNak(int& x);
bool* gremlin(Packet * pack, int corruptProb, int lossProb, int delayProb);
Packet createPacket(int index);
bool sendPacket();

bool seqNum = true;
struct sockaddr_in a;
struct sockaddr_in ca;
socklen_t calen;
int rlen;
int s;
bool ack;
string fstr;
char * file;
int probCorrupt;
int probLoss;
int probDelay;
int delayT;
Packet p;
int length;
bool dropPck;
bool delayPck;
unsigned char b[BUFSIZE];

int main(int argc, char** argv) {

  if(!init(argc, argv)) return -1;

  calen = sizeof(ca);

  unsigned char packet[PAKSIZE + 1];
  rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&ca, &calen);

  sendFile();

  return 0;
}

bool init(int argc, char** argv){

  if(argc != 6) {
    cout << USAGE << endl;
    return false;
  }

  char * probCorruptStr = argv[2];
  probCorrupt = boost::lexical_cast<int>(probCorruptStr);
  char * probLossStr = argv[3];
  probLoss = boost::lexical_cast<int>(probLossStr);
  char * probDelayStr = argv[4];
  probDelay = boost::lexical_cast<int>(probDelayStr);
  char* delayTStr = argv[5];
  delayT = boost::lexical_cast<int>(delayTStr);

  static int timeout = TIMEOUT;

  /* Create our socket. */
  if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    cout << "Socket creation failed. (socket s)" << endl;
    return 0;
  }

  setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));

  /*
   * Bind our socket to an IP (whatever the computer decides)
   * and a specified port.
   *
   */

  if (!loadFile()) {
    cout << "Loading file failed. (filename FILENAME)" << endl;
    return false;
  }

  memset((char *)&a, 0, sizeof(a));
  a.sin_family = AF_INET;
  a.sin_addr.s_addr = htonl(INADDR_ANY);
  a.sin_port = htons(PORT);

  if (bind(s, (struct sockaddr *)&a, sizeof(a)) < 0) {
    cout << "Socket binding failed. (socket s, address a)" << endl;
    return 0;
  }

  fstr = string(file);
  cout << "File: " << endl << fstr << endl;

  seqNum = true;
  dropPck = false;

  return true;
}

bool isvpack(unsigned char * p) {
  cout << endl << "=== IS VALID PACKET TESTING" << endl;

  char * sns = new char[2];
  memcpy(sns, &p[0], 1);
  sns[1] = '\0';

  char * css = new char[7];
  memcpy(css, &p[1], 6);
  css[6] = '\0';

  char * db = new char[121 + 1];
  memcpy(db, &p[2], 121);
  db[121] = '\0';

  cout << "Seq. num: " << sns << endl;
  cout << "Checksum: " << css << endl;
  cout << "Message: " << db << endl;

  int sn = boost::lexical_cast<int>(sns);
  int cs = boost::lexical_cast<int>(css);

  Packet pk (0, db);
  pk.setSequenceNum(sn);

  // change to validate based on checksum and sequence number

  if(sn == seqNum) return false;
  if(cs != pk.generateCheckSum()) return false;
  return true;
}

bool getFile(){

  /* Loop forever, waiting for messages from a client. */
  cout << "Waiting on port " << PORT << "..." << endl;

  ofstream file("Dumpfile");

  bool isSeqNumSet = false;
  for (;;) {
    unsigned char packet[PAKSIZE + 1];
    unsigned char dataPull[PAKSIZE - 7 + 1];
    rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&ca, &calen);
    if(!isSeqNumSet) {
      isSeqNumSet = true;
      char * str = new char[1];
      memcpy(str, &packet[0], 1);
      seqNum = boost::lexical_cast<int>(str);
    }
    for(int x = 0; x < PAKSIZE - 7; x++) {
      dataPull[x] = packet[x + 7];
    }
    dataPull[PAKSIZE - 7] = '\0';
    packet[PAKSIZE] = '\0';
    if (rlen > 0) {
      char * css = new char[6];
      memcpy(css, &packet[1], 5);
      css[5] = '\0';
      cout << endl << endl << "=== RECEIPT" << endl;
      cout << "Seq. num: " << packet[0] << endl;
      cout << "Checksum: " << css << endl;
      cout << "Received message: " << dataPull << endl;
      cout << "Sent response: ";
      if(isvpack(packet)) {
        ack = ACK;
        seqNum = (seqNum) ? false : true;
        file << dataPull;
      } else {
        ack = NAK;
      }
      cout << ((ack == ACK) ? "ACK" : "NAK") << endl;
      Packet p ((seqNum) ? false : true, reinterpret_cast<const char *>(dataPull));
      p.setCheckSum(boost::lexical_cast<int>(css));
      p.setAckNack(ack);

      if(sendto(s, p.str(), PAKSIZE, 0, (struct sockaddr *)&ca, calen) < 0) {
        cout << "Acknowledgement failed. (socket s, acknowledgement message ack, client address ca, client address length calen)" << endl;
        return 0;
      }
      delete css;
    }
  }
  file.close();
  return true;
}

bool loadFile() {

  ifstream is (FILENAME, ifstream::binary);

  if(is) {
    is.seekg(0, is.end);
    length = is.tellg();
    is.seekg(0, is.beg);

    file = new char[length];

    cout << "Reading " << length << " characters..." << endl;
    is.read(file, length);

    if(!is) { cout << "File reading failed. (filename " << FILENAME << "). Only " << is.gcount() << " could be read."; return false; }
    is.close();
  }
  return true;
}

bool sendFile() {

  for(int x = 0; x <= (length / BUFSIZE) + 1; x++) {
    p = createPacket(x);
    if(!sendPacket()) continue;

    while(recvfrom(s, b, BUFSIZE + 7, 0, (struct sockaddr *)&ca, &calen) < 0) {
		cout << "Timed out. Resending..." << endl;
		sendPacket();
	}

    if(isAck()) {
      handleAck();
    } else {
      handleNak(x);
    }

    memset(b, 0, BUFSIZE);
  }
  return true;
}

Packet createPacket(int index){
    cout << endl;
    cout << "=== TRANSMISSION START" << endl;
    string mstr = fstr.substr(index * BUFSIZE, BUFSIZE);

	if(mstr.length() < BUFSIZE) {
      mstr[length - (index * BUFSIZE)] = '\0';
    }
    return Packet (seqNum, mstr.c_str());
}

bool sendPacket(){
    int pc = probCorrupt; int pl = probLoss; int pd = probDelay;
	bool* pckStatus = gremlin(&p, pc, pl, pd);

	dropPck = pckStatus[0];
	delayPck = pckStatus[1];

	cout << "dropPck: " << dropPck << endl;
	cout << "delayPck: " << delayPck << endl;

	if (dropPck == 1) return false;
	if (delayPck == 1) sleep(delayT);

	cout << "Made it to sending mode..." << endl;

    if(sendto(s, p.str(), BUFSIZE + 7, 0, (struct sockaddr *)&ca, sizeof(ca)) < 0) {
		cout << "Package sending failed. (socket s, server address sa, message m)" << endl;
		return false;
    }
    return true;
}

bool isAck() {
    cout << endl << "=== SERVER RESPONSE TEST" << endl;
    cout << "Data: " << b << endl;
    if(b[6] == '0') return true;
    else return false;
}
void handleAck() {

}
void handleNak(int& x) {

      char * sns = new char[2];
      memcpy(sns, &b[0], 1);
      sns[1] = '\0';

      char * css = new char[5];
      memcpy(css, &b[1], 5);

      char * db = new char[BUFSIZE + 1];
      memcpy(db, &b[2], BUFSIZE);
      db[BUFSIZE] = '\0';

      cout << "Sequence number: " << sns << endl;
      cout << "Checksum: " << css << endl;

      Packet pk (0, db);
      pk.setSequenceNum(boost::lexical_cast<int>(sns));
      pk.setCheckSum(boost::lexical_cast<int>(css));

      if(!pk.chksm()) x--;
      else x = (x - 2 > 0) ? x - 2 : 0;
}

bool* gremlin(Packet * pack, int corruptProb, int lossProb, int delayProb){
  bool dropPacket = false;
  bool delayPacket = false;
  bool* packStatus = new bool[2];
  int r = rand() % 100;

  cout << "Corruption probability: " << corruptProb << endl;
  cout << "Random number: " << r << endl;

  if(r <= (lossProb)){
    dropPacket = true;
	packStatus[0] = dropPacket;
    cout << "Dropped!" << endl;
  }
  else if(r <= (delayProb)){
	  delayPacket = true;
	  packStatus[1] = delayPacket;
	  cout << "Delayed!" << endl;
  }
  else if(r <= (corruptProb)){
    cout << "Corrupted!" << endl;
    pack->loadDataBuffer((char*)"GREMLIN LOL");
  }
  else seqNum = (seqNum) ? false : true;
  cout << "Seq. num: " << pack->getSequenceNum() << endl;
  cout << "Checksum: " << pack->getCheckSum() << endl;
  cout << "Message: "  << pack->getDataBuffer() << endl;

  return packStatus;
}<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Extension port manager takes care of managing the state of
// connecting and connected ports.
#include "ceee/ie/plugin/bho/extension_port_manager.h"
#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "base/values.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "ceee/ie/common/chrome_frame_host.h"
#include "ceee/ie/plugin/scripting/content_script_native_api.h"
#include "chrome/browser/automation/extension_automation_constants.h"

namespace ext = extension_automation_constants;

ExtensionPortManager::ExtensionPortManager() {
}

ExtensionPortManager::~ExtensionPortManager() {
}

void ExtensionPortManager::Initialize(IChromeFrameHost* chrome_frame_host) {
  chrome_frame_host_ = chrome_frame_host;
}

void ExtensionPortManager::CloseAll(IContentScriptNativeApi* instance) {
  DCHECK(instance != NULL);

  // TODO(siggi@chromium.org): Deal better with these cases. Connected
  //    ports probably ought to be closed, and connecting ports may
  //    need to hang around until we get the connected message, to be
  //    terminated at that point.
  ConnectedPortMap::iterator it(connected_ports_.begin());
  while (it != connected_ports_.end()) {
    if (it->second.instance = instance) {
      connected_ports_.erase(it++);
    } else {
      ++it;
    }
  }

  ConnectingPortMap::iterator jt(connecting_ports_.begin());
  while (jt != connecting_ports_.end()) {
    if (jt->second.instance = instance) {
      connecting_ports_.erase(jt++);
    } else {
      ++jt;
    }
  }
}

HRESULT ExtensionPortManager::OpenChannelToExtension(
    IContentScriptNativeApi* instance, const std::string& extension,
    const std::string& channel_name, Value* tab, int cookie) {
  int connection_id = next_connection_id_++;

  // Prepare the connection request.
  scoped_ptr<DictionaryValue> dict(new DictionaryValue());
  if (dict.get() == NULL)
    return E_OUTOFMEMORY;

  dict->SetInteger(ext::kAutomationRequestIdKey, ext::OPEN_CHANNEL);
  dict->SetInteger(ext::kAutomationConnectionIdKey, connection_id);
  dict->SetString(ext::kAutomationExtensionIdKey, extension);
  dict->SetString(ext::kAutomationChannelNameKey, channel_name);
  dict->Set(ext::kAutomationTabJsonKey, tab);

  // JSON encode it.
  std::string request_json;
  base::JSONWriter::Write(dict.get(), false, &request_json);
  // And fire it off.
  HRESULT hr = PostMessageToHost(request_json,
                                 ext::kAutomationPortRequestTarget);
  if (FAILED(hr))
    return hr;

  ConnectingPort connecting_port = { instance, cookie };
  connecting_ports_[connection_id] = connecting_port;

  return S_OK;
}

HRESULT ExtensionPortManager::PostMessage(int port_id,
                                          const std::string& message) {
  // Wrap the message for sending as a port request.
  scoped_ptr<DictionaryValue> dict(new DictionaryValue());
  if (dict.get() == NULL)
    return E_OUTOFMEMORY;

  dict->SetInteger(ext::kAutomationRequestIdKey, ext::POST_MESSAGE);
  dict->SetInteger(ext::kAutomationPortIdKey, port_id);
  dict->SetString(ext::kAutomationMessageDataKey, message);

  // JSON encode it.
  std::string message_json;
  base::JSONWriter::Write(dict.get(), false, &message_json);

  // And fire it off.
  return PostMessageToHost(message_json,
                           std::string(ext::kAutomationPortRequestTarget));
}

void ExtensionPortManager::OnPortMessage(BSTR message) {
  std::string message_json = CW2A(message);
  scoped_ptr<Value> value(base::JSONReader::Read(message_json, true));
  if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY)) {
    NOTREACHED();
    LOG(ERROR) << "Invalid message";
    return;
  }

  DictionaryValue* dict = static_cast<DictionaryValue*>(value.get());
  int request = -1;
  if (!dict->GetInteger(ext::kAutomationRequestIdKey, &request)) {
    NOTREACHED();
    LOG(ERROR) << "Request ID missing";
    return;
  }

  if (request == ext::CHANNEL_OPENED) {
    int connection_id = -1;
    if (!dict->GetInteger(ext::kAutomationConnectionIdKey, &connection_id)) {
      NOTREACHED();
      LOG(ERROR) << "Connection ID missing";
      return;
    }

    int port_id = -1;
    if (!dict->GetInteger(ext::kAutomationPortIdKey, &port_id)) {
      NOTREACHED();
      LOG(ERROR) << "Port ID missing";
      return;
    }

    ConnectingPortMap::iterator it(connecting_ports_.find(connection_id));
    if (it == connecting_ports_.end()) {
      // TODO(siggi@chromium.org): This can happen legitimately on a
      //    race between connect and document unload. We should
      //    probably respond with a close port message here.
      LOG(ERROR) << "No such connection id " << connection_id;
      return;
    }
    ConnectingPort port = it->second;
    connecting_ports_.erase(it);
    // Did it connect successfully?
    if (port_id != -1)
      connected_ports_[port_id].instance = port.instance;

    port.instance->OnChannelOpened(port.cookie, port_id);
    return;
  } else if (request == ext::POST_MESSAGE) {
    int port_id = -1;
    if (!dict->GetInteger(ext::kAutomationPortIdKey, &port_id)) {
      NOTREACHED();
      LOG(ERROR) << "No port id";
      return;
    }

    std::string data;
    if (!dict->GetString(ext::kAutomationMessageDataKey, &data)) {
      NOTREACHED();
      LOG(ERROR) << "No message data";
      return;
    }

    ConnectedPortMap::iterator it(connected_ports_.find(port_id));
    if (it == connected_ports_.end()) {
      LOG(ERROR) << "No such port " << port_id;
      return;
    }

    it->second.instance->OnPostMessage(port_id, data);
    return;
  } else if (request == ext::CHANNEL_CLOSED) {
    // TODO(siggi@chromium.org): handle correctly.
    return;
  }

  NOTREACHED();
}

HRESULT ExtensionPortManager::PostMessageToHost(const std::string& message,
                                                const std::string& target) {
  // Post our message through the ChromeFrameHost. We allow queueing,
  // because we don't synchronize to the destination extension loading.
  return chrome_frame_host_->PostMessage(CComBSTR(message.c_str()),
                                         CComBSTR(target.c_str()));
}
<commit_msg>Small fix to make sure we always start connection ID sequences to the same value.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Extension port manager takes care of managing the state of
// connecting and connected ports.
#include "ceee/ie/plugin/bho/extension_port_manager.h"
#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/win/scoped_bstr.h"
#include "ceee/ie/common/chrome_frame_host.h"
#include "ceee/ie/plugin/scripting/content_script_native_api.h"
#include "chrome/browser/automation/extension_automation_constants.h"

namespace ext = extension_automation_constants;

ExtensionPortManager::ExtensionPortManager() : next_connection_id_(0) {
}

ExtensionPortManager::~ExtensionPortManager() {
}

void ExtensionPortManager::Initialize(IChromeFrameHost* chrome_frame_host) {
  chrome_frame_host_ = chrome_frame_host;
}

void ExtensionPortManager::CloseAll(IContentScriptNativeApi* instance) {
  DCHECK(instance != NULL);

  // TODO(siggi@chromium.org): Deal better with these cases. Connected
  //    ports probably ought to be closed, and connecting ports may
  //    need to hang around until we get the connected message, to be
  //    terminated at that point.
  ConnectedPortMap::iterator it(connected_ports_.begin());
  while (it != connected_ports_.end()) {
    if (it->second.instance = instance) {
      connected_ports_.erase(it++);
    } else {
      ++it;
    }
  }

  ConnectingPortMap::iterator jt(connecting_ports_.begin());
  while (jt != connecting_ports_.end()) {
    if (jt->second.instance = instance) {
      connecting_ports_.erase(jt++);
    } else {
      ++jt;
    }
  }
}

HRESULT ExtensionPortManager::OpenChannelToExtension(
    IContentScriptNativeApi* instance, const std::string& extension,
    const std::string& channel_name, Value* tab, int cookie) {
  int connection_id = next_connection_id_++;

  // Prepare the connection request.
  scoped_ptr<DictionaryValue> dict(new DictionaryValue());
  if (dict.get() == NULL)
    return E_OUTOFMEMORY;

  dict->SetInteger(ext::kAutomationRequestIdKey, ext::OPEN_CHANNEL);
  dict->SetInteger(ext::kAutomationConnectionIdKey, connection_id);
  dict->SetString(ext::kAutomationExtensionIdKey, extension);
  dict->SetString(ext::kAutomationChannelNameKey, channel_name);
  dict->Set(ext::kAutomationTabJsonKey, tab);

  // JSON encode it.
  std::string request_json;
  base::JSONWriter::Write(dict.get(), false, &request_json);
  // And fire it off.
  HRESULT hr = PostMessageToHost(request_json,
                                 ext::kAutomationPortRequestTarget);
  if (FAILED(hr))
    return hr;

  ConnectingPort connecting_port = { instance, cookie };
  connecting_ports_[connection_id] = connecting_port;

  return S_OK;
}

HRESULT ExtensionPortManager::PostMessage(int port_id,
                                          const std::string& message) {
  // Wrap the message for sending as a port request.
  scoped_ptr<DictionaryValue> dict(new DictionaryValue());
  if (dict.get() == NULL)
    return E_OUTOFMEMORY;

  dict->SetInteger(ext::kAutomationRequestIdKey, ext::POST_MESSAGE);
  dict->SetInteger(ext::kAutomationPortIdKey, port_id);
  dict->SetString(ext::kAutomationMessageDataKey, message);

  // JSON encode it.
  std::string message_json;
  base::JSONWriter::Write(dict.get(), false, &message_json);

  // And fire it off.
  return PostMessageToHost(message_json,
                           std::string(ext::kAutomationPortRequestTarget));
}

void ExtensionPortManager::OnPortMessage(BSTR message) {
  std::string message_json = CW2A(message);
  scoped_ptr<Value> value(base::JSONReader::Read(message_json, true));
  if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY)) {
    NOTREACHED();
    LOG(ERROR) << "Invalid message";
    return;
  }

  DictionaryValue* dict = static_cast<DictionaryValue*>(value.get());
  int request = -1;
  if (!dict->GetInteger(ext::kAutomationRequestIdKey, &request)) {
    NOTREACHED();
    LOG(ERROR) << "Request ID missing";
    return;
  }

  if (request == ext::CHANNEL_OPENED) {
    int connection_id = -1;
    if (!dict->GetInteger(ext::kAutomationConnectionIdKey, &connection_id)) {
      NOTREACHED();
      LOG(ERROR) << "Connection ID missing";
      return;
    }

    int port_id = -1;
    if (!dict->GetInteger(ext::kAutomationPortIdKey, &port_id)) {
      NOTREACHED();
      LOG(ERROR) << "Port ID missing";
      return;
    }

    ConnectingPortMap::iterator it(connecting_ports_.find(connection_id));
    if (it == connecting_ports_.end()) {
      // TODO(siggi@chromium.org): This can happen legitimately on a
      //    race between connect and document unload. We should
      //    probably respond with a close port message here.
      LOG(ERROR) << "No such connection id " << connection_id;
      return;
    }
    ConnectingPort port = it->second;
    connecting_ports_.erase(it);
    // Did it connect successfully?
    if (port_id != -1)
      connected_ports_[port_id].instance = port.instance;

    port.instance->OnChannelOpened(port.cookie, port_id);
    return;
  } else if (request == ext::POST_MESSAGE) {
    int port_id = -1;
    if (!dict->GetInteger(ext::kAutomationPortIdKey, &port_id)) {
      NOTREACHED();
      LOG(ERROR) << "No port id";
      return;
    }

    std::string data;
    if (!dict->GetString(ext::kAutomationMessageDataKey, &data)) {
      NOTREACHED();
      LOG(ERROR) << "No message data";
      return;
    }

    ConnectedPortMap::iterator it(connected_ports_.find(port_id));
    if (it == connected_ports_.end()) {
      LOG(ERROR) << "No such port " << port_id;
      return;
    }

    it->second.instance->OnPostMessage(port_id, data);
    return;
  } else if (request == ext::CHANNEL_CLOSED) {
    // TODO(siggi@chromium.org): handle correctly.
    return;
  }

  NOTREACHED();
}

HRESULT ExtensionPortManager::PostMessageToHost(const std::string& message,
                                                const std::string& target) {
  // Post our message through the ChromeFrameHost. We allow queueing,
  // because we don't synchronize to the destination extension loading.
  return chrome_frame_host_->PostMessage(
      base::win::ScopedBstr(ASCIIToWide(message).c_str()),
      base::win::ScopedBstr(ASCIIToWide(target).c_str()));
}
<|endoftext|>