KIO
ksambashare.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project 00002 Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de> 00003 Copyright 2010 Rodrigo Belem <rclbelem@gmail.com> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License version 2 as published by the Free Software Foundation. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "ksambashare.h" 00021 #include "ksambashare_p.h" 00022 #include "ksambasharedata.h" 00023 #include "ksambasharedata_p.h" 00024 00025 #include <QtCore/QMap> 00026 #include <QtCore/QMutableMapIterator> 00027 #include <QtCore/QFile> 00028 #include <QtCore/QRegExp> 00029 #include <QtCore/QFileInfo> 00030 #include <QtCore/QTextStream> 00031 #include <QtCore/QStringList> 00032 00033 #include <kdirwatch.h> 00034 #include <kdebug.h> 00035 #include <kglobal.h> 00036 #include <kprocess.h> 00037 #include <kuser.h> 00038 00039 // Default smb.conf locations 00040 // sorted by priority, most priority first 00041 static const char * const DefaultSambaConfigFilePathList[] = 00042 { 00043 "/etc/samba/smb.conf", 00044 "/etc/smb.conf", 00045 "/usr/local/etc/smb.conf", 00046 "/usr/local/samba/lib/smb.conf", 00047 "/usr/samba/lib/smb.conf", 00048 "/usr/lib/smb.conf", 00049 "/usr/local/lib/smb.conf" 00050 }; 00051 static const int DefaultSambaConfigFilePathListSize = sizeof(DefaultSambaConfigFilePathList) 00052 / sizeof(char*); 00053 00054 KSambaSharePrivate::KSambaSharePrivate(KSambaShare *parent) 00055 : q_ptr(parent) 00056 , data() 00057 , smbConf() 00058 , userSharePath() 00059 , skipUserShare(false) 00060 { 00061 setUserSharePath(); 00062 findSmbConf(); 00063 sync(); 00064 } 00065 00066 KSambaSharePrivate::~KSambaSharePrivate() 00067 { 00068 } 00069 00070 bool KSambaSharePrivate::isSambaInstalled() 00071 { 00072 if (QFile::exists("/usr/sbin/smbd") 00073 || QFile::exists("/usr/local/sbin/smbd")) { 00074 return true; 00075 } 00076 00077 kDebug() << "Samba is not installed!"; 00078 00079 return false; 00080 } 00081 00082 // Try to find the samba config file path 00083 // in several well-known paths 00084 bool KSambaSharePrivate::findSmbConf() 00085 { 00086 for (int i = 0; i < DefaultSambaConfigFilePathListSize; ++i) { 00087 const QString filePath(DefaultSambaConfigFilePathList[i]); 00088 if (QFile::exists(filePath)) { 00089 smbConf = filePath; 00090 return true; 00091 } 00092 } 00093 00094 kWarning() << "KSambaShare: Could not find smb.conf!"; 00095 00096 return false; 00097 } 00098 00099 void KSambaSharePrivate::setUserSharePath() 00100 { 00101 const QString rawString = testparmParamValue(QLatin1String("usershare path")); 00102 const QFileInfo fileInfo(rawString); 00103 if (fileInfo.isDir()) { 00104 userSharePath = rawString; 00105 } 00106 } 00107 00108 int KSambaSharePrivate::runProcess(const QString &progName, const QStringList &args, 00109 QByteArray &stdOut, QByteArray &stdErr) 00110 { 00111 KProcess process; 00112 00113 process.setProgram(progName, args); 00114 process.setOutputChannelMode(KProcess::SeparateChannels); 00115 process.start(); 00116 //TODO: make it async in future 00117 process.waitForFinished(); 00118 00119 stdOut = process.readAllStandardOutput(); 00120 stdErr = process.readAllStandardError(); 00121 return process.exitCode(); 00122 } 00123 00124 QString KSambaSharePrivate::testparmParamValue(const QString ¶meterName) 00125 { 00126 if (!isSambaInstalled()) { 00127 return QString(); 00128 } 00129 00130 QStringList args; 00131 QByteArray stdErr; 00132 QByteArray stdOut; 00133 00134 args << QLatin1String("-d0") << QLatin1String("-s") << QLatin1String("--parameter-name") 00135 << parameterName; 00136 00137 runProcess(QLatin1String("testparm"), args, stdOut, stdErr); 00138 00139 //TODO: parse and process error messages. 00140 // create a parser for the error output and 00141 // send error message somewhere 00142 if (!stdErr.isEmpty()) { 00143 QList<QByteArray> err; 00144 err << stdErr.trimmed().split('\n'); 00145 if ((err.count() == 2) 00146 && err.at(0).startsWith("Load smb config files from") 00147 && err.at(1).startsWith("Loaded services file OK.")) { 00148 kDebug() << "Running testparm" << args; 00149 } else { 00150 kWarning() << "We got some errors while running testparm" << stdErr; 00151 } 00152 } 00153 00154 if (!stdOut.isEmpty()) { 00155 return QString::fromLocal8Bit(stdOut.trimmed()); 00156 } 00157 00158 return QString(); 00159 } 00160 00161 QByteArray KSambaSharePrivate::getNetUserShareInfo() 00162 { 00163 if (skipUserShare || !isSambaInstalled()) { 00164 return QByteArray(); 00165 } 00166 00167 QStringList args; 00168 QByteArray stdOut; 00169 QByteArray stdErr; 00170 00171 args << QLatin1String("usershare") << QLatin1String("info"); 00172 00173 runProcess(QLatin1String("net"), args, stdOut, stdErr); 00174 00175 if (!stdErr.isEmpty()) { 00176 if (stdErr.contains("You do not have permission to create a usershare")) { 00177 skipUserShare = true; 00178 } else if (stdErr.contains("usershares are currently disabled")) { 00179 skipUserShare = true; 00180 } else { 00181 //TODO: parse and process other error messages. 00182 // create a parser for the error output and 00183 // send error message somewhere 00184 kWarning() << "We got some errors while running 'net usershare info'"; 00185 kWarning() << stdErr; 00186 } 00187 } 00188 00189 return stdOut; 00190 } 00191 00192 QStringList KSambaSharePrivate::shareNames() const 00193 { 00194 return data.keys(); 00195 } 00196 00197 QStringList KSambaSharePrivate::sharedDirs() const 00198 { 00199 QStringList dirs; 00200 00201 QMap<QString, KSambaShareData>::ConstIterator i; 00202 for (i = data.constBegin(); i != data.constEnd(); ++i) { 00203 if (!dirs.contains(i.value().path())) { 00204 dirs << i.value().path(); 00205 } 00206 } 00207 00208 return dirs; 00209 } 00210 00211 KSambaShareData KSambaSharePrivate::getShareByName(const QString &shareName) const 00212 { 00213 return data.value(shareName); 00214 } 00215 00216 QList<KSambaShareData> KSambaSharePrivate::getSharesByPath(const QString &path) const 00217 { 00218 QList<KSambaShareData> shares; 00219 00220 QMap<QString, KSambaShareData>::ConstIterator i; 00221 for (i = data.constBegin(); i != data.constEnd(); ++i) { 00222 if (i.value().path() == path) { 00223 shares << i.value(); 00224 } 00225 } 00226 00227 return shares; 00228 } 00229 00230 bool KSambaSharePrivate::isShareNameValid(const QString &name) const 00231 { 00232 // Samba forbidden chars 00233 const QRegExp notToMatchRx(QLatin1String("[%<>*\?|/\\+=;:\",]")); 00234 return (notToMatchRx.indexIn(name) == -1); 00235 } 00236 00237 bool KSambaSharePrivate::isDirectoryShared(const QString &path) const 00238 { 00239 QMap<QString, KSambaShareData>::ConstIterator i; 00240 for (i = data.constBegin(); i != data.constEnd(); ++i) { 00241 if (i.value().path() == path) { 00242 return true; 00243 } 00244 } 00245 00246 return false; 00247 } 00248 00249 bool KSambaSharePrivate::isShareNameAvailable(const QString &name) const 00250 { 00251 // Samba does not allow to name a share with a user name registered in the system 00252 return (!KUser::allUserNames().contains(name) || !data.keys().contains(name)); 00253 } 00254 00255 KSambaShareData::UserShareError KSambaSharePrivate::isPathValid(const QString &path) const 00256 { 00257 QFileInfo pathInfo = path; 00258 00259 if (!pathInfo.exists()) { 00260 return KSambaShareData::UserSharePathNotExists; 00261 } 00262 00263 if (!pathInfo.isDir()) { 00264 return KSambaShareData::UserSharePathNotDirectory; 00265 } 00266 00267 if (pathInfo.isRelative()) { 00268 if (pathInfo.makeAbsolute()) { 00269 return KSambaShareData::UserSharePathNotAbsolute; 00270 } 00271 } 00272 00273 // TODO: check if the user is root 00274 if (KSambaSharePrivate::testparmParamValue(QLatin1String("usershare owner only")) 00275 == QLatin1String("Yes")) { 00276 if (!pathInfo.permission(QFile::ReadUser | QFile::WriteUser)) { 00277 return KSambaShareData::UserSharePathNotAllowed; 00278 } 00279 } 00280 00281 return KSambaShareData::UserSharePathOk; 00282 } 00283 00284 KSambaShareData::UserShareError KSambaSharePrivate::isAclValid(const QString &acl) const 00285 { 00286 const QRegExp aclRx("(?:(?:(\\w+\\s*)\\\\|)(\\w+\\s*):([fFrRd]{1})(?:,|))*"); 00287 // TODO: check if user is a valid smb user 00288 return aclRx.exactMatch(acl) ? KSambaShareData::UserShareAclOk 00289 : KSambaShareData::UserShareAclInvalid; 00290 } 00291 00292 KSambaShareData::UserShareError KSambaSharePrivate::guestsAllowed(const 00293 KSambaShareData::GuestPermission &guestok) const 00294 { 00295 if (guestok == KSambaShareData::GuestsAllowed) { 00296 if (KSambaSharePrivate::testparmParamValue("usershare allow guests") 00297 == QLatin1String("No")) { 00298 return KSambaShareData::UserShareGuestsNotAllowed; 00299 } 00300 } 00301 00302 return KSambaShareData::UserShareGuestsOk; 00303 } 00304 00305 KSambaShareData::UserShareError KSambaSharePrivate::add(const KSambaShareData &shareData) 00306 { 00307 // TODO: 00308 // * check for usershare max shares 00309 00310 if (!isSambaInstalled()) { 00311 return KSambaShareData::UserShareSystemError; 00312 } 00313 00314 QStringList args; 00315 QByteArray stdOut; 00316 QByteArray stdErr; 00317 00318 if (data.contains(shareData.name())) { 00319 if (data.value(shareData.name()).path() != shareData.path()) { 00320 return KSambaShareData::UserShareNameInUse; 00321 } 00322 } else { 00323 // It needs to be added here, otherwise another instance of KSambaShareDataPrivate 00324 // will be created and added to data. 00325 data.insert(shareData.name(), shareData); 00326 } 00327 00328 QString guestok = QString("guest_ok=%1").arg( 00329 (shareData.guestPermission() == KSambaShareData::GuestsNotAllowed) 00330 ? QLatin1String("n") : QLatin1String("y")); 00331 00332 args << QLatin1String("usershare") << QLatin1String("add") << shareData.name() 00333 << shareData.path() << shareData.comment() << shareData.acl() << guestok; 00334 00335 int ret = runProcess(QLatin1String("net"), args, stdOut, stdErr); 00336 00337 //TODO: parse and process error messages. 00338 if (!stdErr.isEmpty()) { 00339 // create a parser for the error output and 00340 // send error message somewhere 00341 kWarning() << "We got some errors while running 'net usershare add'" << args; 00342 kWarning() << stdErr; 00343 } 00344 00345 return (ret == 0) ? KSambaShareData::UserShareOk : KSambaShareData::UserShareSystemError; 00346 } 00347 00348 KSambaShareData::UserShareError KSambaSharePrivate::remove(const KSambaShareData &shareData) const 00349 { 00350 if (!isSambaInstalled()) { 00351 return KSambaShareData::UserShareSystemError; 00352 } 00353 00354 QStringList args; 00355 00356 if (!data.contains(shareData.name())) { 00357 return KSambaShareData::UserShareNameInvalid; 00358 } 00359 00360 args << QLatin1String("usershare") << QLatin1String("delete") << shareData.name(); 00361 00362 int result = KProcess::execute(QLatin1String("net"), args); 00363 return (result == 0) ? KSambaShareData::UserShareOk : KSambaShareData::UserShareSystemError; 00364 } 00365 00366 bool KSambaSharePrivate::sync() 00367 { 00368 const QRegExp headerRx(QLatin1String("^\\s*\\[" 00369 "([^%<>*\?|/\\+=;:\",]+)" 00370 "\\]")); 00371 00372 const QRegExp OptValRx(QLatin1String("^\\s*([\\w\\d\\s]+)" 00373 "=" 00374 "(.*)$")); 00375 00376 QTextStream stream(getNetUserShareInfo()); 00377 QString currentShare; 00378 QStringList shareList; 00379 00380 while (!stream.atEnd()) { 00381 const QString line = stream.readLine().trimmed(); 00382 00383 if (headerRx.exactMatch(line)) { 00384 currentShare = headerRx.cap(1).trimmed(); 00385 shareList << currentShare; 00386 00387 if (!data.contains(currentShare)) { 00388 KSambaShareData shareData; 00389 shareData.dd->name = currentShare; 00390 data.insert(currentShare, shareData); 00391 } 00392 } else if (OptValRx.exactMatch(line)) { 00393 const QString key = OptValRx.cap(1).trimmed(); 00394 const QString value = OptValRx.cap(2).trimmed(); 00395 KSambaShareData shareData = getShareByName(currentShare); 00396 00397 if (key == QLatin1String("path")) { 00398 shareData.dd->path = value; 00399 } else if (key == QLatin1String("comment")) { 00400 shareData.dd->comment = value; 00401 } else if (key == QLatin1String("usershare_acl")) { 00402 shareData.dd->acl = value; 00403 } else if (key == QLatin1String("guest_ok")) { 00404 shareData.dd->guestPermission = value; 00405 } else { 00406 kWarning() << "Something nasty happen while parsing 'net usershare info'" 00407 << "share:" << currentShare << "key:" << key; 00408 } 00409 } else if (line.trimmed().isEmpty()) { 00410 continue; 00411 } else { 00412 return false; 00413 } 00414 } 00415 00416 QMutableMapIterator<QString, KSambaShareData> i(data); 00417 while (i.hasNext()) { 00418 i.next(); 00419 if (!shareList.contains(i.key())) { 00420 i.remove(); 00421 } 00422 } 00423 00424 return true; 00425 } 00426 00427 void KSambaSharePrivate::_k_slotFileChange(const QString &path) 00428 { 00429 sync(); 00430 kDebug() << "path changed:" << path; 00431 Q_Q(KSambaShare); 00432 emit q->changed(); 00433 } 00434 00435 KSambaShare::KSambaShare() 00436 : QObject(0) 00437 , d_ptr(new KSambaSharePrivate(this)) 00438 { 00439 Q_D(const KSambaShare); 00440 if (QFile::exists(d->userSharePath)) { 00441 KDirWatch::self()->addDir(d->userSharePath, KDirWatch::WatchFiles); 00442 connect(KDirWatch::self(), SIGNAL(dirty(const QString &)), this, 00443 SLOT(_k_slotFileChange(const QString &))); 00444 } 00445 } 00446 00447 KSambaShare::~KSambaShare() 00448 { 00449 Q_D(const KSambaShare); 00450 if (KDirWatch::exists() && KDirWatch::self()->contains(d->userSharePath)) { 00451 KDirWatch::self()->removeDir(d->userSharePath); 00452 } 00453 delete d_ptr; 00454 } 00455 00456 QString KSambaShare::smbConfPath() const 00457 { 00458 Q_D(const KSambaShare); 00459 return d->smbConf; 00460 } 00461 00462 bool KSambaShare::isDirectoryShared(const QString &path) const 00463 { 00464 Q_D(const KSambaShare); 00465 return d->isDirectoryShared(path); 00466 } 00467 00468 bool KSambaShare::isShareNameAvailable(const QString &name) const 00469 { 00470 Q_D(const KSambaShare); 00471 return d->isShareNameValid(name) && d->isShareNameAvailable(name); 00472 } 00473 00474 QStringList KSambaShare::shareNames() const 00475 { 00476 Q_D(const KSambaShare); 00477 return d->shareNames(); 00478 } 00479 00480 QStringList KSambaShare::sharedDirectories() const 00481 { 00482 Q_D(const KSambaShare); 00483 return d->sharedDirs(); 00484 } 00485 00486 KSambaShareData KSambaShare::getShareByName(const QString &name) const 00487 { 00488 Q_D(const KSambaShare); 00489 return d->getShareByName(name); 00490 } 00491 00492 QList<KSambaShareData> KSambaShare::getSharesByPath(const QString &path) const 00493 { 00494 Q_D(const KSambaShare); 00495 return d->getSharesByPath(path); 00496 } 00497 00498 KSambaShare *KSambaShare::instance() 00499 { 00500 K_GLOBAL_STATIC(KSambaShare, _instance) 00501 return _instance; 00502 } 00503 00504 #include "moc_ksambashare.cpp"
KDE 4.7 API Reference