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>
24#include <filesystem/filename.h>
26
27#include <stdio.h>
28
29using namespace basis;
30using namespace configuration;
31using namespace loggers;
32using 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
59namespace versions {
60
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
99bool 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
110void *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
120astring version_checker::module_name(const void *module_handle)
121{
122#if defined(__UNIX__)
123//|| defined(__GNU_WINDOWS__)
124 if (module_handle) {}
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.")
136#endif
137}
138
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
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
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
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.
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
bool t() const
t() is a shortcut for the string being "true", as in non-empty.
Definition astring.h:97
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
Definition astring.h:113
virtual void zap(int start, int end)
Deletes the characters between "start" and "end" inclusively.
Definition astring.cpp:524
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:531
A very common template for a dynamic array of bytes.
Definition byte_array.h:36
static astring TMP()
provides a single place to get the temporary directory.
static basis::astring application_name()
returns the full name of the current application.
Holds all information about a file's versioning.
Holds a file's version identifier.
virtual basis::astring text_form() const
static version from_text(const basis::astring &to_convert)
returns a version structure parsed from "to_convert".
static void * __global_module_handle()
a static resource required to identify the actual win32 module that this lives in.
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
version_checker(const basis::astring &library_file_name, const structures::version &expected, const basis::astring &version_complaint)
Constructs a checking object and ensures the version is appropriate.
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