You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdepim/knode/knconvert.cpp

448 lines
12 KiB

/*
KNode, the KDE newsreader
Copyright (c) 1999-2005 the KNode authors.
See file AUTHORS for details
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US
*/
#include <qlayout.h>
#include <qwidgetstack.h>
#include <qlabel.h>
#include <qcheckbox.h>
#include <klocale.h>
#include <kfiledialog.h>
#include <kseparator.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>
#include <klineedit.h>
#include <kprocess.h>
#include <kapplication.h>
#include <kpushbutton.h>
#include <kmime_util.h>
#include "knconvert.h"
#include "resource.h"
bool KNConvert::needToConvert(const QString &oldVersion)
{
bool ret=(
(oldVersion.left(3)=="0.3") ||
(oldVersion.left(3)=="0.4")
);
return ret;
}
KNConvert::KNConvert(const QString &version)
: QDialog(0,0,true), l_ogList(0), c_onversionDone(false), v_ersion(version)
{
setCaption(kapp->makeStdCaption(i18n("Conversion")));
QVBoxLayout *topL=new QVBoxLayout(this, 5,5);
s_tack=new QWidgetStack(this);
topL->addWidget(s_tack, 1);
topL->addWidget(new KSeparator(this));
QHBoxLayout *btnL=new QHBoxLayout(topL, 5);
s_tartBtn=new QPushButton(i18n("Start Conversion..."), this);
s_tartBtn->setDefault(true);
btnL->addStretch(1);
btnL->addWidget(s_tartBtn);
c_ancelBtn=new KPushButton(KStdGuiItem::cancel(), this);
btnL->addWidget(c_ancelBtn);
connect(s_tartBtn, SIGNAL(clicked()), this, SLOT(slotStart()));
connect(c_ancelBtn, SIGNAL(clicked()), this, SLOT(reject()));
w_1=new QWidget(s_tack);
s_tack->addWidget(w_1, 1);
QGridLayout *w1L=new QGridLayout(w_1, 5,3, 5,5);
QLabel *l1=new QLabel(i18n(
"<b>Congratulations, you have upgraded to KNode version %1.</b><br>\
Unfortunately this version uses a different format for some data-files, so \
in order to keep your existing data it is necessary to convert it first. This is \
now done automatically by KNode. If you want to, a backup of your existing data \
will be created before the conversion starts.").arg(KNODE_VERSION), w_1);
w1L->addMultiCellWidget(l1, 0,0, 0,2);
c_reateBkup=new QCheckBox(i18n("Create backup of old data"), w_1);
w1L->addMultiCellWidget(c_reateBkup, 2,2, 0,2);
connect(c_reateBkup, SIGNAL(toggled(bool)), this, SLOT(slotCreateBkupToggled(bool)));
b_ackupPathLabel=new QLabel(i18n("Save backup in:"), w_1);
w1L->addWidget(b_ackupPathLabel, 3,0);
b_ackupPath=new KLineEdit(QDir::homeDirPath()+QString("/knodedata-")+v_ersion+".tar.gz", w_1);
w1L->addWidget(b_ackupPath, 3,1);
b_rowseBtn= new QPushButton(i18n("Browse..."), w_1);
connect(b_rowseBtn, SIGNAL(clicked()), this, SLOT(slotBrowse()));
w1L->addWidget(b_rowseBtn, 3,2);
w1L->setColStretch(1,1);
w1L->addRowSpacing(1,15);
w1L->setRowStretch(4,1);
w1L->addRowSpacing(4,15);
w_2=new QLabel(s_tack);
w_2->setText(i18n("<b>Converting, please wait...</b>"));
w_2->setAlignment(AlignCenter);
s_tack->addWidget(w_2, 2);
w_3=new QWidget(s_tack);
s_tack->addWidget(w_3, 3);
QVBoxLayout *w3L=new QVBoxLayout(w_3, 5,5);
r_esultLabel=new QLabel(w_3);
w3L->addWidget(r_esultLabel);
QLabel *l2=new QLabel(i18n("Processed tasks:"), w_3);
l_ogList=new QListBox(w_3);
w3L->addSpacing(15);
w3L->addWidget(l2);
w3L->addWidget(l_ogList, 1);
s_tack->raiseWidget(w_1);
slotCreateBkupToggled(false);
}
KNConvert::~KNConvert()
{
for ( QValueList<Converter*>::Iterator it = mConverters.begin(); it != mConverters.end(); ++it )
delete (*it);
}
void KNConvert::convert()
{
int errors=0;
for ( QValueList<Converter*>::Iterator it = mConverters.begin(); it != mConverters.end(); ++it )
if( !(*it)->doConvert() )
errors++;
if(errors>0)
r_esultLabel->setText(i18n(
"<b>Some errors occurred during the conversion.</b>\
<br>You should now examine the log to find out what went wrong."));
else
r_esultLabel->setText(i18n(
"<b>The conversion was successful.</b>\
<br>Have a lot of fun with this new version of KNode. ;-)"));
s_tartBtn->setText(i18n("Start KNode"));
s_tartBtn->setEnabled(true);
c_ancelBtn->setEnabled(true);
l_ogList->insertStringList(l_og);
s_tack->raiseWidget(w_3);
c_onversionDone=true;
}
void KNConvert::slotStart()
{
if(c_onversionDone) {
accept();
return;
}
s_tartBtn->setEnabled(false);
c_ancelBtn->setEnabled(false);
s_tack->raiseWidget(w_2);
if(v_ersion.left(3)=="0.3" || v_ersion.left(7)=="0.4beta") {
//Version 0.4
mConverters.append( new Converter04( &l_og ) );
}
//create backup of old data using "tar"
if(c_reateBkup->isChecked()) {
if(b_ackupPath->text().isEmpty()) {
KMessageBox::error(this, i18n("Please select a valid backup path."));
return;
}
QString dataDir=locateLocal("data","knode/");
t_ar=new KProcess;
*t_ar << "tar";
*t_ar << "-cz" << dataDir
<< "-f" << b_ackupPath->text();
connect(t_ar, SIGNAL(processExited(KProcess*)), this, SLOT(slotTarExited(KProcess*)));
if(!t_ar->start()) {
delete t_ar;
t_ar = 0;
slotTarExited(0);
}
}
else
convert(); //convert files without backup
}
void KNConvert::slotCreateBkupToggled(bool b)
{
b_ackupPathLabel->setEnabled(b);
b_ackupPath->setEnabled(b);
b_rowseBtn->setEnabled(b);
}
void KNConvert::slotBrowse()
{
QString newPath=KFileDialog::getSaveFileName(b_ackupPath->text());
if(!newPath.isEmpty())
b_ackupPath->setText(newPath);
}
void KNConvert::slotTarExited(KProcess *proc)
{
bool success=true;
if(!proc || !proc->normalExit() || proc->exitStatus()!=0) {
success=false;
if(KMessageBox::Cancel==KMessageBox::warningContinueCancel(this, i18n("<b>The backup failed</b>; do you want to continue anyway?"))) {
delete t_ar;
t_ar = 0;
reject();
return;
}
}
delete t_ar;
t_ar = 0;
if(success)
l_og.append(i18n("created backup of the old data-files in %1").arg(b_ackupPath->text()));
else
l_og.append(i18n("backup failed."));
// now we actually convert the files
convert();
}
//============================================================================================
bool KNConvert::Converter04::doConvert()
{
QString dir=locateLocal("data","knode/")+"folders/";
int num;
bool error=false;
//Drafts
if(QFile::exists(dir+"folder1.idx")) {
num=convertFolder(dir+"folder1", dir+"drafts_1");
if(num==-1) {
error=true;
l_og->append(i18n("conversion of folder \"Drafts\" to version 0.4 failed."));
}
else {
l_og->append(i18n("converted folder \"Drafts\" to version 0.4"));
}
}
else
l_og->append(i18n("nothing to be done for folder \"Drafts\""));
//Outbox
if(QFile::exists(dir+"folder2.idx")) {
num=convertFolder(dir+"folder2", dir+"outbox_2");
if(num==-1) {
error=true;
l_og->append(i18n("conversion of folder \"Outbox\" to version 0.4 failed."));
}
else {
l_og->append(i18n("converted folder \"Outbox\" to version 0.4"));
}
}
else
l_og->append(i18n("nothing to be done for folder \"Outbox\""));
//Sent
if(QFile::exists(dir+"folder3.idx")) {
num=convertFolder(dir+"folder3", dir+"sent_3");
if(num==-1) {
error=true;
l_og->append(i18n("conversion of folder \"Sent\" to version 0.4 failed."));
}
else {
l_og->append(i18n("converted folder \"Sent\" to version 0.4"));
}
}
else
l_og->append(i18n("nothing to be done for folder \"Sent\""));
//remove old info-files
QFile::remove(dir+"standard.info");
QFile::remove(dir+".standard.info");
return (!error);
}
int KNConvert::Converter04::convertFolder(QString srcPrefix, QString dstPrefix)
{
QFile srcMBox(srcPrefix+".mbox"),
srcIdx(srcPrefix+".idx"),
dstMBox(dstPrefix+".mbox"),
dstIdx(dstPrefix+".idx");
QTextStream ts(&dstMBox);
ts.setEncoding(QTextStream::Latin1);
OldFolderIndex oldIdx;
NewFolderIndex newIdx;
int lastId=0;
bool filesOpen;
//open files
filesOpen=srcMBox.open(IO_ReadOnly);
filesOpen=filesOpen && srcIdx.open(IO_ReadOnly);
if(dstIdx.exists() && dstIdx.size()>0) { //we are converting from 0.4beta*
if( (filesOpen=filesOpen && dstIdx.open(IO_ReadOnly)) ) {
dstIdx.at( dstIdx.size()-sizeof(NewFolderIndex) ); //set filepointer to last entry
dstIdx.readBlock( (char*)(&newIdx), sizeof(NewFolderIndex) );
lastId=newIdx.id;
dstIdx.close();
}
}
filesOpen=filesOpen && dstMBox.open(IO_WriteOnly | IO_Append);
filesOpen=filesOpen && dstIdx.open(IO_WriteOnly | IO_Append);
if(!filesOpen) {
srcMBox.close();
srcIdx.close();
dstMBox.close();
dstIdx.close();
return -1;
}
//conversion starts here
while(!srcIdx.atEnd()) {
//read index data
srcIdx.readBlock( (char*)(&oldIdx), sizeof(OldFolderIndex));
newIdx.id=++lastId;
newIdx.sId=oldIdx.sId;
newIdx.ti=oldIdx.ti;
switch(oldIdx.status) {
case 0: //AStoPost
newIdx.flags[0]=false; //doMail()
newIdx.flags[1]=false; //mailed()
newIdx.flags[2]=true; //doPost()
newIdx.flags[3]=false; //posted()
newIdx.flags[4]=false; //canceled()
newIdx.flags[5]=false; //editDisabled()
break;
case 1: //AStoMail
newIdx.flags[0]=true; //doMail()
newIdx.flags[1]=false; //mailed()
newIdx.flags[2]=false; //doPost()
newIdx.flags[3]=false; //posted()
newIdx.flags[4]=false; //canceled()
newIdx.flags[5]=false; //editDisabled()
break;
case 2: //ASposted
newIdx.flags[0]=false; //doMail()
newIdx.flags[1]=false; //mailed()
newIdx.flags[2]=true; //doPost()
newIdx.flags[3]=true; //posted()
newIdx.flags[4]=false; //canceled()
newIdx.flags[5]=true; //editDisabled()
break;
case 3: //ASmailed
newIdx.flags[0]=true; //doMail()
newIdx.flags[1]=true; //mailed()
newIdx.flags[2]=false; //doPost()
newIdx.flags[3]=false; //posted()
newIdx.flags[4]=false; //canceled()
newIdx.flags[5]=true; //editDisabled()
break;
case 6: //AScanceled
newIdx.flags[0]=false; //doMail()
newIdx.flags[1]=false; //mailed()
newIdx.flags[2]=true; //doPost()
newIdx.flags[3]=true; //posted()
newIdx.flags[4]=true; //canceled()
newIdx.flags[5]=true; //editDisabled()
break;
default: //what the ..
newIdx.flags[0]=false; //doMail()
newIdx.flags[1]=false; //mailed()
newIdx.flags[2]=false; //doPost()
newIdx.flags[3]=false; //posted()
newIdx.flags[4]=false; //canceled()
newIdx.flags[5]=false; //editDisabled()
break;
}
//read mbox-data
unsigned int size=oldIdx.eo-oldIdx.so;
QCString buff(size+10);
srcMBox.at(oldIdx.so);
int readBytes=srcMBox.readBlock(buff.data(), size);
buff.at(readBytes)='\0'; //terminate string;
//remove "X-KNode-Overview"
int pos=buff.find('\n');
if(pos>-1)
buff.remove(0, pos+1);
//write mbox-data
ts << "From aaa@aaa Mon Jan 01 00:00:00 1997\n";
newIdx.so=dstMBox.at(); //save start-offset
ts << "X-KNode-Overview: ";
ts << KMime::extractHeader(buff, "Subject") << '\t';
ts << KMime::extractHeader(buff, "Newsgroups") << '\t';
ts << KMime::extractHeader(buff, "To") << '\t';
ts << KMime::extractHeader(buff, "Lines") << '\n';
ts << buff;
newIdx.eo=dstMBox.at(); //save end-offset
ts << '\n';
//write index-data
dstIdx.writeBlock((char*)(&newIdx), sizeof(NewFolderIndex));
}
//close/remove files and return number of articles in the new folder
srcMBox.remove();
srcIdx.remove();
dstMBox.close();
dstIdx.close();
return ( dstIdx.size()/sizeof(NewFolderIndex) );
}
//-----------------------------
#include "knconvert.moc"