text:
#pragma warning(disable:4786)

#include <qmaccount.h>
#include <qmapplication.h>
#include <qmmessage.h>
#include <qmmessageholder.h>
#include <qmsecurity.h>

#include <qsconv.h>
#include <qsencoder.h>
#include <qsfile.h>
#include <qsosutil.h>
#include <qsregex.h>
#include <qsstl.h>
#include <qsstream.h>
#include <qsstring.h>
#include <qstextutil.h>

#include <algorithm>

#include <boost/bind.hpp>
#include <boost/bind/make_adaptable.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>

#include "message.h"
#include "messagecontext.h"
#include "signature.h"
#include "uri.h"
#include "zip.h"

using namespace qm;
using namespace qs;

/****************************************************************************
 *
 * Message
 *
 */

qm::Message::Message() :
	flag_(FLAG_EMPTY),
	nSecurity_(SECURITY_NONE)
{
}

qm::Message::~Message()
{
	clearParams();
}

bool qm::Message::create(const CHAR* pszMessage,
						 size_t nLen,
						 Flag flag)
{
	return create(pszMessage, nLen, flag, SECURITY_NONE);
}

bool qm::Message::create(const CHAR* pszMessage,
						 size_t nLen,
						 Flag flag,
						 unsigned int nSecurity)
{
	if (!Part::create(0, pszMessage, nLen))
		return false;

	flag_ = flag;
	nSecurity_ = nSecurity;

	return true;
}

bool qm::Message::createHeader(const CHAR* pszHeader,
							   size_t nLen)
{
	if (nLen == -1)
		nLen = strlen(pszHeader);

	const CHAR* pBody = Part::getBody(pszHeader, nLen);
	return create(pszHeader, pBody ? pBody - pszHeader + 4 : nLen, FLAG_HEADERONLY);
}

void qm::Message::clear()
{
	flag_ = FLAG_EMPTY;
	Part::clear();
	clearParams();
}

Message::Flag qm::Message::getFlag() const
{
	return flag_;
}

void qm::Message::setFlag(Flag flag)
{
	flag_ = flag;
}

unsigned int qm::Message::getSecurity() const
{
	return nSecurity_;
}

void qm::Message::setSecurity(unsigned int nSecurity)
{
	nSecurity_ = nSecurity;
}

const WCHAR* qm::Message::getParam(const WCHAR* pwszName) const
{
	ParamList::const_iterator it = std::find_if(
		listParam_.begin(), listParam_.end(),
		boost::bind(string_equal<WCHAR>(),
			boost::bind(&ParamList::value_type::first, _1), pwszName));
	return it != listParam_.end() ? (*it).second : 0;
}

void qm::Message::setParam(const WCHAR* pwszName,
						   const WCHAR* pwszValue)
{
	assert(pwszName);

	wstring_ptr wstrValue;
	if (pwszValue)
		wstrValue = allocWString(pwszValue);

	ParamList::iterator it = std::find_if(
		listParam_.begin(), listParam_.end(),
		boost::bind(string_equal<WCHAR>(),
			boost::bind(&ParamList::value_type::first, _1), pwszName));
	if (it != listParam_.end()) {
		freeWString((*it).second);
		if (wstrValue.get())
			(*it).second = wstrValue.release();
		else
			listParam_.erase(it);
	}
	else if (wstrValue.get()) {
		wstring_ptr wstrName(allocWString(pwszName));
		listParam_.push_back(std::make_pair(wstrName.get(), wstrValue.get()));
		wstrName.release();
		wstrValue.release();
	}
}

bool qm::Message::removePrivateFields()
{
	PrefixFieldFilter filter("x-qmail-");
	return removeFields(&filter);
}

void qm::Message::clearParams()
{
	using namespace boost::lambda;
	using boost::lambda::_1;
	std::for_each(listParam_.begin(), listParam_.end(),
		(bind(&freeWString, bind(&ParamList::value_type::first, _1)),
		 bind(&freeWString, bind(&ParamList::value_type::second, _1))));
	listParam_.clear();
}

/****************************************************************************
 *
 * HeaderData
 *
 */

namespace {

struct Header
{
	size_t nLowerName_;
	size_t nName_;
	size_t nValue_;
};

}

/****************************************************************************
 *
 * MessageCreator
 *
 */

qm::MessageCreator::MessageCreator() :
	nFlags_(0),
	nSecurityMode_(SECURITYMODE_NONE),
	pURIResolver_(0),
	pSignatureManager_(0),
	pAccount_(0)
{
}

qm::MessageCreator::MessageCreator(unsigned int nFlags,
								   unsigned int nSecurityMode) :
	nFlags_(nFlags),
	nSecurityMode_(nSecurityMode),
	pURIResolver_(0),
	pSignatureManager_(0),
	pAccount_(0)
{
	assert(!(nFlags & FLAG_ENCODETEXT) &&
		!(nFlags & FLAG_EXTRACTATTACHMENT) &&
		!(nFlags & FLAG_ADDSIGNATURE));
}

qm::MessageCreator::MessageCreator(unsigned int nFlags,
								   unsigned int nSecurityMode,
								   const WCHAR* pwszTransferEncodingFor8Bit) :
	nFlags_(nFlags),
	nSecurityMode_(nSecurityMode),
	pURIResolver_(0),
	pSignatureManager_(0),
	pAccount_(0)
{
	assert(!(nFlags & FLAG_EXTRACTATTACHMENT) &&
		!(nFlags & FLAG_ADDSIGNATURE));

	if (pwszTransferEncodingFor8Bit && *pwszTransferEncodingFor8Bit)
		wstrTransferEncodingFor8Bit_ = allocWString(pwszTransferEncodingFor8Bit);
}

qm::MessageCreator::MessageCreator(unsigned int nFlags,
								   unsigned int nSecurityMode,
								   const WCHAR* pwszTransferEncodingFor8Bit,
								   const URIResolver* pURIResolver,
								   SignatureManager* pSignatureManager,
								   Account* pAccount,
								   const WCHAR* pwszArchiveAttachmentExcludePattern,
								   const WCHAR* pwszTempDir) :
	nFlags_(nFlags),
	nSecurityMode_(nSecurityMode),
	pURIResolver_(pURIResolver),
	pSignatureManager_(pSignatureManager),
	pAccount_(pAccount)
{
	assert(pURIResolver);
	assert(pSignatureManager);
	assert(pAccount);
	assert(pwszTempDir);

	if (pwszTransferEncodingFor8Bit && *pwszTransferEncodingFor8Bit)
		wstrTransferEncodingFor8Bit_ = allocWString(pwszTransferEncodingFor8Bit);
	if (pwszArchiveAttachmentExcludePattern)
		wstrArchiveAttachmentExcludePattern_ = allocWString(pwszArchiveAttachmentExcludePattern);
	wstrTempDir_ = allocWString(pwszTempDir);
}

qm::MessageCreator::~MessageCreator()
{
}

unsigned int qm::MessageCreator::getFlags() const
{
	return nFlags_;
}

void qm::MessageCreator::setFlags(unsigned int nFlags,
								  unsigned int nMask)
{
	nFlags_ &= ~nMask;
	nFlags_ |= nFlags & nMask;
}

std::auto_ptr<Message> qm::MessageCreator::createMessage(const WCHAR* pwszMessage,
														 size_t nLen) const
{
	std::auto_ptr<Part> pPart(createPart(pwszMessage, nLen, 0, true));
	if (!pPart.get())
		return std::auto_ptr<Message>();

	std::auto_ptr<Message> pMessage(static_cast<Message*>(pPart.release()));
	pMessage->setFlag(Message::FLAG_NONE);

	return pMessage;
}

std::auto_ptr<Part> qm::MessageCreator::createPart(const WCHAR* pwszMessage,
												   size_t nLen,
												   Part* pParent,
												   bool bMessage) const
{
	if (nLen == -1)
		nLen = wcslen(pwszMessage);

	std::auto_ptr<Part> pPart;
	if (bMessage)
		pPart.reset(new Message());
	else
		pPart.reset(new Part());

	const WCHAR* pBody = 0;
	size_t nBodyLen = 0;
	if (*pwszMessage == L'\n') {
		pBody = pwszMessage + 1;
		nBodyLen = nLen - 1;
	}
	else {
		BMFindString<WSTRING> bmfs(L"\n\n");
		pBody = bmfs.find(pwszMessage, nLen);
		if (pBody) {
			if (!createHeader(pPart.get(), pwszMessage, pBody - pwszMessage + 1))
				return std::auto_ptr<Part>();
			pBody += 2;
			nBodyLen = nLen - (pBody - pwszMessage);
		}
		else {
			StringBuffer<WSTRING> buf(pwszMessage, nLen);
			if (*(buf.getCharArray() + buf.getLength() - 1) != L'\n')
				buf.append(L'\n');
			if (!createHeader(pPart.get(), buf.getCharArray(), buf.getLength()))
				return std::auto_ptr<Part>();
		}
	}

	const ContentTypeParser* pContentType = pPart->getContentType();
	const WCHAR* pwszMediaType = pContentType ?
		pContentType->getMediaType() : L"text";
	bool bMultipart = PartUtil::isMultipart(pContentType);

	if (pBody) {
		bool bRFC822 = false;
		if (!bMultipart) {
			bRFC822 = PartUtil::isContentType(pContentType, L"message", L"rfc822");
			if (!bRFC822 && !pContentType && pParent)
				bRFC822 = PartUtil::isContentType(pParent->getContentType(),
					L"multipart", L"digest");
		}
		if (bMultipart) {
			wstring_ptr wstrBoundary(pContentType->getParameter(L"boundary"));
			if (!wstrBoundary.get())
				return std::auto_ptr<Part>();

			BoundaryFinder<WCHAR, WSTRING> finder(pBody - 1, nBodyLen + 1,
				wstrBoundary.get(), L"\n", (nFlags_ & FLAG_RECOVERBODY) != 0);

			while (true) {
				const WCHAR* pBegin = 0;
				const WCHAR* pEnd = 0;
				bool bEnd = false;
				if (!finder.getNext(&pBegin, &pEnd, &bEnd))
					return std::auto_ptr<Part>();
				if (pBegin) {
					MessageCreator creator(getCreatorForChild());
					std::auto_ptr<Part> pChild(creator.createPart(
						pBegin, pEnd - pBegin, pPart.get(), false));
					if (!pChild.get())
						return std::auto_ptr<Part>();
					pPart->addPart(pChild);
				}
				if (bEnd)
					break;
			}

			std::pair<const WCHAR*, size_t> preamble(finder.getPreamble());
			if (preamble.first) {
				if (!pPart->setPreamble(wcs2mbs(preamble.first, preamble.second).get()))
					return std::auto_ptr<Part>();
			}
			std::pair<const WCHAR*, size_t> epilogue(finder.getEpilogue());
			if (epilogue.first) {
				if (!pPart->setEpilogue(wcs2mbs(epilogue.first, epilogue.second).get()))
					return std::auto_ptr<Part>();
			}
		}
		else if (bRFC822) {
			MessageCreator creator(getCreatorForChild());
			std::auto_ptr<Part> pEnclosed(creator.createPart(pBody, nBodyLen, 0, false));
			if (!pEnclosed.get())
				return std::auto_ptr<Part>();
			pPart->setEnclosedPart(pEnclosed);
		}
		else if (_wcsicmp(pwszMediaType, L"text") == 0) {
			wstring_ptr wstrCharset;
			if (pContentType) {
				wstrCharset = pContentType->getParameter(L"charset");
				if (!wstrCharset.get())
					wstrCharset = allocWString(L"us-ascii");
			}

			std::auto_ptr<Converter> pConverter;
			if (wstrCharset.get())
				pConverter = ConverterFactory::getInstance(wstrCharset.get());

			xstring_size_ptr strBody;

			if (!pConverter.get()) {
				bool bDefault = true;
				const WCHAR* pwszCharset = Part::getDefaultCharset();
				size_t n = 0;
				while (n < nBodyLen && *(pBody + n) < 0x80)
					++n;
				if (n == nBodyLen) {
					pwszCharset = L"us-ascii";
				}
				else {
					SimpleParser charset(0);
					if (pPart->getField(L"X-QMAIL-OriginalCharset", &charset) == Part::FIELD_EXIST &&
						_wcsicmp(charset.getValue(), L"us-ascii") != 0 &&
						_wcsicmp(charset.getValue(), pwszCharset) != 0) {
						bDefault = false;
						pwszCharset = L"utf-8";
					}
				}
				wstrCharset = allocWString(pwszCharset);
				pConverter = ConverterFactory::getInstance(wstrCharset.get());

				xstring_size_ptr strBody(convertBody(pConverter.get(), pBody, nBodyLen));
				if (!strBody.get()) {
					wstrCharset = allocWString(L"utf-8");
					pConverter = ConverterFactory::getInstance(wstrCharset.get());
					bDefault = false;
				}

				if (nFlags_ & FLAG_ADDCONTENTTYPE ||
					(nFlags_ & FLAG_ADDNODEFAULTCONTENTTYPE && !bDefault)) {
					ContentTypeParser contentType(L"text", L"plain");
					contentType.setParameter(L"charset", wstrCharset.get());
					if (!pPart->replaceField(L"Content-Type", contentType))
						return std::auto_ptr<Part>();
					pContentType = pPart->getContentType();
				}
			}
			assert(pConverter.get());

			pPart->removeField(L"X-QMAIL-OriginalCharset");

			if (!strBody.get()) {
				strBody = convertBody(pConverter.get(), pBody, nBodyLen);
				if (!strBody.get())
					return std::auto_ptr<Part>();
			}

			std::auto_ptr<Encoder> pEncoder;
			if (nFlags_ & FLAG_ENCODETEXT) {
				pEncoder = pPart->getEncoder();
				if (!pEncoder.get()) {
					unsigned char* p = reinterpret_cast<unsigned char*>(strBody.get());
					unsigned char* pEnd = p + strBody.size();
					size_t nLineLen = 0;
					while (p < pEnd) {
						if (*p >= 0x80)
							break;

						if (*p == '\r' && p + 1 < pEnd && *(p + 1) == '\n') {
							nLineLen = 0;
							++p;
						}
						else {
							++nLineLen;
							if (nLineLen >= MAX_LINE_LENGTH)
								break;
						}

						++p;
					}

					const WCHAR* pwszEncoding = 0;
					if (p == pEnd) {
						pwszEncoding = L"7bit";
					}
					else {
						if (wstrTransferEncodingFor8Bit_.get())
							pwszEncoding = wstrTransferEncodingFor8Bit_.get();
						else
							pwszEncoding = getEncodingForCharset(wstrCharset.get());
						pEncoder = EncoderFactory::getInstance(pwszEncoding);
						if (!pEncoder.get())
							return std::auto_ptr<Part>();
					}

					if (nFlags_ & FLAG_ADDCONTENTTYPE) {
						ContentTransferEncodingParser contentTransferEncoding(pwszEncoding);
						if (!pPart->replaceField(L"Content-Transfer-Encoding", contentTransferEncoding))
							return std::auto_ptr<Part>();
					}
				}
			}

			if (pEncoder.get()) {
				malloc_size_ptr<unsigned char> pBody(pEncoder->encode(
					reinterpret_cast<unsigned char*>(strBody.get()), strBody.size()));
				if (!pBody.get())
					return std::auto_ptr<Part>();

				if (!pPart->setBody(reinterpret_cast<CHAR*>(pBody.get()), pBody.size()))
					return std::auto_ptr<Part>();
			}
			else {
				pPart->setBody(xstring_ptr(strBody.release()));
			}
		}
		else {
			xstring_ptr strBody(PartUtil::w2a(pBody, nBodyLen));
			if (!strBody.get() && nFlags_ & FLAG_RECOVERBODY) {
				std::auto_ptr<Converter> pConverter(
					ConverterFactory::getInstance(Part::getDefaultCharset()));
				size_t nLen = nBodyLen;
				strBody.reset(pConverter->encode(pBody, &nLen).release());
			}
			if (!strBody.get())
				return std::auto_ptr<Part>();
			pPart->setBody(strBody);
		}
	}

	if (bMessage && (nFlags_ & FLAG_ADDCONTENTTYPE || nFlags_ & FLAG_ADDNODEFAULTCONTENTTYPE)) {
		SimpleParser mimeVersion(L"1.0", 0);
		if (!pPart->replaceField(L"MIME-Version", mimeVersion))
			return std::auto_ptr<Part>();
	}

	if (bMessage && nFlags_ & FLAG_ADDSIGNATURE) {
		assert(pSignatureManager_);

		UnstructuredParser signature;
		if (pPart->getField(L"X-QMAIL-Signature", &signature) == Part::FIELD_EXIST) {
			const Signature* pSignature = pSignatureManager_->getSignature(
				pAccount_, signature.getValue());
			if (pSignature && *pSignature->getSignature()) {
				if (PartUtil::isContentType(pContentType, L"text", L"plain")) {
					std::auto_ptr<Part> p(createPartWithSignature(pPart.get(), 0, pSignature));
					if (!p.get())
						return std::auto_ptr<Part>();
					pPart = p;
				}
				else if (PartUtil::isContentType(pContentType, L"multipart", L"mixed")) {
					if (pPart->getPartCount() != 0 &&
						PartUtil::isContentType(pPart->getPart(0)->getContentType(), L"text", L"plain")) {
						Part* pTextPart = pPart->getPart(0);
						std::auto_ptr<Part> p(createPartWithSignature(pTextPart, pPart.get(), pSignature));
						if (!p.get())
							return std::auto_ptr<Part>();
						pPart->removePart(pTextPart);
						delete pTextPart;
						pPart->insertPart(0, p);
					}
					else {
						std::auto_ptr<Part> p(createPartWithSignature(0, pPart.get(), pSignature));
						if (!p.get())
							return std::auto_ptr<Part>();
						pPart->insertPart(0, p);
					}
				}
				else {
					std::auto_ptr<Message> pParent(new Message());
					if (!makeMultipart(pParent.get(), pPart))
						return std::auto_ptr<Part>();
					pPart = pParent;

					std::auto_ptr<Part> p(createPartWithSignature(0, pPart.get(), pSignature));
					if (!p.get())
						return std::auto_ptr<Part>();
					pPart->insertPart(0, p);
				}
				pContentType = pPart->getContentType();
			}
			pPart->removeField(L"X-QMAIL-Signature");
		}
	}

	if (bMessage && nFlags_ & FLAG_EXTRACTATTACHMENT) {
		assert(pURIResolver_);

		XQMAILAttachmentParser attachment;
		if (pPart->getField(L"X-QMAIL-Attachment", &attachment) == Part::FIELD_EXIST &&
			!attachment.getAttachments().empty()) {
			const XQMAILAttachmentParser::AttachmentList& l = attachment.getAttachments();
			assert(pContentType);
			if (!PartUtil::isContentType(pContentType, L"multipart", L"mixed")) {
				std::auto_ptr<Message> pParent(new Message());
				if (!makeMultipart(pParent.get(), pPart))
					return std::auto_ptr<Part>();
				pPart = pParent;
				pContentType = pPart->getContentType();
			}

			const WCHAR* pwszArchive = 0;
#ifdef QMZIP
			UnstructuredParser archive;
			if (pPart->getField(L"X-QMAIL-ArchiveAttachment", &archive) == Part::FIELD_EXIST)
				pwszArchive = archive.getValue();
#endif
			if (!attachFilesOrURIs(pPart.get(), l, pURIResolver_, nSecurityMode_,
				pwszArchive, wstrArchiveAttachmentExcludePattern_.get(), wstrTempDir_.get()))
				return std::auto_ptr<Part>();
#ifdef QMZIP
			pPart->removeField(L"X-QMAIL-ArchiveAttachment");
#endif
		}
		pPart->removeField(L"X-QMAIL-Attachment");
	}

	return pPart;
}

bool qm::MessageCreator::createHeader(Part* pPart,
									  const WCHAR* pwszMessage,
									  size_t nLen) const
{
	assert(pPart);
	assert(pwszMessage);
	assert(*(pwszMessage + nLen - 1) == L'\n');

	typedef std::vector<Header> HeaderList;
	HeaderList headers;

	typedef std::vector<WCHAR> Buffer;
	Buffer buf;

	size_t nLine = 0;
	for (size_t n = 0; n < nLen; ++n) {
		WCHAR c = pwszMessage[n];
		assert(c != L'\r');
		if (c == L'\n') {
			if (n == nLen - 1 ||
				(pwszMessage[n + 1] != L' ' && pwszMessage[n + 1] != '\t')) {
				buf.push_back(L'\0');

				WCHAR* pLine = &buf[0] + nLine;
				WCHAR* p = wcschr(pLine, L':');
				if (p) {
					Header header;

					const WCHAR* pName = pLine;
					WCHAR* pNameEnd = p;
					while (*(pNameEnd - 1) == L' ')
						--pNameEnd;
					*pNameEnd = L'\0';
					const WCHAR* pValue = p + 1;
					while (*pValue == L' ')
						++pValue;
					header.nName_ = nLine;
					header.nValue_ = nLine + (pValue - pLine);

					size_t nNameLen = pNameEnd - pName;
					wstring_ptr wstrNameLower(tolower(pName, nNameLen));
					header.nLowerName_ = buf.size();
					buf.resize(buf.size() + nNameLen + 1);
					std::copy(wstrNameLower.get(), wstrNameLower.get() + nNameLen + 1,
						buf.begin() + header.nLowerName_);

					headers.push_back(header);
				}
				nLine = buf.size();
			}
			else {
				++n;
				while (n < nLen) {
					if (pwszMessage[n + 1] != L' ' &&
						pwszMessage[n + 1] != L'\t')
						break;
					++n;
				}
				buf.push_back(L' ');
			}
		}
		else {
			buf.push_back(c);
		}
	}

	const WCHAR* ppwszAddresses[] = {
		L"to",
		L"cc",
		L"bcc",
		L"from",
		L"sender",
		L"reply-to",
		L"errors-to",
		L"resent-to",
		L"resent-cc",
		L"resent-bcc",
		L"resent-from",
		L"resent-sender",
		L"recent-reply-to"
	};
	const WCHAR* ppwszReplaceAliases[] = {
		L"to",
		L"cc",
		L"bcc"
	};
	const WCHAR* ppwszReplaceAliasesResent[] = {
		L"resent-to",
		L"resent-cc",
		L"resent-bcc"
	};

	const WCHAR* pBuf = &buf[0];
	for (HeaderList::iterator it = headers.begin(); it != headers.end(); ++it) {
		const WCHAR* pwszName = pBuf + (*it).nName_;
		const WCHAR* pwszNameLower = pBuf + (*it).nLowerName_;
		const WCHAR* pwszValue = pBuf + (*it).nValue_;

		bool bSet = false;
		if (std::find_if(ppwszAddresses, ppwszAddresses + countof(ppwszAddresses),
			std::bind2nd(string_equal<WCHAR>(), pwszNameLower)) != ppwszAddresses + countof(ppwszAddresses)) {
			if (nFlags_ & FLAG_EXPANDALIAS) {
				// TODO
				// Replace alias
			}

			bSet = setField(pPart, pwszName, pwszValue, FIELDTYPE_ADDRESSLIST);
		}
		else if (wcscmp(pwszNameLower, L"content-type") == 0) {
			bSet = setField(pPart, pwszName, pwszValue, FIELDTYPE_CONTENTTYPE);
		}
		else if (wcscmp(pwszNameLower, L"content-transfer-encoding") == 0) {
			bSet = setField(pPart, pwszName, pwszValue, FIELDTYPE_CONTENTTRANSFERENCODING);
		}
		else if (wcscmp(pwszNameLower, L"content-disposition") == 0) {
			bSet = setField(pPart, pwszName, pwszValue, FIELDTYPE_CONTENTDISPOSITION);
		}
		else if (wcscmp(pwszNameLower, L"message-id") == 0) {
			bSet = setField(pPart, pwszName, pwszValue, FIELDTYPE_MESSAGEID);
		}
		else if (wcscmp(pwszNameLower, L"references") == 0 ||
			wcscmp(pwszNameLower, L"in-reply-to") == 0) {
			bSet = setField(pPart, pwszName, pwszValue, FIELDTYPE_REFERENCES);
		}
		else if (wcscmp(pwszNameLower, L"x-qmail-attachment") == 0) {
			bSet = setField(pPart, pwszName, pwszValue, FIELDTYPE_XQMAILATTACHMENT);
		}
		else {
			bool bMulti = wcslen(pwszNameLower) < 9 ||
				wcsncmp(pwszNameLower, L"x-qmail-", 8) != 0;
			bSet = setField(pPart, pwszName, pwszValue,
				bMulti ? FIELDTYPE_MULTIUNSTRUCTURED : FIELDTYPE_SINGLEUNSTRUCTURED);
		}
		if (!bSet) {
			if ((nFlags_ & FLAG_RECOVERHEADER) == 0 ||
				!setField(pPart, pwszName, pwszValue, FIELDTYPE_MULTIUNSTRUCTURED))
				return false;
		}
	}

	return true;
}

xstring_size_ptr qm::MessageCreator::convertBody(Converter* pConverter,
												 const WCHAR* pwszBody,
												 size_t nBodyLen) const
{
	assert(pConverter);
	assert(pwszBody);

	if (nBodyLen == -1)
		nBodyLen = wcslen(pwszBody);

	XStringBuffer<XSTRING> buf;

	size_t n = 0;
	const WCHAR* p = pwszBody;
	const WCHAR* pBegin = p;
	const WCHAR* pEnd = p + nBodyLen;
	while (true) {
		while (p  < pEnd && *p != L'\n')
			++p;
		size_t nLen = p - pBegin;
		if (nLen > 0) {
			xstring_size_ptr strEncoded(pConverter->encode(pBegin, &nLen));
			if (!strEncoded.get())
				return xstring_size_ptr();
			if (!buf.append(strEncoded.get(), strEncoded.size()))
				return xstring_size_ptr();
		}

		if (p == pEnd)
			break;

		assert(*p == L'\n');
		if (!buf.append("\r\n", 2))
			return xstring_size_ptr();

		++p;
		pBegin = p;
	}

	return buf.getXStringSize();
}

std::auto_ptr<Part> qm::MessageCreator::createPartWithSignature(const Part* pPart,
																Part* pParent,
																const Signature* pSignature) const
{
	assert(pSignature);

	XStringBuffer<WXSTRING> buf;
	if (pPart) {
		if (!PartUtil::a2w(pPart->getHeader(), -1, &buf) ||
			!buf.append(L'\n') ||
			!pPart->getBodyText(0, &buf))
			return std::auto_ptr<Part>();
	}
	else {
		if (!buf.append(L'\n'))
			return std::auto_ptr<Part>();
	}
	buf.append(pSignature->getSignature());

	MessageCreator creator(nFlags_ & ~FLAG_ADDSIGNATURE, SECURITYMODE_NONE,
		wstrTransferEncodingFor8Bit_.get(), pURIResolver_, pSignatureManager_,
		pAccount_, wstrArchiveAttachmentExcludePattern_.get(), wstrTempDir_.get());
	return creator.createPart(buf.getCharArray(), buf.getLength(), pParent, pParent == 0);
}

MessageCreator qm::MessageCreator::getCreatorForChild() const
{
	unsigned int nFlags = nFlags_ & (FLAG_ADDCONTENTTYPE | FLAG_ENCODETEXT | FLAG_RECOVER);
	return MessageCreator(nFlags, SECURITYMODE_NONE, wstrTransferEncodingFor8Bit_.get());
}

bool qm::MessageCreator::setField(Part* pPart,
								  const WCHAR* pwszName,
								  const WCHAR* pwszValue,
								  FieldType type)
{
	assert(pPart);
	assert(pwszName);
	assert(pwszValue);

	Part partDummy;
	switch (type) {
	case FIELDTYPE_ADDRESSLIST:
		{
			UTF8Parser field(pwszValue);
			if (!partDummy.setField(pwszName, field))
				return false;
			AddressListParser addressList(AddressListParser::FLAG_ALLOWUTF8);
			if (partDummy.getField(pwszName, &addressList) != Part::FIELD_EXIST)
				return false;
			if (!pPart->replaceField(pwszName, addressList))
				return false;
		}
		break;
	case FIELDTYPE_CONTENTTYPE:
		{
			DummyParser field(pwszValue, DummyParser::FLAG_TSPECIAL);
			if (!partDummy.setField(pwszName, field))
				return false;
			ContentTypeParser contentType;
			if (partDummy.getField(pwszName, &contentType) != Part::FIELD_EXIST)
				return false;
			if (!pPart->replaceField(pwszName, contentType))
				return false;
		}
		break;
	case FIELDTYPE_CONTENTTRANSFERENCODING:
		{
			ContentTransferEncodingParser contentTransferEncoding(pwszValue);
			if (!pPart->replaceField(pwszName, contentTransferEncoding))
				return false;
		}
		break;
	case FIELDTYPE_CONTENTDISPOSITION:
		{
			DummyParser field(pwszValue, DummyParser::FLAG_TSPECIAL);
			if (!partDummy.setField(pwszName, field))
				return false;
			ContentDispositionParser contentDisposition;
			if (partDummy.getField(pwszName, &contentDisposition) != Part::FIELD_EXIST)
				return false;
			if (!pPart->replaceField(pwszName, contentDisposition))
				return false;
		}
		break;
	case FIELDTYPE_MESSAGEID:
		{
			DummyParser field(pwszValue, 0);
			if (!partDummy.setField(pwszName, field))
				return false;
			MessageIdParser messageId;
			if (partDummy.getField(pwszName, &messageId) != Part::FIELD_EXIST)
				return false;
			if (!pPart->replaceField(pwszName, messageId))
				return false;
		}
		break;
	case FIELDTYPE_REFERENCES:
		{
			DummyParser field(pwszValue, 0);
			if (!partDummy.setField(pwszName, field))
				return false;
			ReferencesParser references;
			if (partDummy.getField(pwszName, &references) != Part::FIELD_EXIST)
				return false;
			if (!pPart->replaceField(pwszName, references))
				return false;
		}
		break;
	case FIELDTYPE_XQMAILATTACHMENT:
		{
			XQMAILAttachmentParser attachment(pwszValue);
			if (!pPart->replaceField(pwszName, attachment))
				return false;
		}
		break;
	case FIELDTYPE_SINGLEUNSTRUCTURED:
		{
			UnstructuredParser field(pwszValue);
			if (!pPart->replaceField(pwszName, field))
				return false;
		}
		break;
	case FIELDTYPE_MULTIUNSTRUCTURED:
		{
			UnstructuredParser field(pwszValue);
			if (!pPart->setField(pwszName, field, true))
				return false;
		}
		break;
	default:
		assert(false);
		break;
	}

	return true;
}

bool qm::MessageCreator::getAddressList(const WCHAR* pwszAddresses,
										AddressListParser* pAddressList)
{
	Part part;
	if (!MessageCreator::setField(&part, L"Address",
		pwszAddresses, MessageCreator::FIELDTYPE_ADDRESSLIST))
		return false;
	return part.getField(L"Address", pAddressList) == Part::FIELD_EXIST;
}

bool qm::MessageCreator::getAddress(const WCHAR* pwszAddress,
									AddressParser* pAddress)
{
	Part part;
	if (!MessageCreator::setField(&part, L"Address",
		pwszAddress, MessageCreator::FIELDTYPE_ADDRESSLIST))
		return false;
	return part.getField(L"Address", pAddress) == Part::FIELD_EXIST;
}

bool qm::MessageCreator::makeMultipart(Part* pParentPart,
									   std::auto_ptr<Part> pPart)
{
	assert(pParentPart);
	assert(pPart.get());

	int n = 0;

	Part partTemp;
	if (!PartUtil(*pPart).copyContentFields(&partTemp))
		return false;

	if (!pParentPart->setHeader(pPart->getHeader()))
		return false;

	PrefixFieldFilter filter("content-");
	if (!pParentPart->removeFields(&filter))
		return false;

	ContentTypeParser contentTypeNew(L"multipart", L"mixed");
	WCHAR wszBoundary[128];
	Time time(Time::getCurrentTime());
	_snwprintf(wszBoundary, countof(wszBoundary),
		L"__boundary-%04d%02d%02d%02d%02d%02d%03d%04d__",
		time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute,
		time.wSecond, time.wMilliseconds, ::GetCurrentThreadId());
	contentTypeNew.setParameter(L"boundary", wszBoundary);
	if (!pParentPart->replaceField(L"Content-Type", contentTypeNew))
		return false;

	if (!pPart->setHeader(0))
		return false;
	if (!PartUtil(partTemp).copyContentFields(pPart.get()))
		return false;

	pParentPart->addPart(pPart);
	pParentPart->setEpilogue("");

	return true;
}

bool qm::MessageCreator::attachFilesOrURIs(qs::Part* pPart,
										   const AttachmentList& l,
										   const URIResolver* pURIResolver,
										   unsigned int nSecurityMode,
										   const WCHAR* pwszArchiveName,
										   const WCHAR* pwszExcludePattern,
										   const WCHAR* pwszTempDir)
{
#ifdef QMZIP
	bool bArchive = pwszArchiveName && *pwszArchiveName;
#else
	bool bArchive = false;
#endif

	typedef std::vector<const WCHAR*> List;
	List listAttach;
	List listArchive;

	if (bArchive) {
		std::auto_ptr<RegexPattern> pExcludePattern;
		if (pwszExcludePattern && *pwszExcludePattern)
			pExcludePattern = RegexCompiler().compile(
				pwszExcludePattern, RegexCompiler::MODE_CASEINSENSITIVE);
		for (AttachmentList::const_iterator it = l.begin(); it != l.end(); ++it) {
			const WCHAR* pwszFileOrURI = *it;
			if (isAttachmentURI(pwszFileOrURI) ||
				(pExcludePattern.get() && pExcludePattern->search(pwszFileOrURI)))
				listAttach.push_back(pwszFileOrURI);
			else
				listArchive.push_back(pwszFileOrURI);
		}
	}
	else {
		std::copy(l.begin(), l.end(), std::back_inserter(listAttach));
	}

#ifdef QMZIP
	if (!listArchive.empty()) {
		if (!attachArchivedFile(pPart, pwszArchiveName, listArchive, pwszTempDir))
			return false;
	}
#endif
	if (std::find_if(listAttach.begin(), listAttach.end(),
		std::not1(boost::make_adaptable<bool, const WCHAR*>(
			boost::bind(&MessageCreator::attachFileOrURI,
				pPart, _1, pURIResolver, nSecurityMode)))) != listAttach.end())
		return false;

	return true;
}

bool qm::MessageCreator::attachFileOrURI(qs::Part* pPart,
										 const WCHAR* pwszFileOrURI,
										 const URIResolver* pURIResolver,
										 unsigned int nSecurityMode)
{
	assert(pPart->isMultipart());

	std::auto_ptr<Part> pChildPart;
	if (isAttachmentURI(pwszFileOrURI)) {
		std::auto_ptr<URI> pURI(URIFactory::parseURI(pwszFileOrURI));
		if (!pURI.get())
			return false;

		std::auto_ptr<MessageContext> pContext(pURI->resolve(pURIResolver));
		if (!pContext.get())
			return false;

		Message* pMessage = pContext->getMessage(Account::GMF_ALL, 0, nSecurityMode);
		if (!pMessage)
			return false;

		const URIFragment& fragment = pURI->getFragment();
		const Part* pPart = fragment.getPart(pMessage);
		if (!pPart)
			return false;
		switch (fragment.getType()) {
		case URIFragment::TYPE_NONE:
			pChildPart = createRfc822Part(*pPart, false);
			break;
		case URIFragment::TYPE_MIME:
			pChildPart = createRfc822Part(*pPart, true);
			break;
		case URIFragment::TYPE_BODY:
			pChildPart = createClonedPart(*pPart);
			break;
		case URIFragment::TYPE_HEADER:
			assert(pPart->getEnclosedPart());
			pChildPart = createRfc822Part(*pPart->getEnclosedPart(), true);
			break;
		case URIFragment::TYPE_TEXT:
			assert(pPart->getEnclosedPart());
			pChildPart = createClonedPart(*pPart->getEnclosedPart());
			break;
		default:
			assert(false);
			break;
		}
	}
	else {
		pChildPart = createPartFromFile(pPart, pwszFileOrURI);
	}
	if (!pChildPart.get())
		return false;
	pPart->addPart(pChildPart);

	return true;
}

#ifdef QMZIP
bool qm::MessageCreator::attachArchivedFile(Part* pPart,
											const WCHAR* pwszFileName,
											const FileNameList& l,
											const WCHAR* pwszTempDir)
{
	assert(pPart);
	assert(pPart->isMultipart());
	assert(pwszFileName);
	assert(!l.empty());
	assert(pwszTempDir);

	ZipFile::PathList listPath;
	listPath.resize(l.size());
	std::copy(l.begin(), l.end(), listPath.begin());

	ZipFile zip(pwszFileName, listPath, pwszTempDir);
	const WCHAR* pwszPath = zip.getPath();
	if (!pwszPath)
		return false;

	std::auto_ptr<Part> pChildPart(createPartFromFile(pPart, pwszPath));
	if (!pChildPart.get())
		return false;
	pPart->addPart(pChildPart);

	return true;
}
#endif

std::auto_ptr<Part> qm::MessageCreator::createPartFromFile(Part* pParentPart,
														   const WCHAR* pwszPath)
{
	assert(pwszPath);

	// Add a new part to parent part temporarily to make that
	// the file name is encoded using parent part's header charset.
	// The part is removed from its parent before the function returns.
	std::auto_ptr<Part> pNewPart(new Part());
	Part* pPart = pNewPart.get();
	pParentPart->addPart(pNewPart);
	struct Remover
	{
		Remover(Part* pParentPart,
				Part* pPart) :
			pParentPart_(pParentPart),
			pPart_(pPart)
		{
		}

		~Remover()
		{
			pParentPart_->removePart(pPart_);
		}

		Part* pParentPart_;
		Part* pPart_;
	} remover(pParentPart, pPart);

	const WCHAR* pwszContentType = L"application/octet-stream";
	wstring_ptr wstrContentType;
	const WCHAR* pFileName = wcsrchr(pwszPath, L'\\');
	pFileName = pFileName ? pFileName + 1 : pwszPath;
	const WCHAR* pExt = wcschr(pFileName, L'.');
	if (pExt) {
		wstrContentType = getContentTypeFromExtension(pExt + 1);
		if (wstrContentType.get())
			pwszContentType = wstrContentType.get();
	}

	DummyParser dummyContentType(pwszContentType, 0);
	if (!pPart->setField(L"Content-Type", dummyContentType))
		return std::auto_ptr<Part>();
	ContentTypeParser contentType;
	if (pPart->getField(L"Content-Type", &contentType) != Part::FIELD_EXIST) {
		ContentTypeParser dummyContentType(L"application", L"octet-stream");
		if (!pPart->setField(L"Content-Type", dummyContentType))
			return std::auto_ptr<Part>();
		if (pPart->getField(L"Content-Type", &contentType) != Part::FIELD_EXIST)
			return std::auto_ptr<Part>();
	}
	contentType.setParameter(L"name", pFileName);
	if (!pPart->replaceField(L"Content-Type", contentType))
		return std::auto_ptr<Part>();

	bool bBase64 = !PartUtil::isContentType(&contentType, L"message", L"rfc822");
	if (bBase64) {
		ContentTransferEncodingParser contentTransferEncoding(L"base64");
		if (!pPart->setField(L"Content-Transfer-Encoding", contentTransferEncoding))
			return std::auto_ptr<Part>();
	}

	ContentDispositionParser contentDisposition(L"attachment");
	contentDisposition.setParameter(L"filename", pFileName);
	if (!pPart->setField(L"Content-Disposition", contentDisposition))
		return std::auto_ptr<Part>();

	W2T(pwszPath, ptszPath);
	WIN32_FIND_DATA fd;
	AutoFindHandle hFind(::FindFirstFile(ptszPath, &fd));
	if (!hFind.get())
		return std::auto_ptr<Part>();
	size_t nSize = fd.nFileSizeLow;
	hFind.close();

	FileInputStream stream(pwszPath);
	if (!stream)
		return std::auto_ptr<Part>();
	BufferedInputStream bufferedStream(&stream, false);

	if (bBase64) {
		Base64Encoder encoder(true);
		XStringOutputStream outputStream;
		if (!outputStream.reserve((nSize/3 + 1)*4 + nSize/45*2))
			return std::auto_ptr<Part>();
		if (!encoder.encode(&bufferedStream, &outputStream))
			return std::auto_ptr<Part>();
		pPart->setBody(outputStream.getXString());
	}
	else {
		xstring_ptr strBody(allocXString(nSize));
		if (!strBody.get())
			return std::auto_ptr<Part>();
		if (bufferedStream.read(reinterpret_cast<unsigned char*>(strBody.get()), nSize) != nSize)
			return std::auto_ptr<Part>();
		pPart->setBody(strBody);
	}

	return std::auto_ptr<Part>(pPart);
}

std::auto_ptr<Part> qm::MessageCreator::createRfc822Part(const Part& part,
														 bool bHeaderOnly)
{
	std::auto_ptr<Part> pPart(new Part());

	ContentTypeParser contentType(L"message", L"rfc822");
	if (!pPart->setField(L"Content-Type", contentType))
		return std::auto_ptr<Part>();

	wstring_ptr wstrFileName;
	UnstructuredParser subject;
	if (part.getField(L"Subject", &subject) == Part::FIELD_EXIST && *subject.getValue())
		wstrFileName = concat(subject.getValue(), L".eml");
	else
		wstrFileName = allocWString(L"Untitled.eml");

	ContentDispositionParser contentDisposition(L"attachment");
	contentDisposition.setParameter(L"filename", wstrFileName.get());
	if (!pPart->setField(L"Content-Disposition", contentDisposition))
		return std::auto_ptr<Part>();

	if (bHeaderOnly) {
		pPart->setBody(part.getHeader(), -1);
	}
	else {
		xstring_size_ptr strContent(part.getContent());
		if (!strContent.get())
			return std::auto_ptr<Part>();
		xstring_ptr strBody(strContent.release());
		pPart->setBody(strBody);
	}

	return pPart;
}

std::auto_ptr<Part> qm::MessageCreator::createClonedPart(const Part& part)
{
	std::auto_ptr<Part> pPart(part.clone());
	if (!pPart.get())
		return std::auto_ptr<Part>();

	PrefixFieldFilter filter("content-", true);
	if (!pPart->removeFields(&filter))
		return std::auto_ptr<Part>();

	return pPart;
}

wstring_ptr qm::MessageCreator::getContentTypeFromExtension(const WCHAR* pwszExtension)
{
	assert(pwszExtension);

	wstring_ptr wstrExt(concat(L".", pwszExtension));
	wstring_ptr wstrContentType;

	Registry reg(HKEY_CLASSES_ROOT, wstrExt.get(), true);
	if (reg) {
		if (reg.getValue(L"Content Type", &wstrContentType)) {
			if (wstrContentType.get() &&
				_wcsnicmp(wstrContentType.get(), L"text/", 5) == 0)
				wstrContentType.reset(0);
		}
	}

	return wstrContentType;
}

const WCHAR* qm::MessageCreator::getEncodingForCharset(const WCHAR* pwszCharset)
{
	size_t nLen = wcslen(pwszCharset);
	if (_wcsicmp(pwszCharset, L"us-ascii") == 0 ||
		((nLen == 10 || nLen == 11) && _wcsnicmp(pwszCharset, L"iso-8859-", 9) == 0) ||
		_wcsicmp(pwszCharset, L"utf-7") == 0)
		return L"quoted-printable";
	else
		return L"base64";
}

bool qm::MessageCreator::isAttachmentURI(const WCHAR* pwszAttachment)
{
	return URIFactory::isURI(pwszAttachment);
}

/****************************************************************************
 *
 * BodyData
 *
 */

qm::BodyData::BodyData(malloc_size_ptr<unsigned char>& p) :
	p_(p)
{
}

qm::BodyData::BodyData(xstring_size_ptr& str) :
	str_(str)
{
}

qm::BodyData::BodyData(BodyData& bodyData) :
	p_(bodyData.p_),
	str_(bodyData.str_)
{
}

qm::BodyData::~BodyData()
{
}

const unsigned char* qm::BodyData::get() const
{
	return p_.get() ? p_.get() : reinterpret_cast<const unsigned char*>(str_.get());
}

size_t qm::BodyData::size() const
{
	return p_.get() ? p_.size() : str_.size();
}

/****************************************************************************
 *
 * PartUtil
 *
 */

qm::PartUtil::PartUtil(const Part& part) :
	part_(part)
{
}

qm::PartUtil::~PartUtil()
{
}

bool qm::PartUtil::isResent() const
{
	const WCHAR* pwszFields[] = {
		L"Resent-To",
		L"Resent-Cc",
		L"Resent-Bcc",
		L"Resent-From",
		L"Resent-Date",
		L"Resent-Message-Id"
	};

	for (int n = 0; n < countof(pwszFields); ++n) {
		if (part_.hasField(pwszFields[n]))
			return true;
	}

	return false;
}

wstring_ptr qm::PartUtil::getNames(const WCHAR* pwszField) const
{
	assert(pwszField);

	StringBuffer<WSTRING> buf;

	AddressListParser address;
	if (part_.getField(pwszField, &address) == Part::FIELD_EXIST) {
		const AddressListParser::AddressList& l = address.getAddressList();
		for (AddressListParser::AddressList::const_iterator it = l.begin(); it != l.end(); ++it) {
			if (it != l.begin())
				buf.append(L", ");

			if ((*it)->getPhrase()) {
				buf.append((*it)->getPhrase());
			}
			else {
				wstring_ptr wstrAddress((*it)->getAddress());
				buf.append(wstrAddress.get());
			}
		}
	}
	return buf.getString();
}

wstring_ptr qm::PartUtil::getReference() const
{
	const WCHAR* pwszFields[] = {
		L"References",
		L"In-Reply-To"
	};
	for (int n = 0; n < countof(pwszFields); ++n) {
		ReferencesParser references;
		if (part_.getField(pwszFields[n], &references) == Part::FIELD_EXIST) {
			const ReferencesParser::ReferenceList& l = references.getReferences();
			for (ReferencesParser::ReferenceList::const_reverse_iterator it = l.rbegin(); it != l.rend(); ++it) {
				if ((*it).second == ReferencesParser::T_MSGID)
					return allocWString((*it).first);
			}
		}
	}

	return 0;
}

void qm::PartUtil::getReferences(ReferenceList* pList) const
{
	assert(pList);

	ReferencesParser references;
	if (part_.getField(L"References", &references) == Part::FIELD_EXIST) {
		const ReferencesParser::ReferenceList& l = references.getReferences();
		for (ReferencesParser::ReferenceList::const_iterator it = l.begin(); it != l.end(); ++it) {
			if ((*it).second == ReferencesParser::T_MSGID) {
				wstring_ptr wstr(allocWString((*it).first));
				pList->push_back(wstr.get());
				wstr.release();
			}
		}
	}
}

wxstring_size_ptr qm::PartUtil::getAllText(const WCHAR* pwszQuote,
										   const WCHAR* pwszCharset,
										   bool bBodyOnly) const
{
	XStringBuffer<WXSTRING> buf;
	if (!getAllText(pwszQuote, pwszCharset, bBodyOnly, &buf))
		return wxstring_size_ptr();
	return buf.getXStringSize();
}

bool qm::PartUtil::getAllText(const WCHAR* pwszQuote,
							  const WCHAR* pwszCharset,
							  bool bBodyOnly,
							  qs::XStringBuffer<qs::WXSTRING>* pBuf) const
{
	if (!bBodyOnly) {
		if (part_.getHeader()) {
			Part::FieldList l;
			Part::FieldListFree free(l);
			part_.getFields(&l);
			for (Part::FieldList::const_iterator it = l.begin(); it != l.end(); ++it) {
				if (!normalizeHeader((*it).second, pBuf) || !pBuf->append(L'\n'))
					return false;
			}
		}
		if (!pBuf->append(L'\n'))
			return false;
	}

	if (part_.isMultipart()) {
		const ContentTypeParser* pContentType = part_.getContentType();
		wstring_ptr wstrBoundary(pContentType->getParameter(L"boundary"));

		const CHAR* pszPreamble = part_.getPreamble();
		if (pszPreamble) {
			if (!pBuf->append(mbs2wcs(pszPreamble).get()) || !pBuf->append(L"\n"))
				return false;
		}

		const Part::PartList& l = part_.getPartList();
		if (!l.empty()) {
			for (Part::PartList::const_iterator it = l.begin(); it != l.end(); ++it) {
				if (!pBuf->append(L"\n--") ||
					!pBuf->append(wstrBoundary.get()) ||
					!pBuf->append(L"\n") ||
					!PartUtil(**it).getAllText(pwszQuote, pwszCharset, false, pBuf))
					return false;
			}
			if (!pBuf->append(L"\n--") ||
				!pBuf->append(wstrBoundary.get()) ||
				!pBuf->append(L"--\n"))
				return false;
		}

		const CHAR* pszEpilogue = part_.getEpilogue();
		if (pszEpilogue) {
			if (!pBuf->append(L"\n") || !pBuf->append(mbs2wcs(pszEpilogue).get()))
				return false;
		}
	}
	else if (part_.getEnclosedPart()) {
		PartUtil util(*part_.getEnclosedPart());
		if (!util.getAllText(pwszQuote, pwszCharset, false, pBuf))
			return false;
	}
	else {
		if (part_.isText()) {
			if (pwszQuote && *pwszQuote) {
				wxstring_size_ptr wstrBody(part_.getBodyText(pwszCharset));
				if (!wstrBody.get())
					return false;
				if (!quote(wstrBody.get(), pwszQuote, pBuf))
					return false;
			}
			else {
				if (!part_.getBodyText(pwszCharset, pBuf))
					return false;
			}
		}
		else {
			wxstring_ptr wstrBody(a2w(part_.getBody()));
			if (!pBuf->append(wstrBody.get()))
				return false;
		}
	}
	return true;
}

qs::wstring_ptr qm::PartUtil::getAllTextCharset() const
{
	wstring_ptr wstrAllCharset;
	if (part_.isMultipart()) {
		const Part::PartList& l = part_.getPartList();
		for (Part::PartList::const_iterator it = l.begin(); it != l.end(); ++it) {
			wstring_ptr wstrCharset(PartUtil(**it).getAllTextCharset());
			if (wstrAllCharset.get()) {
				if (_wcsicmp(wstrAllCharset.get(), L"us-ascii") == 0) {
					wstrAllCharset = wstrCharset;
				}
				else if (_wcsicmp(wstrCharset.get(), L"us-ascii") != 0 &&
					_wcsicmp(wstrAllCharset.get(), wstrCharset.get()) != 0) {
					wstrAllCharset = allocWString(L"utf-8");
					break;
				}
			}
			else {
				wstrAllCharset = wstrCharset;
			}
		}
	}
	else if (part_.getEnclosedPart()) {
		wstrAllCharset = PartUtil(*part_.getEnclosedPart()).getAllTextCharset();
	}
	else {
		wstrAllCharset = part_.getCharset();
	}
	return wstrAllCharset.get() ? wstrAllCharset : allocWString(L"us-ascii");
}

wxstring_size_ptr qm::PartUtil::getBodyText(const WCHAR* pwszQuote,
											const WCHAR* pwszCharset,
											Rfc822Mode rfc822Mode) const
{
	XStringBuffer<WXSTRING> buf;
	if (!getBodyText(pwszQuote, pwszCharset, rfc822Mode, &buf))
		return wxstring_size_ptr();
	return buf.getXStringSize();
}

bool qm::PartUtil::getBodyText(const WCHAR* pwszQuote,
							   const WCHAR* pwszCharset,
							   Rfc822Mode rfc822Mode,
							   XStringBuffer<WXSTRING>* pBuf) const
{
	if (part_.isMultipart()) {
		const ContentTypeParser* pContentType = part_.getContentType();
		assert(pContentType);
		bool bAlternative = _wcsicmp(pContentType->getSubType(), L"alternative") == 0;
		bool bFirst = true;
		const Part::PartList& l = part_.getPartList();
		for (Part::PartList::const_iterator it = l.begin(); it != l.end(); ++it) {
			size_t nLen = pBuf->getLength();

			if (bFirst) {
				bFirst = false;
			}
			else {
				if (*(pBuf->getCharArray() + pBuf->getLength() - 1) == L'\n' && pwszQuote && *pwszQuote) {
					if (!pBuf->append(pwszQuote))
						return false;
				}
				if (!pBuf->append(L"\n"))
					return false;
				if (pwszQuote && *pwszQuote) {
					if (!pBuf->append(pwszQuote))
						return false;
				}
				if (!pBuf->append(L"------------------------------------"
					L"------------------------------------\n"))
					return false;
			}

			size_t nPrevLen = pBuf->getLength();
			if (!PartUtil(**it).getBodyText(pwszQuote, pwszCharset, rfc822Mode, pBuf))
				return false;
			if (pBuf->getLength() == nPrevLen)
				pBuf->remove(nLen, -1);

			if (bAlternative)
				break;
		}
	}
	else {
		bool bAttachment = part_.isAttachment();
		if (pwszQuote && *pwszQuote) {
			wxstring_size_ptr wstrBody;
			if (part_.getEnclosedPart() && isRfc822Rendered(rfc822Mode, bAttachment)) {
				PartUtil util(*part_.getEnclosedPart());
				wstrBody = util.getFormattedText(false, pwszCharset, rfc822Mode);
				if (!wstrBody.get())
					return false;
			}
			else if (!bAttachment) {
				wstrBody = part_.getBodyText(pwszCharset);
				if (!wstrBody.get())
					return false;
			}

			if (wstrBody.get()) {
				if (!quote(wstrBody.get(), pwszQuote, pBuf))
					return false;
			}
		}
		else {
			if (part_.getEnclosedPart() && isRfc822Rendered(rfc822Mode, bAttachment)) {
				PartUtil util(*part_.getEnclosedPart());
				if (!util.getFormattedText(false, pwszCharset, rfc822Mode, pBuf))
					return false;
			}
			else if (!bAttachment) {
				if (!part_.getBodyText(pwszCharset, pBuf))
					return false;
			}
		}
	}

	return true;
}

qs::wstring_ptr qm::PartUtil::getBodyTextCharset(Rfc822Mode rfc822Mode) const
{
	wstring_ptr wstrAllCharset;
	if (part_.isMultipart()) {
		const ContentTypeParser* pContentType = part_.getContentType();
		assert(pContentType);
		bool bAlternative = _wcsicmp(pContentType->getSubType(), L"alternative") == 0;
		const Part::PartList& l = part_.getPartList();
		for (Part::PartList::const_iterator it = l.begin(); it != l.end(); ++it) {
			wstring_ptr wstrCharset = PartUtil(**it).getBodyTextCharset(rfc822Mode);
			if (wstrAllCharset.get()) {
				if (_wcsicmp(wstrAllCharset.get(), L"us-ascii") == 0) {
					wstrAllCharset = wstrCharset;
				}
				else if (_wcsicmp(wstrCharset.get(), L"us-ascii") != 0 &&
					_wcsicmp(wstrAllCharset.get(), wstrCharset.get()) != 0) {
					wstrAllCharset = allocWString(L"utf-8");
					break;
				}
			}
			else {
				wstrAllCharset = wstrCharset;
			}
			if (bAlternative)
				break;
		}
	}
	else {
		bool bAttachment = part_.isAttachment();
		if (part_.getEnclosedPart() && isRfc822Rendered(rfc822Mode, bAttachment))
			wstrAllCharset = PartUtil(*part_.getEnclosedPart()).getBodyTextCharset(rfc822Mode);
		else if (!bAttachment)
			wstrAllCharset = part_.getCharset();
	}
	return wstrAllCharset.get() ? wstrAllCharset : allocWString(L"us-ascii");
}

wxstring_size_ptr qm::PartUtil::getFormattedText(bool bUseSendersTimeZone,
												 const WCHAR* pwszCharset,
												 Rfc822Mode rfc822Mode) const
{
	XStringBuffer<WXSTRING> buf;
	if (!getFormattedText(bUseSendersTimeZone, pwszCharset, rfc822Mode, &buf))
		return wxstring_size_ptr();
	return buf.getXStringSize();
}

bool qm::PartUtil::getFormattedText(bool bUseSendersTimeZone,
									const WCHAR* pwszCharset,
									Rfc822Mode rfc822Mode,
									XStringBuffer<WXSTRING>* pBuf) const
{
	const WCHAR* pwszFields[] = {
		L"To",
		L"Cc",
		L"From"
	};
	const WCHAR* pwszFieldNames[] = {
		L"To:      ",
		L"Cc:      ",
		L"From:    "
	};

	for (int n = 0; n < countof(pwszFields); ++n) {
		AddressListParser address;
		if (part_.getField(pwszFields[n], &address) == Part::FIELD_EXIST) {
			if (!address.getAddressList().empty()) {
				if (!pBuf->append(pwszFieldNames[n]))
					return false;
				wstring_ptr wstrNames(address.getNames());
				if (!pBuf->append(wstrNames.get()) ||
					!pBuf->append(L"\n"))
					return false;
			}
		}
	}

	UnstructuredParser subject;
	if (part_.getField(L"Subject", &subject) == Part::FIELD_EXIST) {
		if (!pBuf->append(L"Subject: ") ||
			!pBuf->append(subject.getValue()) ||
			!pBuf->append(L'\n'))
			return false;
	}

	DateParser date;
	if (part_.getField(L"Date", &date) == Part::FIELD_EXIST) {
		wstring_ptr wstrFormat(concat(L"Date:    ", Time::getDefaultFormat(), L"\n"));
		wstring_ptr wstrDate(date.getTime().format(wstrFormat.get(),
			bUseSendersTimeZone ? Time::FORMAT_ORIGINAL : Time::FORMAT_LOCAL));
		if (!pBuf->append(wstrDate.get()))
			return false;
	}

	AttachmentParser::AttachmentList names;
	AttachmentParser::AttachmentListFree free(names);
	AttachmentParser(part_).getAttachments(AttachmentParser::GAF_INCLUDEDELETED, &names);
	if (!names.empty()) {
		if (!pBuf->append(L"Attach:  "))
			return false;
		for (AttachmentParser::AttachmentList::iterator it = names.begin(); it != names.end(); ++it) {
			if (it != names.begin()) {
				if (!pBuf->append(L", "))
					return false;
			}
			if (!pBuf->append((*it).first))
				return false;
		}
		if (!pBuf->append(L"\n"))
			return false;
	}

	if (!pBuf->append(L"\n"))
		return false;
	if (!getBodyText(0, pwszCharset, rfc822Mode, pBuf))
		return false;

	return true;
}

BodyData qm::PartUtil::getBodyData() const
{
	const Part* pEnclosedPart = part_.getEnclosedPart();
	if (pEnclosedPart)
		return BodyData(pEnclosedPart->getContent());
	else
		return BodyData(part_.getBodyData());
}

bool qm::PartUtil::getDigest(MessageList* pList) const
{
	assert(pList);

	CONTAINER_DELETER(deleter, *pList);

	DigestMode mode = getDigestMode();
	if (mode == DIGEST_NONE)
		return true;

	AddressListParser to;
	Part::Field fieldTo = part_.getField(L"To", &to);

	AddressListParser replyTo;
	Part::Field fieldReplyTo = part_.getField(L"Reply-To", &replyTo);

	switch (mode) {
	case DIGEST_MULTIPART:
		{
			const Part::PartList& l = part_.getPartList();
			for (Part::PartList::const_iterator it = l.begin(); it != l.end(); ++it) {
				const Part* pEnclosedPart = (*it)->getEnclosedPart();
				if (pEnclosedPart || (*it)->isText()) {
					xstring_size_ptr strContent;
					if (pEnclosedPart)
						strContent = pEnclosedPart->getContent();
					else
						strContent = (*it)->getContent();

					std::auto_ptr<Message> pMessage(new Message());
					if (!pMessage->create(strContent.get(), strContent.size(), Message::FLAG_NONE))
						return false;

					if (fieldTo == Part::FIELD_EXIST) {
						if (!pMessage->setField(L"To", to))
							return false;
					}
					if (fieldReplyTo == Part::FIELD_EXIST) {
						if (!pMessage->setField(L"Reply-To", replyTo))
							return false;
					}
					pList->push_back(pMessage.get());
					pMessage.release();
				}
			}
		}
		break;
	case DIGEST_RFC1153:
		{
			BMFindString<WSTRING> bmfsStart(
				L"\n-----------------------------------"
				L"-----------------------------------\n");
			const WCHAR* pwszSeparator = L"\n------------------------------\n";
			size_t nSeparatorLen = wcslen(pwszSeparator);
			BMFindString<WSTRING> bmfsSeparator(pwszSeparator);

			MessageCreator creator;

			wxstring_size_ptr wstrBody(part_.getBodyText());
			if (!wstrBody.get())
				return false;

			const WCHAR* p = bmfsStart.find(wstrBody.get());
			while (p) {
				p = wcschr(p + 1, L'\n');
				assert(p);
				++p;
				if (*p == L'\n')
					++p;

				const WCHAR* pEnd = bmfsSeparator.find(p);
				if (!pEnd)
					break;

				std::auto_ptr<Message> pMessage(creator.createMessage(p, pEnd - p));

				if (!pMessage->hasField(L"To") && fieldTo == Part::FIELD_EXIST) {
					if (!pMessage->setField(L"To", to))
						return false;
				}
				if (!pMessage->hasField(L"Reply-To") && fieldReplyTo == Part::FIELD_EXIST) {
					if (!pMessage->setField(L"Reply-To", replyTo))
						return false;
				}
				pList->push_back(pMessage.get());
				pMessage.release();

				if (wcsncmp(pEnd + nSeparatorLen + 1, L"End of ", 7) == 0)
					break;
				p = pEnd;
			}
		}
		break;
	default:
		assert(false);
		break;
	}

	deleter.release();

	return true;
}

PartUtil::DigestMode qm::PartUtil::getDigestMode() const
{
	const ContentTypeParser* pContentType = part_.getContentType();
	if (!pContentType || _wcsicmp(pContentType->getMediaType(), L"text") == 0) {
		UnstructuredParser subject;
		if (part_.getField(L"Subject", &subject) == Part::FIELD_EXIST) {
			wstring_ptr wstrSubject(tolower(subject.getValue()));
			if (wcsstr(wstrSubject.get(), L"digest")) {
				wxstring_size_ptr wstrBody = part_.getBodyText();
				if (wstrBody.get()) {
					BMFindString<WSTRING> bmfs(
						L"\n----------------------------------"
						L"------------------------------------\n");
					if (bmfs.find(wstrBody.get()))
						return DIGEST_RFC1153;
				}
			}
		}
	}
	else if (PartUtil::isMultipart(pContentType)) {
		if (_wcsicmp(pContentType->getSubType(), L"mixed") == 0 ||
			_wcsicmp(pContentType->getSubType(), L"digest") == 0) {
			const Part::PartList& l = part_.getPartList();
			Part::PartList::const_iterator it = l.begin();
			while (it != l.end()) {
				const Part* pEnclosedPart = (*it)->getEnclosedPart();
				if (!pEnclosedPart && !(*it)->isText())
					break;
				++it;
			}
			if (it == l.end())
				return DIGEST_MULTIPART;
		}
	}

	return DIGEST_NONE;
}

string_ptr qm::PartUtil::getHeader(const WCHAR* pwszName) const
{
	assert(pwszName);

	string_ptr strName(wcs2mbs(pwszName));

	StringBuffer<STRING> buf;

	unsigned int nIndex = 0;
	bool bExist = true;
	while (bExist) {
		string_ptr str(part_.getRawField(pwszName, nIndex));
		if (!str.get())
			break;

		buf.append(strName.get());
		buf.append(": ");
		buf.append(str.get());
		buf.append("\r\n");

		++nIndex;
	}
	if (buf.getLength() == 0)
		return 0;

	buf.append("\r\n");
	return buf.getString();
}

void qm::PartUtil::getAlternativeContentTypes(ContentTypeList* pList) const
{
	assert(pList);

	const ContentTypeParser* pContentType = part_.getContentType();
	if (PartUtil::isMultipart(pContentType)) {
		const Part::PartList& l = part_.getPartList();
		if (_wcsicmp(pContentType->getSubType(), L"alternative") == 0) {
			for (Part::PartList::const_reverse_iterator it = l.rbegin(); it != l.rend(); ++it)
				PartUtil(**it).getAlternativeContentTypes(pList);
		}
		else {
			if (!l.empty())
				PartUtil(*l.front()).getAlternativeContentTypes(pList);
		}
	}
	else {
		pList->push_back(pContentType);
	}
}

const Part* qm::PartUtil::getAlternativePart(const WCHAR* pwszMediaType,
											 const WCHAR* pwszSubType) const
{
	assert(pwszMediaType);
	assert(pwszSubType);

	const ContentTypeParser* pContentType = part_.getContentType();
	if (PartUtil::isMultipart(pContentType)) {
		const Part::PartList& l = part_.getPartList();
		if (_wcsicmp(pContentType->getSubType(), L"alternative") == 0) {
			for (Part::PartList::const_reverse_iterator it = l.rbegin(); it != l.rend(); ++it) {
				const Part* pPart = PartUtil(**it).getAlternativePart(pwszMediaType, pwszSubType);
				if (pPart)
					return pPart;
			}
		}
		else {
			if (!l.empty())
				return PartUtil(*l.front()).getAlternativePart(pwszMediaType, pwszSubType);
		}
	}
	else {
		if (isContentType(pContentType, pwszMediaType, pwszSubType))
			return &part_;
	}

	return 0;
}

const Part* qm::PartUtil::getPartByContentId(const WCHAR* pwszContentId) const
{
	assert(pwszContentId);

	if (part_.isMultipart()) {
		const Part::PartList& l = part_.getPartList();
		for (Part::PartList::const_iterator it = l.begin(); it != l.end(); ++it) {
			const Part* pPart = PartUtil(**it).getPartByContentId(pwszContentId);
			if (pPart)
				return pPart;
		}
	}
	else {
		MessageIdParser contentId;
		if (part_.getField(L"Content-Id", &contentId) == Part::FIELD_EXIST &&
			wcscmp(contentId.getMessageId(), pwszContentId) == 0)
			return &part_;
	}

	return 0;
}

const Part* qm::PartUtil::getEnclosingPart(const Part* pCandidatePart) const
{
	assert(pCandidatePart);

	const Part* pPart = 0;
	if (pCandidatePart->isMultipart()) {
		const Part::PartList& l = pCandidatePart->getPartList();
		for (Part::PartList::const_iterator it = l.begin(); it != l.end() && !pPart; ++it)
			pPart = getEnclosingPart(*it);
	}
	else {
		Part* pEnclosedPart = pCandidatePart->getEnclosedPart();
		if (pEnclosedPart == &part_)
			pPart = pCandidatePart;
		else if (pEnclosedPart)
			pPart = getEnclosingPart(pEnclosedPart);
	}

	return pPart;
}

Part* qm::PartUtil::getEnclosingPart(Part* pCandidatePart) const
{
	return const_cast<Part*>(getEnclosingPart(static_cast<const Part*>(pCandidatePart)));
}

bool qm::PartUtil::copyContentFields(Part* pPart) const
{
	PrefixFieldFilter filter("content-");
	return pPart->copyFields(part_, &filter);
}

wxstring_ptr qm::PartUtil::a2w(const CHAR* psz)
{
	return a2w(psz, -1);
}

wxstring_ptr qm::PartUtil::a2w(const CHAR* psz,
							   size_t nLen)
{
	XStringBuffer<WXSTRING> buf;
	if (!a2w(psz, nLen, &buf))
		return 0;
	return buf.getXString();
}

bool qm::PartUtil::a2w(const CHAR* psz,
					   size_t nLen,
					   XStringBuffer<WXSTRING>* pBuf)
{
	assert(pBuf);

	if (nLen == -1)
		nLen = strlen(psz);

	for (size_t n = 0; n < nLen; ++n) {
		CHAR c = *(psz + n);
		if (c != '\r') {
			if (!pBuf->append(static_cast<WCHAR>(c)))
				return false;
		}
	}

	return true;
}

xstring_ptr qm::PartUtil::w2a(const WCHAR* pwsz)
{
	return w2a(pwsz, -1);
}

xstring_ptr qm::PartUtil::w2a(const WCHAR* pwsz,
							  size_t nLen)
{
	XStringBuffer<XSTRING> buf;
	if (!w2a(pwsz, nLen, &buf))
		return 0;
	return buf.getXString();
}

bool qm::PartUtil::w2a(const WCHAR* pwsz,
					   size_t nLen,
					   XStringBuffer<XSTRING>* pBuf)
{
	assert(pBuf);

	if (nLen == -1)
		nLen = wcslen(pwsz);

	for (size_t n = 0; n < nLen; ++n) {
		WCHAR c = *(pwsz + n);
		if (c >= 0x80)
			return false;
		if (c == L'\n') {
			if (!pBuf->append('\r'))
				return false;
		}
		if (!pBuf->append(static_cast<CHAR>(c)))
			return false;
	}

	return true;
}

bool qm::PartUtil::normalizeHeader(const CHAR* pszHeader,
								   XStringBuffer<qs::WXSTRING>* pBuf)
{
	assert(pszHeader);
	assert(pBuf);

	StringBuffer<STRING> buf;
	bool bSkipWhitespace = false;
	for (const CHAR* p = pszHeader; *p; ++p) {
		if (*p == '\r' && *(p + 1) == '\n') {
			buf.append(' ');
			bSkipWhitespace = true;
			++p;
		}
		else if (bSkipWhitespace) {
			if (*p != ' ' && *p != '\t') {
				bSkipWhitespace = false;
				buf.append(*p);
			}
		}
		else {
			buf.append(*p);
		}
	}

	wstring_ptr wstr(FieldParser::decode(buf.getCharArray(), buf.getLength(), false, 0));
	if (wstr.get()) {
		if (!pBuf->append(wstr.get()))
			return false;
	}
	else {
		if (!a2w(buf.getCharArray(), buf.getLength(), pBuf))
			return false;
	}

	return true;
}

wxstring_ptr qm::PartUtil::quote(const WCHAR* pwsz,
								 const WCHAR* pwszQuote)
{
	XStringBuffer<WXSTRING> buf;
	if (!quote(pwsz, pwszQuote, &buf))
		return 0;
	return buf.getXString();
}

bool qm::PartUtil::quote(const WCHAR* pwsz,
						 const WCHAR* pwszQuote,
						 XStringBuffer<WXSTRING>* pBuf)
{
	assert(pwsz);
	assert(pwszQuote);
	assert(pBuf);

	if (!pBuf->append(pwszQuote))
		return false;
	while (*pwsz) {
		if (!pBuf->append(*pwsz))
			return false;
		if (*pwsz == L'\n' && *(pwsz + 1) != L'\0') {
			if (!pBuf->append(pwszQuote))
				return false;
		}
		++pwsz;
	}

	return true;
}

wstring_ptr qm::PartUtil::expandNames(const WCHAR** ppwszNames,
									  unsigned int nCount)
{
	assert(ppwszNames);

	StringBuffer<WSTRING> buf;
	for (unsigned int n = 0; n < nCount; ++n) {
		if (n != 0)
			buf.append(L", ");
		buf.append(ppwszNames[n]);
	}
	return buf.getString();
}

bool qm::PartUtil::isContentType(const ContentTypeParser* pContentType,
								 const WCHAR* pwszMediaType,
								 const WCHAR* pwszSubType)
{
	if (pContentType)
		return _wcsicmp(pContentType->getMediaType(), pwszMediaType) == 0 &&
			_wcsicmp(pContentType->getSubType(), pwszSubType) == 0;
	else
		return _wcsicmp(pwszMediaType, L"text") == 0 &&
			_wcsicmp(pwszSubType, L"plain") == 0;
}

bool qm::PartUtil::isMultipart(const qs::ContentTypeParser* pContentType)
{
	return pContentType && _wcsicmp(pContentType->getMediaType(), L"multipart") == 0;
}

bool qm::PartUtil::isRfc822Rendered(Rfc822Mode rfc822Mode,
									bool bAttachment)
{
	switch (rfc822Mode) {
	case RFC822_AUTO:
		return !bAttachment;
	case RFC822_INLINE:
		return true;
	case RFC822_ATTACHMENT:
		return false;
	default:
		assert(false);
		return false;
	}
}

/****************************************************************************
 *
 * AttachmentParser
 *
 */

qm::AttachmentParser::AttachmentParser(const Part& part) :
	part_(part)
{
}

qm::AttachmentParser::~AttachmentParser()
{
}

bool qm::AttachmentParser::hasAttachment() const
{
	if (part_.isMultipart()) {
		const Part::PartList& l = part_.getPartList();
		for (Part::PartList::const_iterator it = l.begin(); it != l.end(); ++it) {
			if (AttachmentParser(**it).hasAttachment())
				return true;
		}
		return false;
	}
	else if (part_.getEnclosedPart()) {
		return AttachmentParser(*part_.getEnclosedPart()).hasAttachment();
	}
	else {
		return part_.isAttachment();
	}
}

wstring_ptr qm::AttachmentParser::getName() const
{
	wstring_ptr wstrName;

	ContentDispositionParser contentDisposition;
	if (part_.getField(L"Content-Disposition", &contentDisposition) == Part::FIELD_EXIST)
		wstrName = contentDisposition.getParameter(L"filename");

	if (!wstrName.get()) {
		const ContentTypeParser* pContentType = part_.getContentType();
		if (pContentType)
			wstrName = pContentType->getParameter(L"name");
	}

	if (!wstrName.get() && PartUtil::isContentType(part_.getContentType(), L"message", L"rfc822")) {
		UnstructuredParser subject;
		if (part_.getEnclosedPart()->getField(L"Subject", &subject) == Part::FIELD_EXIST)
			wstrName = concat(subject.getValue(), L".eml");
	}

	if (wstrName.get()) {
		if (File::isDeviceName(wstrName.get()))
			wstrName = concat(L"_", wstrName.get());

#ifdef UNICODE
		for (WCHAR* p = wstrName.get(); *p; ++p) {
			if (!TextUtil::isFileNameChar(*p))
				*p = L'_';
		}
#else
		string_ptr strName(wcs2mbs(wstrName.get()));
		for (CHAR* p = strName.get(); *p; ++p) {
			if (::IsDBCSLeadByte(*p))
				++p;
			else if (!TextUtil::isFileNameChar(*p))
				*p = '_';
		}
		wstrName = mbs2wcs(strName.get());
#endif
	}

	return wstrName;
}

void qm::AttachmentParser::getAttachments(unsigned int nFlags,
										  AttachmentList* pList) const
{
	assert(pList);

	if ((nFlags & GAF_INCLUDEDELETED) == 0 && isAttachmentDeleted())
		return;

	if (part_.isMultipart()) {
		bool bAppleDouble = _wcsicmp(part_.getContentType()->getSubType(), L"appledouble") == 0;
		const Part::PartList& l = part_.getPartList();
		for (Part::PartList::const_iterator it = l.begin(); it != l.end(); ++it) {
			const Part* p = *it;
			if ((nFlags & GAF_INCLUDEAPPLEFILE) == 0 && bAppleDouble) {
				const ContentTypeParser* pContentType = p->getContentType();
				if (pContentType &&
					_wcsicmp(pContentType->getMediaType(), L"application") == 0 &&
					_wcsicmp(pContentType->getSubType(), L"applefile") == 0)
					continue;
			}
			AttachmentParser(*p).getAttachments(nFlags, pList);
		}
	}
	else if (part_.isAttachment()) {
		wstring_ptr wstrName(getName());
		if (!wstrName.get() || !*wstrName.get())
			wstrName = allocWString(L"Untitled");
		wstring_ptr wstrOrigName = allocWString(wstrName.get());

		int n = 1;
		while (true) {
			AttachmentList::iterator it = std::find_if(pList->begin(), pList->end(),
				boost::bind(string_equal<WCHAR>(),
					boost::bind(&AttachmentList::value_type::first, _1), wstrName.get()));
			if (it == pList->end())
				break;

			WCHAR wsz[32];
			_snwprintf(wsz, countof(wsz), L"[%d]", n++);
			const WCHAR* pExt = wcsrchr(wstrOrigName.get(), L'.');
			StringBuffer<WSTRING> buf;
			buf.append(wstrOrigName.get(), pExt ? pExt - wstrOrigName.get() : -1);
			buf.append(wsz);
			if (pExt)
				buf.append(pExt);
			wstrName = buf.getString();
		}
		pList->push_back(AttachmentList::value_type(
			wstrName.get(), const_cast<Part*>(&part_)));
		wstrName.release();
	}

	if (nFlags & GAF_INCLUDERFC822 && part_.getEnclosedPart())
		AttachmentParser(*part_.getEnclosedPart()).getAttachments(nFlags, pList);
}

AttachmentParser::Result qm::AttachmentParser::detach(const WCHAR* pwszDir,
													  const WCHAR* pwszName,
													  bool bAddZoneId,
													  DetachCallback* pCallback,
													  wstring_ptr* pwstrPath) const
{
	assert(pwszDir);
	assert(pwszName);
	assert(pCallback);

	if (pwstrPath)
		pwstrPath->reset(0);

	StringBuffer<WSTRING> buf(pwszDir);
	if (buf.getLength() != 0 && buf.get(buf.getLength() - 1) == L'\\')
		buf.remove(buf.getLength() - 1, buf.getLength());

	if (!File::createDirectory(buf.getCharArray()))
		return RESULT_FAIL;

	buf.append(L'\\');
	buf.append(pwszName);

	wstring_ptr wstrPath(buf.getString());

	if (File::isFileExisting(wstrPath.get())) {
		wstring_ptr wstr(pCallback->confirmOverwrite(wstrPath.get()));
		if (!wstr.get())
			return RESULT_CANCEL;
		wstrPath = wstr;
	}

	FileOutputStream stream(wstrPath.get());
	if (!stream || !detach(&stream) || !stream.close())
		return RESULT_FAIL;

#ifndef _WIN32_WCE
	if (bAddZoneId) {
		wstring_ptr wstrZonePath = concat(wstrPath.get(), L":Zone.Identifier");
		FileOutputStream zoneStream(wstrZonePath.get());
		if (!!zoneStream) {
			const CHAR* pszZoneInfo = "[ZoneTransfer]\r\nZoneId=3\r\n";
			bool b = zoneStream.write(reinterpret_cast<const unsigned char*>(pszZoneInfo), strlen(pszZoneInfo)) != -1;
			b = zoneStream.close() && b;
			if (!b) {
				W2T(wstrZonePath.get(), ptszZonePath);
				::DeleteFile(ptszZonePath);
			}
		}
	}
#endif

	if (pwstrPath)
		*pwstrPath = wstrPath;

	return RESULT_OK;
}

bool qm::AttachmentParser::detach(OutputStream* pStream) const
{
	const Part* pEnclosedPart = part_.getEnclosedPart();

	if (pEnclosedPart) {
		xstring_size_ptr strContent(pEnclosedPart->getContent());
		size_t nLen = strContent.size();
		const unsigned char* p = reinterpret_cast<const unsigned char*>(strContent.get());
		if (pStream->write(p, nLen) != nLen)
			return false;
	}
	else {
		const CHAR* pBody = part_.getBody();
		size_t nLen = strlen(pBody);
		const unsigned char* p = reinterpret_cast<const unsigned char*>(pBody);

		std::auto_ptr<Encoder> pEncoder;
		ContentTransferEncodingParser contentTransferEncoding;
		if (part_.getField(L"Content-Transfer-Encoding", &contentTransferEncoding) == Part::FIELD_EXIST) {
			const WCHAR* pwszEncoding = contentTransferEncoding.getEncoding();
			if (_wcsicmp(pwszEncoding, L"7bit") != 0 &&
				_wcsicmp(pwszEncoding, L"8bit") != 0) {
				pEncoder= EncoderFactory::getInstance(pwszEncoding);
				if (!pEncoder.get())
					return false;
			}
		}
		if (pEncoder.get()) {
			ByteInputStream inputStream(p, nLen, false);
			BufferedOutputStream bufferedStream(pStream, false);
			if (!pEncoder->decode(&inputStream, &bufferedStream) ||
				!bufferedStream.close())
				return false;
		}
		else {
			if (pStream->write(p, nLen) != nLen)
				return false;
		}
	}

	return true;
}

bool qm::AttachmentParser::isAttachmentDeleted() const
{
	NumberParser field(0);
	Part::Field f = part_.getField(L"X-QMAIL-AttachmentDeleted", &field);
	return f == Part::FIELD_EXIST && field.getValue() != 0;
}

void qm::AttachmentParser::removeAttachments(Part* pPart)
{
	assert(pPart);

	if (pPart->isMultipart()) {
		const Part::PartList& l = pPart->getPartList();
		for (Part::PartList::const_iterator it = l.begin(); it != l.end(); ++it)
			removeAttachments(*it);
	}
	else {
		Part* pEnclosedPart = pPart->getEnclosedPart();
		if (pPart->isAttachment()) {
			if (pEnclosedPart) {
				pEnclosedPart->setBody("", 0);
				pEnclosedPart->setHeader("");
			}
			else {
				pPart->setBody("", 0);
			}
		}
		else if (pEnclosedPart) {
			removeAttachments(pEnclosedPart);
		}
	}
}

void qm::AttachmentParser::setAttachmentDeleted(Part* pPart)
{
	assert(pPart);

	NumberParser field(1, 0);
	pPart->replaceField(L"X-QMAIL-AttachmentDeleted", field);
}

/****************************************************************************
 *
 * AttachmentParser::AttachmentListFree
 *
 */

qm::AttachmentParser::AttachmentListFree::AttachmentListFree(AttachmentList& l) :
	l_(l)
{
}

qm::AttachmentParser::AttachmentListFree::~AttachmentListFree()
{
	free();
}

void qm::AttachmentParser::AttachmentListFree::free()
{
	std::for_each(l_.begin(), l_.end(),
		boost::bind(&freeWString,
			boost::bind(&AttachmentList::value_type::first, _1)));
	l_.clear();
}

/****************************************************************************
 *
 * AttachmentParser::DetachCallback
 *
 */

qm::AttachmentParser::DetachCallback::~DetachCallback()
{
}

/****************************************************************************
 *
 * XQMAILAttachmentParser
 *
 */

qm::XQMAILAttachmentParser::XQMAILAttachmentParser()
{
}

qm::XQMAILAttachmentParser::XQMAILAttachmentParser(const WCHAR* pwsz)
{
	assert(pwsz);

	parse(pwsz, &listAttachment_);
}

qm::XQMAILAttachmentParser::~XQMAILAttachmentParser()
{
	std::for_each(listAttachment_.begin(), listAttachment_.end(), &freeWString);
}

const XQMAILAttachmentParser::AttachmentList& qm::XQMAILAttachmentParser::getAttachments() const
{
	return listAttachment_;
}

Part::Field qm::XQMAILAttachmentParser::parse(const Part& part,
											  const WCHAR* pwszName)
{
	assert(pwszName);

	string_ptr strValue(part.getRawField(pwszName, 0));
	if (!strValue.get())
		return Part::FIELD_NOTEXIST;

	wstring_ptr wstrValue(decode(strValue.get(), -1, false, 0));
	parse(wstrValue.get(), &listAttachment_);

	return Part::FIELD_EXIST;
}

string_ptr qm::XQMAILAttachmentParser::unparse(const Part& part) const
{
	wstring_ptr wstrValue(format(listAttachment_));
	return encode(wstrValue.get(), -1, L"utf-8", L"B", false, false);
}

void qm::XQMAILAttachmentParser::parse(const WCHAR* pwsz,
									   AttachmentList* pList)
{
	assert(pwsz);
	assert(pList);
	assert(pList->empty());

	StringBuffer<WSTRING> buf;
	const WCHAR* p = pwsz;
	while (true) {
		if (*p == L',' || *p == L'\0') {
			wstring_ptr wstrName(buf.getString());
			if (*wstrName.get()) {
				pList->push_back(wstrName.get());
				wstrName.release();
			}

			if (*p == L'\0')
				break;
		}
		else if (*p == L'\\') {
			if (*(p + 1) != L'\0') {
				++p;
				buf.append(*p);
			}
		}
		else {
			if ((*p != L' ' && *p != L'\t') || buf.getLength() != 0)
				buf.append(*p);
		}
		++p;
	}
}

wstring_ptr qm::XQMAILAttachmentParser::format(const AttachmentList& l)
{
	StringBuffer<WSTRING> buf;

	for (AttachmentList::const_iterator it = l.begin(); it != l.end(); ++it) {
		if (it != l.begin())
			buf.append(L",\n ");

		if (**it == L' ' || **it == L'\t')
			buf.append(L'\\');

		for (const WCHAR* p = *it; *p; ++p) {
			if (*p == L'\\' || *p == L',')
				buf.append(L'\\');
			buf.append(*p);
		}
	}

	return buf.getString();
}

redpajama_set_name: RedPajamaGithub


text:
Company Profile History Awards R&D Team
Latest News Media Reports
Join Us Business Branches
Home > News & Media > Latest News
Sinosynergy Attends Green Low Carbon and Cleaner Production Technology Seminar
Release time:2022-08-30
Source:Sinosynergy
On August 25, the 2022 Green Low Carbon and Cleaner Production Technology Seminar took place in Guangzhou Pazhou Convention & Exhibition Center. About 400 experts and entrepreneurs from green low carbon, cleaner production, energy saving and environmental protection fields attended the seminar for exchanging achievements in green low carbon and cleaner production technologies to boost the realization of the national carbon peak and carbon neutrality goals.
Sinosynergy was invited to the event and Dr. Zhao Gang, supervisor of the Technology and Quality Center, delivered the theme speech of "Sinosynergy Boosts Green and Low Carbon Transport Development".
"In China, the transport industry accounts for 10% of the total carbon emission of the entire society, where road transportation takes about 86.76%. And among road transportation, commercial vehicles are most challenged in the reduction of carbon emission. Heavy-duty trucks, with an ownership of 3% only, contribute 54%, 65% and 85% of the emission of carbon dioxide, particulate matters and NOx, respectively. Zero-carbon hydrogen fuel cell vehicles are undoubtedly the best clean energy solution for the transport industry." Dr. Zhao Gang pointed out, from the perspectives of the current carbon emission of the transport industry and the advantage of hydrogen fuel cell technology, that the application of hydrogen energy in transportation is critical to the realization of the carbon peak and carbon neutrality goals.
He then introduced to the audience the great contribution of Sinosynergy to the green low carbon transportation. "Sinosynergy is a leading hydrogen fuel cell company in China, and its system products are widely used in buses, heavy-duty trucks, rail transport, forklifts, fixed power generation, etc. in many provinces and cities across the country. Having accumulated over 200 million kilometers of safe operation mileage, Sinosynergy has commissioned demonstrative application of its products in 40 cities of 20 provinces in the country." Dr. Zhao Gang's splendid share of ideas was no less than a plentiful green feast to those present on the occasion.
In November, 2021, the National Development and Reform Commission and other ministries issued the "14th Five-Year Plan National Cleaner Production Implementation Plan" which points out that the implementation of cleaner production is an important means to cut pollution and carbon emission and to increase effect in synergy as well as an effective path to accelerate the formation of green production and to boost the overall green transformation of the socioeconomic development. Sinosynergy will give full play to its all-dimensional advantages as a leading hydrogen enterprise, promote cleaner production in various fields and boost the construction of a green low carbon circular economic system so as to contribute more to the realization of the carbon peak and carbon neutrality targets.
Witnessing the Surging Power of Sinosynergy in Hannover, Germany
Sinosynergy Joins Uxin Banner and Qingdao City Construction Investment (Group) Limited in Wind Solar Hydrogen Storage Project
Public@sinosynergypower.com
23/F,Building C, Vanke Financial Center, 55 Jihuawu Road, Chancheng District, Foshan, Guangdong Province 528012, China
10 South Area, Foshan (Yunfu) Industrial Transfer Park, Silao Town, Yuncheng District, Yunfu City, Guangdong Province, China 527326
About Us Products and Solutions News & Media Investors Contact Us
Copyright 2022 Sinosynergy 粤ICP备2022150460

redpajama_set_name: RedPajamaCommonCrawl


According to the European Environment Agency's (EEA) updated environmental indicator report published today, the European Union continues to fall short of achieving a number of environmental objectives by 2020, especially in areas aimed at protecting biodiversity and natural capital. When it comes to 'boosting sustainable, resource-efficient, low-carbon economy', trends and outlooks cause more concern compared to the assessment from last year, while progress in addressing environment-related threats to health remains rather mixed.
The annual EEA Environmental Indicator Report 2018 provides an updated scoreboard that monitors progress in 29 selected environmental objectives that are relevant to achieving the three key priority objectives under the 7th Environment Action Programme (EAP) that address: natural capital (including biodiversity); sustainable, resource efficient, low-carbon economy; and people's health and well-being. The scoreboard paints a bleak picture for progress in improving the EU's natural capital. The overall progress is mixed for the other two priority objectives.
Compared to last year's, this year's scoreboard revised downwards the prospects of meeting three more objectives, important to the achievement of the 7th EAP priority objective that addresses sustainable, resource efficient low carbon growth. The outlooks for meeting the EU's 2020 energy efficiency target and reducing the overall environmental impact of the housing sector by 2020 were revised from 'likely' to 'uncertain'. Increases in the overall energy consumption and in the household sector were the main cause. The outlook for reducing the environmental impact of the mobility sector was also revised to 'unlikely' as greenhouse gas emissions from transport increased.
The report notes that for a number of indicators across the three 7th priority objectives the positive past trends seen in the scoreboard were mainly because of the low economic activity right after the 2008 financial crisis and that in several cases progress has slowed in recent years due to increased economic growth.
'The 'grow now, clean up later' economic model that dominates our world and which does not account for climate change, pollution or the degradation of our natural capital is unsustainable. Europe needs to urgently step up efforts to transform its key systems of production and consumption towards sustainability.' said Hans Bruyninckx, EEA Executive Director.


>> battery, which is obviously incorrect.
>> Is there something I should be looking for?
> I haven't seen that before... is this shelf or the new bryce one?
> battery, which is obviously incorrect.
> Is there something I should be looking for?
I haven't seen that before... is this shelf or the new bryce one?
>> It works back again after resume if I restart E.
> Logout and login? Ctrl-Alt-BackSp doesn't seem to work.
configuration (you can change it from the keybinding menu).
> [...] It works back again after resume if I restart E.
Found it. In the menus, where it should be.
> Inviato dal mio dispositivo Android con K-9 Mail. Perdonate la brevità.
> Did you suspend your laptop?
> was before suspend. It works back again after resume if I restart E.
Logout and login? Ctrl-Alt-BackSp doesn't seem to work.
> Instead, the battery indicator in the shelf always works, even after resume.
indicator in the shelf always works, even after resume.
>battery, which is obviously incorrect.
>Is there something I should be looking for?


text:
Richard E. Ferdig
Summit Professor of Learning Technologies, Kent State University
Writing and Technology Book
Games & Simulations
Ferdig Technologies, LLC
Home » Press
Links to interviews, articles, and other media highlights.
NEH Grant on May 4th Augmented Reality App
Goist, R. (January 17, 2020). National Endowment for the Arts awards grant to Kent State for May 4, 1970 augmented reality project. Cleveland.com
Ryan, T. (January 17, 2020). Congressman Tim Ryan Announces $175,000 to Kent State for an Augmented Reality Project to Honor the 50th Anniversary of the 1970 Campus Shooting. Press Release.
Lockwood, D. (January 14, 2020). National Endowment for the Humanities Announces New Grants. The New York Times.
NEH List of 2020 Grant Awards. NEH
Neese, A.W. (January 8, 2020). Online teachers face real challenges in virtual classrooms. The Jordan Times.
XRI Initiative at Kent State
XRI Initiative
Youtube Introduction
Youtube XRI Channel
SPEDAPPS/AT&T
Ravipati, S. (September 6, 2016). Kent State U Project Catalogs, Creates Apps for Special Education.
Vincent, E. (August 18, 2016). Kent State researchers launch free math multiplication learning app for elementary grades. E-Inside; KSU.
AT&T Ohio Team. (December 11, 2015). Kent State Receives Contribution From AT&T to Improve Mobile Technologies for Special Education. AT&T Blog.
McGuinness, D. (December 10, 2015). Kent State Receives Contribution From AT&T to Improve Mobile Technologies for Special Education. KSU.
2016 Outstanding Research Award
Hvostal, Z. (August 18, 2016) – Kent State instructional technology professor wins 2016 Outstanding Research and Scholarship Award. E-Inside; KSU.
Pompili, D. (April 4, 2016) – Kent State honors 2016 Outstanding Research and Scholarship Award recipients. E-Inside; KSU.
St. Clair, J. (March 23, 2015). Exploradio: The challenge of finding a healthy balance with technology. WKSU News.
Cuyahoga Valley National Park/Cleveland Metroparks/National Science Foundation Project
Montano, P. (August 18, 2016). Celebrating Informal STEM Education & Learning in America's National Parks. CAISE.
KSU (June 24, 2016). Cuyahoga Valley Natinal Park partners with Kent State and Cleveland Metroparks to launch science learning app. E-Inside
St. Clair, J. (June 21, 2016). New app helps parkgoers interact with Northeast Ohio's trails. WKSU News.
Douglas J. Guth (June 13, 2016). New app puts key to green spaces in your pocket. Fresh Water Cleveland
NSF (June 3, 2016). News from the field. National Science Foundation.
AAAS (June 3, 2016). Cuyahoga Valley National Park, Kent State & Cleveland Metroparks launch learning app. EurekAlert!
Phys.org (June 3, 2016). Cuyahoga Valley National Park, Kent State and Cleveland Metroparks launch learning app. Phys.org
Beacon Journal staff report (June 2, 2016). Kent State, Cuyahoga Valley, Cleveland Metroparks develop new science-based park app. Akron Beacon Journal.
Crain's Cleveland Business (June 2, 2016). New app helps visitors explore Cuyahoga Valley National Park, Cleveland Metroparks. Crain's Cleveland Business.
Jeff St. Clair (May 31, 2016). New App Helps Parkgoers Interact With Northeast Ohio's Trails. WKSU
Whitney Blair Wyckoff (April 20, 2015). Third-party apps proliferate inside national parks. fedscoop
Bob Downing (October 4, 2014). Kent State team to develop science-based mobile app for Cuyahoga Valley National Park. Akron Beacon Journal.
Hudson Hub Times (September 21, 2014). Kent State researchers to develop mobile app for Cuyahoga Valley National Park. Hudson Hub Times.
Ariel Black (September 10, 2014). Virtual Park Ranger App to Launch in September 2015. Actionhub: Mountain and Trail News.
Hannah Weinberger (September 9, 2014). NPS Launching Virtual Ranger Program in Ohio. Outside Online.
Joanne Richards (September 8, 2014). App could help visitors enjoy Cuyahoga Valley National Park. ideastream/WCPN ; Also on WKSU.
Karen Farkas (September 8, 2014). Cuyahoga Valley National Park visitors will soon have a "virtual park ranger" in their smartphones. The Plain Dealer.
Scott Suttell (September 6, 2014). Get outside — but don't forget to bring your phone. Crain's Cleveland Business.
ECN (September 5, 2014). Kent State researchers to develop mobile app for Cuyahoga Valley National Park. ECN Mag.
Cindy Weiss (September 5, 2014). NSF grant funds "citizen science" at Cuyahoga Valley National Park. Kent State News.
Bugel, A. (October 30, 2014). KSU professors work to find elementary and secondary education math apps. KentWired.
Kathy Pierre (June 18, 2014). Should Ivy Leagues offer online degrees? Students are conflicted. USA Today – Online.
Kent State (May 5, 2014). College Starts Summer Online Adventure for High Schoolers. KentState
WOOD Radio News Team (March 30, 2014). Michigan Virtual University, Kent State University Study Online Learning. WOOD News.
Michigan Virtual University (March 6, 2014). Report concludes that MOOCs can lead to positive outcomes. MIVU
Davis, Michelle R. (October 16, 2013). MOOC Targets Needs of K-12 Teachers and Students. Education Week's Blogs: Digital Education.
Roush, M. (October 8, 2013). MOOCs Examined In MVU-Kent State University Report. CBS-Detroit.
Livingston, Tom. (July 26, 2013). Akron-area family opts to send son to online high school. News Channel 5.
Kozlowski, Kim. (May 14, 2012). Virtual School Provides New Learning Experience. The Detroit News.
Davis, Michelle R. (February 8, 2012). Virtual Education Seen as Understudied. Education Week.
Watson et al. (2011). Does Online Learning Work? In Keeping Pace 2011.
Quillen, Ian. (August 24, 2011). Virtual Ed. Seeks Right Fit for Special Populations. Education Week.
Schulte, Brigid. Hybrid Schools for the iGeneration: New schools combine "bricks" and "clicks". Harvard Education Letter, 27(2), March/April, 2011.
Davis, Michelle R. (March 14, 2011). Researchers evaluate tech.-oriented, personalized learning: But determining what works is proving to be challenging. Education Week
Chivvis, Dana. (Feb. 9, 2011). Homepage High: Computer No Substitute for Teacher at Tech High. http://www.aolnews.com/2011/02/09/tech-high-computer-no-substitute-for-a-teacher/
Chivvis, Dana. (Feb. 9, 2011). Homepage High: Teachers, Computers, No Textbooks. http://www.aolnews.com/2011/02/09/hudson-high-teachers-computers-no-textbooks/
PR Newswire (Nov. 15, 2010). iNACOL announces first published book: Lessons learned from virtual schools: Experiences and recommendations from the field. http://www.prnewswire.com/news-releases/inacol-announces-first-published-book-lessons-learned-from-virtual-schools-experiences-and-recommendations-from-the-field-108285764.html
MIVU.Org (2010). Reports address dropout recovery and professional development for online learning. http://www.mivu.org/News/tabid/297/mid/696/newsid696/44/Default.aspx
MLive.Com (2010). Grand Rapids schools to hold panel discussion about online courses. http://www.mlive.com/news/grand-rapids/index.ssf/2010/05/grand_rapids_schools_to_hold_p.html
Davis, M.R. (2010). Schools Factor E-Courses Into the Daily Learning Mix. Education Week, April 28, 2010. http://www.edweek.org/ew/articles/2010/04/28/30edtech_blended.h29.html
EHHS Facebook: Faculty profile (2010)
Butterfly City: The Butterfly Education Project (2009). http://www.firstbutterflycity.org/faq/
Davis, M.R. (2009). E-Learning's Gender Factor: Educators are examining how boys and girls learn differently in online environments. Education Week, October 14, 2009. http://www.edweek.org/dd/articles/2009/10/21/01e-gender.h03.html
Ferdig, R.E. (Ed.) (2009). Handbook of research on effective electronic gaming in education. Information Science Reference; Hershey, PA (1759 pages; 3 volumes).
Handbook home
Review by Book News
Viadero, D. (2009). Research Shows Evolving Picture of E-Education. Education Week, March 26, 2009. http://www.edweek.org/ew/articles/2009/03/26/26research.h28.html
Watson et al. (2009). Keeping Pace: Notes from the Field. (Innovations in Online K-12 Teaching and Learning). http://www.kpk12.com/downloads/KeepingPace09-notesfield.pdf
Lockette, T. (2008). Schools without walls. http://www.research.ufl.edu/publications/explore/past/fall2008/story_4/index.html
UF College of Education. (2008). Power-ups for Education. http://news.education.ufl.edu/news/rick-ferdig-powerups-education
UF College of Education. (2008). Ferdig to edit new journal on video games and learning. http://news.education.ufl.edu/news/ferdig-edit-new-journal-video-games-and-learning
Watson, J. (2008). Keeping Pace with K-12 Online Learning: The Virtual School Clearinghouse. http://www.kpk12.com/downloads/KeepingPace_2008.pdf
Watson, J. (2008). Online Learning: The National Landscape. http://www.ciconline.org/c/document_library/get_file?folderId=117&name=THFall08TheNationalLandscape.pdf
AT&T (2007). AT&T Joins University of Florida to Present Results of Online Learning Research: Study Expands and Examines Effectiveness of Online Learning. http://www.att.com/gen/press-room?pid=4800&cdvn=news&newsarticleid=24493
Peterson, T. (2007). Supporting pedagogy in higher education. CDW-G: Chicago, IL. (PDF)
Tiegen, A. (2007). New students learn about UF on Facebook, MySpace. Gainesville Sun, July 2, 2007. http://www.gainesville.com/article/20070702/LOCAL/707020308/-1/xml
Kelly, R. (2003). Asynchronous discussion design principles: Four steps to better asynchronous discussions. Interview with Richard E. Ferdig. Online Cl@ssroom [Online Serial], October, 2003.
Kelly, R. (2003). The deeper psychology of technology: Making online collaboration work. Interview with Richard E. Ferdig. Online Cl@ssroom [Online Serial], October, 2003.
Buy the truth and do not sell it; get wisdom, discipline and understanding. - Proverbs 23:23 NIV
Connect with Rick
Copyright ©2020 Richard E. Ferdig, All rights reserved.| Powered by WordPress| Original theme by India Fascinates


text:
Coast Guard cutter behind massive cocaine bust returns home to Alameda
ALAMEDA (KTVU) - Family and friends celebrated the return of the Coast Guard Cutter Waesche on Monday as it docked in Alameda, after a nearly two month-long deployment during which the crew seized more than 17,000 pounds of cocaine in the waters off Central America.
The drugs carried a street value of more than $260 million.
The crew also arrested 20 suspected narcotics traffickers.
The Cutter Waesche's most recent seizure was on June 8, when the crew intercepted a 55-foot boat with some 5,500 pounds of cocaine onboard, according to the Coast Guard.
The drugs were discovered on a so-called Low Profile Vessel. LPVs are designed and colored to blend in with the ocean, making them difficult for law enforcement to detect.
A Coast Guard photo last week showed that banner seizure along with another $288 million in cocaine seized by other Coast Guard cutters, as the drugs were being prepared to be off-loaded in San Diego.
The seizures were part of the Coast Guard's ongoing effort to focus personnel and resources on known drug transit zones in the Pacific.
"The men and women of Waesche's crew performed flawlessly during this deployment, preventing millions in illicit narcotics from reaching U.S. shores," said Capt. James Passarelli, Waesche's commanding officer.
"Their efforts under challenging and dangerous conditions speak volumes to their collective sense of dedication and sacrifice in keeping America safe," Passarelli added.


text:
Content: Volume 6, Issue 1
showing 1-5 of 36 breaks
Bronze Age food diversity: ceci n'est pas un bagel
The primary motivation of archaeology is to find out how people in the past lived their lives. Among all the different activities that people follow, eating and drinking are certainly among the most important ones: We just have to eat. This makes all activities involved... click to read more
Andreas G. Heiss | Senior Researcher at Austrian Academy of Sciences (ÖAW), Austrian Archaeological Institute (ÖAI), Wien, Austria
cereal products
eastern central Europe
charred food remains
prehistoric cuisine
Late Bronze Age
Rapid increase of nuclear weapons in India and Pakistan may lead to local and global catastrophes
India and Pakistan achieved independence from the United Kingdom just after World War II. During the partition of the British Indian Empire, the princely State of Jammu and Kashmir became divided between India, Pakistan and China. India and Pakistan have had four wars over control... click to read more
Owen B. Toon | Professor at Department of Atmospheric and Oceanic Sciences, Laboratory for Atmospheric and Space Physics, University of Colorado, Boulder, Colorado, USA
Alan Robock | Distinguished Professor at Department of Environmental Sciences, Rutgers University, New Brunswick, New Jersey
crop failure
Beware of humans and glacial maximums – the story of cave bear extinction
The cave bear is one of the dozens of species of large Pleistocene mammals that faced extinction during the last Ice Age. Until today, the reasons for their extinctions remain mysterious, especially since the cave bear Ursus spelaeus populated vast areas of Eurasia for more... click to read more
Verena J. Schuenemann | Assistant Professor at Institute for Evolutionary Medicine, University of Zurich, Zurich, Switzerland
Joscha Gretzinger | PhD student at Max Planck Institute for the Science of Human History, Jena, Germany
cave bears
English and Welsh hospital patients in the Lyme-light
Lyme disease is the most common vector-borne disease in the Northern Hemisphere. It is spread through the bite of ticks and can cause a "bull's eye" rash, fever, joint pain, and sometimes nerve problems. The amount of Lyme disease in each country in Europe varies... click to read more
John S. P. Tulloch | Research Fellow at NIHR Health Protection Research Unit in Emerging and Zoonotic Infections, University of Liverpool, Liverpool, UK
Lyme borreliosis
Reading time 3.5 min
The bumpy-effect of climate change on transatlantic flights
The North Atlantic flight corridor is the busiest oceanic airspace in the world. Fundamental to its operation is the jet stream - a narrow ribbon of fast west-to-east flowing air (sometimes over 200 mph) that peaks in strength at around 30,000 - 40,000 feet (the... click to read more
Simon H. Lee | PhD student at Department of Meteorology, University of Reading, Reading, UK


text:
La « coupe de Nestor » de Mycènes est une coupe en or, trouvée dans la tombe à fosse IV du cercle A des tombes de Mycènes, que l'archéologue Heinrich Schliemann a découverte en 1876 et identifiée comme étant la coupe de Nestor décrite dans l'Iliade :

« Nestor et Machaon [...] sautèrent du char sur la terre nourricière, et le serviteur du vieillard, Eurymédon, détela les chevaux. Et les deux rois, ayant séché leur sueur au vent de la mer, entrèrent sous la tente et prirent des sièges, et Hékamède à la belle chevelure leur prépara à boire. Nestor l'avait amenée de Ténédos qu'Achille venait de détruire : c'était la fille du magnanime Arsinoos, et les Achéens l'avaient donnée au Néléide parce qu'il les surpassait tous par sa prudence.

« Elle posa devant eux une belle table aux pieds de métal azuré et, sur cette table, un bassin d'airain poli avec des oignons pour exciter à boire, et du miel vierge, et de la farine sacrée ; puis une très belle coupe enrichie de clous d'or, que le vieillard avait apportée de ses demeures. Cette coupe avait quatre anses et deux fonds, et, sur chaque anse, deux colombes d'or semblaient manger. Tout autre l'eût soulevée avec peine quand elle était remplie, mais le vieux Nestor la soulevait facilement.

« La jeune femme, semblable aux déesses, prépara une boisson de vin de Pramnéios, et sur ce vin elle râpa, avec de l'airain, du fromage de chèvre, qu'elle aspergea de blanche farine. Après ces préparatifs, elle invita les deux rois à boire ; et ceux-ci, ayant bu et étanché la soif brûlante, charmèrent leur repos en parlant tour à tour.

— Iliade (XI, 632-641), Édition Lemaire, 1866, .

En réalité, la coupe trouvée à Mycènes diffère de la description d'Homère à plusieurs égards. La coupe de Mycènes est beaucoup plus petite ; elle a deux anses, tandis que la coupe homérique en a quatre ; la coupe d'Homère a deux colombes par anse, mais la coupe trouvée dans la fosse mycénienne n'a qu'un seul oiseau sur chaque anse, et au lieu des colombes décrites sur la coupe homérique, les oiseaux de la coupe de Mycènes sont des faucons.

Cette coupe mycénienne est conservée au Musée national archéologique d'Athènes.

Historique
En 1876, Heinrich Schliemann a fouillé les cercles des tombes de Mycènes et, dans plusieurs tombes à fosse, il a trouvé de riches dépôts de mobilier funéraire, dont de nombreux objets en or. La tombe à fosse IV a produit les découvertes les plus riches et parmi celles-ci, Schliemann a trouvé un vase en or qu'il a identifié comme la « coupe de Nestor » décrite dans l'Iliade (XI, 632-641). Schliemann pensait que les fosses remontaient à l'époque de la guerre de Troie, et a identifié la tombe à fosses V comme la tombe d'Agamemnon. Cependant, l'identification par Schliemann des fosses communes avec celles des héros homériques n'a pas été acceptée par de nombreux archéologues, même de son temps. Les tombes à fosse sont conventionnellement datées d'environ 1600-1500 avant JC, environ trois siècles avant la date de la guerre de Troie (si la guerre doit être considérée comme un événement historique). S'il en est ainsi, la « coupe de Nestor » de Mycènes aurait été enterrée des centaines d'années avant que Nestor n'en fasse utilisation à Troie.

Description
La coupe de Nestor ou coupe aux colombes est un gobelet en or découvert en 1876 par Heinrich Schliemann dans la fosse IV du premier cercle royal (cercle  A). La coupe mesure  de haut et  de diamètre ; elle pèse . Elle comporte une tige (ou jambe), un corps de forme analogue à celle des gobelets de Vaphio et deux poignées (ou anses) soudées à la base (ou cuvette), dans le style d'un canthare. Chaque anse est ornée d'un oiseau en or, qui, selon Schliemann, rappelle la coupe de Nestor décrite dans l'Iliade. Les oiseaux ont depuis été identifiés par Spyrídon Marinátos comme des faucons, plutôt que comme les colombes décrites sur la coupe iliadique. Selon J.T. Hooker, la coupe pourrait être l'œuvre d'un artisan grec du continent d'après un modèle crétois.

Notes et références

Bibliographie

Articles connexes
 Nestor
 Coupe de Nestor (mythologie)
 Coupe de Nestor (Pithécusses)

Artéfact archéologique
Art de la Grèce antique
Civilisation mycénienne
Objet en or
Musée national archéologique d'Athènes

redpajama_set_name: RedPajamaWikipedia


If you are sourcing in a new area or going to locations you have not been to before then it can save time to have a plan to route you to each location in order to ensure that you are on the fastest route to each location. If you need to get the best route to 12 locations or less, Speedyroute.com offers a free service that does just that for you. They have a limit of 5 free route calculations from one client IP Address over a rolling 24 hour period. You can calculate more than 5 routes from the same IP Address within 24 hours and more than 12 locations in one route, but you must purchase a subscription in order to do so. For most people, the free service will be more than adequate for their needs.
A service like this is especially useful when you are going to garage sales. Since each location changes every week, you do not develop a general idea for the best route to the locations like you would a retail store, thrift store, or wholesale store that you frequent regularly. Make sure to plan your sourcing outing in the most efficient method using a route planning tool like SpeedyRoute. If you have navigation systems like Garmin or TomTom then SpeedyRoute's export feature supports GPX files as well as CSV files (GPX files can be used by some SatNavs such as TomTom and Garmin devices).