2 * KFontInst - KDE Font Installer
4 * Copyright 2003-2007 Craig Drummond <craig@kde.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
24 #include "JobRunner.h"
25 #include "KfiConstants.h"
27 #include <KDE/KIO/JobUiDelegate>
28 #include <KDE/KIO/NetAccess>
29 #include <KDE/KIO/CopyJob>
30 #include <KDE/KIO/DeleteJob>
31 #include <KDE/KMessageBox>
32 #include <KDE/KGlobal>
33 #include <KDE/KIconLoader>
34 #include <KDE/KLocale>
35 #include <KDE/KPasswordDialog>
36 #include <KDE/SuProcess>
37 #include <KDE/OrgKdeKDirNotifyInterface>
38 #include <QtGui/QGridLayout>
39 #include <QtGui/QProgressBar>
40 #include <QtGui/QLabel>
41 #include <QtGui/QX11Info>
42 #include <QtCore/QTimer>
45 #include <sys/resource.h>
46 #if defined USE_POLICYKIT && USE_POLICYKIT==1
47 #include "PolicyKitAuthenticator.h"
50 using namespace KDESu
;
55 static KUrl
toggle(const KUrl
&orig
, bool enable
)
59 url
.setFileName(enable
60 ? Misc::getFile(orig
.path()).mid(1)
61 : QChar('.')+Misc::getFile(orig
.path()));
65 class CSkipDialog
: public KDialog
76 CSkipDialog(QWidget
*parent
, bool multi
, const QString
&errorText
)
80 setCaption(i18n( "Information"));
81 setButtons(multi
? Cancel
|User1
|User2
: Cancel
);
82 setButtonText(User1
, i18n("Skip"));
83 setButtonText(User2
, i18n("AutoSkip"));
84 setMainWidget(new QLabel(errorText
, this));
95 void slotButtonClicked(int button
)
117 class CPasswordDialog
: public KPasswordDialog
121 CPasswordDialog(QWidget
*parent
)
122 : KPasswordDialog(parent
),
123 itsSuProc(KFI_SYS_USER
)
125 setCaption(i18n("Authorization Required"));
127 if(itsSuProc
.useUsersOwnPassword())
128 setPrompt(i18n("The requested action requires administrator privileges.\n"
129 "If you have these privileges then please enter your password."));
131 setPrompt(i18n("The requested action requires administrator privileges.\n"
132 "Please enter the system administrator's password."));
134 setPixmap(DesktopIcon("dialog-password"));
139 switch (itsSuProc
.checkInstall(password().toLocal8Bit()))
142 showErrorMessage(itsSuProc
.useUsersOwnPassword()
143 ? i18n("Insufficient privileges.")
144 : i18n("Conversation with su failed."), UsernameError
);
148 case SuProcess::SuNotFound
:
149 showErrorMessage(i18n("<p>Could not launch '%1'.</p>"
150 "<p>Make sure your PATH is set correctly.</p>",
151 itsSuProc
.useUsersOwnPassword() ? "sudo" : "su"), FatalError
);
153 case SuProcess::SuNotAllowed
:
154 showErrorMessage(i18n("Insufficient privileges."), FatalError
);
156 case SuProcess::SuIncorrectPassword
:
157 showErrorMessage(i18n("Incorrect password, please try again."), PasswordError
);
160 showErrorMessage(i18n("Internal error: illegal return from "
161 "SuProcess::checkInstall()"), FatalError
);
170 CJobRunner::CJobRunner(QWidget
*parent
, int xid
)
171 : CActionDialog(parent
),
172 itsIt(itsUrls
.end()),
175 itsCancelClicked(false),
178 #if !(defined USE_POLICYKIT && USE_POLICYKIT==1)
179 // Set core dump size to 0 because we will have root's password in memory.
181 rlim
.rlim_cur
=rlim
.rlim_max
=0;
182 setrlimit(RLIMIT_CORE
, &rlim
);
186 setButtons(KDialog::Cancel
);
187 if(NULL
==parent
&& 0!=xid
)
188 XSetTransientForHint(QX11Info::display(), winId(), xid
);
190 QFrame
*page
= new QFrame(this);
193 QGridLayout
*layout
=new QGridLayout(page
);
194 layout
->setMargin(KDialog::marginHint());
195 layout
->setSpacing(KDialog::spacingHint());
196 itsStatusLabel
=new QLabel(page
);
197 itsProgress
=new QProgressBar(page
);
198 layout
->addWidget(itsPixmapLabel
, 0, 0, 2, 1);
199 layout
->addWidget(itsStatusLabel
, 0, 1);
200 layout
->addWidget(itsProgress
, 1, 1);
203 CJobRunner::~CJobRunner()
207 bool CJobRunner::getAdminPasswd(QWidget
*parent
)
211 #if defined USE_POLICYKIT && USE_POLICYKIT==1
212 if(!PolicyKitAuthenticator::instance()->authenticate(KFI_IFACE
, parent
, true))
214 KMessageBox::error(parent
, i18n("Authentication failed."));
218 // Prompt the user for a password, if we do not already have it...
219 if(itsPasswd
.isEmpty() || 0!=SuProcess(KFI_SYS_USER
).checkInstall(itsPasswd
.toLocal8Bit()))
221 CPasswordDialog
dlg(parent
);
225 itsPasswd
=dlg
.password().toLocal8Bit();
233 void CJobRunner::getAssociatedUrls(const KUrl
&url
, KUrl::List
&list
, bool afmAndPfm
, QWidget
*widget
)
235 QString
ext(url
.path());
236 int dotPos(ext
.lastIndexOf('.'));
239 if(-1==dotPos
) // Hmm, no extension - check anyway...
241 else // Cool, got an extension - see if its a Type1 font...
243 ext
=ext
.mid(dotPos
+1);
244 check
=0==ext
.compare("pfa", Qt::CaseInsensitive
) ||
245 0==ext
.compare("pfb", Qt::CaseInsensitive
);
250 const char *afm
[]={"afm", "AFM", "Afm", NULL
},
251 *pfm
[]={"pfm", "PFM", "Pfm", NULL
};
253 localFile(url
.isLocalFile());
256 for(e
=0; afm
[e
]; ++e
)
261 statUrl
.setPath(Misc::changeExt(url
.path(), afm
[e
]));
263 if(localFile
? Misc::fExists(statUrl
.path()) : KIO::NetAccess::stat(statUrl
, uds
, widget
))
265 list
.append(statUrl
);
271 if(afmAndPfm
|| !gotAfm
)
272 for(e
=0; pfm
[e
]; ++e
)
276 statUrl
.setPath(Misc::changeExt(url
.path(), pfm
[e
]));
277 if(localFile
? Misc::fExists(statUrl
.path()) : KIO::NetAccess::stat(statUrl
, uds
, widget
))
279 list
.append(statUrl
);
286 int CJobRunner::exec(ECommand cmd
, const ItemList
&urls
, const KUrl
&dest
)
291 setCaption(i18n("Installing"));
294 setCaption(i18n("Uninstalling"));
297 setCaption(i18n("Enabling"));
300 setCaption(i18n("Copying"));
303 setCaption(i18n("Moving"));
306 setCaption(i18n("Updating"));
310 setCaption(i18n("Disabling"));
316 qSort(itsUrls
.begin(), itsUrls
.end()); // Sort list of fonts so that we have type1 fonts followed by their metrics...
317 itsIt
=itsUrls
.constBegin();
318 itsEnd
=itsUrls
.constEnd();
319 itsProgress
->setValue(0);
320 itsProgress
->setRange(0, itsUrls
.count()+1);
323 itsStatusLabel
->setText(QString());
324 itsAutoSkip
=itsCancelClicked
=itsModified
=false;
325 QTimer::singleShot(0, this, SLOT(doNext()));
326 return CActionDialog::exec();
329 void CJobRunner::doNext()
331 if(itsIt
==itsEnd
|| CMD_UPDATE
==itsCmd
)
333 if(itsModified
|| CMD_UPDATE
==itsCmd
)
336 // Installation, deletion, enabling, disabling, completed - so now reconfigure...
337 QByteArray packedArgs
;
338 QDataStream
stream(&packedArgs
, QIODevice::WriteOnly
);
340 itsStatusLabel
->setText(i18n("Updating font configuration. Please wait..."));
342 stream
<< KFI::SPECIAL_CONFIGURE
;
344 if(CMD_UPDATE
==itsCmd
)
347 for(; itsIt
!=itsEnd
; ++itsIt
)
351 itsProgress
->setValue(itsProgress
->maximum());
354 itsIt
=itsEnd
=itsUrls
.constEnd();
356 KIO::Job
*job
=KIO::special(KUrl(KFI_KIO_FONTS_PROTOCOL
":/"), packedArgs
, KIO::HideProgressInfo
);
358 connect(job
, SIGNAL(result(KJob
*)), SLOT(cfgResult(KJob
*)));
359 job
->ui()->setWindow(this);
360 job
->ui()->setAutoErrorHandlingEnabled(false);
361 job
->ui()->setAutoWarningHandlingEnabled(false);
377 dest
.setFileName(Misc::getFile((*itsIt
).path()));
378 itsStatusLabel
->setText(CMD_INSTALL
==itsCmd
? i18n("Installing %1", (*itsIt
).displayName())
379 : i18n("Copying %1", (*itsIt
).displayName()) );
380 job
=KIO::file_copy(*itsIt
, modifyUrl(dest
), -1, KIO::HideProgressInfo
);
384 itsStatusLabel
->setText(i18n("Deleting %1", (*itsIt
).displayName()));
385 job
=KIO::file_delete(modifyUrl(*itsIt
), KIO::HideProgressInfo
);
388 itsStatusLabel
->setText(i18n("Enabling %1", (*itsIt
).displayName()));
389 job
=KIO::rename(*itsIt
, modifyUrl(toggle(*itsIt
, true)), KIO::HideProgressInfo
);
392 itsStatusLabel
->setText(i18n("Disabling %1", (*itsIt
).displayName()));
393 job
=KIO::rename(*itsIt
, modifyUrl(toggle(*itsIt
, false)), KIO::HideProgressInfo
);
399 dest
.setFileName(Misc::getFile((*itsIt
).path()));
400 itsStatusLabel
->setText(i18n("Moving %1", (*itsIt
).displayName()));
401 job
=KIO::file_move(modifyUrl(*itsIt
), modifyUrl(dest
), -1, KIO::HideProgressInfo
);
407 itsProgress
->setValue(itsProgress
->value()+1);
408 job
->setUiDelegate(0L); // Remove the ui-delegate, so that we can handle all error messages...
410 connect(job
, SIGNAL(result(KJob
*)), SLOT(jobResult(KJob
*)));
414 void CJobRunner::jobResult(KJob
*job
)
421 if(KMessageBox::Yes
==KMessageBox::warningYesNo(this, i18n("Are you sure you wish to cancel?")))
423 itsCancelClicked
=false;
427 // itsIt will equal itsEnd if user decided to cancel the current op
430 else if (!job
->error())
438 bool cont(itsAutoSkip
&& itsUrls
.count()>1);
444 ItemList::ConstIterator
next(itsIt
==itsEnd
? itsEnd
: itsIt
+1);
446 if(1==itsUrls
.count() || next
==itsEnd
)
448 if(!itsAutoSkip
&& !job
->errorString().isEmpty())
449 KMessageBox::error(this, job
->errorString());
453 CSkipDialog
dlg(this, true, job
->errorString());
457 case CSkipDialog::SKIP
:
460 case CSkipDialog::AUTO_SKIP
:
461 cont
=itsAutoSkip
=true;
463 case CSkipDialog::CANCEL
:
472 if(CMD_INSTALL
==itsCmd
&& Item::TYPE1_FONT
==(*itsIt
).type
) // Did we error on a pfa/pfb? if so, exclude the afm/pfm...
474 QString
oldFile((*itsIt
).fileName
);
478 if(itsIt
!=itsEnd
&& (*itsIt
).fileName
==oldFile
&& Item::TYPE1_METRICS
==(*itsIt
).type
)
481 if(itsIt
!=itsEnd
&& (*itsIt
).fileName
==oldFile
&& Item::TYPE1_METRICS
==(*itsIt
).type
)
490 itsIt
=itsEnd
=itsUrls
.constEnd();
496 void CJobRunner::cfgResult(KJob
*job
)
500 // KIO::file_xxxx() do not seem to emit kdirnotify signals, so do this now...
501 if(itsModified
&& (CMD_COPY
==itsCmd
|| CMD_INSTALL
==itsCmd
))
502 org::kde::KDirNotify::emitFilesAdded(itsDest
.url());
504 if(job
&& 0==job
->error())
507 KMessageBox::information(parentWidget(),
508 i18n("<p>Please note that any open applications will need to be "
509 "restarted in order for any changes to be noticed.</p>"),
510 i18n("Success"), "FontChangesAndOpenApps");
517 void CJobRunner::slotButtonClicked(int)
520 itsCancelClicked
=true;
524 // Tell the io-slave not to clear, and re-read, the list of fonts. And also, tell it to not
525 // automatically recreate the config files - we want that to happen after all fonts are installed,
527 void CJobRunner::setMetaData(KIO::Job
*job
) const
529 job
->addMetaData(KFI_KIO_TIMEOUT
, "0");
530 job
->addMetaData(KFI_KIO_NO_CLEAR
, "1");
533 KUrl
CJobRunner::modifyUrl(const KUrl
&orig
) const
537 #if defined USE_POLICYKIT && USE_POLICYKIT==1
540 url
.setUser(KFI_SYS_USER
);
541 url
.setPass(QString().setNum(getpid()));
544 if(!Misc::root() && !itsPasswd
.isEmpty())
546 url
.setUser(KFI_SYS_USER
);
547 url
.setPass(itsPasswd
);
555 CJobRunner::Item::Item(const KUrl
&u
, const QString
&n
)
556 : KUrl(u
), name(n
), fileName(Misc::getFile(u
.path()))
558 type
=Misc::checkExt(fileName
, "pfa") || Misc::checkExt(fileName
, "pfb")
560 : Misc::checkExt(fileName
, "afm") || Misc::checkExt(fileName
, "pfm")
566 int pos(fileName
.lastIndexOf('.'));
569 fileName
=fileName
.left(pos
);
573 bool CJobRunner::Item::operator<(const Item
&o
) const
575 // Do not care about the order of non type1 fonts/metrics...
579 int nameComp(fileName
.compare(o
.fileName
));
581 return nameComp
<0 || (0==nameComp
&& type
<o
.type
);
586 #include "JobRunner.moc"