/* Copyright (c) 2002-2004 Jan Schaefer 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sambafile.h" #define FILESHARE_DEBUG 5009 SambaConfigFile::SambaConfigFile(SambaFile* sambaFile) { TQDict(10,false); setAutoDelete(true); _sambaFile = sambaFile; } TQString SambaConfigFile::getDefaultValue(const TQString & name) { SambaShare* defaults = _sambaFile->getTestParmValues(); TQString s = defaults->getValue(name,false,false); return s; } SambaShare* SambaConfigFile::addShare(const TQString & name) { SambaShare* newShare = new SambaShare(name,this); addShare(name,newShare); return newShare; } void SambaConfigFile::addShare(const TQString & name, SambaShare* share) { insert(name,share), _shareList.append(name); } void SambaConfigFile::removeShare(const TQString & name) { remove(name); _shareList.remove(name); } TQStringList SambaConfigFile::getShareList() { return _shareList; } SambaFile::SambaFile(const TQString & _path, bool _readonly) : readonly(_readonly), changed(false), path(_path), localPath(_path), _sambaConfig(0), _testParmValues(0), _sambaVersion(-1), _tempFile(0) { } SambaFile::~SambaFile() { delete _sambaConfig; delete _testParmValues; delete _tempFile; } bool SambaFile::isRemoteFile() { return ! KURL(path).isLocalFile(); } /** No descriptions */ TQString SambaFile::findShareByPath(const TQString & path) const { TQDictIterator it(*_sambaConfig); KURL url(path); url.adjustPath(-1); for ( ; it.current(); ++it ) { SambaShare* share = it.current(); TQString *s = share->find("path"); if (s) { KURL curUrl(*s); curUrl.adjustPath(-1); kdDebug(5009) << url.path() << " =? " << curUrl.path() << endl; if (url.path() == curUrl.path()) return it.currentKey(); } } return TQString(); } bool SambaFile::save() { return slotApply(); } bool SambaFile::slotApply() { if (readonly) { kdDebug(FILESHARE_DEBUG) << "SambaFile::slotApply: readonly=true" << endl; return false; } // If we have write access to the smb.conf // we simply save the values to it // if not we have to save the results in // a temporary file and copy it afterwards // over the smb.conf file with kdesu. if (TQFileInfo(path).isWritable()) { saveTo(path); changed = false; return true; } // Create a temporary smb.conf file delete _tempFile; _tempFile = new KTempFile(); _tempFile->setAutoDelete(true); if (!saveTo(_tempFile->name())) { kdDebug(5009) << "SambaFile::slotApply: Could not save to temporary file" << endl; delete _tempFile; _tempFile = 0; return false; } TQFileInfo fi(path); KURL url(path); if (KURL(path).isLocalFile()) { KProcess proc; kdDebug(5009) << "SambaFile::slotApply: is local file!" << endl; TQString suCommand=TQString("cp %1 %2; rm %3") .arg(_tempFile->name()) .arg(path) .arg(_tempFile->name()); proc << "kdesu" << "-d" << suCommand; if (! proc.start(KProcess::Block)) { kdDebug(5009) << "SambaFile::slotApply: saving to " << path << " failed!" << endl; //KMessageBox::sorry(0,i18n("Saving the results to %1 failed.").arg(path)); delete _tempFile; _tempFile = 0; return false; } else { changed = false; delete _tempFile; _tempFile = 0; kdDebug(5009) << "SambaFile::slotApply: changes successfully saved!" << endl; return true; } } else { kdDebug(5009) << "SambaFile::slotApply: is remote file!" << endl; _tempFile->setAutoDelete(true); KURL srcURL; srcURL.setPath( _tempFile->name() ); KIO::FileCopyJob * job = KIO::file_copy( srcURL, url, -1, true ); connect( job, TQT_SIGNAL( result( KIO::Job * ) ), this, TQT_SLOT( slotSaveJobFinished ( KIO::Job * ) ) ); return (job->error()==0); } return true; } /** * Returns a name which isn't already used for a share **/ TQString SambaFile::getUnusedName(const TQString alreadyUsedName) const { TQString init = i18n("Unnamed"); if (alreadyUsedName != TQString()) init = alreadyUsedName; TQString s = init; int i = 2; while (_sambaConfig->find(s)) { s = init+TQString::number(i); i++; } return s; } SambaShare* SambaFile::newShare(const TQString & name) { if (_sambaConfig->find(name)) return 0L; SambaShare* share = new SambaShare(name,_sambaConfig); _sambaConfig->addShare(name,share); changed = true; return share; } SambaShare* SambaFile::newShare(const TQString & name, const TQString & path) { SambaShare* share = newShare(name); if (share) { share->setValue("path",path); } return share; } SambaShare* SambaFile::newPrinter(const TQString & name, const TQString & printer) { SambaShare* share = newShare(name); if (share) { share->setValue("printable",true); share->setValue("printer name",printer); } return share; } /** No descriptions */ void SambaFile::removeShare(const TQString & share) { changed = true; _sambaConfig->removeShare(share); } void SambaFile::removeShare(SambaShare* share) { removeShare(share->getName()); } void SambaFile::removeShareByPath(const TQString & path) { TQString share = findShareByPath(path); removeShare(share); } /** No descriptions */ SambaShare* SambaFile::getShare(const TQString & share) const { SambaShare *s = _sambaConfig->find(share); return s; } /** * Returns a list of all shared directories **/ SambaShareList* SambaFile::getSharedDirs() const { SambaShareList* list = new SambaShareList(); TQDictIterator it(*_sambaConfig); for( ; it.current(); ++it ) { if (!it.current()->isPrinter() && it.current()->getName() != "global") { list->append(it.current()); } } return list; } /** * Returns a list of all shared printers **/ SambaShareList* SambaFile::getSharedPrinters() const { SambaShareList* list = new SambaShareList(); TQDictIterator it(*_sambaConfig); for( ; it.current(); ++it ) { if (it.current()->isPrinter()) list->append(it.current()); } return list; } int SambaFile::getSambaVersion() { if (_sambaVersion > -1) return _sambaVersion; KProcess testParam; testParam << "testparm"; testParam << "-V"; _parmOutput = TQString(""); _sambaVersion = 2; connect( &testParam, TQT_SIGNAL(receivedStdout(KProcess*,char*,int)), this, TQT_SLOT(testParmStdOutReceived(KProcess*,char*,int))); if (testParam.start(KProcess::Block,KProcess::Stdout)) { if (_parmOutput.find("3") > -1) _sambaVersion = 3; } kdDebug(5009) << "Samba version = " << _sambaVersion << endl; return _sambaVersion; } SambaShare* SambaFile::getTestParmValues(bool reload) { if (_testParmValues && !reload) return _testParmValues; KProcess testParam; testParam << "testparm"; testParam << "-s"; if (getSambaVersion() == 3) testParam << "-v"; testParam << "/dev/null"; _parmOutput = TQString(""); connect( &testParam, TQT_SIGNAL(receivedStdout(KProcess*,char*,int)), this, TQT_SLOT(testParmStdOutReceived(KProcess*,char*,int))); if (testParam.start(KProcess::Block,KProcess::Stdout)) { parseParmStdOutput(); } else _testParmValues = new SambaShare(_sambaConfig); return _testParmValues; } void SambaFile::testParmStdOutReceived(KProcess *, char *buffer, int buflen) { _parmOutput+=TQString::fromLatin1(buffer,buflen); } void SambaFile::parseParmStdOutput() { TQTextIStream s(&_parmOutput); if (_testParmValues) delete _testParmValues; _testParmValues = new SambaShare(_sambaConfig); TQString section=""; while (!s.atEnd()) { TQString line = s.readLine().stripWhiteSpace(); // empty lines if (line.isEmpty()) continue; // comments if ('#' == line[0]) continue; // sections if ('[' == line[0]) { // get the name of the section section = line.mid(1,line.length()-2).lower(); continue; } // we are only interested in the global section if (section != KGlobal::staticQString("global")) continue; // parameter // parameter int i = line.find('='); if (i>-1) { TQString name = line.left(i).stripWhiteSpace(); TQString value = line.mid(i+1).stripWhiteSpace(); _testParmValues->setValue(name,value,false,false); } } } /** * Try to find the samba config file position * First tries the config file, then checks * several common positions * If nothing is found returns TQString() **/ TQString SambaFile::findSambaConf() { return KSambaShare::instance()->smbConfPath(); } void SambaFile::slotSaveJobFinished( KIO::Job * job ) { delete _tempFile; _tempFile = 0; } void SambaFile::slotJobFinished( KIO::Job * job ) { if (job->error()) emit canceled( job->errorString() ); else { openFile(); emit completed(); } } bool SambaFile::load() { if (path.isNull() || path.isEmpty()) return false; kdDebug(FILESHARE_DEBUG) << "SambaFile::load: path=" << path << endl; KURL url(path); if (!url.isLocalFile()) { KTempFile tempFile; localPath = tempFile.name(); KURL destURL; destURL.setPath( localPath ); KIO::FileCopyJob * job = KIO::file_copy( url, destURL, 0600, true, false, true ); // emit started( d->m_job ); connect( job, TQT_SIGNAL( result( KIO::Job * ) ), this, TQT_SLOT( slotJobFinished ( KIO::Job * ) ) ); return true; } else { localPath = path; bool ret = openFile(); if (ret) emit completed(); return ret; } } bool SambaFile::openFile() { TQFile f(localPath); if (!f.open(IO_ReadOnly)) { //throw SambaFileLoadException(TQString("Could not open file %1 for reading.").arg(path)); return false; } TQTextStream s(&f); delete _sambaConfig; _sambaConfig = new SambaConfigFile(this); SambaShare *currentShare = 0L; bool continuedLine = false; // is true if the line before ended with a backslash TQString completeLine; TQStringList comments; while (!s.eof()) { TQString currentLine = s.readLine().stripWhiteSpace(); if (continuedLine) { completeLine += currentLine; continuedLine = false; } else completeLine = currentLine; // is the line continued in the next line ? if ( completeLine[completeLine.length()-1] == '\\' ) { continuedLine = true; // remove the ending backslash completeLine.truncate( completeLine.length()-1 ); continue; } // comments or empty lines if (completeLine.isEmpty() || '#' == completeLine[0] || ';' == completeLine[0]) { comments.append(completeLine); continue; } // sections if ('[' == completeLine[0]) { // get the name of the section TQString section = completeLine.mid(1,completeLine.length()-2); currentShare = _sambaConfig->addShare(section); currentShare->setComments(comments); comments.clear(); continue; } // parameter int i = completeLine.find('='); if (i>-1) { TQString name = completeLine.left(i).stripWhiteSpace(); TQString value = completeLine.mid(i+1).stripWhiteSpace(); if (currentShare) { currentShare->setComments(name,comments); currentShare->setValue(name,value,true,true); comments.clear(); } } } f.close(); // Make sure there is a global share if (!getShare("global")) { _sambaConfig->addShare("global"); } return true; } bool SambaFile::saveTo(const TQString & path) { TQFile f(path); if (!f.open(IO_WriteOnly)) return false; TQTextStream s(&f); TQStringList shareList = _sambaConfig->getShareList(); for ( TQStringList::Iterator it = shareList.begin(); it != shareList.end(); ++it ) { SambaShare* share = _sambaConfig->find(*it); // First add all comments of the share to the file TQStringList comments = share->getComments(); for ( TQStringList::Iterator cmtIt = comments.begin(); cmtIt != comments.end(); ++cmtIt ) { s << *cmtIt << endl; kdDebug(5009) << *cmtIt << endl; } // If there are no lines before the section add // a blank line if (comments.isEmpty()) s << endl; // Add the name of the share / section s << "[" << share->getName() << "]" << endl; // Add all options of the share TQStringList optionList = share->getOptionList(); for ( TQStringList::Iterator optionIt = optionList.begin(); optionIt != optionList.end(); ++optionIt ) { // Add the comments of the option comments = share->getComments(*optionIt); for ( TQStringList::Iterator cmtIt = comments.begin(); cmtIt != comments.end(); ++cmtIt ) { s << *cmtIt << endl; } // Add the option s << *optionIt << " = " << *share->find(*optionIt) << endl; } } f.close(); return true; } SambaConfigFile* SambaFile::getSambaConfigFile(KSimpleConfig* config) { TQStringList groups = config->groupList(); SambaConfigFile* samba = new SambaConfigFile(this); for ( TQStringList::Iterator it = groups.begin(); it != groups.end(); ++it ) { TQMap entries = config->entryMap(*it); SambaShare *share = new SambaShare(*it,samba); samba->insert(*it,share); for (TQMap::Iterator it2 = entries.begin(); it2 != entries.end(); ++it2 ) { if (!it2.data().isEmpty()) share->setValue(it2.key(),TQString(it2.data()),false,false); } } return samba; } KSimpleConfig* SambaFile::getSimpleConfig(SambaConfigFile* sambaConfig, const TQString & path) { KSimpleConfig *config = new KSimpleConfig(path,false); TQDictIterator it(*sambaConfig); for ( ; it.current(); ++it ) { SambaShare* share = it.current(); config->setGroup(it.currentKey()); TQDictIterator it2(*share); for (; it2.current(); ++it2 ) { config->writeEntry(it2.currentKey(), *it2.current()); } } return config; } #include "sambafile.moc"