src/tools/tResourceManager.cpp

Go to the documentation of this file.
00001 #include "aa_config.h"
00002 
00003 #include <errno.h>
00004 #include <stdio.h>
00005 #include <stdlib.h>
00006 #include <string.h>
00007 #include <sys/stat.h>
00008 #include <sys/types.h>
00009 
00010 #include <libxml/nanohttp.h>
00011 
00012 #include "tConfiguration.h"
00013 #include "tDirectories.h"
00014 #include "tResourceManager.h"
00015 #include "tString.h"
00016 
00017 void tResourceManager::RegisterLoader()
00018 {
00019 }
00020 
00021 tResource* tResourceManager::GetResource(const char *file, int typeID)
00022 {
00023     // stub
00024     return NULL;
00025 }
00026 
00027 int tResourceManager::RegisterResourceType(tNewResourceFunc func) {
00028     // stub
00029     return 0;
00030 }
00031 
00032 //tResource* GetResource(const char *file, int typeID) {
00033 //    // stub
00034 //}
00035 
00036 // server determined resource repository
00037 tString & tResourceManager::AccessRepoServer()
00038 {
00039     static tString resRepoServer("http://resource.armagetronad.net/resource/");
00040     return resRepoServer;
00041 }
00042 // the nSettingItem is in gStuff.cpp
00043 
00044 // client determined resource repository
00045 tString & tResourceManager::AccessRepoClient()
00046 {
00047     static tString resRepoClient("http://resource.armagetronad.net/resource/");
00048     return resRepoClient;
00049 }
00050 
00051 static tSettingItem<tString> conf_res_repo("RESOURCE_REPOSITORY_CLIENT", tResourceManager::AccessRepoClient());
00052 
00053 static int myHTTPFetch(const char *URI, const char *filename, const char *savepath)
00054 {
00055     void *ctxt = NULL;
00056     char *buf = NULL;
00057     FILE* fd;
00058     int len, rc;
00059 
00060     con << tOutput( "$resource_downloading", URI );
00061     // con << "Downloading " << URI << "...\n";
00062 
00063     ctxt = xmlNanoHTTPOpen(URI, NULL);
00064     if (ctxt == NULL) {
00065         con << "ERROR: ctxt is NULL\n";
00066         return 1;
00067     }
00068 
00069     if ( (rc = xmlNanoHTTPReturnCode(ctxt)) != 200 ) {
00070         con << tOutput( rc == 404 ? "$resource_fetcherror_404" : "$resource_fetcherror", rc );
00071         return 2;
00072     }
00073 
00074     fd = fopen(savepath, "wb");
00075     if (fd < 0) {
00076         xmlNanoHTTPClose(ctxt);
00077         con << tOutput( "$resource_no_write", savepath );
00078         return 3;
00079     }
00080 
00081     //xmlNanoHTTPFetchContent( ctxt, &buf, &len );
00082     int maxlen = 10000;
00083     buf = (char*)malloc(maxlen);
00084     while( (len = xmlNanoHTTPRead(ctxt, buf, maxlen)) > 0 ) {
00085         fwrite(buf, len, 1, fd);
00086     }
00087     free(buf);
00088 
00089     xmlNanoHTTPClose(ctxt);
00090     fclose(fd);
00091 
00092 
00093     con << "OK\n";
00094 
00095     return 0;
00096 }
00097 
00098 static int myFetch(const char *URIs, const char *filename, const char *savepath) {
00099     const char *r = URIs, *p, *n;
00100     char *u;
00101     size_t len;
00102     int rv = -1;
00103     // r = unprocessed data             p = end-of-item + 1             u = item
00104     // n = to-be r                              len = length of item    savepath = result filepath
00105 
00106     while (r[0] != '\0') {
00107         while (r[0] == ' ') ++r;                        // skip spaces at the start of the item
00108         (p = strchr(r, ';')) ? 0 : (p = strchr(r, '\0'));
00109         n = (p[0] == '\0') ? p : (p + 1);       // next item starts after the semicolon
00110         // NOTE: skip semicolons, *NOT* nulls
00111         while (p[-1] == ' ') --p;                       // skip spaces at the end of the item
00112         len = (size_t)(p - r);
00113         if (len > 0) {                                          // skip this for null-length items
00114             u = (char*)malloc((len + 1) * sizeof(char));
00115             strncpy(u, r, len);
00116             u[len] = '\0';                                      // u now contains the individual URI
00117             rv = myHTTPFetch(u, filename, savepath);    // TODO: handle other protocols?
00118             free(u);
00119             if (rv == 0) return 0;              // If successful, return the file retrieved
00120         }
00121         r = n;                                                          // move onto the next item
00122     }
00123 
00124     return rv;  // last error
00125 }
00126 
00127 /*
00128 Allows for the fetching and caching of ressources available on the web,
00129 such as maps (xml), texture (jpg, gif, bmp), sound and models.
00130 Nota: On some forums (such as guru3.sytes.net), it is possible for the
00131 download link not give information about the filename or type, ie:
00132 http://guru3.sytes.net/download.php?id=1191. This is why the filename
00133 parameter is required.
00134 Parameters:
00135 uri: The full uri to obtain the ressource
00136 filename: The filename to use for the local ressource
00137 Return a file handle to the ressource
00138 NOTE: There must be *at least* one directory level, even if it is ./
00139 */
00140 tString tResourceManager::locateResource(const char *file, const char *uri) {
00141     tString filepath, a_uri = tString(), savepath;
00142     int rv;
00143 
00144     char * to_free = NULL; // string to delete later
00145 
00146     {
00147         char const *pos, *posb;
00148         char *nf;
00149         size_t l;
00150 
00151         // Step 1: If 'file' has an open paren, cut everything after it off
00152         if ( (pos = strchr(file, '(')) ) {
00153             l = (size_t)(pos - file);
00154             nf = (char*)malloc((l + 1) * sizeof(char));
00155             strncpy(nf, file, l);
00156             nf[l] = '\0';
00157             file = nf;
00158             to_free = nf;
00159 
00160             // Step 2: Extract URI, if any
00161             ++pos;
00162             if ( (posb = strchr(pos, ')')) ) {
00163                 l = (size_t)(posb - pos);
00164                 nf = (char*)malloc((l + 1) * sizeof(char));
00165                 strncpy(nf, pos, l);
00166                 nf[l] = '\0';
00167                 a_uri << nf << ';';
00168                 free( nf );
00169             }
00170         }
00171     }
00172     // Validate paths and determine detination savepath
00173     if (!file || file[0] == '\0') {
00174         con << tOutput( "$resource_no_filename" );
00175         return (tString) NULL;
00176     }
00177     if (file[0] == '/' || file[0] == '\\') {
00178         con << tOutput( "$resource_abs_path" );
00179         return (tString) NULL;
00180     }
00181     savepath = tDirectories::Resource().GetWritePath(file);
00182     if (savepath == "") {
00183         con << tOutput( "$resource_no_writepath" );
00184         return (tString) NULL;
00185     }
00186 
00187     // Do we have this file locally ?
00188     filepath = tDirectories::Resource().GetReadPath(file);
00189 
00190     if (filepath != "")
00191     {
00192         if ( NULL != to_free )
00193             free( to_free );
00194         return filepath;
00195     }
00196 
00197     // Some sort of File not found
00198     if (uri && strcmp("0", uri))
00199         a_uri << uri << ';';
00200 
00201     // add repositories to uri
00202     if ( AccessRepoServer().Len() > 2 )
00203         a_uri << AccessRepoServer() << file << ';';
00204 
00205     if ( AccessRepoClient().Len() > 2 && AccessRepoClient() != AccessRepoServer() )
00206         a_uri << AccessRepoClient() << file << ';';
00207 
00208     con << tOutput( "$resource_not_cached", file );
00209 
00210     rv = myFetch((const char *)a_uri, file, (const char *)savepath);
00211 
00212     if ( NULL != to_free )
00213         free( to_free );
00214 
00215     if (rv)
00216         return (tString) NULL;
00217     return savepath;
00218 }
00219 
00220 FILE* tResourceManager::openResource(const char *file, const char *uri) {
00221     tString filepath;
00222     filepath = locateResource(file, uri);
00223     if ( filepath.Len() <= 1 )
00224         return NULL;
00225     return fopen((const char *)filepath, "r");
00226 }
00227 
00228 static void RInclude(std::istream& s)
00229 {
00230     tString file;
00231     s >> file;
00232 
00233     tString rclcl = tResourceManager::locateResource(NULL, file);
00234     if ( rclcl ) {
00235         std::ifstream rc(rclcl);
00236         tConfItemBase::LoadAll(rc);
00237         return;
00238     }
00239 
00240     con << tOutput( "$config_rinclude_not_found", file );
00241 }
00242 
00243 static tConfItemFunc s_RInclude("RINCLUDE",  &RInclude);
00244 
00245 static bool st_checkAuthor(tString const &Author) {
00246     if(Author.empty() || Author[0] < 'A' || Author[0] > 'z' || Author[0] > 'Z' && Author[0] < 'a' || Author.find('/') != tString::npos) {
00247         tERR_WARN("Resource authors must start with a letter and may not contain slashes");
00248         return false;
00249     }
00250     return true;
00251 }
00252 static bool st_checkCategory(tString const &Category) {
00253     if(Category[0] == '/' || *Category.rbegin() == '/' || Category.find("/.") != tString::npos) {
00254         tERR_WARN("Resource categories must not start or end with a slash or dot or contain the sequence \"./\".");
00255         return false;
00256     }
00257     return true;
00258 }
00259 static bool st_checkName(tString const &Name) {
00260     if(Name.empty() || Name[0] == '.' || Name.find_first_of("-/") != tString::npos) {
00261         tERR_WARN("Resource names must not start with a dot or contain slashes or minus signs");
00262         return false;
00263     }
00264     return true;
00265 }
00266 static bool st_checkExtension(tString const &Extension) {
00267     if(Extension.empty() || Extension.find_first_of("/.") != tString::npos) {
00268         tERR_WARN("Resource extensions must not contain slashes or dots");
00269         return false;
00270     }
00271     return true;
00272 }
00273 static bool st_checkType(tString const &Type) {
00274     if(Type.empty() || Type.find_first_of("/.") != tString::npos) {
00275         tERR_WARN("Resource types must not contain slashes or dots");
00276         return false;
00277     }
00278     return true;
00279 }
00280 static bool st_checkVersion(tString const &Version) {
00281     if(Version.empty() || Version.find('/') != tString::npos) {
00282         tERR_WARN("Resource versions must not contain slashes");
00283         return false;
00284     }
00285     return true;
00286 }
00287 
00288 tResourcePath::tResourcePath(tString const &Author,
00289                              tString const &Category,
00290                              tString const &Name,
00291                              tString const &Version,
00292                              tString const &Type,
00293                              tString const &Extension,
00294                              tString const &URI) :
00295     m_Author   (Author   ),
00296     m_Category (Category ),
00297     m_Name     (Name     ),
00298     m_Version  (Version  ),
00299     m_Type     (Type     ),
00300     m_Extension(Extension),
00301     m_URI      (URI      ),
00302     m_Valid(false) {
00303     m_Path << Author << '/';
00304     if(!st_checkAuthor(Author)) return;
00305     if(!Category.empty()) {
00306         if(!st_checkCategory(Category)) return;
00307         m_Path << Category << '/';
00308     }
00309     if(!st_checkName(Name)) return;
00310     if(!st_checkExtension(Extension)) return;
00311     if(!st_checkType(Extension)) return;
00312     if(!st_checkVersion(Version)) return;
00313     m_Path << Name << '-' << Version << '.' << Type << '.' << Extension;
00314     if(!URI.empty()) {
00315         m_Path << '(' << URI << ')';
00316     }
00317     m_Valid = true;
00318 }
00319 
00320 tResourcePath::tResourcePath(tString const &Path) : m_Path(Path), m_Valid(false) {
00321     // check if an URI is attached
00322     tString::size_type uridelim = m_Path.find('(');
00323     if(uridelim != tString::npos) {
00324         // find the corresponding opening bracket
00325         if(*m_Path.rbegin() != ')') {
00326             tERR_WARN("Incomplete URI specification");
00327             return;
00328         }
00329         m_URI = m_Path.substr(uridelim + 1, m_Path.size() - uridelim - 2);
00330         --uridelim;
00331     } else {
00332         uridelim = m_Path.size() - 1;
00333     }
00334 
00335     tString::size_type authordelim = Path.find('/');
00336     if(authordelim == tString::npos || authordelim >= uridelim) {
00337         tERR_WARN("Resource paths need to contain at least one slash");
00338         return;
00339     }
00340     m_Author = Path.substr(0, authordelim);
00341     if(!st_checkAuthor(m_Author)) return;
00342     tString::size_type categorydelim = Path.rfind('/', uridelim);
00343     if(categorydelim != authordelim) {
00344         m_Category = Path.substr(authordelim + 1, categorydelim - authordelim - 1);
00345         if(!st_checkCategory(m_Category)) return;
00346     }
00347     tString::size_type namedelim = Path.find('-', categorydelim);
00348     if(namedelim == tString::npos || namedelim >= uridelim) {
00349         tERR_WARN("Resource path is missing the version delimiter ('-')");
00350         return;
00351     }
00352     m_Name = Path.substr(categorydelim+1, namedelim - categorydelim - 1);
00353     if(!st_checkName(m_Name)) return;
00354 
00355     // now parse from the back to the front to find the version (which can
00356     // contain dots)
00357     tString::size_type extensiondelim = Path.rfind('.', uridelim);
00358     if(extensiondelim == tString::npos || extensiondelim <= namedelim || extensiondelim >= Path.size() - 1) {
00359         tERR_WARN("Resource path is missing the extension delimiter ('.')");
00360     }
00361     m_Extension = Path.substr(extensiondelim + 1, uridelim - extensiondelim);
00362     if(!st_checkExtension(m_Extension)) return;
00363     tString::size_type typedelim = Path.rfind('.', extensiondelim - 1);
00364     if(typedelim == tString::npos || typedelim <= namedelim) {
00365         tERR_WARN("Resource path is missing the type delimiter ('.')");
00366     }
00367     m_Type = Path.substr(typedelim + 1, extensiondelim - typedelim - 1);
00368     if(!st_checkType(m_Type)) return;
00369 
00370     // the rest is (hopefully) the version, now...
00371     m_Version = Path.substr(namedelim + 1, typedelim - namedelim - 1);
00372     if(!st_checkVersion(m_Version)) return;
00373     m_Valid=true;
00374 }
00375 
00376 bool tResourcePath::operator==(tResourcePath const &other) const {
00377     return m_Author    == other.m_Author    &&
00378            m_Category  == other.m_Category  &&
00379            m_Name      == other.m_Name      &&
00380            m_Version   == other.m_Version   &&
00381            m_Type      == other.m_Type      &&
00382            m_Extension == other.m_Extension;
00383 }
00384 
00385 // separate implementation to exploit lazy condition evaluation
00386 bool tResourcePath::operator!=(tResourcePath const &other) const {
00387     return m_Author    != other.m_Author    ||
00388            m_Category  != other.m_Category  ||
00389            m_Name      != other.m_Name      ||
00390            m_Version   != other.m_Version   ||
00391            m_Type      != other.m_Type      ||
00392            m_Extension != other.m_Extension;
00393 }

Generated on Sat Mar 15 22:56:01 2008 for Armagetron Advanced by  doxygen 1.5.4