10#include "ksambashare.h"
11#include "kiocoredebug.h"
12#include "ksambashare_p.h"
13#include "ksambasharedata.h"
14#include "ksambasharedata_p.h"
16#include "../utils_p.h"
22#include <QLoggingCategory>
25#include <QRegularExpression>
26#include <QStandardPaths>
33Q_DECLARE_LOGGING_CATEGORY(KIO_CORE_SAMBASHARE)
34Q_LOGGING_CATEGORY(KIO_CORE_SAMBASHARE,
"kf.kio.core.sambashare", QtWarningMsg)
36KSambaSharePrivate::KSambaSharePrivate(
KSambaShare *parent)
40 , skipUserShare(false)
43 data =
parse(getNetUserShareInfo());
46KSambaSharePrivate::~KSambaSharePrivate()
50bool KSambaSharePrivate::isSambaInstalled()
52 const bool daemonExists =
55 qCDebug(KIO_CORE_SAMBASHARE) <<
"KSambaShare: Could not find smbd";
60 qCDebug(KIO_CORE_SAMBASHARE) <<
"KSambaShare: Could not find testparm tool, most likely samba-client isn't installed";
63 return daemonExists && clientExists;
66void KSambaSharePrivate::setUserSharePath()
68 const QString rawString = testparmParamValue(QStringLiteral(
"usershare path"));
70 if (fileInfo.isDir()) {
71 userSharePath = rawString;
82 qCWarning(KIO_CORE) <<
"Could not find an executable named:" << progName;
86 process.
start(exec, args);
95QString KSambaSharePrivate::testparmParamValue(
const QString ¶meterName)
97 if (!isSambaInstalled()) {
105 QStringLiteral(
"-d0"),
106 QStringLiteral(
"-s"),
107 QStringLiteral(
"--parameter-name"),
111 runProcess(QStringLiteral(
"testparm"), args, stdOut, stdErr);
119 errArray.
erase(std::remove_if(errArray.
begin(),
122 return line.startsWith(
"Load smb config files from");
125 errArray.
removeOne(
"Loaded services file OK.");
126 errArray.
removeOne(
"Weak crypto is allowed");
128 const int netbiosNameErrorIdx = errArray.
indexOf(
"WARNING: The 'netbios name' is too long (max. 15 chars).");
129 if (netbiosNameErrorIdx >= 0) {
137 if (defaultNetbiosName.
length() > 14) {
138 qCDebug(KIO_CORE) <<
"Your samba 'netbios name' parameter was longer than the authorized 15 characters.\n"
139 <<
"It may be because your hostname is longer than 13 and samba default 'netbios name' defaults to 'hostname-W', here:"
140 << defaultNetbiosName <<
"\n"
141 <<
"If that it is the case simply define a 'netbios name' parameter in /etc/samba/smb.conf at most 15 characters long";
143 qCDebug(KIO_CORE) <<
"Your samba 'netbios name' parameter was longer than the authorized 15 characters."
144 <<
"Please define a 'netbios name' parameter in /etc/samba/smb.conf at most 15 characters long";
146 errArray.
removeAt(netbiosNameErrorIdx);
148 if (errArray.
size() > 0) {
149 qCDebug(KIO_CORE) <<
"We got some errors while running testparm" << errArray.join(
"\n");
160QByteArray KSambaSharePrivate::getNetUserShareInfo()
162 if (skipUserShare || !isSambaInstalled()) {
170 QStringLiteral(
"usershare"),
171 QStringLiteral(
"info"),
174 runProcess(QStringLiteral(
"net"), args, stdOut, stdErr);
177 if (stdErr.
contains(
"You do not have permission to create a usershare")) {
178 skipUserShare =
true;
179 }
else if (stdErr.
contains(
"usershares are currently disabled")) {
180 skipUserShare =
true;
185 qCDebug(KIO_CORE) <<
"We got some errors while running 'net usershare info'";
186 qCDebug(KIO_CORE) << stdErr;
203 for (i = data.constBegin(); i != data.constEnd(); ++i) {
205 dirs << i.
value().path();
214 return data.value(shareName);
222 for (i = data.constBegin(); i != data.constEnd(); ++i) {
223 if (i.
value().path() == path) {
231bool KSambaSharePrivate::isShareNameValid(
const QString &name)
const
235 return !notToMatchRx.match(name).hasMatch();
238bool KSambaSharePrivate::isDirectoryShared(
const QString &path)
const
241 for (i = data.constBegin(); i != data.constEnd(); ++i) {
242 if (i.
value().path() == path) {
250bool KSambaSharePrivate::isShareNameAvailable(
const QString &name)
const
256KSambaShareData::UserShareError KSambaSharePrivate::isPathValid(
const QString &path)
const
260 if (!pathInfo.exists()) {
261 return KSambaShareData::UserSharePathNotExists;
264 if (!pathInfo.isDir()) {
265 return KSambaShareData::UserSharePathNotDirectory;
268 if (pathInfo.isRelative()) {
269 if (pathInfo.makeAbsolute()) {
270 return KSambaShareData::UserSharePathNotAbsolute;
275 if (KSambaSharePrivate::testparmParamValue(QStringLiteral(
"usershare owner only")) ==
QLatin1String(
"Yes")) {
277 return KSambaShareData::UserSharePathNotAllowed;
281 return KSambaShareData::UserSharePathOk;
284KSambaShareData::UserShareError KSambaSharePrivate::isAclValid(
const QString &acl)
const
288 static const auto pattern = uR
"--((?:(?:(\w[-.\w\s]*)\\|)(\w+[-.\w\s]*):([fFrRd]{1})(?:,|))*)--";
291 return aclRx.match(acl).hasMatch() ? KSambaShareData::UserShareAclOk : KSambaShareData::UserShareAclInvalid;
294bool KSambaSharePrivate::areGuestsAllowed()
const
296 return KSambaSharePrivate::testparmParamValue(QStringLiteral(
"usershare allow guests")) !=
QLatin1String(
"No");
299KSambaShareData::UserShareError KSambaSharePrivate::guestsAllowed(
const KSambaShareData::GuestPermission &guestok)
const
301 if (guestok == KSambaShareData::GuestsAllowed && !areGuestsAllowed()) {
302 return KSambaShareData::UserShareGuestsNotAllowed;
305 return KSambaShareData::UserShareGuestsOk;
308KSambaShareData::UserShareError KSambaSharePrivate::add(
const KSambaShareData &shareData)
313 if (!isSambaInstalled()) {
314 return KSambaShareData::UserShareSystemError;
317 if (data.contains(shareData.
name())) {
318 if (data.value(shareData.
name()).path() != shareData.
path()) {
319 return KSambaShareData::UserShareNameInUse;
324 QStringLiteral(
"guest_ok=%1").
arg((shareData.
guestPermission() == KSambaShareData::GuestsNotAllowed) ? QStringLiteral(
"n") : QStringLiteral(
"y"));
327 QStringLiteral(
"usershare"),
328 QStringLiteral(
"add"),
337 int ret = runProcess(QStringLiteral(
"net"), args, stdOut, m_stdErr);
340 if (!m_stdErr.isEmpty()) {
343 qCWarning(KIO_CORE) <<
"We got some errors while running 'net usershare add'" << args;
344 qCWarning(KIO_CORE) << m_stdErr;
347 if (ret == 0 && !data.contains(shareData.
name())) {
351 data.insert(shareData.
name(), shareData);
354 return (ret == 0) ? KSambaShareData::UserShareOk : KSambaShareData::UserShareSystemError;
357KSambaShareData::UserShareError KSambaSharePrivate::remove(
const KSambaShareData &shareData)
359 if (!isSambaInstalled()) {
360 return KSambaShareData::UserShareSystemError;
363 if (!data.contains(shareData.
name())) {
364 return KSambaShareData::UserShareNameInvalid;
368 QStringLiteral(
"usershare"),
369 QStringLiteral(
"delete"),
374 int ret = runProcess(QStringLiteral(
"net"), args, stdOut, m_stdErr);
377 if (!m_stdErr.isEmpty()) {
380 qCWarning(KIO_CORE) <<
"We got some errors while running 'net usershare delete'" << args;
381 qCWarning(KIO_CORE) << m_stdErr;
384 return (ret == 0) ? KSambaShareData::UserShareOk : KSambaShareData::UserShareSystemError;
391 static const char16_t headerPattern[] = uR
"--(^\s*\[([^%<>*?|/+=;:",]+)\])--";
394 static const char16_t valPattern[] = uR
"--(^\s*([\w\d\s]+)=(.*)$)--";
401 while (!stream.atEnd()) {
405 if ((match = headerRx.match(line)).hasMatch()) {
406 currentShare =
match.captured(1).trimmed();
408 if (!shares.
contains(currentShare)) {
410 shareData.dd->name = currentShare;
411 shares.
insert(currentShare, shareData);
413 }
else if ((match = OptValRx.match(line)).hasMatch()) {
421 shareData.dd->path = Utils::trailingSlashRemoved(value);
423 shareData.dd->comment = value;
425 shareData.dd->acl = value;
427 shareData.dd->guestPermission = value;
429 qCWarning(KIO_CORE) <<
"Something nasty happen while parsing 'net usershare info'"
430 <<
"share:" << currentShare <<
"key:" << key;
442void KSambaSharePrivate::slotFileChange(
const QString &path)
444 if (path != userSharePath) {
447 data =
parse(getNetUserShareInfo());
448 qCDebug(KIO_CORE) <<
"reloading data; path changed:" <<
path;
453KSambaShare::KSambaShare()
455 , d_ptr(new KSambaSharePrivate(this))
461 d->slotFileChange(path);
466KSambaShare::~KSambaShare()
478 return d->isDirectoryShared(path);
484 return d->isShareNameValid(name) && d->isShareNameAvailable(name);
490 return d->shareNames();
496 return d->sharedDirs();
502 return d->getShareByName(name);
508 return d->getSharesByPath(path);
520 return d->areGuestsAllowed();
523class KSambaShareSingleton
529Q_GLOBAL_STATIC(KSambaShareSingleton, _instance)
536#include "moc_ksambashare.cpp"
static KDirWatch * self()
void removeDir(const QString &path)
void addDir(const QString &path, WatchModes watchModes=WatchDirOnly)
void dirty(const QString &path)
This class represents a Samba user share.
QString acl() const
Returns a containing a string describing the permission added to the users, such as "[DOMAIN\]usernam...
KSambaShareData::GuestPermission guestPermission() const
This class lists Samba user shares and monitors them for addition, update and removal.
bool areGuestsAllowed() const
Check whether usershares may enable guests.
bool isShareNameAvailable(const QString &name) const
Tests that a share name is valid and does not conflict with system users names or shares.
KSambaShareData getShareByName(const QString &name) const
Returns the KSambaShareData object of the share name.
QList< KSambaShareData > getSharesByPath(const QString &path) const
Returns a list of KSambaShareData matching the path.
QStringList shareNames() const
Returns the list of available shares.
QString lastSystemErrorString() const
Used to obtain UserShareSystemError error strings.
bool isDirectoryShared(const QString &path) const
Whether or not the given path is shared by Samba.
static KSambaShare * instance()
QStringList sharedDirectories() const
Returns a list of all directories shared by local users in Samba.
static QStringList allUserNames(uint maxCount=KCOREADDONS_UINT_MAX)
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
KHEALTHCERTIFICATE_EXPORT QVariant parse(const QByteArray &data)
QString path(const QString &relativePath)
bool contains(QByteArrayView bv) const const
bool isEmpty() const const
QList< QByteArray > split(char sep) const const
QByteArray trimmed() const const
bool exists() const const
iterator erase(const_iterator begin, const_iterator end)
qsizetype indexOf(const AT &value, qsizetype from) const const
qsizetype removeAll(const AT &t)
void removeAt(qsizetype i)
bool removeOne(const AT &t)
qsizetype size() const const
bool contains(const Key &key) const const
iterator insert(const Key &key, const T &value)
T value(const Key &key, const T &defaultValue) const const
int exitCode() const const
QByteArray readAllStandardError()
QByteArray readAllStandardOutput()
void setProcessChannelMode(ProcessChannelMode mode)
void start(OpenMode mode)
bool waitForFinished(int msecs)
QString anchoredPattern(QStringView expression)
QString findExecutable(const QString &executableName, const QStringList &paths)
QString & append(QChar ch)
QString arg(Args &&... args) const const
QString fromLocal8Bit(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
qsizetype length() const const
QString trimmed() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)