/***************************************************************************
 *   Copyright (C) 2004 by Matthew Wlazlo <mwlazlo@gmail.com>              *
 *   Copyright (C) 2007 by Raphael Geissert <atomo64@gmail.com>            *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

// define this symbol if you want to try to detect the language of your account
#define DETECT_GLANGUAGE

#include "gmailparser.h"
#include "gmail_constants.h"
#include "prefs.h"

#include <kdebug.h>
#include <qregexp.h>
#include <klocale.h>
#include <kcharsets.h>

/**
 * Gmail's response parser object constructor.
 *
 * This class parses the resulting data of a call to
 * Gmail's JavaScript interface.
*/
GMailParser::GMailParser() :
		QObject(0, "GMailParser"),
		mInvites(0)
{
	mSummary.inbox = 0;
// 	mSummary.starred = 0;
	mSummary.drafts = 0;
// 	mSummary.sent = 0;
// 	mSummary.all = 0;
	mSummary.spam = 0;
// 	mSummary.trash = 0;
	
	//Gmail versions kcheckgmail works with.
	gGMailVersion.append("1exl39kx7mipo");
	gGMailVersion.append("1x4nkpwjfkc8x");
	gGMailVersion.append("1ddh9n6glzd1c");
	gGMailVersion.append("11qm1wldxu1ww");

	// TODO: read this from a file
#ifdef DETECT_GLANGUAGE
	// Gmail language identifiers:
	gGMailLanguageCode.insert("7fba835ed0312d54",i18n("Spanish"));
	gGMailLanguageCode.insert("7530096a84569c0b",i18n("French"));
	gGMailLanguageCode.insert("21208aa200ae6920",i18n("Italian"));
	gGMailLanguageCode.insert("cd21242a38a63f0",i18n("German"));
	gGMailLanguageCode.insert("9930dc54804b344a",i18n("English (US)"));
	gGMailLanguageCode.insert("f59adb920ee42615",i18n("English (UK)"));
	gGMailLanguageCode.insert("d414bf5ecc193e94",i18n("Portuguese"));
	gGMailLanguageCode.insert("421a229c26e5115",i18n("Turkish"));
	gGMailLanguageCode.insert("f8c7fb73ac445a2f",i18n("Polish"));
	gGMailLanguageCode.insert("d880d89755cacbab",i18n("Russian"));
	gGMailLanguageCode.insert("a680d09b1f097e52",i18n("Croatian"));
	gGMailLanguageCode.insert("690643eba4fb5b28",i18n("Dutch"));
	gGMailLanguageCode.insert("b6a1b7dea1a8a18",i18n("Hungarian"));
	gGMailLanguageCode.insert("fa2444e0ab7696ed",i18n("Swedish"));
	gGMailLanguageCode.insert("cb207eb0643c6e51",i18n("Norwegian"));
	gGMailLanguageCode.insert("bee0f0eace8c0ee8",i18n("Lithuanian"));
	gGMailLanguageCode.insert("e7b392f9cad18fbb",i18n("Hebrew"));
	gGMailLanguageCode.insert("62efb853bef926",i18n("Greek"));
	gGMailLanguageCode.insert("88595fc43e710562",i18n("Chinese (Simplified)"));
	gGMailLanguageCode.insert("61a2f8a31f62e658",i18n("Chinese (Traditional)"));
	gGMailLanguageCode.insert("c368aa1b815d1a8a",i18n("Czech"));
	gGMailLanguageCode.insert("a680d09b1f097e52",i18n("Croatian"));
	gGMailLanguageCode.insert("e35d4c2af8d5feba",i18n("Catalan"));
	gGMailLanguageCode.insert("b8e15ea37ed4f16",i18n("Arabic"));
	gGMailLanguageCode.insert("1a0f6d9fde5216d",i18n("Indonesian"));
	gGMailLanguageCode.insert("eda8b40a2ecad8c4",i18n("Japanese"));
	gGMailLanguageCode.insert("4e86fd3e11e3c97f",i18n("Korean"));
	gGMailLanguageCode.insert("22aa46bbafe71b1c",i18n("Hindi"));
	gGMailLanguageCode.insert("41525b3ab51fbde0",i18n("Thai"));
	gGMailLanguageCode.insert("2a874ffaa00d8aef",i18n("Danish"));
	gGMailLanguageCode.insert("f7b8471d482333cb",i18n("Estonian"));
	gGMailLanguageCode.insert("2b1540853e61b819",i18n("Icelandic"));
	gGMailLanguageCode.insert("b8245e71d2838794",i18n("Latvian"));
	gGMailLanguageCode.insert("68b96b309bad80f3",i18n("Romanian"));
	gGMailLanguageCode.insert("5597c0ee7cb73b7f",i18n("Slovak"));
	gGMailLanguageCode.insert("54b802d9a90ad926",i18n("Slovenian"));
	gGMailLanguageCode.insert("2afe8d7fe1757459",i18n("Finnish"));
	gGMailLanguageCode.insert("2381ac21a233a92c",i18n("Tagalog"));
	gGMailLanguageCode.insert("5592d8feddc3e8da",i18n("Vietnamese"));
	gGMailLanguageCode.insert("ba76ef5e9ef44145",i18n("Ukrainian"));
	gGMailLanguageCode.insert("ffa2983afaf20325",i18n("Bulgarian"));
#endif
}

/**
 * Object destructor.
*/
GMailParser::~GMailParser()
{
}

/**
 * Main parser.
 *
 * The main parsing process starts here.
 * It splits the content of _data into blocks
 * which are later passed to sub-parsers.
 *
 * @param _data Gmail's JavaScript response
*/
void GMailParser::parse(const QString &_data)
{
	static QRegExp rx("D\\(\\[(.*)\\][\\s\\n]*\\);");
	int pos = 0;
	unsigned int oldNewCount, NewCount = 0;

	rx.setMinimal(true);

	if(!rx.isValid()) {
		kdWarning() << k_funcinfo << "Invalid RX!\n"
			<< rx.errorString() << endl;
	} 

	mCurMsgId = 0;
	oldNewCount = getNewCount();
	QMap<QString,bool> *oldMap = getThreadList();
	freeThreadList();

	QString data = QString::fromUtf8(_data);

	while((pos = rx.search(data, pos)) != -1) {
		QString str = rx.cap(1);
		QRegExp rxType("^\"([a-z]+)\",");

		int tokPos = -1;
		if((tokPos = rxType.search(str)) >= 0) {
			QString tok = rxType.cap(1);
			int tokLen = rxType.matchedLength();

			// strip token
			str.remove(tokPos, tokLen);
			
			if(tok == D_THREAD) {
				NewCount += parseThread(str, oldMap);
			} else if(tok == D_VERSION) {
				parseVersion(str);
			} else if(tok == D_QUOTA) {
				parseQuota(str);
			} else if(tok == D_DEFAULT_SUMMARY) {
				parseDefaultSummary(str);
			} else if(tok == D_CATEGORIES) {
				parseLabel(str);
			} else if(tok == D_INVITE_STATUS) {
				parseInvite(str);
			} else if(tok == D_GAIA_NAME) {
				parseGName(str);
			}/* else if(tok == D_THREADLIST_SUMMARY) {
				parseThreadSummary(str);
			}*///D(["ts",0,20/*max shown*/,4/*total results*/,0,"Search results for: in:inbox is:unread","in:inbox is:unread","113e12c0cc4"/*search id?*/,9,,"",""]

		}

		pos += rx.matchedLength();
	}

	if(oldMap)
		delete oldMap;

	kdDebug() << k_funcinfo << "NewCount=" << NewCount << endl;
	kdDebug() << k_funcinfo << "oldNewCount=" << oldNewCount << endl;
	
	int realCount = getNewCount(true);
	if (oldNewCount == 0 && realCount != 0)
		NewCount = realCount;
	
	if(NewCount > 0)
		emit mailArrived(NewCount);
	if(oldNewCount != NewCount)
		emit mailCountChanged();
	if(realCount == 0)
		emit noUnreadMail();
}

///////////////////////////////////////////////////////////////////////////
// Parsers
///////////////////////////////////////////////////////////////////////////


/**
 * Threads/emails parser.
 *
 * This parser takes care of extracting the available data from the emails block.
 *
 * @param _data The messages data block
 * @param oldMap The old messages map, used to detect whether a message was already reported as new or not
 * @return The number of unread messages that were found in _data
*/
uint GMailParser::parseThread(const QString &_data, const QMap<QString,bool>* oldMap)
{
	//Matches messages when snippets are on
	static QRegExp rx(
			"\\[\"([a-fA-F0-9]+)\"\\s*,"	// replyID
			"\\s*([0-9]+)\\s*,"		// isNew
			"\\s*([0-9]+)\\s*,"		// unknown1
			"\\s*\"([^\"]*)\"\\s*,"		// date_short
			"\\s*\"([^\"]*)\"\\s*,"		// senders
			"\\s*\"([^\"]*)\"\\s*,"		// chevron
			"\\s*\"([^\"]*)\"\\s*,"		// subject
			"\\s*\"([^\"]*)\"\\s*,"		// snippet
			"\\s*\\[([^\\]]*)\\]\\s*,"	// labels
			"\\s*\"([^\"]*)\"\\s*,"		// attachments
			"\\s*\"([a-fA-F0-9]+)\"\\s*,"	// msgID
			"\\s*([0-9]+)\\s*,"		// unknown2
			"\\s*\"([^\"]*)\"\\s*"		// date_long
			"(,\\s*([0-9]+)\\s*,)?"		// unknown3
			"(\\s*\"([^\"]*)\"\\s*,)?"	// unknown4
			"(\\s*([0-9]+)\\s*\\])?"	// unknown5
			 );

	//Matches messages when snippets are off
	static QRegExp rx2(
			"\\[\"([a-fA-F0-9]+)\"\\s*,"	// replyID
			"\\s*([0-9]+)\\s*,"		// isNew
			"\\s*([0-9]+)\\s*,"		// unknown1
			"\\s*\"([^\"]*)\"\\s*,"		// date_short
			"\\s*\"([^\"]*)\"\\s*,"		// senders
			"\\s*\"([^\"]*)\"\\s*,"		// chevron
			"\\s*\"([^\"]*)\"\\s*,"		// subject
			"(\\s*),"			// snippet
			"\\s*\\[([^\\]]*)\\]\\s*,"	// labels
			"\\s*\"([^\"]*)\"\\s*,"		// attachments
			"\\s*\"([a-fA-F0-9]+)\"\\s*,"	// msgID
			"\\s*([0-9]+)\\s*,"		// unknown2
			"\\s*\"([^\"]*)\"\\s*"		// date_long
			"(,\\s*([0-9]+)\\s*,)?"		// unknown3
			"(\\s*\"([^\"]*)\"\\s*,)?"	// unknown4
			"(\\s*([0-9]+)\\s*\\])?"	// unknown5
			  );

	QString data = _data;
	data.replace("\\\"","&quot;");

	int pos = 0;

	rx.setMinimal(true);
	rx2.setMinimal(true);

	if(!rx.isValid()) {
		kdWarning() << k_funcinfo << "Invalid RX!\n"
				<< rx.errorString() << endl;
	}

	if(!rx2.isValid()) {
		kdWarning() << k_funcinfo << "Invalid RX2!\n"
				<< rx2.errorString() << endl;
	}
	
	/*
	replyId == msgId if latest message on this 
	thread is not from you
	*/

	unsigned int newMsgCount = 0;
	QString oldLatestThread;
	
	if(oldMap) {
		kdDebug() << k_funcinfo << "oldmap.size=" << oldMap->size() << endl;
		oldLatestThread = oldMap->begin().key();
	} else {
		kdDebug() << k_funcinfo << "no oldmap" << endl;
	}
	kdDebug() << k_funcinfo << "oldLatestThread=" << oldLatestThread << endl;

	while((pos = rx.search(data, pos)) != -1) {
		Thread *t = new Thread;
		t->id = mCurMsgId ++;
		t->replyId = rx.cap(1);
		t->isNew = rx.cap(2).toInt();
		t->unknown1 = rx.cap(3).toUInt();
		t->date_short = rx.cap(4);
		t->senders = cleanUpData(rx.cap(5));
		t->chevron = rx.cap(6);
		t->subject = cleanUpData(rx.cap(7));
		t->snippet = cleanUpData(rx.cap(8));
		t->labels = rx.cap(9);
		t->attachments = QStringList::split(",", rx.cap(10));
		t->msgId = rx.cap(11);
		t->unknown2 = rx.cap(12).toUInt();
		t->date_long = rx.cap(13);
		t->unknown3 = rx.cap(14).toUInt();
		t->isNull = false;

		if(t->isNew && (!oldMap || 
				 (oldMap->find(t->msgId) == oldMap->end() && (t->msgId > oldLatestThread || t->replyId > oldLatestThread)))) {
			kdDebug() << "Message [" << t->msgId << "] is new." << endl;
			newMsgCount ++;
		} else
			kdDebug() << "Message [" << t->msgId << "] is NOT new." << endl;
		
		// (re-)insert
		mThreads.insert(t->msgId, t);

		pos += rx.matchedLength();
	}

	pos = 0;
	
	while((pos = rx2.search(data, pos)) != -1) {
		Thread *t = new Thread;
		t->id = mCurMsgId ++;
		t->replyId = rx2.cap(1);
		t->isNew = rx2.cap(2).toInt();
		t->unknown1 = rx2.cap(3).toUInt();
		t->date_short = rx2.cap(4);
		t->senders = cleanUpData(rx2.cap(5));
		t->chevron = rx2.cap(6);
		t->subject = cleanUpData(rx2.cap(7));
		t->snippet = cleanUpData(rx2.cap(8));
		t->labels = rx2.cap(9);
		t->attachments = QStringList::split(",", rx.cap(10));
		t->msgId = rx2.cap(11);
		t->unknown2 = rx2.cap(12).toUInt();
		t->date_long = rx2.cap(13);
		t->unknown3 = rx2.cap(14).toUInt();
		t->isNull = false;

		if(t->isNew && (!oldMap || 
				 (oldMap->find(t->msgId) == oldMap->end() && (t->msgId > oldLatestThread || t->replyId > oldLatestThread)))) {
			kdDebug() << "Message [" << t->msgId << "] is new." << endl;
			newMsgCount ++;
		} else
			kdDebug() << "Message [" << t->msgId << "] is NOT new." << endl;
		
		// (re-)insert
		mThreads.insert(t->msgId, t);

		pos += rx2.matchedLength();
	}

	kdDebug() << k_funcinfo << "Finished searching for threads in: " << endl;
	kdDebug() << data << endl;
	kdDebug() << k_funcinfo << "newMsgCount: " << newMsgCount << endl;

	return newMsgCount;
}

/**
 * Gmail version information parser.
 *
 * This parser extracts some information from the version string.
 *
 * @param _data The data block
*/
void GMailParser::parseVersion(const QString &_data)
{
	QString data = _data;
	data.remove('"');
	
	kdDebug() << k_funcinfo << "Version string: " << data << endl;
	
	QStringList list = QStringList::split(",",data);
	if(list.size() != 5)
		kdWarning() << k_funcinfo << "Wrong number of elements: "
				<< list.size() << ", should be: 5." << endl;
	
	QStringList::Iterator iter = list.begin();
	unsigned int i = 0;
	while(iter != list.end()) {
		QString str = *iter;
		switch(i) {
			case 0:
				mVersion.unknown1 = str;
				break;
			case 1:
				mVersion.language = str;
				break;
			case 2:
				mVersion.unknown2 = str.toUInt();
				break;
			case 3:
				mVersion.unknown3 = str.toUInt();
				break;
			case 4:
				mVersion.version = str;
				break;
			default:
				kdWarning() << k_funcinfo << "Unknown version token: " << str << "(" << i <<")" << endl;
				break;
		}
		iter++;
		i++;
	}
	kdDebug() << "GMail version " << mVersion.version << endl;
	
	bool ok = false;
	
	for( i = 0; i < gGMailVersion.size() ; i++ ) {
		if( gGMailVersion[i] == mVersion.version )
			ok = true;
	}
	
#ifdef DETECT_GLANGUAGE
	if(gGMailLanguageCode.contains(mVersion.language))
		kdDebug() << "GMail language: " << gGMailLanguageCode[mVersion.language] << endl;
	else
		kdWarning() << k_funcinfo << "Unknown language code: " << mVersion.language << endl;
#endif
	
	if(!ok) {
		kdWarning() << k_funcinfo << "GMail version " << mVersion.version << " is not supported, check for updates!" << endl;
		emit versionMismatch();
	}
}

/**
 * Quota information parser.
 *
 * This parser extracts quota information like 
 * the amount of space used, available, the used percentage, etc.
 *
 * @param data The data block
*/
void GMailParser::parseQuota(const QString &data)
{	
	QStringList list = QStringList::split(",",data);
	if(list.size() == 4 || list.size() == 9) {
		QStringList::Iterator iter = list.begin();
		unsigned int i = 0;
		while(iter != list.end()) {
			QString val = *iter;
			val.remove('"');
			switch(i) {
				case 0:
					mQuota.used = val;
					break;
				case 1:
					mQuota.total = val;
					break;
				case 2:
					mQuota.percent = val;
					break;
				case 3:
					mQuota.colour = val;
					break;
				default:
					break;
			}
			iter++;
			i++;
		}
	} else
		kdWarning() << k_funcinfo << "Wrong number of elements in qu: "
			<< list.size() << ", should be 4 or 9." << endl;
}

/**
 * Default summary parser.
 *
 * This parser extracts the number of unread messages in the inbox, drafts and spam.
 *
 * @param _data The data block
*/
void GMailParser::parseDefaultSummary(const QString &_data)
{
	static QRegExp rx("\"([a-z]+)\",([0-9]+)");

	if(!rx.isValid()) {
		kdWarning() << k_funcinfo << "Invalid RX!\n"
			<< rx.errorString() << endl;
	}
	QString data = _data;
	int pos = 0;

	while((pos = rx.search(data, pos)) != -1) {
		QString str_name = rx.cap(1), str_val = rx.cap(2);
		int val = str_val.toUInt();

		if( QString::compare(str_name,"inbox") == 0)
			mSummary.inbox = val;
		else if( QString::compare(str_name,"drafts") == 0)
			mSummary.drafts = val;
		else if( QString::compare(str_name,"spam") == 0)
			mSummary.spam = val;
		else kdWarning() << k_funcinfo << "unkown identifier " << str_name << endl;

		pos += rx.matchedLength();
	}
	kdDebug() << k_funcinfo << endl  
		<< "inbox=" << mSummary.inbox << "\n"
		<< "drafts=" << mSummary.drafts << "\n"
		<< "spam=" << mSummary.spam << "\n" << endl;
}

/**
 * Lables parser.
 *
 * This parser extracts the number of unread messages per label.
 *
 * @param data The data block
 * @todo Store a QMap with the labels information
*/
void GMailParser::parseLabel(const QString &data)
{
	static QRegExp rx(
		"\\[\"([^\"]+)\""	// label name
		",([0-9]+)\\]"		// unread count
		);

	if(!rx.isValid()) {
		kdWarning() << k_funcinfo << "Invalid RX!\n"
			<< rx.errorString() << endl;
	}
	int pos = 0;
	
	mLabels.clear();
	eLabels.clear();
	
	kdDebug() << k_funcinfo << endl;

	while((pos = rx.search(data, pos)) != -1) {
		mLabels.insert(rx.cap(1), rx.cap(2).toUInt());
		
		QString k = rx.cap(1);
		k.replace(" ", "-");
		eLabels.insert(k, rx.cap(1));
		
		kdDebug() << rx.cap(1) << " has " << rx.cap(2) << " unread messages" << endl;
		pos += rx.matchedLength();
	}
}

/**
 * Invites information parser.
 *
 * This parser extracts the number of available invites.
 *
 * @param data The data block
*/
void GMailParser::parseInvite(const QString &data)
{
	bool ok = true;
	mInvites = data.toUInt(&ok);
	if(!ok) {
		mInvites = 0;
	}
	kdDebug() << k_funcinfo << "Invites=" << mInvites << endl;
}

/**
 * Gaia Name parser.
 *
 * This parser extracts the account's owner name (a.k.a. Gaia Name)
 *
 * @param data The data block
*/
void GMailParser::parseGName(const QString &data)
{
	QString newName = data;
	
	newName.remove('"');
	
	if(newName != gName) {
		gName = newName;
		kdDebug() << "Gaia name: " << gName << endl;
		emit gNameUpdate(gName);
	}
}

///////////////////////////////////////////////////////////////////////////
// Data accessors
///////////////////////////////////////////////////////////////////////////

/**
 * Return the list of parsed messages together with their isNew value
 *
 * @return A list with the msgId's as the keys and isNew as the value
 */
QMap<QString, bool> *GMailParser::getThreadList() const
{
	QMap<QString, bool> *ret = 0;

	if(!mThreads.isEmpty()) {
		ret = new QMap<QString, bool>();

		QValueList<QString> klist = mThreads.keys();
		QValueList<QString>::iterator iter = klist.begin();

		while(iter != klist.end()) {
			Thread *t = mThreads[*iter];
			ret->insert(t->msgId, t->isNew);
			iter ++;
		}
	}

	return ret;
}

/**
 * Return the thread information of the thread specified by msgId
 *
 * @param msgId The message Id of the thread
 * @return A copy of the Thread
*/
const GMailParser::Thread& GMailParser::getThread(const QString &msgId) const
{
	static Thread nullThread;

	QMap<QString, Thread*>::const_iterator iter = mThreads.find(msgId);
	
	if(iter == mThreads.end()) {
		nullThread.isNull = true;
		return nullThread;
	} else
		return *(*iter);
}

/**
 * Return the thread information of the thread specified by id.
 *
 * @param id The numerical id of the thread
 * @return A copy of the Thread
*/
const GMailParser::Thread& GMailParser::getThread(int id) const
{
	static Thread nullThread;
	Thread *ret = &nullThread;
	ret->isNull = true;

	QMap<QString, Thread*>::const_iterator iter = mThreads.begin();
	
	while(ret->isNull == true && iter != mThreads.end()) {
		Thread *t = *iter;
		if(t->id == id)
			ret = t;
		iter ++;
	}
	
	return *ret;
}

/**
 * Return the thread information of last thread in the map
 *
 * @return A copy of the Thread
 */
const GMailParser::Thread& GMailParser::getLastThread() const
{
	static Thread nullThread;

	QMap<QString, Thread*>::const_iterator iter = mThreads.constEnd();
	
	iter--;
	
	if(iter == mThreads.end()) {
		nullThread.isNull = true;
		return nullThread;
	} else
		return *(*iter);
}

const QString GMailParser::getGaiaName() const
{	
	return gName;
}

/**
 * Retrieve the number of unread messages.
 *
 * If realCount is false the box parameter is ignored.
 *
 * @param realCount If the number of unread messages should be taken from the totals or only from the parsed messages
 * @param box The name of the box (inbox, drafts, spam; or in the future: label) from where the real number of unread messages should be taken from
 * @return The number of unread messages
 * @example getNewCount(true,"inbox") Get the real number of unread messages in the inbox
*/
unsigned int GMailParser::getNewCount(bool realCount, QString box) const
{
	unsigned int ret = 0;
	
	if(realCount == true) {
		if(box.compare("inbox") == 0)
			return mSummary.inbox;
		else if(box.compare("drafts") == 0)
			return mSummary.drafts;
		else if(box.compare("spam") == 0)
			return mSummary.spam;
		else {
			if (mLabels.contains(box))
				return mLabels[box];
		}
		kdWarning() << k_funcinfo << "The box " << box << " doesn't exist! returning value as if realCount=false" << endl;
	}
	
	QMap<QString, bool> *lst = getThreadList();
	
	if(lst) {
		QMap<QString,bool>::iterator iter;
		iter = lst->begin();
		while(iter != lst->end()) {
			if(*iter == true)
				ret ++;
			iter ++;
		}
	}
	
	return ret;
}

/**
 * Retrieve the number of unread messages.
 *
 * @param realCount If the number of unread messages should be taken from the totals or only from the parsed messages
 * @return The number of unread messages
*/
unsigned int GMailParser::getNewCount(bool realCount) const
{
	QRegExp rx ("in:([^ ]+)");
	QRegExp rx2("label:([^ ]+)");
	QString box;
	
	if (realCount) {
		if (rx.search(Prefs::searchFor()) == -1 && rx2.search(Prefs::searchFor()) == -1) {
			// If none are specified gmail will return any unread mail (except spam and drafts)
			// TODO: to fix this we need to count all messages (!drafts,!spam, inbox + labels)
			realCount = false;
			//box = "inbox";
		} else if (rx.search(Prefs::searchFor()) != -1 && rx2.search(Prefs::searchFor()) != -1) {
			//there's no other way to know how many emails are in:inbox and in specified label:LABEL
			realCount = false;
		} else if (rx.search(Prefs::searchFor()) != -1) {
			box = rx.cap(1);
		} else if (rx2.search(Prefs::searchFor()) != -1) {
			box = rx2.cap(1);
			
			if (eLabels.contains(box)) {
				box = eLabels[box];
			}
		}
	}
	
	return getNewCount(realCount, box);
}

/**
 * Retrieve the number of unread messages.
 *
 * @return The number of unread messages that were parsed
*/
unsigned int GMailParser::getNewCount() const
{
	return getNewCount(false);
}

///////////////////////////////////////////////////////////////////////////
// Clean up functions
///////////////////////////////////////////////////////////////////////////

void GMailParser::freeThreadList()
{
	if(!mThreads.isEmpty()) {

		QValueList<QString> klist = mThreads.keys();
		QValueList<QString>::iterator iter = klist.begin();

		while(iter != klist.end()) {
			Thread *t = mThreads[*iter];
			delete t;
			iter ++;
		}
	}

	mThreads.clear();
}

/**
 * Tags stripper.
 *
 * This function removes all tags from data.
 *
 * @param data The data to be processed
 * @return The content of data without tags
*/
QString GMailParser::stripTags(QString data)
{
	static QRegExp tags("<[^>]+>|</[^>]+>|<[^>]+/>");
	
	if(!tags.isValid()) {
		kdWarning() << k_funcinfo << "Invalid RX!\n"
				<< tags.errorString() << endl;
	}
	
	data.remove(tags);
	
	return data;
}

/**
 * JavaScript entities converter.
 *
 * This function converts all \uXXXX to their right representation.
 *
 * @param data The data to be processed
 * @return The content of data with the converted entities
*/
QString GMailParser::convertEntities(QString data)
{
	QChar c;
	QString found;
	static QRegExp format("\\\\u([0-9a-zA-Z]{4})");
	
	if(!format.isValid()) {
		kdWarning() << k_funcinfo << "Invalid RX!\n"
				<< format.errorString() << endl;
	}
	
	while(format.search(data) != -1) {
		found = format.cap(1);
		c = QChar(found.toUInt(0,16));
		data.replace("\\u"+format.cap(1),c);
	}
	return data;
}

/**
 * All-in-one data cleaner.
 *
 * This function passes the data to 
 * convertEntities, stripTags and KCharsets::resolveEntities
 *
 * @see convertEntities
 * @see stripTags
 * @param data The data to be cleaned up
 * @return The cleaned up data
*/
QString GMailParser::cleanUpData(QString data)
{
	data = convertEntities(data);
	data = stripTags(data);
	data = KCharsets::resolveEntities(data);
	return data;
}



syntax highlighted by Code2HTML, v. 0.9.1