package.cpp

00001 
00002 // Name:        package.cpp
00003 // Purpose:     wxPackage, wxPackageArray
00004 // Author:      Julian Smart, Francesco Montorsi, Daniel J. Lauk
00005 // Modified by:
00006 // Created:     2004-11-07
00007 // RCS-ID:      $Id: package.cpp,v 1.24 2007/02/01 20:28:16 frm Exp $
00008 // Copyright:   (c) Julian Smart, Francesco Montorsi
00009 // Licence:     wxWidgets licence
00011 
00012 
00013 // For compilers that support precompilation, includes "wx/wx.h".
00014 #include "wx/wxprec.h"
00015 
00016 #ifdef __BORLANDC__
00017 #pragma hdrstop
00018 #endif
00019 
00020 #ifndef WX_PRECOMP
00021     #include "wx/wx.h"
00022 #endif
00023 
00024 // includes
00025 #include <wx/fileconf.h>
00026 #include <wx/filename.h>
00027 #include <wx/ptr_scpd.h>
00028 #include <wx/dir.h>
00029 
00030 // streams
00031 #include <wx/zipstrm.h>
00032 #include <wx/wfstream.h>
00033 #include <wx/tarstrm.h>
00034 #include <wx/mstream.h>
00035 #include <wx/txtstrm.h>
00036 #include <wx/zstream.h>
00037 
00038 #include <wx/protocol/ftp.h>
00039 
00040 #include "wxp/package.h"
00041 #include "wxp/wxp.h"
00042 #include "wxp/packagewxp.h"
00043 #include "wxp/wxbuild.h"
00044 
00045 #include <memory>       // for auto_ptr
00046 using namespace std;
00047 
00048 // static
00049 wxArrayString wxPackage::s_arrLocalRepo;
00050 wxArrayString wxPackage::s_arrRemoteRepo;
00051 
00052 // definitions
00053 
00054 wxString wxPackageStatusName[] =
00055 {
00056     _("Remote"),
00057     _("Downloaded"),
00058     _("Decompressed"),
00059     _("Built"),
00060     _("Installed")
00061 };
00062 
00063 
00064 // RTTI
00065 IMPLEMENT_DYNAMIC_CLASS(wxPackage, wxObject)
00066 
00067 // wx array implementations
00068 #include <wx/arrimpl.cpp> // this is a magic incantation which must be done!
00069 WX_DEFINE_OBJARRAY(wxPackageArrayHelper);
00070 
00071 // global objects
00072 wxPackage wxEmptyPackage;
00073 wxPackageArray wxEmptyPackageArray;
00074 
00075 
00076 
00077 // ----------------------------------------------------------------------------
00078 // string2enum helpers
00079 // ----------------------------------------------------------------------------
00080 
00081 wxIMPLEMENT_STRING2ENUM(PackageStatus, wxPS_MAX)
00082 
00083 
00084 
00085 // ----------------------------------------------------------------------------
00086 // wxPackage
00087 // ----------------------------------------------------------------------------
00088 
00089 bool wxPackage::IsOk() const
00090 {
00091     if (!wxPackageInfo::IsOk())
00092         return false;
00093 
00094     // can check anything of m_compiler ?
00095 
00096     switch (m_status)
00097     {
00098     case wxPS_INSTALLED:
00099         if (!wxFileName::DirExists(m_strInstallationPath))
00100             return false;
00101         // don't break !!
00102 
00103     case wxPS_DECOMPRESSED:
00104         if (m_strWXPPath.IsEmpty() || !wxFileName(m_strWXPPath).IsRelative())
00105             return false;
00106         if (!wxFileName::DirExists(m_strDecompressionPath))
00107             return false;
00108         // don't break !!
00109 
00110     case wxPS_DOWNLOADED:
00111         if (!wxFileName::FileExists(m_strDownloadPath))
00112             return false;
00113         // don't break !!
00114 
00115     case wxPS_REMOTE:
00116         break;
00117 
00118     default:
00119         return true;
00120     }
00121 
00122     return true;
00123 }
00124 
00125 void wxPackage::DoUpdateSubstitutionHashMap(wxPackageSubstituteInfoContext ctx)
00126 {
00127     wxPackageInfo::DoUpdateSubstitutionHashMap(ctx);
00128 
00129     wxStringHashMap &keywords = m_hashmapSubstitution[ctx];
00130 
00131 #define ADD_KEYWORD(name, value)   \
00132     keywords[wxT(name)] = value
00133 
00134     ADD_KEYWORD("packageroot", m_strDecompressionPath);
00135 
00136     switch (m_status)
00137     {
00138     case wxPS_REMOTE:
00139         if (!GetDownloadLink().IsEmpty())
00140             ADD_KEYWORD("location", GetDownloadLink());
00141         else
00142             ADD_KEYWORD("location", wxT("not available"));
00143         break;
00144 
00145     case wxPS_DOWNLOADED:
00146         ADD_KEYWORD("location", m_strDownloadPath);
00147         break;
00148     case wxPS_DECOMPRESSED:
00149     case wxPS_BUILT:
00150     case wxPS_INSTALLED:
00151         ADD_KEYWORD("location", m_strDecompressionPath);
00152         break;
00153 
00154     default:
00155         ADD_KEYWORD("location", wxT("not available"));
00156     }
00157 
00158     // merge the compiler's substitution hashmap with the "keywords" hashmap
00159     wxStringHashMap compiler = 
00160         m_compiler.GetSubstitutionHashMap(m_buildSystemType);
00161     wxMergeHashMap(keywords, compiler);
00162 }
00163 
00164 wxPackageCommand wxPackage::GetCmd(const wxPackageCommand &cmd,
00165                                    wxPackageBuildSystemStage stage,
00166                                    long flags,
00167                                    const wxPackageCondition &cond) const
00168 {
00169     // substitute basic info:
00170     wxPackageCommand ret(wxPackageInfo::GetCmd(cmd, stage, flags, cond));
00171 
00172     // substitute even more info
00173     wxString subst = SubstituteInfo(ret.GetCommand());
00174     ret.SetCommand(subst);
00175 
00176 #ifdef __WXDEBUG__
00177     // at this stage typically all $() keys should have been substituted:
00178     if (subst.Contains(wxWPM_SUBSTITUTION_START_MARKER))
00179         wxLogDebug(wxT("wxPackage::GetCmd - found unknown keywords in the command: [%s]"),
00180                    subst.c_str());
00181 #endif
00182 
00183     return ret;
00184 }
00185 
00186 void wxPackage::SetRemoteStatus(const wxString &downloadpath)
00187 {
00188     m_status = wxPS_REMOTE;
00189 
00190     // this is the path where the downloaded package will be placed
00191     if (!downloadpath.IsEmpty())
00192     {
00193         wxFileName fn(downloadpath);
00194         wxASSERT(!fn.IsDir() && fn.IsAbsolute());
00195         m_strDownloadPath = fn.GetFullPath();
00196     }
00197     else
00198         m_strDownloadPath = wxEmptyString;
00199 
00200     UpdatePackageInfo();
00201 }
00202 
00203 void wxPackage::SetDownloadedStatus(const wxString &downloadpath)
00204 {
00205     wxFileName dp(downloadpath);
00206 
00207     // downloadpath should be an ABSOLUTE fully qualified filename: path + name + ext
00208     wxASSERT(!dp.IsDir() && dp.IsAbsolute());
00209 
00210     m_status = wxPS_DOWNLOADED;
00211     m_strDownloadPath = dp.GetFullPath();
00212 
00213     UpdatePackageInfo();
00214 }
00215 
00216 void wxPackage::SetDecompressedStatus(const wxString &decompresspath)
00217 {
00218     wxFileName dp = wxFileName::DirName(decompresspath);
00219 
00220     // decompresspath should be an ABSOLUTE directory name
00221     wxASSERT(dp.IsDir() && dp.IsAbsolute());
00222 
00223     m_status = wxPS_DECOMPRESSED;
00224     m_strDecompressionPath = dp.GetPath();
00225 
00226     UpdatePackageInfo();
00227 }
00228 
00229 void wxPackage::SetBuiltStatus()
00230 {
00231     m_status = wxPS_BUILT;
00232     UpdatePackageInfo();
00233 }
00234 
00235 void wxPackage::SetInstalledStatus(const wxString &installpath)
00236 {
00237     m_status = wxPS_INSTALLED;
00238     m_strInstallationPath = installpath;
00239 
00240     UpdatePackageInfo();
00241 }
00242 
00243 void wxPackage::UpdatePackageInfo()
00244 {
00245     if (m_status >= wxPS_DECOMPRESSED)
00246     {
00247         // make local references as absolute
00248         m_refLogo.ExpandLocalRef(m_strDecompressionPath);
00249         for (size_t i=0; i < m_arrScreenshots.GetCount(); i++)
00250             m_arrScreenshots[i].ExpandLocalRef(m_strDecompressionPath);
00251         for (size_t i=0; i < m_arrDocumentation.GetCount(); i++)
00252             m_arrDocumentation[i].ExpandLocalRef(m_strDecompressionPath);
00253 
00254         UpdateCompressedSize();
00255         UpdateMD5();
00256     }
00257     else if (m_status >= wxPS_DOWNLOADED)
00258     {
00259         // the package has not been decompressed yet but we still can make
00260         // the browsing faster loading references to the logo,screenshots and
00261         // documentation from the WXZ instead of from WWW
00262         m_refLogo.ExpandLocalRefToWXZRef(m_strDownloadPath);
00263         for (size_t i=0; i < m_arrScreenshots.GetCount(); i++)
00264             m_arrScreenshots[i].ExpandLocalRefToWXZRef(m_strDownloadPath);
00265         for (size_t i=0; i < m_arrDocumentation.GetCount(); i++)
00266             m_arrDocumentation[i].ExpandLocalRefToWXZRef(m_strDownloadPath);
00267 
00268         UpdateCompressedSize();
00269         UpdateMD5();
00270     }
00271     else if (m_status == wxPS_REMOTE)
00272     {
00273         // force the use of href instead of localref
00274         m_refLogo.RemoveLocalRef();
00275         for (size_t i=0; i < m_arrScreenshots.GetCount(); i++)
00276             m_arrScreenshots[i].RemoveLocalRef();
00277         for (size_t i=0; i < m_arrDocumentation.GetCount(); i++)
00278             m_arrDocumentation[i].RemoveLocalRef();
00279     }
00280 }
00281 
00282 bool wxPackage::LoadStatusInfo(wxConfigBase *cfg, const wxString &path)
00283 {
00284     if (!cfg->Read(path + wxT("/Status"), (long *)&m_status))
00285         return false;
00286 
00287     // wxPS_REMOTE is a valid state for a package even if it means
00288     // that there are no status info for it.
00289     // A package's status may have been saved on the user's system
00290     // as wxPS_REMOTE when e.g. the package has been first installed
00291     // and then uninstalled and finally removed from the system...
00292     if (m_status == wxPS_REMOTE)
00293     {
00294         // let's see however if the package do exist
00295         if (!m_strDownloadPath.IsEmpty() &&
00296             wxFileName::FileExists(m_strDownloadPath))
00297             m_status = wxPS_DOWNLOADED;
00298     }
00299 
00300     if (m_status != wxPS_DOWNLOADED &&
00301         m_status != wxPS_DECOMPRESSED &&
00302         m_status != wxPS_BUILT &&
00303         m_status != wxPS_INSTALLED)
00304         return false;
00305 
00306     // read all paths
00307     wxString str;
00308     if (!cfg->Read(path + wxT("/DownloadPath"), &str))
00309         return false;
00310 
00311     // if the status of the package has been fixed previously, don't
00312     // overwrite the, maybe correct, m_strDownloadPath
00313     if (!str.IsEmpty())
00314         m_strDownloadPath = str;
00315 
00316     if (!cfg->Read(path + wxT("/WXPPath"), &m_strWXPPath))
00317         return false;
00318     if (m_status >= wxPS_DECOMPRESSED &&
00319         !cfg->Read(path + wxT("/DecompressionPath"), &m_strDecompressionPath))
00320         return false;
00321     if (m_status >= wxPS_INSTALLED &&
00322         !cfg->Read(path + wxT("/InstallationPath"), &m_strInstallationPath))
00323         return false;
00324 
00325     // check them
00326     if (m_status >= wxPS_DOWNLOADED && !wxFileName::FileExists(m_strDownloadPath))
00327     {
00328         wxLogWarning(_("The package %s %s is marked as downloaded but\n\t%s\ndoes not exist. Resetting it to the not-downloaded-yet package status."),
00329                      GetName().c_str(), GetVersion().GetAsString().c_str(), m_strDownloadPath.c_str());
00330         m_status = wxPS_REMOTE;
00331         m_strDownloadPath = wxEmptyString;
00332     }
00333 
00334     if (m_status >= wxPS_DECOMPRESSED)
00335     {
00336         if (!wxFileName::DirExists(m_strDecompressionPath))
00337         {
00338             wxLogWarning(_("The package %s %s is marked as decompressed but the folder\n\t%s\ndoes not exist. Resetting it to the just-downloaded package status."),
00339                         GetName().c_str(), GetVersion().GetAsString().c_str(), m_strDecompressionPath.c_str());
00340             m_status = wxPS_DOWNLOADED;
00341             m_strDecompressionPath = wxEmptyString;
00342         }
00343         else if (!wxFileName::FileExists(GetAbsWXPPath()))
00344         {
00345             wxLogWarning(_("The package %s %s is marked as decompressed in the folder\n\t%s\nbut the file\n\t%s\ndoes not exist. Resetting it to the just-downloaded package status."),
00346                         GetName().c_str(), GetVersion().GetAsString().c_str(), m_strDecompressionPath.c_str(),
00347                         GetAbsWXPPath().c_str());
00348             m_status = wxPS_DOWNLOADED;
00349             m_strDecompressionPath = wxEmptyString;
00350         }
00351         else
00352         {
00353             /*wxLogNull nul;
00354 
00355             // try to load the WXP of this package
00356             wxPackageXMLDescriptor wxp;
00357             if (wxp.Load(GetAbsWXPPath()))
00358                 *this = wxp.GetPackageInfo();*/
00359         }
00360     }
00361 
00362     // now that our status has changed
00363     // (and that we gathered new info about this package) we can eventually
00364     // update some of our fields.
00365     UpdatePackageInfo();
00366 
00367     return true;
00368 }
00369 
00370 bool wxPackage::SaveStatusInfo(wxConfigBase *cfg, const wxString &path) const
00371 {
00372     cfg->Write(path + wxT("/Status"), m_status);
00373 
00374     cfg->Write(path + wxT("/DownloadPath"), m_strDownloadPath);
00375     cfg->Write(path + wxT("/DecompressionPath"), m_strDecompressionPath);
00376     cfg->Write(path + wxT("/InstallationPath"), m_strInstallationPath);
00377     cfg->Write(path + wxT("/WXPPath"), m_strWXPPath);
00378 
00379     return true;
00380 }
00381 
00382 wxULongLong wxPackage::GetDecompressedSize() const
00383 {
00384     wxCHECK_MSG(m_status >= wxPS_DOWNLOADED, 0, wxT("Invalid package status"));
00385     wxULongLong ret;
00386 
00387     // compute the size of the decompressed package summing ZIP entries sizes
00388     auto_ptr<wxArchiveInputStream> arc( GetInputStream(GetDownloadPath()) );
00389     auto_ptr<wxArchiveEntry> entry;
00390 
00391     if (arc.get() == NULL)
00392         return false;
00393 
00394     while (entry.reset(arc->GetNextEntry()), entry.get() != NULL)
00395         ret += entry->GetSize();
00396 
00397     return ret;
00398 }
00399 
00400 wxWidgetsBuildArray wxPackage::GetSupportedWxBuilds(bool docompilercheck) const
00401 {
00402     wxWidgetsBuildArray ret;
00403     for (size_t i=0; i < wxWidgetsBuildArray::s_arrWxBuilds.GetCount(); i++)
00404     {
00405         const wxWidgetsBuild &b = wxWidgetsBuildArray::s_arrWxBuilds[i];
00406 
00407         // does the port+version match any of our supported ports ?
00408         if (!SupportsWxPort(b.GetPortId(), b.GetVersion()))
00409             continue;       // skip this wxbuild
00410 
00411         if (docompilercheck)
00412             if (b.GetCompilerUsed() != m_compiler.GetSelFormat())
00413                 continue;
00414 
00415         // all checks successful
00416         ret.Add(b);
00417     }
00418 
00419     return ret;
00420 }
00421 
00422 bool wxPackage::GetRecursiveDependencies(wxPackageDependencyArray *ret, 
00423                                          const wxPackageArray &arr,
00424                                          wxPackageDependencyType type, 
00425                                          wxPackageDependencyArray *notfound) const
00426 {
00427     static wxPackageIdArray s_temp;
00428     s_temp.Clear();
00429 
00430     if (!DoGetRecursiveDependencies(ret, arr, type, notfound, &s_temp))
00431     {
00432         // false means that there were unresolved dependencies
00433         if (notfound)
00434         {
00435             // because of the organization of wxPackage::DoGetRecursiveDependencies,
00436             // even if these deps are missing, they've been added to 'ret'.
00437             // We need to remove them !
00438             for (size_t i=0; i<notfound->GetCount(); i++)
00439             {
00440                 int n = ret->Index(notfound->Item(i));
00441                 wxASSERT(n != wxNOT_FOUND);
00442                 ret->RemoveAt(n);
00443             }
00444         }
00445 
00446         return false;
00447     }
00448 
00449     return true;
00450 }
00451 
00452 bool wxPackage::DoGetRecursiveDependencies(wxPackageDependencyArray *ret, 
00453                                            const wxPackageArray &arr,
00454                                            wxPackageDependencyType type, 
00455                                            wxPackageDependencyArray *notfound, 
00456                                            wxPackageIdArray *alreadyprocessed) const
00457 {
00458     // some of given pointers cannot be NULL !!
00459     wxASSERT(ret && alreadyprocessed);
00460 
00461     if (alreadyprocessed->Index(*this) != wxNOT_FOUND)
00462     {
00463         // we've found a circular dependency !
00464         // but this is not a problem ;)
00465         return true;
00466     }
00467 
00468     // add to the given list of dependencies our dependencies of given type
00469     wxPackageDependencyArray temp;
00470     const wxPackageDependencyArray *toadd;
00471     if (type == wxPDT_INVALID)
00472     {
00473         toadd = &m_arrDependencies;
00474     }
00475     else
00476     {
00477         // NB: we create a temp array otherwise the macro WX_APPEND_ARRAY
00478         //     would generate lots of calls to GetDependenciesOfType !!
00479         temp = m_arrDependencies.GetDependenciesOfType(type);
00480         toadd = &temp;
00481     }
00482 
00483     for (size_t i=0; i < toadd->GetCount(); i++)
00484     {
00485         int n = ret->Index(toadd->Item(i));
00486         if (n != wxNOT_FOUND)
00487         {
00488             ret->Item(n).AddOwnerId(*this);
00489             continue;       // do not add duplicates!!
00490         }
00491 
00492         // add our i-th dependency to the array
00493         wxASSERT(toadd->Item(i).GetOwnersId().Index(*this) != wxNOT_FOUND);
00494         ret->Add(toadd->Item(i));
00495     }
00496 
00497     // add ourselves to the list of already processed packages
00498     // this is very important in order to avoid being trapped in
00499     // an infinite loop in case of circular dependencies !
00500     alreadyprocessed->Add(*this);
00501 
00502     // add all our dependencies recursively
00503     return m_arrDependencies.DoGetRecursiveDependencies(ret, arr, type, notfound, alreadyprocessed);
00504 }
00505 
00506 wxArchiveInputStream *wxPackage::GetInputStream(const wxString &wxzpath) const
00507 {
00508     wxFileName fn(wxzpath);
00509     if (!fn.IsAbsolute())
00510         fn.MakeAbsolute();
00511     wxString filename = fn.GetFullPath();
00512 
00513     // generic code to read any type of archive supported by the
00514     // wxArchive* classes taken from wxDocs:
00515 
00516     auto_ptr<wxInputStream> in(new wxFFileInputStream(filename));
00517 
00518     // if the file ends with .wxz it may be any of the compressed formats
00519     // supported by wxPackage
00520     if (fn.GetExt().CmpNoCase(wxT("wxz")) == 0)
00521     {
00522         // try to detect the format type reading e.g. the first 
00523         wxByte signature[12];
00524         if (!in->Read(signature, 10*sizeof(wxByte)))
00525             return NULL;
00526 
00527         // seek back to the start
00528         in->SeekI(0, wxFromStart);
00529 
00530         struct
00531         {
00532             wxByte magic[4];
00533             size_t nByteLen;
00534             wxPackageCompressionMode compression;
00535         } magics[2] = 
00536         {
00537             {
00538                 { 0x50, 0x4B, 0x03, 0x04 },
00539                 4,
00540                 wxPCM_ZIP
00541             },
00542             {
00543                 { 0x1F, 0x8B, 0x08 },
00544                 3,
00545                 wxPCM_TAR_GZ
00546             }
00547         };
00548 
00549         wxPackageCompressionMode detected = wxPCM_INVALID;
00550         for (size_t i=0; i < WXSIZEOF(magics); i++)
00551         {
00552             bool match = true;
00553             for (size_t j=0; j < magics[i].nByteLen; j++)
00554                 match &= (signature[j] == magics[i].magic[j]);
00555 
00556             if (match)
00557             {
00558                 detected = magics[i].compression;
00559                 break;
00560             }
00561         }
00562 
00563         switch (detected)
00564         {
00565         case wxPCM_ZIP:
00566             // should be a ZIP archive
00567             return new wxZipInputStream(in.release());  
00568 
00569 
00570         case wxPCM_TAR_GZ:
00571             {
00572                 // should be a .tar.gz archive
00573                 wxZlibInputStream *filter = new wxZlibInputStream(in.release());
00574                 return new wxTarInputStream(filter);
00575             }
00576 
00577         case wxPCM_INVALID:
00578         default:
00579             wxLogError(wxT("The archive '%s' is of unknown type."), wxzpath.c_str());
00580             return NULL;       // unknown archive type
00581         }
00582     }
00583 
00584     // look for a filter handler, e.g. for '.gz'
00585     const wxFilterClassFactory *fcf;
00586     fcf = wxFilterClassFactory::Find(filename, wxSTREAM_FILEEXT);
00587     if (fcf)
00588     {
00589         in.reset(fcf->NewStream(in.release()));
00590 
00591         // pop the extension, so if it was '.tar.gz' it is now just '.tar'
00592         filename = fcf->PopExtension(filename);
00593     }
00594     //else: ignore, no filter required for this archive
00595 
00596     // look for a archive handler, e.g. for '.zip' or '.tar'
00597     const wxArchiveClassFactory *acf;
00598     acf = wxArchiveClassFactory::Find(filename, wxSTREAM_FILEEXT);
00599     if (!acf)
00600     {
00601         wxLogError(wxT("The archive '%s' is of unknown type."), wxzpath.c_str());
00602         return NULL;       // unknown archive type
00603     }
00604 
00605     return acf->NewStream(in.release());
00606 }
00607 
00608 bool wxPackage::LoadCompressedPackage(const wxString &wxzpath)
00609 {
00610     auto_ptr<wxArchiveInputStream> arc( GetInputStream(wxzpath) );
00611     auto_ptr<wxArchiveEntry> entry;
00612 
00613     if (arc.get() == NULL)
00614         return false;
00615 
00616     // scan this ZIP in search of the WXP it should contain
00617     while (entry.reset(arc->GetNextEntry()), entry.get() != NULL)
00618     {
00619         wxString name = entry->GetName();
00620         if (name.EndsWith(wxT(".wxp")))
00621         {
00622             wxPackageXMLDescriptor wxp;
00623             if (!wxp.Load(*arc))
00624                 return false;       // something failed!
00625 
00626             // load the package info
00627             *this = wxp.GetPackageInfo();
00628             if (!IsOk())
00629             {
00630                 wxLogError(wxT("The WXP contained in '%s' is invalid!"),
00631                            wxzpath.c_str());
00632                 return false;
00633             }
00634 
00635             // we must provide an absolute path as download path
00636             wxFileName fn(wxzpath);
00637             if (!fn.IsAbsolute())
00638                 fn.MakeAbsolute();
00639 
00640             // this is a local package and we know where its WXP file is...
00641             SetDownloadedStatus(fn.GetFullPath());
00642             SetRelativeWXPPath(name);
00643 
00644             // ok, WXP loaded successfully from this WXZ
00645             return true;
00646         }
00647     }
00648 
00649     // WXP not found in given archive!
00650     return false;
00651 }
00652 
00653 bool wxPackage::Decompress(const wxString &dir, wxProgressHandler *progress)
00654 {
00655     wxASSERT(wxFileName(dir).IsAbsolute());
00656 
00657     // check that the output dir already exists
00658     wxString path(dir);
00659     if (path.Last() == wxFileName::GetPathSeparator())
00660         path.RemoveLast();
00661     if (!wxFileName::DirExists(path))
00662     {
00663         wxLogError(wxT("The decompression folder '%s' does not exist!"), dir.c_str());
00664         return false;
00665     }
00666 
00667     wxULongLong decompressedbytes = 0,
00668                 totalbytes = GetDecompressedSize();
00669 
00670     auto_ptr<wxArchiveInputStream> arc( GetInputStream(GetDownloadPath()) );
00671     auto_ptr<wxArchiveEntry> entry;
00672 
00673     if (arc.get() == NULL)
00674         return false;
00675 
00676     while (entry.reset(arc->GetNextEntry()), entry.get() != NULL)
00677     {
00678         wxFileName fn(path + wxFileName::GetPathSeparator() + entry->GetName());
00679 
00680         if (entry->IsDir())
00681         {
00682             // create this folder if it does not exist
00683             if (!fn.DirExists() && !fn.Mkdir(0777, wxPATH_MKDIR_FULL))
00684             {
00685                 wxLogError(_("Cannot create folder '%s' - maybe because of a permission problem"),
00686                            fn.GetFullPath().c_str());
00687                 return false;
00688             }
00689             else
00690                 continue;
00691         }
00692 
00693         // open the output file
00694         wxString outpath = fn.GetFullPath();
00695         wxFileName basepath(outpath);
00696         if (!basepath.DirExists() && !basepath.Mkdir(0777, wxPATH_MKDIR_FULL))
00697         {
00698             wxLogError(_("Cannot create folder '%s' - maybe because of a permission problem"),
00699                         basepath.GetPath().c_str());
00700             return false;
00701         }
00702 
00703         wxFFileOutputStream out(outpath);
00704 
00705         // and decompress data inside it
00706         out.Write(*arc);
00707         if (out.GetSize() != entry->GetSize())
00708         {
00709             wxLogError(_("An error occurred when decompressing the '%s' file.\nIt should be %d bytes long but the size of the decompressed file is %d bytes. Please check the archive's integrity."),
00710                        fn.GetFullPath().c_str(), entry->GetSize(), out.GetSize());
00711             return false;
00712         }
00713 
00714         decompressedbytes += entry->GetSize();
00715 
00716         // advance the progress handler: remember that we must pass
00717         // a value between [0;1000] to it!
00718         if (progress)
00719             progress->OnUpdate((int)(1000 * ( decompressedbytes.ToDouble() / totalbytes.ToDouble() )),
00720                                entry->GetName());
00721     }
00722 
00723     SetDecompressedStatus(path);
00724     return true;
00725 }
00726 
00727 // helper class for wxPackage::GetIncludedFiles
00728 class wxPackageDirTraverser : public wxDirTraverser
00729 {
00730 public:
00731     wxPackageDirTraverser(const wxString &prefix,
00732                           const wxArrayString &filesToExclude,
00733                           const wxArrayString &dirsToExclude,
00734                           const wxArrayString &patternsToExclude,
00735                           wxULongLong *totalsize)
00736         : m_prefix(prefix), 
00737           m_pTotalSize(totalsize)
00738     { 
00739         // we need to compare dirs/files/patterns using native path separator!
00740         ConvertToNativeSeparator(filesToExclude, m_arrFilesExcluded);
00741         ConvertToNativeSeparator(dirsToExclude, m_arrDirsExcluded);
00742         ConvertToNativeSeparator(patternsToExclude, m_arrPatternsExcluded);
00743     }
00744 
00745     void ConvertToNativeSeparator(const wxArrayString &source, wxArrayString &arr)
00746     {
00747         const wxString sep = wxString(wxFileName::GetPathSeparator(), 1);
00748         for (size_t i=0; i<source.GetCount(); i++)
00749         {
00750             wxString str = source[i];
00751             str.Replace(wxT("/"), sep);
00752             arr.Add(str);
00753         }
00754     }
00755 
00756     virtual wxDirTraverseResult OnFile(const wxString& filename)
00757     {
00758         wxASSERT(filename.StartsWith(m_prefix));
00759         wxString name = filename.Mid(m_prefix.Len());
00760 
00761         // remove the separator, if present
00762         if (name[0] == wxFileName::GetPathSeparator())
00763             name = name.Mid(1);
00764 
00765         if (m_arrFilesExcluded.Index(name) != wxNOT_FOUND)
00766             return wxDIR_CONTINUE;      // skip this file
00767 
00768         for (size_t i=0; i<m_arrPatternsExcluded.GetCount(); i++)
00769             if (wxMatchWild(m_arrPatternsExcluded[i], name, false /* dot_special */))
00770                 return wxDIR_CONTINUE;      // skip this file
00771 
00772         // this file must be included
00773         m_files.Add(filename);
00774 
00775         if (m_pTotalSize)
00776             *m_pTotalSize += wxFileName::GetSize(filename);
00777 
00778         return wxDIR_CONTINUE;
00779     }
00780 
00781     virtual wxDirTraverseResult OnDir(const wxString& dirname)
00782     {
00783         wxASSERT(dirname.StartsWith(m_prefix));
00784         wxString name = dirname.Mid(m_prefix.Len());
00785 
00786         // remove the separator, if present
00787         if (name[0] == wxFileName::GetPathSeparator())
00788             name = name.Mid(1);
00789 
00790         if (m_arrDirsExcluded.Index(name) != wxNOT_FOUND)
00791             return wxDIR_IGNORE;      // skip this directory
00792 
00793         // recurse into this directory
00794         return wxDIR_CONTINUE;
00795     }
00796 
00797     wxArrayString GetFiles() const
00798         { return m_files; }
00799 
00800 protected:
00801     wxString m_prefix;
00802     wxArrayString m_arrFilesExcluded, 
00803                   m_arrDirsExcluded, 
00804                   m_arrPatternsExcluded;
00805 
00806     wxULongLong *m_pTotalSize;
00807 
00808     // the list filled in by this class
00809     wxArrayString m_files;
00810 };
00811 
00812 wxArrayString wxPackage::GetIncludedFiles(wxULongLong *totalsize) const
00813 {
00814     // we need the decompression path in order to proceed
00815     wxCHECK_MSG(m_status >= wxPS_DECOMPRESSED, 0, wxT("Invalid package status"));
00816     wxString prefix = GetDecompressionPath().BeforeLast(wxFileName::GetPathSeparator());
00817 
00818     wxPackageDirTraverser 
00819         traverser(prefix,
00820                   m_arrExcluded[wxPEM_FILE],
00821                   m_arrExcluded[wxPEM_DIRECTORY],
00822                   m_arrExcluded[wxPEM_PATTERN],
00823                   totalsize);
00824 
00825     // NOTE: we want the decompression path here and not the "prefix" !
00826     wxDir dir(GetDecompressionPath());
00827 
00828     if (!dir.IsOpened())
00829     {
00830         wxArrayString empty;
00831         return empty;
00832     }
00833 
00834     // search included files
00835     dir.Traverse(traverser);
00836     return traverser.GetFiles();
00837 }
00838 
00839 // helper class for wxPackage::Compress
00840 class wxPackageDirTraverserArchiver : public wxDirTraverser
00841 {
00842 public:
00843     wxPackageDirTraverserArchiver(const wxString &prefix,
00844                                   const wxArrayString &filelist,
00845                                   wxArchiveOutputStream &z, 
00846                                   wxProgressHandler *h,
00847                                   wxULongLong totalsize)
00848         : m_prefix(prefix), m_fileList(filelist), m_arch(z), 
00849           m_progress(h), m_totalSize(totalsize)
00850     { 
00851         m_bAllCompressed=true; 
00852         m_compressedBytes=0; 
00853     }
00854 
00855     virtual wxDirTraverseResult OnFile(const wxString& filename)
00856     {
00857         if (m_fileList.Index(filename) == wxNOT_FOUND)
00858         {
00859             //wxLogDebug(wxT("Skipping file '%s'"), filename.c_str());
00860             return wxDIR_CONTINUE;
00861         }
00862 
00863         wxASSERT(filename.StartsWith(m_prefix));
00864         wxString name = filename.Mid(m_prefix.Len());
00865 
00866         // remove the separator, if present
00867         if (name[0] == wxFileName::GetPathSeparator())
00868             name = name.Mid(1);
00869 
00870         // read the file
00871         wxFFileInputStream in(filename);
00872 
00873         // create a new entry in the archive
00874         // since when creating a .tar.gz we are tarring data to a non-seekable
00875         // stream (i.e. the gzipper stream) we need to provude the data size
00876         // in advance, together with its timestamp:
00877         wxFileName fn(filename);
00878         if (!m_arch.PutNextEntry(name, fn.GetModificationTime(), in.GetSize()))
00879         {
00880             m_bAllCompressed = false;       // continue ignoring this error
00881             return wxDIR_CONTINUE;
00882         }
00883 
00884         // compress it using the output stream (which should be
00885         // a wxFilterOutputStream which pass data to the archive stream)
00886         if (m_arch.Write(in).GetLastError() != wxSTREAM_NO_ERROR)
00887             m_bAllCompressed = false;       // continue ignoring this error
00888         else
00889             m_compressedBytes += in.GetSize();
00890 
00891         // advance the progress handler: remember that we must pass
00892         // a value between [0;1000] to it!
00893         if (m_progress)
00894         {
00895             unsigned long progress =
00896                 (int)(1000 * ( m_compressedBytes.ToDouble() / m_totalSize.ToDouble() ));
00897 
00898             if (progress > 1000)
00899                 // something wrong: m_totalSize was not computed correctly;
00900                 // do not assert however - just clip 'progress'
00901                 progress = 1000;
00902 
00903             m_progress->OnUpdate(progress, filename);
00904         }
00905 
00906         return wxDIR_CONTINUE;
00907     }
00908 
00909     virtual wxDirTraverseResult OnDir(const wxString& WXUNUSED(dirname))
00910     {
00911         // no need to create an entry for a directory!
00912         return wxDIR_CONTINUE;
00913     }
00914 
00915     virtual wxDirTraverseResult OnOpenError(const wxString& dirname)
00916     {
00917         wxLogWarning(wxT("Couldn't compress '%s' directory: cannot open it"), 
00918                      dirname.c_str());
00919         m_bAllCompressed = false;
00920         return wxDIR_IGNORE;
00921     }
00922 
00923     bool AllCompressed() const
00924         { return m_bAllCompressed; }
00925 
00926 protected:
00927     wxString m_prefix;
00928     const wxArrayString &m_fileList;
00929 
00930     wxArchiveOutputStream &m_arch;
00931     wxProgressHandler *m_progress;
00932 
00933     wxULongLong m_totalSize;
00934     wxULongLong m_compressedBytes;
00935     bool m_bAllCompressed;
00936 };
00937 
00938 bool wxPackage::Compress(const wxString &wxz, wxProgressHandler *progress)
00939 {
00940     wxCHECK_MSG(m_status >= wxPS_DECOMPRESSED, 0, wxT("Invalid package status"));
00941     wxString file = wxz;
00942 
00943     // create a filename if none is provided
00944     if (file.IsEmpty())
00945         file = SubstituteInfo(GetOutputFileName());
00946 
00947     // open the output file
00948     wxFFileOutputStream out(file, wxT("wb"));
00949     if (!out.IsOk())
00950     {
00951         wxLogError(wxT("Couldn't create the output file '%s'."), wxz.c_str());
00952         return false;
00953     }
00954 
00955     // create the compressor stream
00956     wxArchiveOutputStream *archiveos = NULL;
00957     wxOutputStream *os = NULL;
00958     switch (GetCompressionMode())
00959     {
00960     case wxPCM_ZIP:
00961         archiveos = new wxZipOutputStream(out, wxPKG_ZIP_COMPRESSION_LEVEL);
00962         os = archiveos;
00963         break;
00964 
00965     case wxPCM_TAR_GZ:
00966         if (!wxZlibOutputStream::CanHandleGZip())
00967         {
00968             wxLogError(wxT("Cannot create tar.gz archives!"));
00969             return false;
00970         }
00971 
00972         // data need first to be tarred and then gzipped
00973         os = new wxZlibOutputStream(out, wxPKG_TARGZ_COMPRESSION_LEVEL, wxZLIB_GZIP);
00974         archiveos = new wxTarOutputStream(*os);
00975         break;
00976     }
00977 
00978     if (!archiveos->IsOk())
00979     {
00980         wxLogError(wxT("Couldn't create the compressor output stream."));
00981         return false;
00982     }
00983 
00984     // get the list of files to compress
00985     wxULongLong totalSz;
00986     wxArrayString filelist(GetIncludedFiles(&totalSz));
00987     if (filelist.GetCount() == 0)
00988     {
00989         wxLogError(wxT("No files to include!"));
00990         return false;
00991     }
00992 
00993     // the prefix is the parent's folder of the project:
00994     wxFileName prefix = wxFileName::DirName(GetDecompressionPath());
00995     prefix.RemoveLastDir();
00996 
00997     // init directory traverser
00998     wxPackageDirTraverserArchiver 
00999         traverser(prefix.GetPath(), filelist, *archiveos, 
01000                   progress, totalSz.ToULong());
01001     wxDir dir(GetDecompressionPath());
01002 
01003     if (!dir.IsOpened())
01004     {
01005         wxLogError(wxT("Cannot open the directory '%s'."),
01006                    GetDecompressionPath().c_str());
01007         return false;
01008     }
01009 
01010     // do compression
01011     dir.Traverse(traverser);
01012 
01013     // clean up
01014     if (!os->IsOk())
01015     {        
01016         wxLogError(wxT("Cannot write to the output stream"));
01017         return false;
01018     }
01019 
01020     switch (GetCompressionMode())
01021     {
01022     case wxPCM_ZIP:
01023         wxDELETE(archiveos);
01024         break;
01025 
01026     case wxPCM_TAR_GZ:
01027         // the order is important!
01028         wxDELETE(archiveos);
01029         wxDELETE(os);
01030         break;
01031     }
01032 
01033     return traverser.AllCompressed() &&
01034            out.IsOk();
01035 }
01036 
01037 bool wxPackage::UploadWithFTP(const wxString &u, const wxString &pass, 
01038                               const wxString &s, const wxString &p)
01039 {
01040     wxCHECK_MSG(m_status >= wxPS_DOWNLOADED, 0, wxT("Invalid package status"));
01041     wxString user(u), password(pass), server(s), path(p);
01042     wxURI dest(GetUploadDestination());
01043     wxFTP ftp;
01044 
01045     if (user.IsEmpty())
01046         user = dest.GetUser();
01047     if (password.IsEmpty())
01048         password = dest.GetPassword();
01049     if (server.IsEmpty())
01050         server = dest.GetServer();
01051     if (path.IsEmpty())
01052         path = dest.GetPath();
01053 
01054     if (!user.IsEmpty())
01055     {
01056         ftp.SetUser(user);
01057         ftp.SetPassword(password);
01058     }
01059     // else: anonymous FTP login
01060 
01061     if ( !ftp.Connect(server) )
01062     {
01063         wxLogError(wxT("Couldn't connect to '%s'."), server.c_str());
01064         return false;
01065     }
01066 
01067     if ( !ftp.ChDir(path) )
01068     {
01069         wxLogError(wxT("Couldn't change the directory to '%s'."), path.c_str());
01070         return false;
01071     }
01072 
01073     // create destination filename
01074     wxString destfile = path;
01075     if (destfile.Last() != wxT('/'))
01076         destfile += wxT('/');
01077     destfile += wxFileName(GetDownloadPath()).GetFullName();
01078 
01079     wxOutputStream *out = ftp.GetOutputStream(destfile);
01080     if ( out )
01081     {
01082         wxFileInputStream source(GetDownloadPath());
01083         if (!IsOk())
01084         {
01085             wxLogError(wxT("Couldn't open the source file '%s'."), GetDownloadPath().c_str());
01086             wxDELETE(out);
01087             return false;
01088         }
01089 
01090         out->Write(source);
01091 
01092         if (out->GetLastError() != wxSTREAM_NO_ERROR)
01093         {
01094             wxLogError(wxT("Couldn't write to the destination file '%s'."), destfile.c_str());
01095             wxDELETE(out);
01096             return false;
01097         }
01098 
01099         wxDELETE(out);
01100     }
01101 
01102     ftp.Close();
01103     return true;
01104 }
01105 
01106 /* static */
01107 bool wxPackage::LoadGlobals(wxConfigBase *p, const wxString &path)
01108 {
01109     int num;
01110     wxString str;
01111 
01112     // local repository paths
01113     if (!p->Read(path + wxT("/LocalRepo/Num"), &num))
01114         return false;
01115     for (int i=0; i < num; i++)
01116     {
01117         if (p->Read(path + wxString::Format(wxT("/LocalRepo/Path%d"), i), &str))
01118         {
01119             if (wxFileName::IsDirWritable(str))
01120                 s_arrLocalRepo.Add(str);
01121             else
01122                 wxLogWarning(_("Discarded the '%s' local repository path as it's not writable."),
01123                              str.c_str());
01124         }
01125     }
01126 
01127     // remote repository paths
01128     if (!p->Read(path + wxT("/RemoteRepo/Num"), &num))
01129         return false;
01130     for (int i=0; i < num; i++)
01131         if (p->Read(path + wxString::Format(wxT("/RemoteRepo/Path%d"), i), &str))
01132             s_arrRemoteRepo.Add(str);
01133 
01134     return s_arrLocalRepo.GetCount() > 0 &&
01135            s_arrRemoteRepo.GetCount() > 0;
01136 }
01137 
01138 /* static */
01139 void wxPackage::SaveGlobals(wxConfigBase *p, const wxString &path)
01140 {
01141     // save local repo urls
01142     p->DeleteGroup(path + wxT("/LocalRepo"));
01143     for (size_t i=0; i < s_arrLocalRepo.GetCount(); i++)
01144         p->Write(path + wxString::Format(wxT("/LocalRepo/Path%d"), i),
01145                  s_arrLocalRepo[i]);
01146     p->Write(path + wxT("/LocalRepo/Num"), (long)s_arrLocalRepo.GetCount());
01147 
01148     // save remote repo urls
01149     p->DeleteGroup(path + wxT("/RemoteRepo"));
01150     for (size_t i=0; i < s_arrRemoteRepo.GetCount(); i++)
01151         p->Write(path + wxString::Format(wxT("/RemoteRepo/Path%d"), i),
01152                  s_arrRemoteRepo[i]);
01153     p->Write(path + wxT("/RemoteRepo/Num"), (long)s_arrRemoteRepo.GetCount());
01154 }
01155 
01156 
01157 
01158 // ----------------------------------------------------------------------------
01159 // wxPackageArray
01160 // ----------------------------------------------------------------------------
01161 
01162 bool wxPackageArray::LoadPackagesStatusInfo(wxConfigBase *cfg, const wxString &path)
01163 {
01164     size_t failedcount=0;
01165 
01166     // read all groups with the same IDs of our packages
01167     for (size_t i=0; i<GetCount(); i++)
01168     {
01169         wxString id = Item(i).GetName() + wxT(" ") + Item(i).GetVersion();
01170 
01171         if (cfg->HasGroup(path + wxT("/Status/") + id))
01172         {
01173             if (!Item(i).LoadStatusInfo(cfg, path + wxT("/Status/") + id))
01174                 failedcount++;
01175         }
01176     }
01177 
01178     return failedcount == 0;
01179 }
01180 
01181 bool wxPackageArray::SavePackagesStatusInfo(wxConfigBase *cfg, const wxString &path) const
01182 {
01183     size_t failedcount=0;
01184 
01185     // save all package status info
01186     cfg->DeleteGroup(path + wxT("/Status"));
01187     for (size_t i=0; i<GetCount(); i++)
01188     {
01189         wxString id = Item(i).GetName() + wxT(" ") + Item(i).GetVersion();
01190         if (!Item(i).SaveStatusInfo(cfg, path + wxT("/Status/") + id))
01191         {
01192             failedcount++;
01193             wxLogError(wxT("Could not save status info for package '%s'."), id.c_str());
01194         }
01195     }
01196 
01197     return failedcount == 0;
01198 }
01199 
01200 bool wxPackageArray::LoadCompressedPackagesFrom(const wxString &path)
01201 {
01202     if (!wxFileName::DirExists(path))
01203         return false;
01204 
01205     wxArrayString wxplist;
01206     size_t failedcount=0;
01207 
01208     const wxString extensions[] = { wxT("*.wxz"), wxT("*.zip"), wxT("*.tar.gz") };
01209 
01210     for (size_t j=0; j < WXSIZEOF(extensions); j++)
01211     {
01212         wxplist.Clear();
01213         wxDir::GetAllFiles(path, &wxplist, extensions[j], wxDIR_FILES);
01214         for (size_t i=0; i<wxplist.GetCount(); i++)
01215         {
01216             wxPackage pkg;
01217             if (!pkg.LoadCompressedPackage(wxplist[i]))
01218             {
01219                 failedcount++;
01220                 wxLogError(
01221                     wxT("Could not load the '%s' package; it's not a valid wxWidgets package."),
01222                     wxplist[i].c_str());
01223             }
01224             else
01225             {
01226                 Add(pkg);
01227             }
01228         }
01229     }
01230 
01231     return GetCount() > 0 ||
01232            failedcount == 0;
01233 }
01234 
01235 bool wxPackageArray::LoadCompressedPackagesFromLocalRepo()
01236 {
01237     bool success = true;
01238 
01239     for (size_t i=0; i<wxPackage::s_arrLocalRepo.GetCount(); i++)
01240         success &= LoadCompressedPackagesFrom(wxPackage::s_arrLocalRepo[i]);
01241 
01242     return success;
01243 }
01244 
01245 size_t wxPackageArray::RemoveDuplicates()
01246 {
01247     size_t removed=0;
01248 
01249     for (size_t i=0; i<GetCount(); i++)
01250     {
01251         for (size_t j=i+1; j<GetCount(); j++)
01252         {
01253             // packages are uniquely identified by name & version:
01254             // two packages with both these properties identic are the same package !
01255             if (Item(i).HasSameIdOf(Item(j)))
01256             {
01257                 RemoveAt(j);
01258                 removed++;
01259 
01260                 // check this item again
01261                 j--;
01262             }
01263         }
01264     }
01265 
01266     return removed;
01267 }
01268 
01269 bool wxPackageArray::UpdatePackage(const wxPackage &pkg)
01270 {
01271     int n = IndexById(pkg);
01272     if (n == wxNOT_FOUND)
01273         return false;
01274 
01275     //ItemById(pkg) = pkg;
01276     RemoveAt(n);
01277     Insert(pkg, n);
01278     return true;
01279 }
01280 
01281 bool wxPackageArray::GetRecursiveDependencies(wxPackageDependencyArray *ret, 
01282                                               const wxPackageArray &arr,
01283                                               wxPackageDependencyType type, 
01284                                               wxPackageDependencyArray *notfound) const
01285 {
01286     bool success = true;
01287     for (size_t i=0; i<GetCount(); i++)
01288         success &= Item(i).GetRecursiveDependencies(ret, arr, type, notfound);
01289 
01290     return success;
01291 }
01292 
01293 wxPackageArray wxPackageArray::GetPackagesWhichBelongTo(wxPackageCategory cat) const
01294 {
01295     wxPackageArray ret;
01296     for (size_t i=0; i<GetCount(); i++)
01297         if (Item(i).GetPrimaryCategory() == cat ||
01298             Item(i).GetSecondaryCategory() == cat)
01299             ret.Add(Item(i));
01300     return ret;
01301 }
01302 
01303 void wxPackageArray::InvalidateCachedSubstHashMap(wxPackageSubstituteInfoContext ctx)
01304 {
01305     for (size_t i=0; i<GetCount(); i++)
01306         Item(i).InvalidateCachedSubstHashMap(ctx);
01307 }

Generated on Thu Feb 1 22:14:31 2007 for wxWidgets Package Manager by  doxygen 1.5.1-p1