00001
00002
00003
00004
00005
00006
00007
00008
00012 #include <iostream>
00013 #include <ctime>
00014 #include <cstdlib>
00015 #include <cstdio>
00016
00017 #include "zypp/base/Logger.h"
00018 #include "zypp/base/Exception.h"
00019 #include "zypp/base/Random.h"
00020 #include "zypp/base/Gettext.h"
00021 #include "zypp/CapFactory.h"
00022 #include "zypp/Digest.h"
00023 #include "zypp/ExternalProgram.h"
00024
00025 #include "zypp/target/store/xml/XMLPatchImpl.h"
00026 #include "zypp/target/store/xml/XMLMessageImpl.h"
00027 #include "zypp/target/store/xml/XMLScriptImpl.h"
00028 #include "zypp/target/store/xml/XMLSelectionImpl.h"
00029 #include "zypp/target/store/xml/XMLProductImpl.h"
00030 #include "zypp/target/store/xml/XMLPatternImpl.h"
00031 #include "zypp/target/store/xml/XMLAtomImpl.h"
00032
00033 #include "zypp/parser/xmlstore/XMLProductParser.h"
00034 #include "zypp/parser/xmlstore/XMLPatternParser.h"
00035 #include "zypp/parser/xmlstore/XMLPatchParser.h"
00036 #include "zypp/parser/xmlstore/XMLLanguageParser.h"
00037
00038 #include <iostream>
00039 #include <fstream>
00040 #include <sstream>
00041 #include <streambuf>
00042
00043 #include <list>
00044
00045 #include <zypp/TmpPath.h>
00046 #include <zypp/ZYppFactory.h>
00047 #include <zypp/ZYpp.h>
00048 #include <zypp/PathInfo.h>
00049
00050 #include "XMLFilesBackend.h"
00051 #include "serialize.h"
00052
00053
00054 #define ZYPP_DB_DIR ( getZYpp()->homePath().asString()+"/db/" )
00055
00056 using std::endl;
00057 using std::string;
00058 using std::list;
00059 using namespace zypp;
00060 using namespace zypp::filesystem;
00061
00063 namespace zypp
00064 {
00065 namespace storage
00066 {
00067
00080 struct PRODUCT_TABLE_ENTRY
00081 {
00082 const char * dist_name;
00083 const char * dist_version;
00084 const char * product_name;
00085 const char * product_version;
00086 };
00087
00093 PRODUCT_TABLE_ENTRY* products_table()
00094 {
00095 static PRODUCT_TABLE_ENTRY products[] = {
00096 { "SUSE-Linux-Enterprise-Desktop-i386", "10-0", "SUSE SLED" , "10" },
00097 { "SUSE-Linux-Enterprise-Desktop-x86_64", "10-0", "SUSE SLED", "10" },
00098 { "SUSE-Linux-Enterprise-Server-i386", "10-0", "SUSE SLES", "10" },
00099 { "SUSE-Linux-Enterprise-Server-x86_64", "10-0", "SUSE SLES", "10" },
00100 { "SUSE-Linux-Enterprise-Server-ppc", "10-0", "SUSE SLES", "10" },
00101 { "SUSE-Linux-Enterprise-Server-ia64", "10-0", "SUSE SLES", "10" },
00102 { "SUSE-Linux-Enterprise-Server-s390x", "10-0", "SUSE SLES", "10" },
00103 { "SUSE-Linux-10.1-CD-download-x86", "10.1-0", "SUSE LINUX", "10.1" },
00104 { "SUSE-Linux-10.1-CD-download-ppc", "10.1-0", "SUSE LINUX", "10.1" },
00105 { "SUSE-Linux-10.1-CD-download-x86_64", "10.1-0", "SUSE LINUX", "10.1" },
00106 { "SUSE-Linux-10.1-CD-x86", "10.1-0", "SUSE LINUX", "10.1" },
00107 { "SUSE-Linux-10.1-CD-ppc", "10.1-0", "SUSE LINUX", "10.1" },
00108 { "SUSE-Linux-10.1-CD-x86_64", "10.1-0", "SUSE LINUX", "10.1" },
00109 { "SUSE-Linux-10.1-DVD9-x86-x86_64", "10.1-0", "SUSE LINUX", "10.1" },
00110 { "SUSE-Linux-10.1-OSS-DVD-x86", "10.1-0", "SUSE LINUX", "10.1" },
00111 { "SUSE-Linux-10.1-DVD-OSS-i386", "10.1-0", "SUSE LINUX", "10.1" },
00112 { "SUSE-Linux-10.1-DVD-OSS-ppc", "10.1-0", "SUSE LINUX", "10.1" },
00113 { "SUSE-Linux-10.1-DVD-OSS-x86_64", "10.1-0", "SUSE LINUX", "10.1" },
00114 { "SUSE-Linux-10.1-FTP", "10.1-0", "SUSE LINUX", "10.1" },
00115 { "SUSE-Linux-10.1-DVD-i386", "10.1-0", "SUSE LINUX", "10.1" },
00116 { "SUSE-Linux-10.1-DVD-x86_64", "10.1-0", "SUSE LINUX", "10.1" },
00117 { "SuSE-Linux-10.1-PromoDVD-i386", "10.1-0", "SUSE LINUX", "10.1" },
00118 { "SUSE-Linux-10.1-DVD9-CTMAGAZIN-x86-x86_64", "10.1-0", "SUSE LINUX", "10.1" },
00119 { "SUSE-Linux-Enterprise-SDK-i386", "10-0", "SLE SDK", "10" },
00120 { "SUSE-Linux-Enterprise-SDK-x86_64", "10-0", "SLE SDK", "10" },
00121 { "SUSE-Linux-Enterprise-SDK-ia64", "10-0", "SLE SDK", "10" },
00122 { "SUSE-Linux-Enterprise-SDK-s390x", "10-0", "SLE SDK", "10" },
00123 { "SUSE-Linux-Enterprise-SDK-ppc", "10-0", "SLE SDK", "10" },
00124 { "SUSE-Linux-Enterprise-RT", "10-0", "SLE RT", "10" },
00125 { 0L, 0L, 0L, 0L }
00126 };
00127
00128 return products;
00129 }
00130
00132
00133
00134
00136 class XMLFilesBackend::Private
00137 {
00138 public:
00139 Private()
00140 { }
00141 bool randomFileName;
00142 std::set<Resolvable::Kind> kinds;
00143 std::set<Resolvable::Kind> kinds_flags;
00144 Pathname root;
00145 };
00146
00148
00149
00150
00152
00154
00155
00156
00157
00158 XMLFilesBackend::XMLFilesBackend(const Pathname &root) : Backend(root)
00159 {
00160 d = new Private;
00161 d->randomFileName = false;
00162 d->root = root;
00163
00164
00165 d->kinds.insert(ResTraits<zypp::Patch>::kind);
00166
00167
00168 d->kinds.insert(ResTraits<zypp::Selection>::kind);
00169 d->kinds.insert(ResTraits<zypp::Product>::kind);
00170 d->kinds.insert(ResTraits<zypp::Pattern>::kind);
00171 d->kinds.insert(ResTraits<zypp::Language>::kind);
00172
00173
00174 d->kinds_flags.insert(ResTraits<zypp::Package>::kind);
00175 d->kinds_flags.insert(ResTraits<zypp::Patch>::kind);
00176
00177 d->kinds_flags.insert(ResTraits<zypp::Script>::kind);
00178 d->kinds_flags.insert(ResTraits<zypp::Selection>::kind);
00179 d->kinds_flags.insert(ResTraits<zypp::Product>::kind);
00180 d->kinds_flags.insert(ResTraits<zypp::Pattern>::kind);
00181 d->kinds_flags.insert(ResTraits<zypp::Language>::kind);
00182
00183
00184
00185 if (!isBackendInitialized())
00186 {
00187 DBG << "Database not initialized" << std::endl;
00188 initBackend();
00189
00190 if (!isBackendInitialized())
00191 DBG << "Error, cant init database" << std::endl;
00192 else
00193 DBG << "Database initialized" << std::endl;
00194 }
00195 else
00196 {
00197 DBG << "Database already initialized" << std::endl;
00198 }
00199 }
00200
00201 Date XMLFilesBackend::timestamp() const
00202 {
00203 PathInfo ts_info = PathInfo( d->root + Pathname(ZYPP_DB_DIR) + "timestamp" );
00204 if ( ts_info.isExist() )
00205 {
00206 return Date(ts_info.mtime());
00207 }
00208 else
00209 {
00210 updateTimestamp();
00211 return Date::now();
00212 }
00213 }
00214
00215 bool
00216 XMLFilesBackend::isBackendInitialized() const
00217 {
00218 bool ok = true;
00219 Pathname dbdir = d->root + ZYPP_DB_DIR;
00220 ok = ok && PathInfo(dbdir).isExist();
00221
00222 bool fixperms = false;
00223
00224
00225
00226 if ( ok && PathInfo(dbdir).perm() == 0700 )
00227 {
00228 if ( geteuid() == 0 )
00229 {
00230 fixperms = true;
00231 WAR << "Wrong permissions for /var/lib/zypp, will fix" << std::endl;
00232
00233 const char* argv[] =
00234 {
00235 "chmod",
00236 "-R",
00237 "0755",
00238 "/var/lib/zypp",
00239 NULL
00240 };
00241
00242 ExternalProgram prog(argv,ExternalProgram::Discard_Stderr, false, -1, true);
00243 prog.close();
00244 }
00245 else
00246 {
00247 WAR << "Wrong permissions for /var/lib/zypp, but can't fix unless you run as root." << std::endl;
00248 }
00249
00250 }
00251
00252
00253 std::set<Resolvable::Kind>::const_iterator it_kinds;
00254 for ( it_kinds = d->kinds.begin() ; it_kinds != d->kinds.end(); ++it_kinds )
00255 {
00256 Resolvable::Kind kind = (*it_kinds);
00257 bool isthere = PathInfo( dirForResolvableKind(kind) ).isExist();
00258 ok = ok && isthere;
00259 }
00260
00261
00262 for ( it_kinds = d->kinds_flags.begin() ; it_kinds != d->kinds_flags.end(); ++it_kinds )
00263 {
00264 Resolvable::Kind kind = (*it_kinds);
00265 bool isthere = PathInfo( dirForResolvableKindFlags(kind) ).isExist();
00266 ok = ok && isthere;
00267 }
00268
00269
00270 bool nmthere = PathInfo( dirForNamedFlags() ).isExist();
00271 ok = ok && nmthere;
00272
00273 return ok;
00274 }
00275
00276 void
00277 XMLFilesBackend::initBackend()
00278 {
00279
00280
00281
00282 Pathname topdir = d->root + Pathname(ZYPP_DB_DIR);
00283 DBG << "Creating directory structure " << topdir << std::endl;
00284
00285 if (0 != assert_dir(topdir, 0755))
00286 ZYPP_THROW(Exception("Cannot create XMLBackend db directory " + topdir.asString()));
00287
00288
00289 std::set<Resolvable::Kind>::const_iterator it_kinds;
00290 for ( it_kinds = d->kinds.begin() ; it_kinds != d->kinds.end(); ++it_kinds )
00291 {
00292 Resolvable::Kind kind = (*it_kinds);
00293 Pathname p(dirForResolvableKind(kind));
00294 if (0 != assert_dir(p, 0755))
00295 ZYPP_THROW(Exception("Cannot create directory " + p.asString()));
00296 else
00297 MIL << "Created " << p.asString() << std::endl;
00298 }
00299
00300
00301 for ( it_kinds = d->kinds_flags.begin() ; it_kinds != d->kinds_flags.end(); ++it_kinds )
00302 {
00303 Resolvable::Kind kind = (*it_kinds);
00304 Pathname p(dirForResolvableKindFlags(kind));
00305 if (0 != assert_dir(p, 0755))
00306 ZYPP_THROW(Exception("Cannot create directory " + p.asString()));
00307 else
00308 MIL << "Created " << p.asString() << std::endl;
00309 }
00310
00311
00312 Pathname namedflags(dirForNamedFlags());
00313 if (0 != assert_dir(namedflags, 0755))
00314 ZYPP_THROW(Exception("Cannot create directory " + namedflags.asString()));
00315 else
00316 MIL << "Created " << namedflags.asString() << std::endl;
00317 }
00318
00319 void XMLFilesBackend::setRandomFileNameEnabled( bool enabled )
00320 {
00321 d->randomFileName = enabled;
00322 }
00323
00324 std::string
00325 XMLFilesBackend::dirForResolvableKind( Resolvable::Kind kind ) const
00326 {
00327 return Pathname( d->root + Pathname(ZYPP_DB_DIR) + Pathname(resolvableKindToString(kind, true)) ).asString();
00328 }
00329
00330 std::string
00331 XMLFilesBackend::dirForResolvableKindFlags( Resolvable::Kind kind ) const
00332 {
00333 return Pathname( d->root + Pathname(ZYPP_DB_DIR) + Pathname("flags") + Pathname(resolvableKindToString(kind, true)) ).asString();
00334 }
00335
00336 std::string
00337 XMLFilesBackend::dirForNamedFlags() const
00338 {
00339 return Pathname( d->root + Pathname(ZYPP_DB_DIR) + Pathname("named-flags")).asString();
00340 }
00341
00342 std::string
00343 XMLFilesBackend::dirForResolvable( ResObject::constPtr resolvable ) const
00344 {
00345 return dirForResolvableKind(resolvable->kind());
00346 }
00347
00348 std::string
00349 XMLFilesBackend::dirForResolvableFlags( ResObject::constPtr resolvable ) const
00350 {
00351 return dirForResolvableKindFlags(resolvable->kind());
00352 }
00353
00354 std::string
00355 XMLFilesBackend::fileNameForNVR( const NVR &nvr ) const
00356 {
00357 std::string filename;
00358 filename = nvr.name;
00359 if ( nvr.edition != Edition::noedition )
00360 {
00361 filename += "-" + nvr.edition.asString();
00362 }
00363
00364 std::stringstream filename_stream(filename);
00365 std::string filename_encoded = Digest::digest("MD5", filename_stream);
00366 return filename_encoded;
00367 }
00368
00369 std::string
00370 XMLFilesBackend::fileNameForResolvable( ResObject::constPtr resolvable ) const
00371 {
00372 return fileNameForNVR( NVR( resolvable->name(), resolvable->edition() ) );
00373 }
00374
00375 std::string
00376 XMLFilesBackend::fullPathForResolvable( ResObject::constPtr resolvable ) const
00377 {
00378 return( Pathname( dirForResolvable(resolvable) ) / fileNameForResolvable(resolvable) ).asString();
00379 }
00380
00381 std::string
00382 XMLFilesBackend::fullPathForNamedFlags( const std::string &key ) const
00383 {
00384 std::stringstream key_stream(key);
00385 std::string key_encoded = Digest::digest("MD5", key_stream);
00386 return( Pathname( dirForNamedFlags() ) / key_encoded ).asString();
00387 }
00388
00389 std::string
00390 XMLFilesBackend::fullPathForResolvableFlags( ResObject::constPtr resolvable ) const
00391 {
00392
00393 return( Pathname( dirForResolvableFlags(resolvable) ) / fileNameForResolvable(resolvable) ).asString();
00394 }
00395
00396 void
00397 XMLFilesBackend::setObjectFlag( ResObject::constPtr resolvable, const std::string &flag )
00398 {
00399 std::set<std::string> flags = objectFlags(resolvable);
00400 flags.insert(flag);
00401 writeObjectFlags(resolvable, flags);
00402 }
00403
00404 void
00405 XMLFilesBackend::removeObjectFlag( ResObject::constPtr resolvable, const std::string &flag )
00406 {
00407 std::set<std::string> flags = objectFlags(resolvable);
00408 flags.erase(flag);
00409 writeObjectFlags(resolvable, flags);
00410 }
00411
00412 void
00413 XMLFilesBackend::writeObjectFlags( ResObject::constPtr resolvable, const std::set<std::string> &flags )
00414 {
00415 std::string filename = fullPathForResolvableFlags(resolvable);
00416 writeFlagsInFile( filename, flags );
00417 MIL << "Wrote " << flags.size() << " flags for " << resolvable->name() << " " << resolvable->edition() << std::endl;
00418 }
00419
00420 std::set<std::string>
00421 XMLFilesBackend::objectFlags( ResObject::constPtr resolvable ) const
00422 {
00423 std::string filename = fullPathForResolvableFlags(resolvable);
00424 return flagsFromFile(filename);
00425 }
00426
00427 bool
00428 XMLFilesBackend::doesObjectHasFlag( ResObject::constPtr resolvable, const std::string &flag ) const
00429 {
00430 std::set<std::string> flags = objectFlags(resolvable);
00431 return (find(flags.begin(), flags.end(), flag) != flags.end());
00432 }
00433
00435
00437
00438 void
00439 XMLFilesBackend::setFlag( const std::string &key, const std::string &flag )
00440 {
00441 std::set<std::string> _flags = flags(key);
00442 _flags.insert(flag);
00443 writeFlags(key, _flags);
00444 }
00445
00446 void
00447 XMLFilesBackend::removeFlag( const std::string &key, const std::string &flag )
00448 {
00449 std::set<std::string> _flags = flags(key);
00450 _flags.erase(flag);
00451 writeFlags(key, _flags);
00452 }
00453
00454 std::set<std::string>
00455 XMLFilesBackend::flags( const std::string &key ) const
00456 {
00457 std::string filename = fullPathForNamedFlags(key);
00458 return flagsFromFile(filename);
00459 }
00460
00461 bool
00462 XMLFilesBackend::hasFlag( const std::string &key, const std::string &flag ) const
00463 {
00464 std::set<std::string> _flags = flags(key);
00465 return (find(_flags.begin(), _flags.end(), flag) != _flags.end());
00466 }
00467
00468 void
00469 XMLFilesBackend::writeFlags( const std::string &key, const std::set<std::string> &pflags )
00470 {
00471 std::string filename = fullPathForNamedFlags(key);
00472 writeFlagsInFile( filename, pflags );
00473 MIL << "Wrote " << pflags.size() << " flags for " << key << std::endl;
00474 }
00475
00477
00479
00480 void
00481 XMLFilesBackend::writeFlagsInFile( const std::string &filename, const std::set<std::string> &pflags )
00482 {
00483 std::ofstream file(filename.c_str());
00484 if (!file) {
00485 ZYPP_THROW (Exception( "Can't open " + filename ) );
00486 }
00487
00488 try
00489 {
00490 for ( std::set<std::string>::const_iterator it = pflags.begin(); it != pflags.end(); it++)
00491 {
00492
00493 if ( *it == std::string() )
00494 continue;
00495 else
00496 file << *it << std::endl;
00497 }
00498 file << std::endl;
00499 MIL << "Wrote " << pflags.size() << " flags in " << filename << std::endl;
00500 }
00501 catch( std::exception &e )
00502 {
00503 ZYPP_THROW(Exception("Can't write flags to store"));
00504 }
00505 updateTimestamp();
00506 }
00507
00508 std::set<std::string>
00509 XMLFilesBackend::flagsFromFile( const std::string &filename ) const
00510 {
00511 std::set<std::string> _flags;
00512
00513 if ( ! PathInfo( filename ).isExist() )
00514 return _flags;
00515
00516 std::ifstream file(filename.c_str());
00517 if (!file) {
00518 ZYPP_THROW (Exception( "Can't open " + filename ) );
00519 }
00520
00521 std::string buffer;
00522 while(file && !file.eof())
00523 {
00524 getline(file, buffer);
00525 if (buffer == std::string())
00526 continue;
00527
00528 _flags.insert(buffer);
00529 }
00530
00531 return _flags;
00532 }
00533
00534 void
00535 XMLFilesBackend::updateTimestamp() const
00536 {
00537 Pathname filename = d->root + Pathname(ZYPP_DB_DIR) + "timestamp";
00538 std::ofstream file(filename.asString().c_str(), std::ios::out);
00539 if (!file)
00540 {
00541 ZYPP_THROW (Exception( "Can't open timestamp file " + filename.asString() ) );
00542 }
00543 file.close();
00544 }
00545
00547
00549
00550 void
00551 XMLFilesBackend::storeObject( ResObject::constPtr resolvable )
00552 {
00553
00554 std::set<Resolvable::Kind>::const_iterator it;
00555 it = find(d->kinds.begin(), d->kinds.end(), resolvable->kind() );
00556 if (it == d->kinds.end())
00557 {
00558 ERR << "This backend was not designed to store resolvable of kind " << resolvableKindToString(resolvable->kind()) << ", ignoring..." << std::endl;
00559 return;
00560 }
00561
00562 std::string xml = castedToXML(resolvable);
00563 std::string filename = fullPathForResolvable(resolvable);
00564
00565 std::ofstream file;
00566
00567 try
00568 {
00569 file.open(filename.c_str());
00570 file << xml;
00571 file.close();
00572 }
00573 catch(std::exception &e)
00574 {
00575 ERR << "Error saving resolvable " << resolvable << std::endl;
00576 ZYPP_THROW(Exception(e.what()));
00577 }
00578 updateTimestamp();
00579 }
00580
00581 void
00582 XMLFilesBackend::deleteFileObject( const Pathname &filename ) const
00583 {
00584 try
00585 {
00586 int ret = filesystem::unlink( filename );
00587 if ( ret != 0 && ret != ENOENT )
00588 {
00589 ERR << "Error removing resolvable file " << filename << std::endl;
00590 ZYPP_THROW(Exception("Error deleting " + filename.asString()));
00591 }
00592 updateTimestamp();
00593 }
00594 catch(std::exception &e)
00595 {
00596 ERR << "Error removing resolvable file " << filename << std::endl;
00597 ZYPP_THROW(Exception(e.what()));
00598 }
00599 }
00600
00601 void
00602 XMLFilesBackend::deleteObject( ResObject::constPtr resolvable )
00603 {
00604
00605 std::set<Resolvable::Kind>::const_iterator it;
00606 it = find(d->kinds.begin(), d->kinds.end(), resolvable->kind() );
00607 if (it == d->kinds.end())
00608 {
00609 ERR << "This backend was not designed to store resolvable of kind " << resolvableKindToString(resolvable->kind()) << ", ignoring..." << std::endl;
00610 return;
00611 }
00612
00613
00614 try
00615 {
00616 deleteFileObject( fullPathForResolvable(resolvable) );
00617 }
00618 catch ( const Exception &e )
00619 {
00620 ERR << "Error removing resolvable " << resolvable << std::endl;
00621 ZYPP_RETHROW(e);
00622 }
00623 }
00624
00625 std::list<ResObject::Ptr> XMLFilesBackend::resolvablesFromFile( std::string file_path, Resolvable::Kind kind ) const
00626 {
00627 MIL << "[" << resolvableKindToString( kind, false ) << "] - " << file_path << std::endl;
00628 std::list<ResObject::Ptr> resolvables;
00629 std::ifstream res_file(file_path.c_str());
00630 if ( kind == ResTraits<zypp::Patch>::kind )
00631 {
00632
00633
00634
00635 try {
00636 XMLPatchParser iter(res_file,"");
00637 for (; !iter.atEnd(); ++iter)
00638 {
00639 Patch::Ptr patch = createPatch(**iter);
00640 resolvables.push_back(patch);
00641 Patch::AtomList atoms = patch->atoms();
00642 for (Patch::AtomList::iterator at = atoms.begin(); at != atoms.end(); at++)
00643 resolvables.push_back(*at);
00644
00645 break;
00646 }
00647 }
00648 catch (const Exception & excpt_r)
00649 {
00650 ZYPP_CAUGHT( excpt_r );
00651 WAR << "Skipping invalid patch file " << file_path << endl;
00652 }
00653 }
00654 else if ( kind == ResTraits<zypp::Product>::kind )
00655 {
00656 XMLProductParser iter(res_file,"");
00657 for (; !iter.atEnd(); ++iter)
00658 {
00659 resolvables.push_back(createProduct(**iter));
00660 break;
00661 }
00662 }
00663 else if ( kind == ResTraits<zypp::Selection>::kind )
00664 {
00665 XMLPatternParser iter(res_file,"");
00666 for (; !iter.atEnd(); ++iter)
00667 {
00668 resolvables.push_back(createSelection(**iter));
00669 break;
00670 }
00671 }
00672 else if ( kind == ResTraits<zypp::Pattern>::kind )
00673 {
00674 XMLPatternParser iter(res_file,"");
00675 for (; !iter.atEnd(); ++iter)
00676 {
00677 resolvables.push_back(createPattern(**iter));
00678 break;
00679 }
00680 }
00681 else if ( kind == ResTraits<zypp::Language>::kind )
00682 {
00683 XMLLanguageParser iter(res_file,"");
00684 for (; !iter.atEnd(); ++iter)
00685 {
00686 resolvables.push_back(createLanguage(**iter));
00687 break;
00688 }
00689 }
00690 else
00691 {
00692
00693 }
00694 return resolvables;
00695 }
00696
00697 std::list<ResObject::Ptr>
00698 XMLFilesBackend::storedObjects() const
00699 {
00700 DBG << std::endl;
00701 std::list<ResObject::Ptr> objects;
00702
00703 std::set<Resolvable::Kind>::const_iterator it_kinds;
00704 for ( it_kinds = d->kinds.begin() ; it_kinds != d->kinds.end(); ++it_kinds )
00705 {
00706 Resolvable::Kind kind = (*it_kinds);
00707 std::list<ResObject::Ptr> objects_for_kind = storedObjects(kind);
00708 std::list<ResObject::Ptr>::iterator it;
00709 for( it = objects_for_kind.begin(); it != objects_for_kind.end(); ++it)
00710 {
00711
00712 objects.push_back(*it);
00713 }
00714 }
00715 return objects;
00716 }
00717
00718 std::list<ResObject::Ptr>
00719 XMLFilesBackend::storedObjects(const Resolvable::Kind kind) const
00720 {
00721 std::list<ResObject::Ptr> objects;
00722 Pathname dir_path(dirForResolvableKind(kind));
00723 DBG << "Reading objects of kind " << resolvableKindToString(kind) << " in " << dir_path << std::endl;
00724
00725 if ( ! PathInfo( dir_path ).isExist() )
00726 {
00727 ERR << "path " << dir_path << " does not exists. Required to read objects of kind " << resolvableKindToString(kind) << std::endl;
00728 return std::list<ResObject::Ptr>();
00729 }
00730
00731 list<string> files;
00732 filesystem::readdir( files, dir_path, false );
00733
00734 for ( list<string>::const_iterator it = files.begin(); it != files.end(); ++it )
00735 {
00736 Pathname curr_file = dir_path + (*it);
00737 DBG << "[" << resolvableKindToString( kind, false ) << "] - " << curr_file << std::endl;
00738 std::list<ResObject::Ptr> objects_for_file;
00739 objects_for_file = resolvablesFromFile( curr_file.asString(), kind);
00740 for ( std::list<ResObject::Ptr>::iterator it = objects_for_file.begin(); it != objects_for_file.end(); ++it)
00741 objects.push_back(*it);
00742 }
00743
00744 MIL << "done reading " << objects.size() << " stored objects for file of kind " << resolvableKindToString(kind) << std::endl;
00745 return objects;
00746 }
00747
00748 std::list<ResObject::Ptr>
00749 XMLFilesBackend::storedObjects(const Resolvable::Kind kind, const std::string & name, bool partial_match) const
00750 {
00751 std::list<ResObject::Ptr> result;
00752 std::list<ResObject::Ptr> all;
00753 all = storedObjects(kind);
00754 std::list<ResObject::Ptr>::const_iterator it;
00755 for( it = all.begin(); it != all.end(); ++it)
00756 {
00757 ResObject::Ptr item = *it;
00758 if (item->name() == name )
00759 result.push_back(item);
00760 }
00761 MIL << "done reading stored objects of kind " << resolvableKindToString(kind) << " and keyword [" << name <<"]" << std::endl;
00762 return result;
00763 }
00764
00765 Patch::Ptr
00766 XMLFilesBackend::createPatch( const zypp::parser::xmlstore::XMLPatchData & parsed ) const
00767 {
00768 try
00769 {
00770 detail::ResImplTraits<XMLPatchImpl>::Ptr impl(new XMLPatchImpl());
00771 impl->_patch_id = parsed.patchId;
00772 impl->_timestamp = str::strtonum<time_t>(parsed.timestamp);
00773 impl->_category = parsed.category;
00774 impl->_reboot_needed = parsed.rebootNeeded;
00775 impl->_affects_pkg_manager = parsed.packageManager;
00776
00777
00778 impl->_summary = parsed.summary;
00779 impl->_description = parsed.summary;
00780
00781 impl->_install_notify = parsed.install_notify;
00782 impl->_delete_notify = parsed.delete_notify;
00783 impl->_license_to_confirm = parsed.license_to_confirm;
00784 impl->_vendor = parsed.vendor;
00785 impl->_size = parsed.size;
00786 impl->_downloadSize = parsed.downloadSize;
00787 impl->_install_only = parsed.install_only;
00788 impl->_build_time = parsed.build_time;
00789 impl->_install_time = parsed.install_time;
00790
00791 Arch arch;
00792 if (!parsed.arch.empty())
00793 arch = Arch(parsed.arch);
00794
00795
00796 NVRAD dataCollect( parsed.name,
00797 Edition( parsed.ver, parsed.rel, parsed.epoch ), arch,
00798 createDependencies(parsed, ResTraits<Patch>::kind) );
00799 Patch::Ptr patch = detail::makeResolvableFromImpl( dataCollect, impl );
00800 CapFactory _f;
00801
00802
00803
00804
00805 ResObject::Ptr atom;
00806 for (std::list<XMLPatchAtomData_Ptr >::const_iterator it = parsed.atoms.begin(); it != parsed.atoms.end(); it++)
00807 {
00808 switch ((*it)->atomType())
00809 {
00810 case XMLPatchAtomData::Atom:
00811 {
00812
00813
00814
00815
00816
00817
00818
00819 XMLPatchAtomData_Ptr atom_data = dynamic_pointer_cast<XMLPatchAtomData>(*it);
00820 atom = createAtom(*atom_data);
00821 impl->_atoms.push_back(atom);
00822 break;
00823 }
00824 case XMLPatchAtomData::Message:
00825 {
00826 XMLPatchMessageData_Ptr message_data = dynamic_pointer_cast<XMLPatchMessageData>(*it);
00827 atom = createMessage(*message_data);
00828 impl->_atoms.push_back(atom);
00829 break;
00830 }
00831 case XMLPatchAtomData::Script:
00832 {
00833 XMLPatchScriptData_Ptr script_data = dynamic_pointer_cast<XMLPatchScriptData>(*it);
00834 atom = createScript(*script_data);
00835 if ( doesObjectHasFlag( atom, "SCRIPT_EXEC_FAILED" ) )
00836 {
00837 WAR << "Patch script not yet successfully executed: " << atom << endl;
00838 } else {
00839 impl->_atoms.push_back(atom);
00840 }
00841 break;
00842 }
00843 default:
00844 ERR << "Unknown type of atom" << endl;
00845 }
00846
00847 Capability cap( _f.parse(atom->kind(), atom->name(), Rel::EQ, atom->edition() ));
00848 patch->injectRequires(cap);
00849 }
00850 return patch;
00851 }
00852 catch (const Exception & excpt_r)
00853 {
00854 ZYPP_CAUGHT(excpt_r);
00855 Exception nexcpt("Cannot create patch object");
00856 nexcpt.remember(excpt_r);
00857 ZYPP_THROW(nexcpt);
00858 }
00859 return 0L;
00860 }
00861
00862 Atom::Ptr
00863 XMLFilesBackend::createAtom( const zypp::parser::xmlstore::XMLPatchAtomData & parsed ) const
00864 {
00865 try
00866 {
00867 detail::ResImplTraits<XMLAtomImpl>::Ptr impl(new XMLAtomImpl());
00868
00869 Arch arch;
00870 if (!parsed.arch.empty())
00871 arch = Arch(parsed.arch);
00872
00873
00874 NVRAD dataCollect( parsed.name, Edition( parsed.ver, parsed.rel, parsed.epoch ), arch, createDependencies(parsed, ResTraits<Atom>::kind) );
00875
00876 impl->_summary = parsed.summary;
00877 impl->_description = parsed.summary;
00878
00879 impl->_install_notify = parsed.install_notify;
00880 impl->_delete_notify = parsed.delete_notify;
00881 impl->_license_to_confirm = parsed.license_to_confirm;
00882 impl->_vendor = parsed.vendor;
00883 impl->_size = parsed.size;
00884 impl->_downloadSize = parsed.downloadSize;
00885 impl->_install_only = parsed.install_only;
00886 impl->_build_time = parsed.build_time;
00887 impl->_install_time = parsed.install_time;
00888
00889 Atom::Ptr atom = detail::makeResolvableFromImpl( dataCollect, impl);
00890 return atom;
00891 }
00892 catch (const Exception & excpt_r)
00893 {
00894 ZYPP_CAUGHT(excpt_r);
00895 Exception nexcpt("Cannot create atom object");
00896 nexcpt.remember(excpt_r);
00897 ZYPP_THROW(nexcpt);
00898 }
00899 return 0L;
00900 }
00901
00902 Message::Ptr
00903 XMLFilesBackend::createMessage( const zypp::parser::xmlstore::XMLPatchMessageData & parsed ) const
00904 {
00905 try
00906 {
00907 detail::ResImplTraits<XMLMessageImpl>::Ptr impl(new XMLMessageImpl());
00908 impl->_text = parsed.text;
00909
00910 Arch arch;
00911 if (!parsed.arch.empty())
00912 arch = Arch(parsed.arch);
00913
00914 impl->_summary = parsed.summary;
00915 impl->_description = parsed.summary;
00916
00917 impl->_install_notify = parsed.install_notify;
00918 impl->_delete_notify = parsed.delete_notify;
00919 impl->_license_to_confirm = parsed.license_to_confirm;
00920 impl->_vendor = parsed.vendor;
00921 impl->_size = parsed.size;
00922 impl->_downloadSize = parsed.downloadSize;
00923 impl->_install_only = parsed.install_only;
00924 impl->_build_time = parsed.build_time;
00925 impl->_install_time = parsed.install_time;
00926
00927
00928 NVRAD dataCollect( parsed.name, Edition( parsed.ver, parsed.rel, parsed.epoch ), arch, createDependencies(parsed, ResTraits<Message>::kind) );
00929 Message::Ptr message = detail::makeResolvableFromImpl( dataCollect, impl);
00930 return message;
00931 }
00932 catch (const Exception & excpt_r)
00933 {
00934 ZYPP_CAUGHT(excpt_r);
00935 Exception nexcpt("Cannot create message object");
00936 nexcpt.remember(excpt_r);
00937 ZYPP_THROW(nexcpt);
00938 }
00939 return 0L;
00940 }
00941
00942 Script::Ptr
00943 XMLFilesBackend::createScript(const zypp::parser::xmlstore::XMLPatchScriptData & parsed ) const
00944 {
00945 try
00946 {
00947 detail::ResImplTraits<XMLScriptImpl>::Ptr impl(new XMLScriptImpl());
00948
00949 impl->_doScript = parsed.doScript;
00950 impl->_undoScript = parsed.undoScript;
00951
00952 Arch arch;
00953 if (!parsed.arch.empty())
00954 arch = Arch(parsed.arch);
00955
00956 impl->_summary = parsed.summary;
00957 impl->_description = parsed.summary;
00958
00959 impl->_install_notify = parsed.install_notify;
00960 impl->_delete_notify = parsed.delete_notify;
00961 impl->_license_to_confirm = parsed.license_to_confirm;
00962 impl->_vendor = parsed.vendor;
00963 impl->_size = parsed.size;
00964 impl->_downloadSize = parsed.downloadSize;
00965 impl->_install_only = parsed.install_only;
00966 impl->_build_time = parsed.build_time;
00967 impl->_install_time = parsed.install_time;
00968
00969
00970 NVRAD dataCollect( parsed.name, Edition( parsed.ver, parsed.rel, parsed.epoch ), arch, createDependencies(parsed, ResTraits<Script>::kind));
00971 Script::Ptr script = detail::makeResolvableFromImpl( dataCollect, impl );
00972 return script;
00973 }
00974 catch (const Exception & excpt_r)
00975 {
00976 ZYPP_CAUGHT(excpt_r);
00977 Exception nexcpt("Cannot create script object");
00978 nexcpt.remember(excpt_r);
00979 ZYPP_THROW(nexcpt);
00980 }
00981 catch (const std::exception & excpt_r)
00982 {
00983 ERR << excpt_r.what() << endl;
00984 ZYPP_THROW(Exception("Cannot create script object"));
00985 }
00986 return 0L;
00987 }
00988
00989 Language::Ptr
00990 XMLFilesBackend::createLanguage( const zypp::parser::xmlstore::XMLLanguageData & parsed ) const
00991 {
00992 try
00993 {
00994 return Language::installedInstance( Locale(parsed.name) );
00995 }
00996 catch (const Exception & excpt_r)
00997 {
00998 ZYPP_CAUGHT(excpt_r);
00999 Exception nexcpt("Cannot create language object");
01000 nexcpt.remember(excpt_r);
01001 ZYPP_THROW(nexcpt);
01002 }
01003 return 0L;
01004 }
01005
01006
01007 Product::Ptr
01008 XMLFilesBackend::createProduct( const zypp::parser::xmlstore::XMLProductData & parsed ) const
01009 {
01010 try
01011 {
01012 detail::ResImplTraits<XMLProductImpl>::Ptr impl(new XMLProductImpl());
01013
01014 Edition parser_edition = ( parsed.parser_version.empty() ? Edition::noedition : Edition(parsed.parser_version) );
01015
01016 impl->_summary = parsed.summary;
01017 impl->_description = parsed.summary;
01018
01019 impl->_install_notify = parsed.install_notify;
01020 impl->_delete_notify = parsed.delete_notify;
01021 impl->_license_to_confirm = parsed.license_to_confirm;
01022 impl->_vendor = parsed.vendor;
01023 impl->_size = parsed.size;
01024 impl->_downloadSize = parsed.downloadSize;
01025 impl->_install_only = parsed.install_only;
01026 impl->_build_time = parsed.build_time;
01027 impl->_install_time = parsed.install_time;
01028
01029 impl->_type = parsed.type;
01030 impl->_short_name = parsed.short_name;
01031 impl->_dist_name = parsed.dist_name;
01032 impl->_dist_version = parsed.dist_version;
01033
01034 if ( parsed.releasenotesurl.size() > 0 )
01035 impl->_release_notes_url = parsed.releasenotesurl;
01036 else
01037 impl->_release_notes_url = Url();
01038
01039
01040 list<string> update_urls = parsed.update_urls;
01041 for ( list<string>::const_iterator it = update_urls.begin(); it != update_urls.end(); ++it )
01042 {
01043 try
01044 {
01045 Url u(*it);
01046 impl->_update_urls.push_back (u);
01047 }
01048 catch ( const Exception &e )
01049 {
01050 ZYPP_CAUGHT(e);
01051 Exception ne("Error parsing update url: " + e.msg());
01052 ne.remember(e);
01053 ZYPP_THROW(ne);
01054 }
01055 }
01056
01057
01058 list<string> extra_urls = parsed.extra_urls;
01059 for ( list<string>::const_iterator it = extra_urls.begin(); it != extra_urls.end(); ++it )
01060 {
01061 try
01062 {
01063 Url u(*it);
01064 impl->_extra_urls.push_back (u);
01065 }
01066 catch ( const Exception &e )
01067 {
01068 ZYPP_CAUGHT(e);
01069 Exception ne("Error parsing extra url: " + e.msg());
01070 ne.remember(e);
01071 ZYPP_THROW(ne);
01072 }
01073 }
01074
01075
01076 list<string> optional_urls = parsed.optional_urls;
01077 for ( list<string>::const_iterator it = optional_urls.begin(); it != optional_urls.end(); ++it )
01078 {
01079 try
01080 {
01081 Url u(*it);
01082 impl->_optional_urls.push_back (u);
01083 }
01084 catch ( const Exception &e )
01085 {
01086 ZYPP_CAUGHT(e);
01087 Exception ne("Error parsing optional url: " + e.msg());
01088 ne.remember(e);
01089 ZYPP_THROW(ne);
01090 }
01091 }
01092
01093 impl->_flags = parsed.flags;
01094
01095 Arch arch;
01096 if (!parsed.arch.empty())
01097 arch = Arch(parsed.arch);
01098
01099 Edition prod_edition( parsed.ver, parsed.rel, parsed.epoch );
01100 string prod_name(parsed.name);
01101
01102
01103 bool save_new_product_again_workaround = false;
01104 if ( parser_edition == Edition::noedition )
01105 {
01106 MIL << "Product " << parsed.name << " " << prod_edition << " possibly suffers from bug #205392. checking..." << endl;
01107 PRODUCT_TABLE_ENTRY *all_products = products_table();
01108 while ( all_products->dist_name != 0L )
01109 {
01110
01111 if ( ( parsed.name == all_products->dist_name ) && ( prod_edition.asString() == all_products->dist_version ) )
01112 {
01113 MIL << "[ATTENTION] Detected bug #205392. Product " << parsed.name << " " << prod_edition << " will be changed to " << all_products->product_name << " " << all_products->product_version << std::endl;
01114
01115
01116 Pathname wrong_product = Pathname(dirForResolvableKind(ResTraits<zypp::Product>::kind)) + fileNameForNVR( NVR( parsed.name, prod_edition) );
01117
01118
01119
01120 prod_name = string(all_products->product_name);
01121 prod_edition = Edition(all_products->product_version);
01122
01123
01124 impl->_dist_name = all_products->dist_name;
01125 impl->_dist_version = Edition(all_products->dist_version);
01126
01127
01128 deleteFileObject( wrong_product );
01129 MIL << "Fix for bug #205392 Old product deleted." << std::endl;
01130 save_new_product_again_workaround = true;
01131 break;
01132 }
01133 ++all_products;
01134 }
01135
01136 }
01137
01138
01139 std::replace(prod_name.begin(), prod_name.end(), ' ', '_');
01140
01141
01142 NVRAD dataCollect( prod_name, prod_edition, arch, createDependencies(parsed, ResTraits<Product>::kind) );
01143 Product::Ptr product = detail::makeResolvableFromImpl( dataCollect, impl );
01144
01145 if ( save_new_product_again_workaround )
01146 {
01147 const_cast<XMLFilesBackend *>(this)->storeObject(product);
01148 MIL << "Fixed Product saved. Fix for bug #205392. complete" << std::endl;
01149 }
01150
01151 return product;
01152 }
01153 catch (const Exception & excpt_r)
01154 {
01155 ZYPP_CAUGHT(excpt_r);
01156 Exception nexcpt("Cannot create product object");
01157 nexcpt.remember(excpt_r);
01158 ZYPP_THROW(nexcpt);
01159 }
01160 return 0L;
01161 }
01162
01163 Pattern::Ptr
01164 XMLFilesBackend::createPattern( const zypp::parser::xmlstore::XMLPatternData & parsed ) const
01165 {
01166 try
01167 {
01168 detail::ResImplTraits<XMLPatternImpl>::Ptr impl(new XMLPatternImpl());
01169
01170 impl->_summary = parsed.summary;
01171 impl->_description = parsed.summary;
01172
01173 impl->_install_notify = parsed.install_notify;
01174 impl->_delete_notify = parsed.delete_notify;
01175 impl->_license_to_confirm = parsed.license_to_confirm;
01176 impl->_vendor = parsed.vendor;
01177 impl->_size = parsed.size;
01178 impl->_downloadSize = parsed.downloadSize;
01179 impl->_install_only = parsed.install_only;
01180 impl->_build_time = parsed.build_time;
01181 impl->_install_time = parsed.install_time;
01182
01183 impl->_user_visible = parsed.userVisible;
01184 impl->_default = ((parsed.default_ == "false" ) ? false : true );
01185 impl->_category = parsed.category;
01186 impl->_icon = parsed.icon;
01187 impl->_script = parsed.script;
01188
01189 Arch arch;
01190 if (!parsed.arch.empty())
01191 arch = Arch(parsed.arch);
01192
01193
01194
01195 if ( impl->_vendor.empty() )
01196 {
01197 impl->_vendor = "SUSE (assumed)";
01198 }
01199
01200
01201 NVRAD dataCollect( parsed.name, Edition( parsed.ver, parsed.rel, parsed.epoch ), arch, createDependencies( parsed, ResTraits<Pattern>::kind));
01202 Pattern::Ptr pattern = detail::makeResolvableFromImpl( dataCollect, impl );
01203 return pattern;
01204 }
01205 catch (const Exception & excpt_r)
01206 {
01207 ZYPP_CAUGHT(excpt_r);
01208 Exception nexcpt("Cannot create installation pattern object");
01209 nexcpt.remember(excpt_r);
01210 ZYPP_THROW(nexcpt);
01211 }
01212 return 0L;
01213 }
01214
01215 Selection::Ptr
01216 XMLFilesBackend::createSelection( const zypp::parser::xmlstore::XMLPatternData & parsed ) const
01217 {
01218 try
01219 {
01220 detail::ResImplTraits<XMLSelectionImpl>::Ptr impl(new XMLSelectionImpl());
01221
01222 impl->_summary = parsed.summary;
01223 impl->_description = parsed.summary;
01224
01225 impl->_install_notify = parsed.install_notify;
01226 impl->_delete_notify = parsed.delete_notify;
01227 impl->_license_to_confirm = parsed.license_to_confirm;
01228 impl->_vendor = parsed.vendor;
01229 impl->_size = parsed.size;
01230 impl->_downloadSize = parsed.downloadSize;
01231 impl->_install_only = parsed.install_only;
01232 impl->_build_time = parsed.build_time;
01233 impl->_install_time = parsed.install_time;
01234
01235 impl->_visible = parsed.userVisible;
01236 impl->_name = parsed.name;
01237
01238 impl->_category = parsed.category;
01239
01240 Arch arch;
01241 if (!parsed.arch.empty())
01242 arch = Arch(parsed.arch);
01243
01244
01245 NVRAD dataCollect( parsed.name, Edition( parsed.ver, parsed.rel, parsed.epoch ), arch, createDependencies( parsed, ResTraits<Pattern>::kind));
01246 Selection::Ptr selection = detail::makeResolvableFromImpl( dataCollect, impl );
01247 return selection;
01248 }
01249 catch (const Exception & excpt_r)
01250 {
01251 ZYPP_CAUGHT(excpt_r);
01252 Exception nexcpt("Cannot create installation selection object");
01253 nexcpt.remember(excpt_r);
01254 ZYPP_THROW(nexcpt);
01255 }
01256 return 0L;
01257 }
01258
01259 Dependencies
01260 XMLFilesBackend::createDependencies( const zypp::parser::xmlstore::XMLResObjectData & parsed, const Resolvable::Kind my_kind ) const
01261 {
01262 Dependencies _deps;
01263 for (std::list<XMLDependency>::const_iterator it = parsed.provides.begin(); it != parsed.provides.end(); it++)
01264 {
01265 _deps[Dep::PROVIDES].insert(createCapability(*it, my_kind));
01266 }
01267 for (std::list<XMLDependency>::const_iterator it = parsed.conflicts.begin(); it != parsed.conflicts.end(); it++)
01268 {
01269 _deps[Dep::CONFLICTS].insert(createCapability(*it, my_kind));
01270 }
01271
01272 for (std::list<XMLDependency>::const_iterator it = parsed.obsoletes.begin(); it != parsed.obsoletes.end(); it++)
01273 {
01274 _deps[Dep::OBSOLETES].insert(createCapability(*it, my_kind));
01275 }
01276
01277 for (std::list<XMLDependency>::const_iterator it = parsed.freshens.begin(); it != parsed.freshens.end(); it++)
01278 {
01279 _deps[Dep::FRESHENS].insert(createCapability(*it, my_kind));
01280 }
01281
01282 for (std::list<XMLDependency>::const_iterator it = parsed.recommends.begin(); it != parsed.recommends.end(); it++)
01283 {
01284 _deps[Dep::RECOMMENDS].insert(createCapability(*it, my_kind));
01285 }
01286
01287 for (std::list<XMLDependency>::const_iterator it = parsed.suggests.begin(); it != parsed.suggests.end(); it++)
01288 {
01289 _deps[Dep::SUGGESTS].insert(createCapability(*it, my_kind));
01290 }
01291
01292 for (std::list<XMLDependency>::const_iterator it = parsed.enhances.begin(); it != parsed.enhances.end(); it++)
01293 {
01294 _deps[Dep::ENHANCES].insert(createCapability(*it, my_kind));
01295 }
01296
01297 for (std::list<XMLDependency>::const_iterator it = parsed.requires.begin(); it != parsed.requires.end(); it++)
01298 {
01299 _deps[Dep::REQUIRES].insert(createCapability(*it, my_kind));
01300 }
01301
01302 for (std::list<XMLDependency>::const_iterator it = parsed.prerequires.begin(); it != parsed.prerequires.end(); it++)
01303 {
01304 _deps[Dep::PREREQUIRES].insert(createCapability(*it, my_kind));
01305 }
01306 return _deps;
01307 }
01308
01309 Capability
01310 XMLFilesBackend::createCapability(const XMLDependency & dep, const Resolvable::Kind & my_kind) const
01311 {
01312 CapFactory _f;
01313 Resolvable::Kind _kind = dep.kind == "" ? my_kind : Resolvable::Kind(dep.kind);
01314 Capability cap;
01315 cap = _f.parse( _kind, dep.encoded );
01316 return cap;
01317 }
01318
01320
01321
01322
01323
01324 XMLFilesBackend::~XMLFilesBackend()
01325 {
01326 delete d;
01327 }
01328
01330
01331
01332
01333
01334 void XMLFilesBackend::doTest()
01335 {}
01336
01337
01338
01339
01340
01341
01342 std::ostream & operator<<( std::ostream & str, const XMLFilesBackend & obj )
01343 {
01344 return str;
01345 }
01346
01348 }
01351 }