mirror of
https://github.com/loimu/zeit.git
synced 2025-09-15 12:58:44 +08:00
447 lines
15 KiB
C++
447 lines
15 KiB
C++
/***************************************************************************
|
|
* CT Task Implementation *
|
|
* -------------------------------------------------------------------- *
|
|
* Copyright (C) 1999, Gary Meyer <gary@meyer.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. *
|
|
***************************************************************************/
|
|
|
|
#include "cttask.h"
|
|
|
|
#include <QRegularExpression>
|
|
#include <QMimeDatabase>
|
|
#include <QUrl>
|
|
|
|
#include "ctHelper.h"
|
|
|
|
#include "logging.h"
|
|
|
|
CTTask::CTTask(const QString& tokenString, const QString& _comment,
|
|
const QString& _userLogin, bool _systemCrontab) :
|
|
systemCrontab(_systemCrontab) {
|
|
|
|
QString tokStr = tokenString;
|
|
if (tokStr.mid(0, 2) == QLatin1String( "#\\" )) {
|
|
tokStr = tokStr.mid(2, tokStr.length() - 2);
|
|
enabled = false;
|
|
} else if (tokStr.mid(0, 1) == QChar::fromLatin1('#')) {
|
|
tokStr = tokStr.mid(1, tokStr.length() - 1);
|
|
enabled = false;
|
|
} else
|
|
enabled = true;
|
|
|
|
// Skip over 'silence' if found... old option in vixie cron
|
|
if (tokStr.mid(0, 1) == QChar::fromLatin1('-'))
|
|
tokStr = tokStr.mid(1, tokStr.length() - 1);
|
|
|
|
reboot = false;
|
|
if (tokStr.mid(0, 1) == QChar::fromLatin1('@')) {
|
|
if (tokStr.mid(1, 6) == QLatin1String( "yearly" )) {
|
|
tokStr = QLatin1String( "0 0 1 1 *" )+tokStr.mid(7, tokStr.length()-1);
|
|
} else if (tokStr.mid(1, 8) == QLatin1String( "annually" )) {
|
|
tokStr = QLatin1String( "0 0 1 1 *" )+tokStr.mid(9, tokStr.length()-1);
|
|
} else if (tokStr.mid(1, 7) == QLatin1String( "monthly" )) {
|
|
tokStr = QLatin1String( "0 0 1 * *" )+tokStr.mid(8, tokStr.length()-1);
|
|
} else if (tokStr.mid(1, 6) == QLatin1String( "weekly" )) {
|
|
tokStr = QLatin1String( "0 0 * * 0" )+tokStr.mid(7, tokStr.length()-1);
|
|
} else if (tokStr.mid(1, 5) == QLatin1String( "daily" )) {
|
|
tokStr = QLatin1String( "0 0 * * *" )+tokStr.mid(6, tokStr.length()-1);
|
|
} else if (tokStr.mid(1, 6) == QLatin1String( "hourly" )) {
|
|
tokStr = QLatin1String( "0 * * * *" )+tokStr.mid(7, tokStr.length()-1);
|
|
} else if (tokStr.mid(1, 6) == QLatin1String( "reboot" )) {
|
|
tokStr = tokStr.mid(7, tokStr.length()-1);
|
|
reboot = true;
|
|
}
|
|
}
|
|
|
|
static const QRegularExpression spacePosRx(QStringLiteral("[ \t]"));
|
|
int spacePos(tokStr.indexOf(spacePosRx));
|
|
// If reboot bypass initialize functions so no keys selected in modify task
|
|
if (reboot == false) {
|
|
|
|
//logDebug() << "Line : " << tokStr << endl;
|
|
minute.initialize(tokStr.mid(0, spacePos));
|
|
|
|
while (isSpace(tokStr, spacePos+1))
|
|
spacePos++;
|
|
tokStr = tokStr.mid(spacePos+1, tokStr.length()-1);
|
|
spacePos = tokStr.indexOf(spacePosRx);
|
|
hour.initialize(tokStr.mid(0, spacePos));
|
|
|
|
while (isSpace(tokStr, spacePos+1))
|
|
spacePos++;
|
|
tokStr = tokStr.mid(spacePos+1, tokStr.length()-1);
|
|
spacePos = tokStr.indexOf(spacePosRx);
|
|
dayOfMonth.initialize(tokStr.mid(0, spacePos));
|
|
|
|
while (isSpace(tokStr, spacePos+1))
|
|
spacePos++;
|
|
tokStr = tokStr.mid(spacePos+1, tokStr.length()-1);
|
|
spacePos = tokStr.indexOf(spacePosRx);
|
|
month.initialize(tokStr.mid(0, spacePos));
|
|
|
|
while (isSpace(tokStr, spacePos+1))
|
|
spacePos++;
|
|
tokStr = tokStr.mid(spacePos+1, tokStr.length()-1);
|
|
spacePos = tokStr.indexOf(spacePosRx);
|
|
dayOfWeek.initialize(tokStr.mid(0, spacePos));
|
|
}
|
|
|
|
if (systemCrontab) {
|
|
while (isSpace(tokStr, spacePos+1))
|
|
spacePos++;
|
|
tokStr = tokStr.mid(spacePos+1, tokStr.length()-1);
|
|
spacePos = tokStr.indexOf(spacePosRx);
|
|
userLogin = tokStr.mid(0, spacePos);
|
|
}
|
|
else {
|
|
userLogin = _userLogin;
|
|
}
|
|
|
|
command = tokStr.mid(spacePos+1, tokStr.length()-1);
|
|
// remove leading whitespace
|
|
while (command.indexOf(spacePosRx) == 0)
|
|
command = command.mid(1, command.length()-1);
|
|
comment = _comment;
|
|
|
|
initialUserLogin = userLogin;
|
|
initialCommand = command;
|
|
initialComment = comment;
|
|
initialEnabled = enabled;
|
|
initialReboot = reboot;
|
|
}
|
|
|
|
CTTask::CTTask(const CTTask &source) :
|
|
month(source.month), dayOfMonth(source.dayOfMonth),
|
|
dayOfWeek(source.dayOfWeek), hour(source.hour), minute(source.minute),
|
|
userLogin(source.userLogin),command(source.command),comment(source.comment),
|
|
enabled(source.enabled), reboot(source.reboot)
|
|
{
|
|
|
|
}
|
|
|
|
CTTask& CTTask::operator=(const CTTask& source) {
|
|
if (this == &source)
|
|
return *this;
|
|
|
|
month = source.month;
|
|
dayOfMonth = source.dayOfMonth;
|
|
dayOfWeek = source.dayOfWeek;
|
|
hour = source.hour;
|
|
minute = source.minute;
|
|
userLogin = source.userLogin;
|
|
command = source.command;
|
|
comment = source.comment;
|
|
enabled = source.enabled;
|
|
reboot = source.reboot;
|
|
initialUserLogin = QString();
|
|
initialCommand = QString();
|
|
initialComment = QString();
|
|
initialEnabled = true;
|
|
initialReboot = false;
|
|
|
|
return *this;
|
|
}
|
|
|
|
QString CTTask::exportTask() {
|
|
QString exportTask;
|
|
exportTask += CTHelper::exportComment(comment);
|
|
if (enabled == false)
|
|
exportTask += QLatin1String( "#\\" );
|
|
exportTask += schedulingCronFormat();
|
|
exportTask += QChar::fromLatin1('\t');
|
|
if (isSystemCrontab() == true)
|
|
exportTask += userLogin + QChar::fromLatin1('\t');
|
|
exportTask += command + QChar::fromLatin1('\n');
|
|
return exportTask;
|
|
}
|
|
|
|
void CTTask::apply() {
|
|
month.apply();
|
|
dayOfMonth.apply();
|
|
dayOfWeek.apply();
|
|
hour.apply();
|
|
minute.apply();
|
|
|
|
initialUserLogin = userLogin;
|
|
initialCommand = command;
|
|
initialComment = comment;
|
|
initialEnabled = enabled;
|
|
initialReboot = reboot;
|
|
}
|
|
|
|
void CTTask::cancel() {
|
|
month.cancel();
|
|
dayOfMonth.cancel();
|
|
dayOfWeek.cancel();
|
|
hour.cancel();
|
|
minute.cancel();
|
|
|
|
userLogin = initialUserLogin;
|
|
command = initialCommand;
|
|
comment = initialComment;
|
|
enabled = initialEnabled;
|
|
reboot = initialReboot;
|
|
}
|
|
|
|
bool CTTask::dirty() const {
|
|
return (month.isDirty() || dayOfMonth.isDirty() || dayOfWeek.isDirty() || hour.isDirty() || minute.isDirty() || (userLogin != initialUserLogin) || (command != initialCommand) || (comment != initialComment) || (enabled != initialEnabled) || (reboot != initialReboot));
|
|
}
|
|
|
|
QString CTTask::schedulingCronFormat() const {
|
|
if (reboot)
|
|
return QStringLiteral("@reboot");
|
|
QString scheduling;
|
|
scheduling += minute.exportUnit();
|
|
scheduling += QChar(0x20) + hour.exportUnit();
|
|
scheduling += QChar(0x20) + dayOfMonth.exportUnit();
|
|
scheduling += QChar(0x20) + month.exportUnit();
|
|
scheduling += QChar(0x20) + dayOfWeek.exportUnit();
|
|
return scheduling;
|
|
}
|
|
|
|
/**
|
|
* Of the whole program, this method is probably the trickiest.
|
|
*
|
|
* This method creates the natural language description, such as
|
|
* "At 1:00am, every Sun".
|
|
*
|
|
* First, I declare some strings for holding what can be internationalized.
|
|
* Note the tokens such as "MONTHS". Translators should reuse these
|
|
* tokens in their translations. See README.translators for more
|
|
* information.
|
|
*
|
|
* Second, I get the descriptions from the component parts such as
|
|
* days of the month.
|
|
*
|
|
* Third, I get hour/minute time combinations. Although a little bit
|
|
* awkward, I use the tm struct and strftime from <time.h>. This
|
|
* way this code is portable across all Unixes.
|
|
*
|
|
* Fourth, I know that "every day of the week" and "every day of the
|
|
* month" simply makes "every day".
|
|
*
|
|
* Fifth and finally I do tag substitution to create the natural language
|
|
* description.
|
|
*
|
|
*/
|
|
QString CTTask::describe() const {
|
|
|
|
if (reboot) {
|
|
return tr("at system startup");
|
|
}
|
|
|
|
QString dateFormat = createDateFormat();
|
|
|
|
QString timeFormat = createTimeFormat();
|
|
|
|
return tr("%1, %2", "1:Time Description, 2:Date Description").arg(
|
|
timeFormat, dateFormat);
|
|
}
|
|
|
|
QString CTTask::describeDayOfWeek() const {
|
|
return tr("every %1", "Every 'days of week'").arg(dayOfWeek.describe());
|
|
}
|
|
|
|
QString CTTask::describeDayOfMonth() const {
|
|
return tr("%1 of %2", "'Days of month' of 'Months'").arg(
|
|
dayOfMonth.describe(), month.describe());
|
|
}
|
|
|
|
QString CTTask::createDateFormat() const {
|
|
/*
|
|
* "* * *" means truly every day.
|
|
* Note: Languages may use different phrases to indicate
|
|
* every day of month versus every day of week.
|
|
*/
|
|
QString dateFormat;
|
|
if ((dayOfMonth.enabledCount() == CTDayOfMonth::MAXIMUM)
|
|
&& (dayOfWeek.enabledCount() == CTDayOfWeek::MAXIMUM)) {
|
|
dateFormat = tr("every day ");
|
|
}
|
|
// Day of month not specified, so use day of week.
|
|
else if (dayOfMonth.enabledCount() == CTDayOfMonth::MAXIMUM) {
|
|
dateFormat = describeDayOfWeek();
|
|
}
|
|
// Day of week not specified, so use day of month.
|
|
else if (dayOfWeek.enabledCount() == CTDayOfWeek::MAXIMUM) {
|
|
dateFormat = describeDayOfMonth();
|
|
}
|
|
else {
|
|
dateFormat = tr("%1 as well as %2",
|
|
"1:Day of month, 2:Day of week").arg(
|
|
describeDayOfMonth(), describeDayOfWeek());
|
|
}
|
|
|
|
return dateFormat;
|
|
|
|
}
|
|
|
|
QString CTTask::describeDateAndHours() const {
|
|
// Create time description.
|
|
int total = minute.enabledCount() * hour.enabledCount();
|
|
|
|
QString timeDesc;
|
|
int count = 0;
|
|
|
|
for (int h = 0; h <= 23; h++) {
|
|
if (hour.isEnabled(h)) {
|
|
for (int m = 0; m <= 59; m++) {
|
|
if (minute.isEnabled(m) == true) {
|
|
QString hourString;
|
|
if (h<10)
|
|
hourString = QChar::fromLatin1('0') + QString::number(h);
|
|
else
|
|
hourString = QString::number(h);
|
|
|
|
QString minuteString;
|
|
if (m<10)
|
|
minuteString = QChar::fromLatin1('0') + QString::number(m);
|
|
else
|
|
minuteString = QString::number(m);
|
|
|
|
QString tmpStr = tr("%1:%2", "1:Hour, 2:Minute").arg(
|
|
hourString, minuteString);
|
|
|
|
timeDesc += tmpStr;
|
|
count++;
|
|
switch (total - count) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
if (total > 2)
|
|
timeDesc += tr(", and ");
|
|
else
|
|
timeDesc += tr(" and ");
|
|
break;
|
|
default:
|
|
timeDesc += tr(", ");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return tr("at %1", "Hour::Minute list").arg(timeDesc);
|
|
}
|
|
|
|
QString CTTask::createTimeFormat() const {
|
|
if (hour.isAllEnabled()) {
|
|
int minutePeriod = minute.findPeriod();
|
|
if (minutePeriod != 0)
|
|
return tr("Every minute", "Every minute");
|
|
}
|
|
|
|
return describeDateAndHours();
|
|
}
|
|
|
|
bool CTTask::isSystemCrontab() const {
|
|
return systemCrontab;
|
|
}
|
|
|
|
void CTTask::setSystemCrontab(bool _systemCrontab) {
|
|
systemCrontab = _systemCrontab;
|
|
}
|
|
|
|
|
|
QIcon CTTask::commandIcon() const {
|
|
QUrl commandPath = QUrl::fromLocalFile(completeCommandPath());
|
|
|
|
QMimeType mimeType = QMimeDatabase().mimeTypeForUrl(commandPath);
|
|
//logDebug() << mimeType->name() << endl;
|
|
if (mimeType.name() == QLatin1String( "application/x-executable" )
|
|
|| mimeType.name() == QLatin1String( "application/octet-stream" )) {
|
|
|
|
//The next line is identical as SmallIcon(commandPath.fileName()), but is able to return a isNull() QPixmap
|
|
//QPixmap pixmap = KIconLoader::global()->loadIcon(commandPath.fileName(), KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), 0L, true);
|
|
//if (pixmap.isNull()) {
|
|
return QIcon::fromTheme(QLatin1String("system-run"));
|
|
//}
|
|
|
|
//return QIcon(pixmap);
|
|
}
|
|
|
|
return QIcon::fromTheme(mimeType.iconName());
|
|
|
|
}
|
|
|
|
QPair<QString, bool> CTTask::unQuoteCommand() const {
|
|
QString fullCommand = command;
|
|
fullCommand = fullCommand.trimmed();
|
|
|
|
const QVector<QChar> quotes {
|
|
QChar::fromLatin1('"'), QChar::fromLatin1('\'') };
|
|
for(const QChar "e: quotes) {
|
|
if (fullCommand.indexOf(quote) == 0) {
|
|
int nextQuote = fullCommand.indexOf(quote, 1);
|
|
if (nextQuote == -1)
|
|
return QPair<QString, bool>(QString(), false);
|
|
|
|
return QPair<QString, bool>(fullCommand.mid(1, nextQuote-1), true);
|
|
}
|
|
|
|
}
|
|
|
|
return QPair<QString, bool>(fullCommand, false);
|
|
|
|
}
|
|
|
|
QString CTTask::decryptBinaryCommand(const QString& command) const {
|
|
QString fullCommand;
|
|
bool found = false;
|
|
for(int i=0; i < command.length(); ++i) {
|
|
if(command.at(i) == QChar(0x20)
|
|
&& command.at(i-1) != QChar::fromLatin1('\\')) {
|
|
fullCommand = command.left(i);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found == false)
|
|
fullCommand = command;
|
|
fullCommand = fullCommand.remove(QChar::fromLatin1('\\'));
|
|
return fullCommand;
|
|
}
|
|
|
|
QStringList CTTask::separatePathCommand(const QString& command, bool quoted) const {
|
|
QStringList pathCommand;
|
|
if (command.at(0) == QChar::fromLatin1('/')) {
|
|
QString fullCommand;
|
|
if (quoted == true)
|
|
fullCommand = command;
|
|
else
|
|
fullCommand = decryptBinaryCommand(command);
|
|
|
|
if (fullCommand.isEmpty()) {
|
|
return QStringList();
|
|
}
|
|
|
|
QString path = fullCommand.section(QChar::fromLatin1('/'), 0, -2);
|
|
QString commandBinary = fullCommand.section(QChar::fromLatin1('/'), -1);
|
|
pathCommand << path << commandBinary;
|
|
} else {
|
|
QString fullCommand;
|
|
if (quoted == true)
|
|
fullCommand = command;
|
|
else
|
|
fullCommand = decryptBinaryCommand(command);
|
|
// relying on $PATH
|
|
pathCommand << QString() << fullCommand;
|
|
}
|
|
return pathCommand;
|
|
}
|
|
|
|
QString CTTask::completeCommandPath() const {
|
|
QPair<QString, bool> commandQuoted = unQuoteCommand();
|
|
if (commandQuoted.first.isEmpty())
|
|
return QString();
|
|
QStringList pathCommand = separatePathCommand(
|
|
commandQuoted.first, commandQuoted.second);
|
|
if (pathCommand.isEmpty()) {
|
|
return QString();
|
|
}
|
|
return pathCommand.join(QChar::fromLatin1('/'));
|
|
}
|