not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kcontrol / kfontinst / kcmfontinst / JobRunner.cpp
blob13ab476f8837bbecef7d72e16d82981aeb56fa8b
1 /*
2 * KFontInst - KDE Font Installer
4 * Copyright 2003-2007 Craig Drummond <craig@kde.org>
6 * ----
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"
26 #include "Misc.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>
43 #include <X11/Xlib.h>
44 #include <fixx11h.h>
45 #include <sys/resource.h>
46 #if defined USE_POLICYKIT && USE_POLICYKIT==1
47 #include "PolicyKitAuthenticator.h"
48 #endif
50 using namespace KDESu;
52 namespace KFI
55 static KUrl toggle(const KUrl &orig, bool enable)
57 KUrl url(orig);
59 url.setFileName(enable
60 ? Misc::getFile(orig.path()).mid(1)
61 : QChar('.')+Misc::getFile(orig.path()));
62 return url;
65 class CSkipDialog : public KDialog
67 public:
69 enum Result
71 SKIP,
72 AUTO_SKIP,
73 CANCEL
76 CSkipDialog(QWidget *parent, bool multi, const QString &errorText)
77 : KDialog(parent),
78 itsResult(CANCEL)
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));
85 resize(sizeHint());
88 Result go()
90 itsResult=CANCEL;
91 exec();
92 return itsResult;
95 void slotButtonClicked(int button)
97 switch(button)
99 case User1:
100 itsResult=SKIP;
101 break;
102 case User2:
103 itsResult=AUTO_SKIP;
104 break;
105 default:
106 itsResult=CANCEL;
109 KDialog::accept();
112 private:
114 Result itsResult;
117 class CPasswordDialog : public KPasswordDialog
119 public:
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."));
130 else
131 setPrompt(i18n("The requested action requires administrator privileges.\n"
132 "Please enter the system administrator's password."));
134 setPixmap(DesktopIcon("dialog-password"));
137 bool checkPassword()
139 switch (itsSuProc.checkInstall(password().toLocal8Bit()))
141 case -1:
142 showErrorMessage(itsSuProc.useUsersOwnPassword()
143 ? i18n("Insufficient privileges.")
144 : i18n("Conversation with su failed."), UsernameError);
145 return false;
146 case 0:
147 return true;
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);
152 return false;
153 case SuProcess::SuNotAllowed:
154 showErrorMessage(i18n("Insufficient privileges."), FatalError);
155 return false;
156 case SuProcess::SuIncorrectPassword:
157 showErrorMessage(i18n("Incorrect password, please try again."), PasswordError);
158 return false;
159 default:
160 showErrorMessage(i18n("Internal error: illegal return from "
161 "SuProcess::checkInstall()"), FatalError);
162 done(Rejected);
163 return false;
167 SuProcess itsSuProc;
170 CJobRunner::CJobRunner(QWidget *parent, int xid)
171 : CActionDialog(parent),
172 itsIt(itsUrls.end()),
173 itsEnd(itsIt),
174 itsAutoSkip(false),
175 itsCancelClicked(false),
176 itsModified(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.
180 struct rlimit rlim;
181 rlim.rlim_cur=rlim.rlim_max=0;
182 setrlimit(RLIMIT_CORE, &rlim);
183 #endif
185 setModal(true);
186 setButtons(KDialog::Cancel);
187 if(NULL==parent && 0!=xid)
188 XSetTransientForHint(QX11Info::display(), winId(), xid);
190 QFrame *page = new QFrame(this);
191 setMainWidget(page);
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)
209 if(!Misc::root())
211 #if defined USE_POLICYKIT && USE_POLICYKIT==1
212 if(!PolicyKitAuthenticator::instance()->authenticate(KFI_IFACE, parent, true))
214 KMessageBox::error(parent, i18n("Authentication failed."));
215 return false;
217 #else
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);
223 if(!dlg.exec())
224 return false;
225 itsPasswd=dlg.password().toLocal8Bit();
227 #endif
230 return true;
233 void CJobRunner::getAssociatedUrls(const KUrl &url, KUrl::List &list, bool afmAndPfm, QWidget *widget)
235 QString ext(url.path());
236 int dotPos(ext.lastIndexOf('.'));
237 bool check(false);
239 if(-1==dotPos) // Hmm, no extension - check anyway...
240 check=true;
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);
248 if(check)
250 const char *afm[]={"afm", "AFM", "Afm", NULL},
251 *pfm[]={"pfm", "PFM", "Pfm", NULL};
252 bool gotAfm(false),
253 localFile(url.isLocalFile());
254 int e;
256 for(e=0; afm[e]; ++e)
258 KUrl statUrl(url);
259 KIO::UDSEntry uds;
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);
266 gotAfm=true;
267 break;
271 if(afmAndPfm || !gotAfm)
272 for(e=0; pfm[e]; ++e)
274 KUrl statUrl(url);
275 KIO::UDSEntry uds;
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);
280 break;
286 int CJobRunner::exec(ECommand cmd, const ItemList &urls, const KUrl &dest)
288 switch(cmd)
290 case CMD_INSTALL:
291 setCaption(i18n("Installing"));
292 break;
293 case CMD_DELETE:
294 setCaption(i18n("Uninstalling"));
295 break;
296 case CMD_ENABLE:
297 setCaption(i18n("Enabling"));
298 break;
299 case CMD_COPY:
300 setCaption(i18n("Copying"));
301 break;
302 case CMD_MOVE:
303 setCaption(i18n("Moving"));
304 break;
305 case CMD_UPDATE:
306 setCaption(i18n("Updating"));
307 break;
308 default:
309 case CMD_DISABLE:
310 setCaption(i18n("Disabling"));
313 itsDest=dest;
314 itsUrls=urls;
315 if(CMD_INSTALL==cmd)
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);
321 itsProgress->show();
322 itsCmd=cmd;
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)
346 itsProgress->hide();
347 for(; itsIt!=itsEnd; ++itsIt)
348 stream << (*itsIt);
350 else
351 itsProgress->setValue(itsProgress->maximum());
353 itsUrls.empty();
354 itsIt=itsEnd=itsUrls.constEnd();
356 KIO::Job *job=KIO::special(KUrl(KFI_KIO_FONTS_PROTOCOL":/"), packedArgs, KIO::HideProgressInfo);
357 setMetaData(job);
358 connect(job, SIGNAL(result(KJob *)), SLOT(cfgResult(KJob *)));
359 job->ui()->setWindow(this);
360 job->ui()->setAutoErrorHandlingEnabled(false);
361 job->ui()->setAutoWarningHandlingEnabled(false);
363 else
364 cfgResult(0L);
366 else
368 KIO::Job *job(NULL);
370 switch(itsCmd)
372 case CMD_COPY:
373 case CMD_INSTALL:
375 KUrl dest(itsDest);
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);
381 break;
383 case CMD_DELETE:
384 itsStatusLabel->setText(i18n("Deleting %1", (*itsIt).displayName()));
385 job=KIO::file_delete(modifyUrl(*itsIt), KIO::HideProgressInfo);
386 break;
387 case CMD_ENABLE:
388 itsStatusLabel->setText(i18n("Enabling %1", (*itsIt).displayName()));
389 job=KIO::rename(*itsIt, modifyUrl(toggle(*itsIt, true)), KIO::HideProgressInfo);
390 break;
391 case CMD_DISABLE:
392 itsStatusLabel->setText(i18n("Disabling %1", (*itsIt).displayName()));
393 job=KIO::rename(*itsIt, modifyUrl(toggle(*itsIt, false)), KIO::HideProgressInfo);
394 break;
395 case CMD_MOVE:
397 KUrl dest(itsDest);
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);
402 break;
404 default:
405 break;
407 itsProgress->setValue(itsProgress->value()+1);
408 job->setUiDelegate(0L); // Remove the ui-delegate, so that we can handle all error messages...
409 setMetaData(job);
410 connect(job, SIGNAL(result(KJob *)), SLOT(jobResult(KJob *)));
414 void CJobRunner::jobResult(KJob *job)
416 Q_ASSERT(job);
418 if(itsCancelClicked)
420 stopAnimation();
421 if(KMessageBox::Yes==KMessageBox::warningYesNo(this, i18n("Are you sure you wish to cancel?")))
422 itsIt=itsEnd;
423 itsCancelClicked=false;
424 startAnimation();
427 // itsIt will equal itsEnd if user decided to cancel the current op
428 if(itsIt==itsEnd)
429 doNext();
430 else if (!job->error())
432 itsModified=true;
433 ++itsIt;
434 doNext();
436 else
438 bool cont(itsAutoSkip && itsUrls.count()>1);
440 if(!cont)
442 stopAnimation();
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());
451 else
453 CSkipDialog dlg(this, true, job->errorString());
455 switch(dlg.go())
457 case CSkipDialog::SKIP:
458 cont=true;
459 break;
460 case CSkipDialog::AUTO_SKIP:
461 cont=itsAutoSkip=true;
462 break;
463 case CSkipDialog::CANCEL:
464 break;
469 startAnimation();
470 if(cont)
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);
475 ++itsIt;
477 // Skip afm/pfm
478 if(itsIt!=itsEnd && (*itsIt).fileName==oldFile && Item::TYPE1_METRICS==(*itsIt).type)
479 ++itsIt;
480 // Skip pfm/afm
481 if(itsIt!=itsEnd && (*itsIt).fileName==oldFile && Item::TYPE1_METRICS==(*itsIt).type)
482 ++itsIt;
484 else
485 ++itsIt;
487 else
489 itsUrls.empty();
490 itsIt=itsEnd=itsUrls.constEnd();
492 doNext();
496 void CJobRunner::cfgResult(KJob *job)
498 stopAnimation();
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())
506 hide();
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");
511 accept();
513 else
514 reject();
517 void CJobRunner::slotButtonClicked(int)
519 if(itsIt!=itsEnd)
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,
526 // deleted, etc.
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
535 KUrl url(orig);
537 #if defined USE_POLICYKIT && USE_POLICYKIT==1
538 if(!Misc::root())
540 url.setUser(KFI_SYS_USER);
541 url.setPass(QString().setNum(getpid()));
543 #else
544 if(!Misc::root() && !itsPasswd.isEmpty())
546 url.setUser(KFI_SYS_USER);
547 url.setPass(itsPasswd);
549 #endif
551 return url;
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")
559 ? TYPE1_FONT
560 : Misc::checkExt(fileName, "afm") || Misc::checkExt(fileName, "pfm")
561 ? TYPE1_METRICS
562 : OTHER_FONT;
564 if(OTHER_FONT!=type)
566 int pos(fileName.lastIndexOf('.'));
568 if(-1!=pos)
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...
576 if(OTHER_FONT==type)
577 return true;
579 int nameComp(fileName.compare(o.fileName));
581 return nameComp<0 || (0==nameComp && type<o.type);
586 #include "JobRunner.moc"