version_checker.cpp

Go to the documentation of this file.
00001 #ifndef VERSION_CHECKER_IMPLEMENTATION_FILE
00002 #define VERSION_CHECKER_IMPLEMENTATION_FILE
00003 
00004 /*****************************************************************************\
00005 *                                                                             *
00006 *  Name   : check_version                                                     *
00007 *  Author : Chris Koeritz                                                     *
00008 *  Credits: John Stockton (for original windows get_version support)          *
00009 *                                                                             *
00010 *******************************************************************************
00011 * Copyright (c) 1996-$now By Author.  This program is free software; you can  *
00012 * redistribute it and/or modify it under the terms of the GNU General Public  *
00013 * License as published by the Free Software Foundation; either version 2 of   *
00014 * the License or (at your option) any later version.  This is online at:      *
00015 *     http://www.fsf.org/copyleft/gpl.html                                    *
00016 * Please send any updates to: fred@gruntose.com                               *
00017 \*****************************************************************************/
00018 
00019 #include "convert_utf.h"
00020 #include "function.h"
00021 #include "guards.h"
00022 #include "istring.h"
00023 #include "portable.h"
00024 #include "version_checker.h"
00025 #include "version_record.h"
00026 
00027 #include <stdio.h>
00028 
00029 #ifndef BOOT_STRAPPING
00030   // pull in the version specified for this build.
00031   #include <__build_version.h>
00032 #else
00033   // plug in a fake version for our bootstrapping process.
00034   #define __build_FILE_VERSION "108.420.1024.10008"
00035 #endif
00036 
00037 #ifdef _MSC_VER
00038   #include <direct.h>
00039   #include <winver.h>
00040 #endif
00041 
00042 #ifdef __WIN32__
00043   // ensures that we handle the data properly regardless of unicode settings.
00044   #ifdef UNICODE
00045     #define render_ptr(ptr) from_unicode_temp( (UTF16 *) ptr)
00046   #else
00047     #define render_ptr(ptr) ( (char *) ptr)
00048   #endif
00049 #endif
00050 
00052 
00053 version_checker::version_checker(const istring &library_file_name,
00054     const version &expected_version, const istring &version_complaint)
00055 : _library_file_name(new istring(library_file_name)),
00056   _expected_version(new version(expected_version)),
00057   _version_complaint(new istring(version_complaint))
00058 {}
00059 
00060 version_checker::~version_checker()
00061 {
00062   WHACK(_library_file_name);
00063   WHACK(_expected_version);
00064   WHACK(_version_complaint);
00065 }
00066 
00067 istring version_checker::text_form() const
00068 {
00069   return istring(class_name()) + ": library_file_name=" + *_library_file_name
00070       + ", expected_version=" + _expected_version->text_form()
00071       + ", complaint_message=" + *_version_complaint;
00072 }
00073 
00074 bool version_checker::good_version() const
00075 {
00076   istring version_disabler = portable::env_string("TMP");
00077   version_disabler += "/no_version_check.txt";
00078   FILE *always_okay = fopen(version_disabler.s(), "r");
00079   if (always_okay) {
00080     fclose(always_okay);
00081     return true;
00082   }
00083 
00084   version version_found = get_version(*_library_file_name);
00085   if (version_found.compatible(*_expected_version)) return true;  // success.
00086   complain_wrong_version(*_library_file_name, *_expected_version,
00087       version_found);
00088   return false;
00089 }
00090 
00091 bool version_checker::loaded(const istring &library_file_name)
00092 {
00093 #ifdef __WIN32__
00094   return bool(get_handle(library_file_name) != 0); 
00095 #else
00096 //temp code. 
00097   return true || library_file_name.t();
00098 #endif
00099 }
00100 
00101 void *version_checker::get_handle(const istring &library_file_name)
00102 {
00103 #ifdef __WIN32__
00104   return GetModuleHandle(to_unicode_temp(library_file_name));
00105 #else
00106 //hmmm: there really isn't this concept on OSes that i'm aware of.
00107   return 0 && library_file_name.t();
00108 #endif
00109 }
00110 
00111 istring version_checker::get_name(void *to_find)
00112 { return portable::module_name(to_find); }
00113 
00114 bool version_checker::retrieve_version_info(const istring &filename,
00115     byte_array &to_fill)
00116 {
00117   to_fill.reset();  // clear the buffer.
00118 
00119   // determine the required size of the version info buffer.
00120   int required_size;
00121 #ifdef __WIN32__
00122   u_long module_handle;  // filled with the dll or exe handle.
00123   required_size = GetFileVersionInfoSize(to_unicode_temp(filename), &module_handle);
00124 #else
00125   required_size = 0 && filename.t();
00126 #endif
00127   if (!required_size) return false;
00128   to_fill.reset(required_size);  // resize the buffer.
00129   
00130   // read the version info into our buffer.
00131   bool success = false;
00132 #ifdef __WIN32__
00133   success = GetFileVersionInfo(to_unicode_temp(filename), module_handle,
00134       required_size, to_fill.access());
00135 #else
00136   success = false;
00137 #endif
00138   return success;
00139 }
00140 
00141 bool version_checker::get_language(byte_array &version_chunk,
00142     u_short &high, u_short &low)
00143 {
00144   high = 0;
00145   low = 0;
00146 #ifdef __WIN32__
00147   // determine the language that the version's written in.
00148   u_int data_size;
00149   void *pointer_to_language_structure;
00150   // query the information from the version blob.
00151   if (!VerQueryValue(version_chunk.access(),
00152       to_unicode_temp("\\VarFileInfo\\Translation"),
00153       &pointer_to_language_structure, &data_size))
00154     return false;
00155   // get the low & high shorts of the structure.
00156   high = LOWORD(*(unsigned int *)pointer_to_language_structure);
00157   low = HIWORD(*(unsigned int *)pointer_to_language_structure);
00158 #else
00159   high = 0 && version_chunk.length();
00160   low = 0;
00161 #endif
00162 
00163   return true;
00164 }
00165 
00166 version version_checker::get_version(const istring &filename)
00167 {
00168 #ifdef UNIX
00169 
00170   // totally bogus stand-in; this just returns the version we were built with
00171   // rather than the version that's actually tagged on the file.
00172 
00173 //hmmm: this is totally temporary *and* bad.  fix it by getting real version
00174 //      info onto the so files.
00175 
00176   return version(__build_FILE_VERSION);
00177 
00178 #endif
00179 
00180   byte_array version_info_found(0, NIL);
00181   if (!retrieve_version_info(filename, version_info_found))
00182     return version(0, 0, 0, 0);
00183 
00184   u_short high, low;  // holds the language of the version data.
00185   if (!get_language(version_info_found, high, low))
00186     return version(0, 0, 0, 0);
00187 
00188   // retrieve the file version from version info using the appropriate
00189   // language.
00190   istring root_key(istring::SPRINTF, "\\StringFileInfo\\%04x%04x", high, low);
00191   istring file_version_key(root_key + istring("\\FileVersion"));
00192 
00193   istring version_string;
00194 #ifdef __WIN32__
00195   byte *file_version_pointer;
00196   u_int data_size;
00197   if (!VerQueryValue(version_info_found.access(),
00198       to_unicode_temp(file_version_key),
00199       (LPVOID *)&file_version_pointer, &data_size))
00200     return version(0, 0, 0, 0);
00201   version_string = render_ptr(file_version_pointer);
00202   // clean any spaces out of the string; people sometimes format these
00203   // very badly.
00204   for (int i = 0; i < version_string.length(); i++) {
00205     if (version_string[i] == ' ') {
00206       version_string.zap(i, i);
00207       i--;  // skip back a beat.
00208     }
00209   }
00210 #else
00211   return version(0, 0, 0, 0);
00212 //tmp.
00213 #endif
00214   return version::from_text(version_string);
00215 }
00216 
00217 bool version_checker::get_record(const istring &filename, 
00218     version_record &to_fill)
00219 {
00220   to_fill = version_record();
00221   byte_array version_info_found(0, NIL);
00222   if (!retrieve_version_info(filename, version_info_found))
00223     return false;
00224 
00225   u_short high, low;  // holds the language of the version data.
00226   if (!get_language(version_info_found, high, low))
00227     return false;
00228 
00229   // set the root key for all accesses of the version chunk.
00230   istring root_key(istring::SPRINTF, "\\StringFileInfo\\%04x%04x", high, low);
00231 
00232   // reports whether all lookups succeeded or not.
00233   bool total_success = true;
00234 
00235   // the various version pieces are retrieved...
00236 
00237 #ifdef __WIN32__
00238   u_int data_size;
00239   void *data_pointer;
00240 
00241   // file version.
00242   if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
00243       + istring("\\FileVersion")), &data_pointer, &data_size))
00244     total_success = false;
00245   else
00246     to_fill.file_version = version::from_text(render_ptr(data_pointer));
00247 
00248   // company name.
00249   if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
00250       + istring("\\CompanyName")), &data_pointer, &data_size))
00251     total_success = false;
00252   else
00253     to_fill.company_name = render_ptr(data_pointer);
00254 
00255   // file description.
00256   if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
00257       + istring("\\FileDescription")), &data_pointer, &data_size))
00258     total_success = false;
00259   else
00260     to_fill.description = render_ptr(data_pointer);
00261 
00262   // internal name.
00263   if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
00264       + istring("\\InternalName")), &data_pointer, &data_size))
00265     total_success = false;
00266   else
00267     to_fill.internal_name = render_ptr(data_pointer);
00268 
00269   // copyright info.
00270   if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
00271       + istring("\\LegalCopyright")), &data_pointer, &data_size))
00272     total_success = false;
00273   else
00274     to_fill.copyright = render_ptr(data_pointer);
00275 
00276   // trademark info.
00277   if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
00278       + istring("\\LegalTrademarks")), &data_pointer, &data_size))
00279     total_success = false;
00280   else
00281     to_fill.trademarks = render_ptr(data_pointer);
00282 
00283   // original file name.
00284   if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
00285       + istring("\\OriginalFilename")), &data_pointer, &data_size))
00286     total_success = false;
00287   else
00288     to_fill.original_name = render_ptr(data_pointer);
00289 
00290   // product name.
00291   if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
00292       + istring("\\ProductName")), &data_pointer, &data_size))
00293     total_success = false;
00294   else
00295     to_fill.product_name = render_ptr(data_pointer);
00296 
00297   // product version.
00298   if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
00299       + istring("\\ProductVersion")), &data_pointer, &data_size))
00300     total_success = false;
00301   else
00302     to_fill.product_version = version::from_text(render_ptr(data_pointer));
00303 #else
00304   // hmmm: chunks missing in version check.
00305 #endif
00306 
00307   return total_success;
00308 }
00309 
00310 void version_checker::complain_wrong_version(const istring &library_file_name,
00311     const version &expected_version, const version &version_found) const
00312 {
00313   istring to_show("There has been a Version Mismatch: The module \"");
00314   // use embedded module handle to retrieve name of dll or exe.
00315   istring module_name = get_name(GET_INSTANCE_HANDLE());
00316   if (!module_name) module_name = "Unknown";
00317   to_show += module_name;
00318   to_show += istring("\" cannot load.  This is because the file \"");
00319   to_show += library_file_name;
00320   to_show += istring("\" was expected to have a version of [");
00321 
00322   to_show += expected_version.flex_text_form(version::DOTS);
00323 
00324   to_show += istring("] but it instead had a version of [");
00325   to_show += version_found.flex_text_form(version::DOTS);
00326 
00327   to_show += istring("].  ");
00328   to_show += *_version_complaint;
00329 #ifdef __UNIX__
00330   continuable_error("version checking", "failure", to_show.s());
00331 #elif defined(__WIN32__)
00332   MessageBox(0, to_unicode_temp(to_show),
00333       to_unicode_temp("version_checking::failure"), MB_OK);
00334 #endif
00335 }
00336 
00337 void version_checker::complain_cannot_load(const istring &lib_file) const
00338 {
00339   istring to_show("There has been a failure in Version Checking: The file \"");
00340   to_show += lib_file;
00341   to_show += istring("\" could not be loaded or found.  ");
00342   to_show += *_version_complaint;
00343   continuable_error("version checking", "loading dll", to_show.s());
00344 }
00345 
00346 
00347 #endif //VERSION_CHECKER_IMPLEMENTATION_FILE
00348 

Generated on Fri Aug 29 04:28:45 2008 for HOOPLE Libraries by  doxygen 1.5.1