/***************************************************************************
                          ncftpimportfilterplugin.cpp  -  description
                             -------------------
    begin                : tis mar 19 2002
    copyright            : (C) 2003 by Björn Sahlström
    email                : kbjorn@users.sourceforge.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/
/***************************************************************************
 * Some code in function NcFTPImportFilterPlugin::parseLine
 * Code in functions parseLine and HexCharToNibble pinched
 * from original NcFTP source code.
 * Copyright (c) 1992-2002 by Mike Gleason.
 * All rights reserved.
 ***************************************************************************/

//////////////////////////////////////////////////////
// Qt specific includes
#include <qdom.h>
#include <qfile.h>
#include <qstringlist.h>
//////////////////////////////////////////////////////
// KDE specific includes
#include <klocale.h>
#include <kconfig.h>
#include <kmdcodec.h>
#include <kinstance.h>
#include <kgenericfactory.h>
#include <kmessagebox.h>
#include <kdebug.h>
//////////////////////////////////////////////////////
// System specific includes
#include <string.h>
//////////////////////////////////////////////////////
// Application specific includes
#include "ncftpimportfilterplugin.h"
#include "kbeartags.h"
#include "misc.h"

#include "ncftpimportfilterplugin.moc"

using namespace KBear;

typedef KGenericFactory<NcFTPImportFilterPlugin> NcFTPImportFilterPluginFactory;
K_EXPORT_COMPONENT_FACTORY( kbearncftpimportfilter, NcFTPImportFilterPluginFactory( "NcFTPImportFilterPlugin" ) );
//-----------------------------------------------
NcFTPImportFilterPlugin::NcFTPImportFilterPlugin(QObject *parent, const char *name, const QStringList& )
	:	SiteImportFilterPluginIface(parent,name),
		m_fileName( QString::null ),
		MIN_BOOKMARK_VERSION( 3 ),
		m_hasError( false )
{
//	setXMLFile("ncftpimportfilterui.rc");
	KGlobal::locale()->insertCatalogue( QString::fromLatin1("kbear") );
	m_domDocument.setContent( QString("<%1 %1=\"%1\"/>").arg(TAG_GROUP).arg(ATT_LABEL).arg(i18n("NcFTP import")) );
}
//-----------------------------------------------
NcFTPImportFilterPlugin::~NcFTPImportFilterPlugin(){
}
//-----------------------------------------------
QString NcFTPImportFilterPlugin::getDomDocument() {
	return m_domDocument.toString();
}
//-----------------------------------------------
void NcFTPImportFilterPlugin::import( const QString& fileName ) {
	if( fileName.isEmpty() || fileName.isNull() ) { // we need a filename
		displayError( NoFile );
		m_hasError = true;
		emit progress( 100 );
		return;
	}
	m_fileName = fileName;
	int bookmarkFileVersion = 0;
	//Open the file in read only mode
	QFile file( m_fileName );
	file.open( IO_ReadOnly );
	QTextStream fileStream( &file );
	//First check version of bookmarks file
	bookmarkFileVersion = getVersion( fileStream );
	if( ! bookmarkFileVersion ) { // no version line
		displayError( InvalidFile );
		file.close();
		m_hasError = true;
		emit progress( 100 );
		return;	
	}
	// For now we ignore the version and try to resolve the file even if invalid version number ( lower than 3 )
		
	// Try to find out how many bookmarks there is in the file
	// The number of bookmarks entry can't be trusted so we ignores it, just use it for progress display
	float numOfBookmarks = getNumOfBookmarks( fileStream );
	if( numOfBookmarks <= 0 )
		numOfBookmarks = float(10); // this gives us a progrress, even if not correct ;-)
	int counter = 0;

	// Time to search for bookmarks
	QDomElement siteElement = m_domDocument.documentElement();
	QString lineStr = QString::null;
	int parseReturn;
	while( (lineStr = fileStream.readLine() ) != QString::null ) { // repeat until EOF
		parseReturn = parseLine( lineStr,siteElement );
		// now it's time to see what we received. Here is also were we have use for the version
		if( parseReturn < 0  ) { // invalid file
			if( bookmarkFileVersion < 3 ) {// We can only handle bookmarks file version >= 3
				displayError( OldFile );
			}
			else
				displayError( InvalidFile );
			
			file.close();
			m_hasError = true;
			emit progress( 100 );
			return;	
		}
		
		emit progress( int(float(counter) / numOfBookmarks * 100) );	
		counter++;	
	}
	
	//Done, so lets close the file
	file.close();
	
	emit progress( 100 );	
}
//-----------------------------------------------
int NcFTPImportFilterPlugin::parseLine( const QString& lineStr, QDomElement siteElement ) {
//ParseHostLine(char *line, BookmarkPtr bmp)

	char token[128];
	char *s, *d;
	char *tokenend;
	int i;
	int result;
	int n, n1, n2;

	QDomElement tmpElement = m_domDocument.createElement(TAG_SITE);
	
	s = (char*)lineStr.latin1();

	tokenend = token;
	tokenend += sizeof(token);
	--tokenend;
	result = -1;
	bool done = false;
	// NcFTP always use FTP so far.
	//This is NOT excplicitly given in bokmarks file so we set this first
	QDomElement e = m_domDocument.createElement(TAG_PROTOCOL);
	QDomText t = m_domDocument.createTextNode( QString::fromLatin1("ftp") );
	e.appendChild( t );
	tmpElement.appendChild( e );

	for (i=1; ! done ; i++) {
		if (*s == '\0')  {
			break;
		}
		/* Some tokens may need to have a comma in them.  Since this is a
		 * field delimiter, these fields use \, to represent a comma, and
		 * \\ for a backslash.  This chunk gets the next token, paying
		 * attention to the escaped stuff.
		 */
		for (d = token; *s != '\0'; ) {
			if ((*s == '\\') && (s[1] != '\0')) {
				if (d < tokenend)
					*d++ = s[1];
				s += 2;
			} else if (*s == ',') {
				++s;
				break;
			} else if ((*s == '$') && (s[1] != '\0') && (s[2] != '\0')) {
				n1 = HexCharToNibble(s[1]);
				n2 = HexCharToNibble(s[2]);
				if ((n1 >= 0) && (n2 >= 0)) {
					n = (n1 << 4) | n2;
					if (d < tokenend)
						*(unsigned char *)d++ = (unsigned int) n;
				}
				s += 3;
			} else {
				if (d < tokenend)
					*d++ = *s;
				++s;
			}
		}
		*d = '\0';
		QString pass;
		QString tmp;
		switch(i) {
			case 1:  //Label
				tmpElement.setAttribute(ATT_LABEL, token );
				break;
			case 2:  //Host
				e = m_domDocument.createElement(TAG_HOST);
				t = m_domDocument.createTextNode( token );
				e.appendChild( t );
				tmpElement.appendChild( e );
				break;
			case 3: //User
				e = m_domDocument.createElement(TAG_USER);
				t = m_domDocument.createTextNode( token );
				e.appendChild( t );
				tmpElement.appendChild( e );
				break;
			case 4: //Password
				pass = token;
				if( pass.contains( QString::fromLatin1( "*encoded*" ) ) )  // encoded password
					pass = pass.mid( 9 );
				else
					pass = encodePassword( pass );

				e = m_domDocument.createElement(TAG_PASS);
				t = m_domDocument.createTextNode( pass );
				e.appendChild( t );
				tmpElement.appendChild( e );
				break;
			case 5:  //Account
				break;
			case 6:  //Remote path
				e = m_domDocument.createElement(TAG_REMOTE_PATH);
				tmp = token;
				if( tmp.isEmpty() )
					t = m_domDocument.createTextNode( QString::fromLatin1("/") );
				else
					t = m_domDocument.createTextNode( token );
				e.appendChild( t );
				tmpElement.appendChild( e );
				result = 0;		/* Good enough to have these fields. */
				break;
/* Removed since this is now a globally option
			case 7: // Transfer type (Binary/ASCII)
				if (token[0] == 'A') {
					e = m_domDocument.createElement(TAG_ASCII);
					tmpElement.appendChild( e );
				}
				break;
*/
			case 8: //Port
				e = m_domDocument.createElement(TAG_PORT);
				/* Most of the time, we won't have a port. */
				if (token[0] == '\0')
					t = m_domDocument.createTextNode( QString::number( 21 ) );
				else
					t = m_domDocument.createTextNode( token );
				e.appendChild( t );
				tmpElement.appendChild( e );
				break;
			case 9: //Last call
				break;
			case 10: //Has size
				break;
			case 11: //MDTM
				if ( atoi(token) ) {
					e = m_domDocument.createElement(TAG_MDTM);
					tmpElement.appendChild( e );
				}
				break;
			case 12: // PASV
				if ( ! atoi(token) ) {
					e = m_domDocument.createElement(TAG_DISABLE_PASV);
					tmpElement.appendChild( e );
				}
				break;
			case 13:  //Is UNIX
				if ( atoi(token) ) {
					e = m_domDocument.createElement(TAG_UNIX);
					tmpElement.appendChild( e );
				}
				result = 3;		/* Version 3 had all fields to here. */
				break;
			case 14: //Last IP
				break;
			case 15: //Description
				e = m_domDocument.createElement(TAG_DESCRIPTION);
				t = m_domDocument.createTextNode( token );
				e.appendChild( t );
				tmpElement.appendChild( e );
				break;
			case 16:
				 break;
			case 17:
				break;
			case 18:
				break;
			case 19:
				break;
			case 20: //Transfer Mode
					result = 7;		/* Version 7 has all fields to here. */
					break;
			case 21: //UTIME
				break;
			case 22: //Local path
				e = m_domDocument.createElement(TAG_LOCAL_PATH);
				tmp = token;
				if( tmp.isEmpty() )
					t = m_domDocument.createTextNode( QDir::homeDirPath() );
				else
					t = m_domDocument.createTextNode( token );
				e.appendChild( t );
				tmpElement.appendChild( e );
				result = 8;		/* Version 8 has all fields to here. */
				break;
			default:
				result = 99;	/* Version >8 ? */
				done = true;
		}
	}
	siteElement.appendChild( tmpElement );
	/* Decode password, if it was base-64 encoded. */
	return (result);
}
//-----------------------------------------------
int NcFTPImportFilterPlugin::getVersion( QTextStream& fileStream ) {
	QString versionTemplateStr = QString::fromLatin1("NcFTP bookmark-file version:");
	int version = 0;
	QString lineStr = fileStream.readLine();
	if( lineStr.contains( versionTemplateStr ) ) { //version line
		QString tmp = lineStr.mid( versionTemplateStr.length() );
		tmp = tmp.stripWhiteSpace();
		version = tmp.toInt();
		return version;
	}

	return 0;	
}
//-----------------------------------------------
int NcFTPImportFilterPlugin::getNumOfBookmarks( QTextStream& fileStream ) {
	QString numBookmarksTemplateStr = QString::fromLatin1("Number of entries:");
	int numOfBookmarks = 0;
	QString lineStr = fileStream.readLine();
	if( lineStr.contains( numBookmarksTemplateStr ) ) { //version line
		QString tmp = lineStr.mid( numBookmarksTemplateStr.length() );
		tmp = tmp.stripWhiteSpace();
		numOfBookmarks = tmp.toInt();
		return numOfBookmarks;
	}

	return 0;	
}
//-----------------------------------------------
void NcFTPImportFilterPlugin::displayError( ErrorType errorType ){
	QString message = QString::null;
	QString caption = QString::null;
	switch( errorType ) {
		case InvalidFile:
			message = i18n("The file: %1\nis not a valid NcFTP bookmarks file.").arg( m_fileName );
			caption = i18n("Invalid import file");
			break;
		case OldFile:
			message = i18n("The file: %1\nis a bookmark file with invalid version number.\n"
			 					"This filter can only handle NcFTP bookmark files"
			 					" with version number larger than 3.").arg( m_fileName );
			caption = i18n("To old import file");
			break;
		case NoFile:
			message = i18n("You have to select a file to import.");
			caption = i18n("No file");
			break;
	}
	KMessageBox::sorry( 0, message, caption );
}
//-----------------------------------------------
int NcFTPImportFilterPlugin::HexCharToNibble(int c) {
	switch (c) {
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			return (c - '0');
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
			return (c - 'a' + 10);
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
			return (c - 'A' + 10);

	}
	return (-1);	/* Error. */
}	/* HexCharToNibble */
//-----------------------------------------------
