feisty meow concerns codebase  2.140
version_checker.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * *
3 * Name : check_version *
4 * Author : Chris Koeritz *
5 * *
6 *******************************************************************************
7 * Copyright (c) 1996-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
14 
15 #include "version_checker.h"
16 
18 #include <basis/astring.h>
19 #include <basis/environment.h>
20 #include <basis/functions.h>
21 #include <basis/guards.h>
22 #include <basis/utf_conversion.h>
24 #include <filesystem/filename.h>
26 
27 #include <stdio.h>
28 
29 using namespace basis;
30 using namespace configuration;
31 using namespace loggers;
32 using namespace structures;
33 
34 #ifndef BOOT_STRAPPING
35  // pull in the version specified for this build.
36  #include <__build_version.h>
37 //why was this include "on hold"?
38 #else
39  // plug in a fake version for our bootstrapping process.
40  #define __build_FILE_VERSION "108.420.1024.10008"
41 #endif
42 
43 #ifdef __WIN32__
44 // #include <direct.h>
45  #include <winver.h>
46 #endif
47 
48 #ifdef __WIN32__
49  // ensures that we handle the data properly regardless of unicode settings.
50  #ifdef UNICODE
51  #define render_ptr(ptr) from_unicode_temp( (UTF16 *) ptr)
52  #else
53  #define render_ptr(ptr) ( (char *) ptr)
54  #endif
55 #endif
56 
58 
59 namespace versions {
60 
61 version_checker::version_checker(const astring &library_file_name,
62  const version &expected_version, const astring &version_complaint)
63 : _library_file_name(new astring(library_file_name)),
64  _expected_version(new version(expected_version)),
65  _version_complaint(new astring(version_complaint))
66 {}
67 
69 {
70  WHACK(_library_file_name);
71  WHACK(_expected_version);
72  WHACK(_version_complaint);
73 }
74 
76 {
77  return astring(class_name()) + ": library_file_name=" + *_library_file_name
78  + ", expected_version=" + _expected_version->text_form()
79  + ", complaint_message=" + *_version_complaint;
80 }
81 
83 {
84  astring version_disabler = environment::TMP();
85  version_disabler += "/no_version_check.txt";
86  FILE *always_okay = fopen(version_disabler.s(), "r");
87  if (always_okay) {
88  fclose(always_okay);
89  return true;
90  }
91 
92  version version_found = retrieve_version(*_library_file_name);
93  if (version_found.compatible(*_expected_version)) return true; // success.
94  complain_wrong_version(*_library_file_name, *_expected_version,
95  version_found);
96  return false;
97 }
98 
99 bool version_checker::loaded(const astring &library_file_name)
100 {
101 //#ifdef __WIN32__
102 #if defined(__WIN32__)
103  return bool(get_handle(library_file_name) != 0);
104 #else
105 //temp code.
106  return true || library_file_name.t();
107 #endif
108 }
109 
110 void *version_checker::get_handle(const astring &library_file_name)
111 {
112 //#ifdef __WIN32__
113 #if defined(__WIN32__)
114  return GetModuleHandle(to_unicode_temp(library_file_name));
115 #else
116  if (library_file_name.t()) return NULL_POINTER; else return NULL_POINTER;
117 #endif
118 }
119 
120 astring version_checker::module_name(const void *module_handle)
121 {
122 #if defined(__UNIX__)
123 //|| defined(__GNU_WINDOWS__)
124  if (module_handle) {}
125  return application_configuration::application_name();
126 #elif defined(__WIN32__)
127 //#elif defined(__WIN32__)
128  flexichar low_buff[MAX_ABS_PATH + 1];
129  GetModuleFileName((HMODULE)module_handle, low_buff, MAX_ABS_PATH - 1);
130  astring buff = from_unicode_temp(low_buff);
131  buff.to_lower();
132  return buff;
133 #else
134  #pragma message("module_name unknown for this operating system.")
135  return application_configuration::application_name();
136 #endif
137 }
138 
139 astring version_checker::get_name(const void *to_find)
140 { return module_name(to_find); }
141 
143  byte_array &to_fill)
144 {
145  to_fill.reset(); // clear the buffer.
146 
147  // determine the required size of the version info buffer.
148  int required_size;
149 #if defined(__WIN32__)
150 //#ifdef __WIN32__
151  DWORD module_handle; // filled with the dll or exe handle.
152  required_size = GetFileVersionInfoSize(to_unicode_temp(filename), &module_handle);
153 #else
154  required_size = 0 && filename.t();
155 #endif
156  if (!required_size) return false;
157  to_fill.reset(required_size); // resize the buffer.
158 
159  // read the version info into our buffer.
160  bool success = false;
161 #if defined(__WIN32__)
162 //#ifdef __WIN32__
163  success = GetFileVersionInfo(to_unicode_temp(filename), module_handle,
164  required_size, to_fill.access());
165 #else
166  success = false;
167 #endif
168  return success;
169 }
170 
172  basis::un_short &high, basis::un_short &low)
173 {
174  high = 0;
175  low = 0;
176 #if defined(__WIN32__)
177 //#ifdef __WIN32__
178  // determine the language that the version's written in.
179  basis::un_int data_size;
180  void *pointer_to_language_structure;
181  // query the information from the version blob.
182  if (!VerQueryValue(version_chunk.access(),
183  to_unicode_temp("\\VarFileInfo\\Translation"),
184  &pointer_to_language_structure, &data_size))
185  return false;
186  // get the low & high shorts of the structure.
187  high = LOWORD(*(unsigned int *)pointer_to_language_structure);
188  low = HIWORD(*(unsigned int *)pointer_to_language_structure);
189 #else
190  high = 0 && version_chunk.length();
191  low = 0;
192 #endif
193 
194  return true;
195 }
196 
198 {
199 //#ifdef UNIX
200 #if defined(__UNIX__)
201 // || defined(__GNU_WINDOWS__)
202 
203  // totally bogus stand-in; this just returns the version we were built with
204  // rather than the version that's actually tagged on the file.
205 
206 //hmmm: fix this! get the version header back.
207 // return version(__build_FILE_VERSION);
208  return version();
209 
210 #endif
211 
212  byte_array version_info_found(0, NULL_POINTER);
213  if (!retrieve_version_info(filename, version_info_found))
214  return version(0, 0, 0, 0);
215 
216  basis::un_short high, low; // holds the language of the version data.
217  if (!get_language(version_info_found, high, low))
218  return version(0, 0, 0, 0);
219 
220  // retrieve the file version from version info using the appropriate
221  // language.
222  astring root_key(astring::SPRINTF, "\\StringFileInfo\\%04x%04x", high, low);
223  astring file_version_key(root_key + astring("\\FileVersion"));
224 
225  astring version_string;
226 #ifdef __WIN32__
227  abyte *file_version_pointer;
228  basis::un_int data_size;
229  if (!VerQueryValue(version_info_found.access(),
230  to_unicode_temp(file_version_key),
231  (LPVOID *)&file_version_pointer, &data_size))
232  return version(0, 0, 0, 0);
233  version_string = render_ptr(file_version_pointer);
234  // clean any spaces out of the string; people sometimes format these
235  // very badly.
236  for (int i = 0; i < version_string.length(); i++) {
237  if (version_string[i] == ' ') {
238  version_string.zap(i, i);
239  i--; // skip back a beat.
240  }
241  }
242 #else
243  return version(0, 0, 0, 0);
244 //tmp.
245 #endif
246  return version::from_text(version_string);
247 }
248 
249 bool version_checker::get_record(const astring &filename,
250  version_record &to_fill)
251 {
252  to_fill = version_record();
253  byte_array version_info_found(0, NULL_POINTER);
254  if (!retrieve_version_info(filename, version_info_found))
255  return false;
256 
257  basis::un_short high, low; // holds the language of the version data.
258  if (!get_language(version_info_found, high, low))
259  return false;
260 
261  // set the root key for all accesses of the version chunk.
262  astring root_key(astring::SPRINTF, "\\StringFileInfo\\%04x%04x", high, low);
263 
264  // reports whether all lookups succeeded or not.
265  bool total_success = true;
266 
267  // the various version pieces are retrieved...
268 
269 //#ifdef __WIN32__
270 #ifdef __WIN32__
271  basis::un_int data_size;
272  void *data_pointer;
273 
274  // file version.
275  if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
276  + astring("\\FileVersion")), &data_pointer, &data_size))
277  total_success = false;
278  else
279  to_fill.file_version = version::from_text(render_ptr(data_pointer));
280 
281  // company name.
282  if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
283  + astring("\\CompanyName")), &data_pointer, &data_size))
284  total_success = false;
285  else
286  to_fill.company_name = render_ptr(data_pointer);
287 
288  // file description.
289  if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
290  + astring("\\FileDescription")), &data_pointer, &data_size))
291  total_success = false;
292  else
293  to_fill.description = render_ptr(data_pointer);
294 
295  // internal name.
296  if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
297  + astring("\\InternalName")), &data_pointer, &data_size))
298  total_success = false;
299  else
300  to_fill.internal_name = render_ptr(data_pointer);
301 
302  // copyright info.
303  if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
304  + astring("\\LegalCopyright")), &data_pointer, &data_size))
305  total_success = false;
306  else
307  to_fill.copyright = render_ptr(data_pointer);
308 
309  // trademark info.
310  if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
311  + astring("\\LegalTrademarks")), &data_pointer, &data_size))
312  total_success = false;
313  else
314  to_fill.trademarks = render_ptr(data_pointer);
315 
316  // original file name.
317  if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
318  + astring("\\OriginalFilename")), &data_pointer, &data_size))
319  total_success = false;
320  else
321  to_fill.original_name = render_ptr(data_pointer);
322 
323  // product name.
324  if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
325  + astring("\\ProductName")), &data_pointer, &data_size))
326  total_success = false;
327  else
328  to_fill.product_name = render_ptr(data_pointer);
329 
330  // product version.
331  if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key
332  + astring("\\ProductVersion")), &data_pointer, &data_size))
333  total_success = false;
334  else
335  to_fill.product_version = version::from_text(render_ptr(data_pointer));
336 #else
337  // hmmm: chunks missing in version check.
338 #endif
339 
340  return total_success;
341 }
342 
343 void version_checker::complain_wrong_version(const astring &library_file_name,
344  const version &expected_version, const version &version_found) const
345 {
346  astring to_show("There has been a Version Mismatch: The module \"");
347  // use embedded module handle to retrieve name of dll or exe.
348  astring module_name = get_name(version::__global_module_handle());
349  if (!module_name) module_name = "Unknown";
350  to_show += module_name;
351  to_show += astring("\" cannot load. This is because the file \"");
352  to_show += library_file_name;
353  to_show += astring("\" was expected to have a version of [");
354 
355  to_show += expected_version.flex_text_form(version::DOTS);
356 
357  to_show += astring("] but it instead had a version of [");
358  to_show += version_found.flex_text_form(version::DOTS);
359 
360  to_show += astring("]. ");
361  to_show += *_version_complaint;
362 //#ifdef __UNIX__
363 #if defined(__UNIX__)
364 //|| defined(__GNU_WINDOWS__)
365  continuable_error("version checking", "failure", to_show.s());
366 #elif defined(__WIN32__)
367  MessageBox(0, to_unicode_temp(to_show),
368  to_unicode_temp("version_checking::failure"), MB_OK);
369 #endif
370 }
371 
373 {
374  astring to_show("There has been a failure in Version Checking: The file \"");
375  to_show += lib_file;
376  to_show += astring("\" could not be loaded or found. ");
377  to_show += *_version_complaint;
378  continuable_error("version checking", "loading dll", to_show.s());
379 }
380 
381 } //namespace.
382 
383 
void reset(int number=0, const contents *initial_contents=NULL_POINTER)
Resizes this array and sets the contents from an array of contents.
Definition: array.h:349
contents * access()
A non-constant access of the underlying C-array. BE REALLY CAREFUL.
Definition: array.h:175
int length() const
Returns the current reported length of the allocated C array.
Definition: array.h:115
Provides a dynamically resizable ASCII character string.
Definition: astring.h:35
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
Definition: astring.h:113
bool t() const
t() is a shortcut for the string being "true", as in non-empty.
Definition: astring.h:97
virtual void zap(int start, int end)
Deletes the characters between "start" and "end" inclusively.
Definition: astring.cpp:521
int length() const
Returns the current length of the string.
Definition: astring.cpp:132
void to_lower()
to_lower modifies "this" by replacing capitals with lower-case.
Definition: astring.cpp:528
A very common template for a dynamic array of bytes.
Definition: byte_array.h:36
Holds all information about a file's versioning.
Holds a file's version identifier.
virtual basis::astring text_form() const
bool compatible(const version &that) const
returns true if this is compatible with "that" version on win32.
basis::astring flex_text_form(version_style style=DOTS, int including=-1) const
returns a textual form of the version number.
void complain_cannot_load(const basis::astring &library_file_name) const
Reports that the dll could not be loaded.
bool good_version() const
Performs the actual version check.
virtual ~version_checker()
Destructor releases any resources.
static bool get_record(const basis::astring &pathname, structures::version_record &to_fill)
Retrieves a version record for the file at "pathname".
static bool retrieve_version_info(const basis::astring &filename, basis::byte_array &to_fill)
Retrieves the version info for the "filename" into the array "to_fill".
static bool get_language(basis::byte_array &version_chunk, basis::un_short &high, basis::un_short &low)
Gets the language identifier out of the "version_chunk".
static basis::astring get_name(const void *to_find)
returns the name of the HMODULE specified by "to_find".
static basis::astring module_name(const void *module_handle)
returns the module name where this object resides; only sensible on win32.
void complain_wrong_version(const basis::astring &library_file_name, const structures::version &expected_version, const structures::version &version_found) const
Reports that the file has the wrong version.
static bool loaded(const basis::astring &library_file_name)
returns true if the "library_file_name" is currently loaded.
basis::astring text_form() const
static structures::version retrieve_version(const basis::astring &pathname)
Returns the version given a "pathname" to the DLL or EXE file.
static void * get_handle(const basis::astring &library_file_name)
retrieves the module handle for the "library_file_name".
#define continuable_error(c, f, i)
#define NULL_POINTER
The value representing a pointer to nothing.
Definition: definitions.h:32
#define MAX_ABS_PATH
Definition: filename.h:37
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
Definition: functions.h:121
unsigned char abyte
A fairly important unit which is seldom defined...
Definition: definitions.h:51
unsigned int un_int
Abbreviated name for unsigned integers.
Definition: definitions.h:62
unsigned short un_short
Abbreviated name for unsigned short integers.
Definition: definitions.h:64
char flexichar
Definition: definitions.h:58
A logger that sends to the console screen using the standard output device.
A dynamic container class that holds any kind of object via pointers.
Definition: amorph.h:55
Support for unicode builds.
Aids in achievement of platform independence.
unsigned long DWORD