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.
448 lines
12 KiB
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 <tqlayout.h>
|
|
#include <tqwidgetstack.h>
|
|
#include <tqlabel.h>
|
|
#include <tqcheckbox.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 TQString &oldVersion)
|
|
{
|
|
bool ret=(
|
|
(oldVersion.left(3)=="0.3") ||
|
|
(oldVersion.left(3)=="0.4")
|
|
);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
KNConvert::KNConvert(const TQString &version)
|
|
: TQDialog(0,0,true), l_ogList(0), c_onversionDone(false), v_ersion(version)
|
|
{
|
|
setCaption(kapp->makeStdCaption(i18n("Conversion")));
|
|
TQVBoxLayout *topL=new TQVBoxLayout(this, 5,5);
|
|
s_tack=new TQWidgetStack(this);
|
|
topL->addWidget(s_tack, 1);
|
|
topL->addWidget(new KSeparator(this));
|
|
|
|
TQHBoxLayout *btnL=new TQHBoxLayout(topL, 5);
|
|
s_tartBtn=new TQPushButton(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, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotStart()));
|
|
connect(c_ancelBtn, TQT_SIGNAL(clicked()), this, TQT_SLOT(reject()));
|
|
|
|
w_1=new TQWidget(s_tack);
|
|
s_tack->addWidget(w_1, 1);
|
|
TQGridLayout *w1L=new TQGridLayout(w_1, 5,3, 5,5);
|
|
|
|
TQLabel *l1=new TQLabel(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.").tqarg(KNODE_VERSION), w_1);
|
|
w1L->addMultiCellWidget(l1, 0,0, 0,2);
|
|
|
|
c_reateBkup=new TQCheckBox(i18n("Create backup of old data"), w_1);
|
|
w1L->addMultiCellWidget(c_reateBkup, 2,2, 0,2);
|
|
connect(c_reateBkup, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(slotCreateBkupToggled(bool)));
|
|
|
|
b_ackupPathLabel=new TQLabel(i18n("Save backup in:"), w_1);
|
|
w1L->addWidget(b_ackupPathLabel, 3,0);
|
|
|
|
b_ackupPath=new KLineEdit(TQDir::homeDirPath()+TQString("/knodedata-")+v_ersion+".tar.gz", w_1);
|
|
w1L->addWidget(b_ackupPath, 3,1);
|
|
|
|
b_rowseBtn= new TQPushButton(i18n("Browse..."), w_1);
|
|
connect(b_rowseBtn, TQT_SIGNAL(clicked()), this, TQT_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 TQLabel(s_tack);
|
|
w_2->setText(i18n("<b>Converting, please wait...</b>"));
|
|
w_2->tqsetAlignment(AlignCenter);
|
|
s_tack->addWidget(w_2, 2);
|
|
|
|
w_3=new TQWidget(s_tack);
|
|
s_tack->addWidget(w_3, 3);
|
|
TQVBoxLayout *w3L=new TQVBoxLayout(w_3, 5,5);
|
|
|
|
r_esultLabel=new TQLabel(w_3);
|
|
w3L->addWidget(r_esultLabel);
|
|
TQLabel *l2=new TQLabel(i18n("Processed tasks:"), w_3);
|
|
l_ogList=new TQListBox(w_3);
|
|
w3L->addSpacing(15);
|
|
w3L->addWidget(l2);
|
|
w3L->addWidget(l_ogList, 1);
|
|
|
|
s_tack->raiseWidget(w_1);
|
|
slotCreateBkupToggled(false);
|
|
}
|
|
|
|
|
|
KNConvert::~KNConvert()
|
|
{
|
|
for ( TQValueList<Converter*>::Iterator it = mConverters.begin(); it != mConverters.end(); ++it )
|
|
delete (*it);
|
|
}
|
|
|
|
|
|
void KNConvert::convert()
|
|
{
|
|
int errors=0;
|
|
for ( TQValueList<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;
|
|
}
|
|
|
|
TQString dataDir=locateLocal("data","knode/");
|
|
t_ar=new KProcess;
|
|
*t_ar << "tar";
|
|
*t_ar << "-cz" << dataDir
|
|
<< "-f" << b_ackupPath->text();
|
|
connect(t_ar, TQT_SIGNAL(processExited(KProcess*)), this, TQT_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()
|
|
{
|
|
TQString 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").tqarg(b_ackupPath->text()));
|
|
else
|
|
l_og.append(i18n("backup failed."));
|
|
|
|
// now we actually convert the files
|
|
convert();
|
|
}
|
|
|
|
|
|
|
|
//============================================================================================
|
|
|
|
|
|
|
|
bool KNConvert::Converter04::doConvert()
|
|
{
|
|
TQString dir=locateLocal("data","knode/")+"folders/";
|
|
int num;
|
|
bool error=false;
|
|
|
|
//Drafts
|
|
if(TQFile::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(TQFile::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(TQFile::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
|
|
TQFile::remove(dir+"standard.info");
|
|
TQFile::remove(dir+".standard.info");
|
|
return (!error);
|
|
}
|
|
|
|
|
|
int KNConvert::Converter04::convertFolder(TQString srcPrefix, TQString dstPrefix)
|
|
{
|
|
TQFile srcMBox(srcPrefix+".mbox"),
|
|
srcIdx(srcPrefix+".idx"),
|
|
dstMBox(dstPrefix+".mbox"),
|
|
dstIdx(dstPrefix+".idx");
|
|
TQTextStream ts(&dstMBox);
|
|
ts.setEncoding(TQTextStream::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;
|
|
TQCString buff(size+10);
|
|
srcMBox.at(oldIdx.so);
|
|
int readBytes=srcMBox.readBlock(buff.data(), size);
|
|
buff.tqat(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"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|