user32: Fix a message test that would only pass on wine.
[wine.git] / dlls / version / install.c
blob5ff25e7b38fd475603474f37cd375b9582521f6a
1 /*
2 * Implementation of VERSION.DLL - File Installer routines
4 * Copyright 1996,1997 Marcus Meissner
5 * Copyright 1997 David Cuthbert
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * TODO
22 * o Check the installation functions.
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <string.h>
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winver.h"
33 #include "winnls.h"
34 #include "wine/unicode.h"
35 #include "winerror.h"
36 #include "lzexpand.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(ver);
42 /******************************************************************************
43 * testFileExistenceA
45 * Tests whether a given path/file combination exists. If the file does
46 * not exist, the return value is zero. If it does exist, the return
47 * value is non-zero.
49 * Revision history
50 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
51 * Original implementation
54 static int testFileExistenceA( char const * path, char const * file, BOOL excl )
56 char filename[1024];
57 int filenamelen;
58 OFSTRUCT fileinfo;
60 fileinfo.cBytes = sizeof(OFSTRUCT);
62 strcpy(filename, path);
63 filenamelen = strlen(filename);
65 /* Add a trailing \ if necessary */
66 if(filenamelen) {
67 if(filename[filenamelen - 1] != '\\')
68 strcat(filename, "\\");
70 else /* specify the current directory */
71 strcpy(filename, ".\\");
73 /* Create the full pathname */
74 strcat(filename, file);
76 return (OpenFile(filename, &fileinfo,
77 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
80 /******************************************************************************
81 * testFileExistenceW
83 static int testFileExistenceW( const WCHAR *path, const WCHAR *file, BOOL excl )
85 char *filename;
86 DWORD pathlen, filelen;
87 int ret;
88 OFSTRUCT fileinfo;
90 fileinfo.cBytes = sizeof(OFSTRUCT);
92 pathlen = WideCharToMultiByte( CP_ACP, 0, path, -1, NULL, 0, NULL, NULL );
93 filelen = WideCharToMultiByte( CP_ACP, 0, file, -1, NULL, 0, NULL, NULL );
94 filename = HeapAlloc( GetProcessHeap(), 0, pathlen+filelen+2 );
96 WideCharToMultiByte( CP_ACP, 0, path, -1, filename, pathlen, NULL, NULL );
97 /* Add a trailing \ if necessary */
98 if (pathlen > 1)
100 if (filename[pathlen-2] != '\\') strcpy( &filename[pathlen-1], "\\" );
102 else /* specify the current directory */
103 strcpy(filename, ".\\");
105 WideCharToMultiByte( CP_ACP, 0, file, -1, filename+strlen(filename), filelen, NULL, NULL );
107 ret = (OpenFile(filename, &fileinfo,
108 OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
109 HeapFree( GetProcessHeap(), 0, filename );
110 return ret;
113 /*****************************************************************************
114 * VerFindFileA [VERSION.@]
116 * Determines where to install a file based on whether it locates another
117 * version of the file in the system. The values VerFindFile returns are
118 * used in a subsequent call to the VerInstallFile function.
120 * Revision history:
121 * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
122 * Reimplementation of VerFindFile from original stub.
124 DWORD WINAPI VerFindFileA(
125 DWORD flags,
126 LPCSTR lpszFilename,
127 LPCSTR lpszWinDir,
128 LPCSTR lpszAppDir,
129 LPSTR lpszCurDir,
130 PUINT lpuCurDirLen,
131 LPSTR lpszDestDir,
132 PUINT lpuDestDirLen )
134 DWORD retval = 0;
135 const char *curDir;
136 const char *destDir;
137 unsigned int curDirSizeReq;
138 unsigned int destDirSizeReq;
139 char systemDir[MAX_PATH];
141 /* Print out debugging information */
142 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
143 flags, debugstr_a(lpszFilename), debugstr_a(lpszWinDir), debugstr_a(lpszAppDir),
144 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
145 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
147 /* Figure out where the file should go; shared files default to the
148 system directory */
150 GetSystemDirectoryA(systemDir, sizeof(systemDir));
151 curDir = "";
152 destDir = "";
154 if(flags & VFFF_ISSHAREDFILE)
156 destDir = systemDir;
157 /* Were we given a filename? If so, try to find the file. */
158 if(lpszFilename)
160 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
161 else if(lpszAppDir && testFileExistenceA(lpszAppDir, lpszFilename, FALSE))
163 curDir = lpszAppDir;
164 retval |= VFF_CURNEDEST;
168 else /* not a shared file */
170 if(lpszAppDir)
172 destDir = lpszAppDir;
173 if(lpszFilename)
175 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
176 else if(testFileExistenceA(systemDir, lpszFilename, FALSE))
178 curDir = systemDir;
179 retval |= VFF_CURNEDEST;
185 /* Check to see if the file exists and is inuse by another application */
186 if (lpszFilename && testFileExistenceA(curDir, lpszFilename, FALSE)) {
187 if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE))
188 retval |= VFF_FILEINUSE;
191 curDirSizeReq = strlen(curDir) + 1;
192 destDirSizeReq = strlen(destDir) + 1;
194 /* Make sure that the pointers to the size of the buffers are
195 valid; if not, do NOTHING with that buffer. If that pointer
196 is valid, then make sure that the buffer pointer is valid, too! */
198 if(lpuDestDirLen && lpszDestDir)
200 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
201 lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen);
202 *lpuDestDirLen = destDirSizeReq;
204 if(lpuCurDirLen && lpszCurDir)
206 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
207 lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen);
208 *lpuCurDirLen = curDirSizeReq;
211 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
212 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
213 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
214 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
215 debugstr_a(lpszCurDir), debugstr_a(lpszDestDir));
217 return retval;
220 /*****************************************************************************
221 * VerFindFileW [VERSION.@]
223 DWORD WINAPI VerFindFileW( DWORD flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir,
224 LPCWSTR lpszAppDir, LPWSTR lpszCurDir,PUINT lpuCurDirLen,
225 LPWSTR lpszDestDir,PUINT lpuDestDirLen )
227 static const WCHAR emptyW;
228 DWORD retval = 0;
229 const WCHAR *curDir;
230 const WCHAR *destDir;
231 unsigned int curDirSizeReq;
232 unsigned int destDirSizeReq;
233 WCHAR systemDir[MAX_PATH];
235 /* Print out debugging information */
236 TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
237 flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir),
238 lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
239 lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
241 /* Figure out where the file should go; shared files default to the
242 system directory */
244 GetSystemDirectoryW(systemDir, sizeof(systemDir)/sizeof(WCHAR));
245 curDir = &emptyW;
246 destDir = &emptyW;
248 if(flags & VFFF_ISSHAREDFILE)
250 destDir = systemDir;
251 /* Were we given a filename? If so, try to find the file. */
252 if(lpszFilename)
254 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
255 else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE))
257 curDir = lpszAppDir;
258 retval |= VFF_CURNEDEST;
262 else /* not a shared file */
264 if(lpszAppDir)
266 destDir = lpszAppDir;
267 if(lpszFilename)
269 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
270 else if(testFileExistenceW(systemDir, lpszFilename, FALSE))
272 curDir = systemDir;
273 retval |= VFF_CURNEDEST;
279 if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE))
280 retval |= VFF_FILEINUSE;
282 curDirSizeReq = strlenW(curDir) + 1;
283 destDirSizeReq = strlenW(destDir) + 1;
285 /* Make sure that the pointers to the size of the buffers are
286 valid; if not, do NOTHING with that buffer. If that pointer
287 is valid, then make sure that the buffer pointer is valid, too! */
289 if(lpuDestDirLen && lpszDestDir)
291 if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
292 lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen);
293 *lpuDestDirLen = destDirSizeReq;
295 if(lpuCurDirLen && lpszCurDir)
297 if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
298 lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen);
299 *lpuCurDirLen = curDirSizeReq;
302 TRACE("ret = %u (%s%s%s) curdir=%s destdir=%s\n", retval,
303 (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
304 (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
305 (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
306 debugstr_w(lpszCurDir), debugstr_w(lpszDestDir));
307 return retval;
310 static LPBYTE
311 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) {
312 DWORD alloclen;
313 LPBYTE buf;
314 DWORD ret;
316 alloclen = 1000;
317 buf=HeapAlloc(GetProcessHeap(), 0, alloclen);
318 if(buf == NULL) {
319 WARN("Memory exausted while fetching version info!\n");
320 return NULL;
322 while (1) {
323 ret = GetFileVersionInfoA(fn,0,alloclen,buf);
324 if (!ret) {
325 HeapFree(GetProcessHeap(), 0, buf);
326 return NULL;
328 if (alloclen<*(WORD*)buf) {
329 alloclen = *(WORD*)buf;
330 HeapFree(GetProcessHeap(), 0, buf);
331 buf = HeapAlloc(GetProcessHeap(), 0, alloclen);
332 if(buf == NULL) {
333 WARN("Memory exausted while fetching version info!\n");
334 return NULL;
336 } else {
337 *vffi = (VS_FIXEDFILEINFO*)(buf+0x14);
338 if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */
339 *vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
340 if ((*vffi)->dwSignature != VS_FFI_SIGNATURE)
341 WARN("Bad VS_FIXEDFILEINFO signature 0x%08x\n",(*vffi)->dwSignature);
342 return buf;
347 static DWORD
348 _error2vif(DWORD error) {
349 switch (error) {
350 case ERROR_ACCESS_DENIED:
351 return VIF_ACCESSVIOLATION;
352 case ERROR_SHARING_VIOLATION:
353 return VIF_SHARINGVIOLATION;
354 default:
355 return 0;
360 /******************************************************************************
361 * VerInstallFileA [VERSION.@]
363 DWORD WINAPI VerInstallFileA(
364 DWORD flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
365 LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,PUINT tmpfilelen )
367 LPCSTR pdest;
368 char destfn[260],tmpfn[260],srcfn[260];
369 HFILE hfsrc,hfdst;
370 DWORD attr,ret,xret,tmplast;
371 LPBYTE buf1,buf2;
372 OFSTRUCT ofs;
374 TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n",
375 flags,srcfilename,destfilename,srcdir,destdir,curdir,tmpfile,*tmpfilelen
377 xret = 0;
378 sprintf(srcfn,"%s\\%s",srcdir,srcfilename);
379 if (!destdir || !*destdir) pdest = srcdir;
380 else pdest = destdir;
381 sprintf(destfn,"%s\\%s",pdest,destfilename);
382 hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ);
383 if (hfsrc < 0)
384 return VIF_CANNOTREADSRC;
385 sprintf(tmpfn,"%s\\%s",pdest,destfilename);
386 tmplast=strlen(pdest)+1;
387 attr = GetFileAttributesA(tmpfn);
388 if (attr != INVALID_FILE_ATTRIBUTES) {
389 if (attr & FILE_ATTRIBUTE_READONLY) {
390 LZClose(hfsrc);
391 return VIF_WRITEPROT;
393 /* FIXME: check if file currently in use and return VIF_FILEINUSE */
395 attr = INVALID_FILE_ATTRIBUTES;
396 if (flags & VIFF_FORCEINSTALL) {
397 if (tmpfile[0]) {
398 sprintf(tmpfn,"%s\\%s",pdest,tmpfile);
399 tmplast = strlen(pdest)+1;
400 attr = GetFileAttributesA(tmpfn);
401 /* if it exists, it has been copied by the call before.
402 * we jump over the copy part...
406 if (attr == INVALID_FILE_ATTRIBUTES) {
407 char *s;
409 GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */
410 s=strrchr(tmpfn,'\\');
411 if (s)
412 tmplast = s-tmpfn;
413 else
414 tmplast = 0;
415 hfdst = OpenFile(tmpfn,&ofs,OF_CREATE);
416 if (hfdst == HFILE_ERROR) {
417 LZClose(hfsrc);
418 return VIF_CANNOTCREATE; /* | translated dos error */
420 ret = LZCopy(hfsrc,hfdst);
421 _lclose(hfdst);
422 if (((LONG)ret) < 0) {
423 /* translate LZ errors into VIF_xxx */
424 switch (ret) {
425 case LZERROR_BADINHANDLE:
426 case LZERROR_READ:
427 case LZERROR_BADVALUE:
428 case LZERROR_UNKNOWNALG:
429 ret = VIF_CANNOTREADSRC;
430 break;
431 case LZERROR_BADOUTHANDLE:
432 case LZERROR_WRITE:
433 ret = VIF_OUTOFSPACE;
434 break;
435 case LZERROR_GLOBALLOC:
436 case LZERROR_GLOBLOCK:
437 ret = VIF_OUTOFMEMORY;
438 break;
439 default: /* unknown error, should not happen */
440 ret = 0;
441 break;
443 if (ret) {
444 LZClose(hfsrc);
445 return ret;
449 xret = 0;
450 if (!(flags & VIFF_FORCEINSTALL)) {
451 VS_FIXEDFILEINFO *destvffi,*tmpvffi;
452 buf1 = _fetch_versioninfo(destfn,&destvffi);
453 if (buf1) {
454 buf2 = _fetch_versioninfo(tmpfn,&tmpvffi);
455 if (buf2) {
456 char *tbuf1,*tbuf2;
457 static const CHAR trans_array[] = "\\VarFileInfo\\Translation";
458 UINT len1,len2;
460 len1=len2=40;
462 /* compare file versions */
463 if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)||
464 ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&&
465 (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS)
468 xret |= VIF_MISMATCH|VIF_SRCOLD;
469 /* compare filetypes and filesubtypes */
470 if ((destvffi->dwFileType!=tmpvffi->dwFileType) ||
471 (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype)
473 xret |= VIF_MISMATCH|VIF_DIFFTYPE;
474 if (VerQueryValueA(buf1,trans_array,(LPVOID*)&tbuf1,&len1) &&
475 VerQueryValueA(buf2,trans_array,(LPVOID*)&tbuf2,&len2)
477 /* Do something with tbuf1 and tbuf2
478 * generates DIFFLANG|MISMATCH
481 HeapFree(GetProcessHeap(), 0, buf2);
482 } else
483 xret=VIF_MISMATCH|VIF_SRCOLD;
484 HeapFree(GetProcessHeap(), 0, buf1);
487 if (xret) {
488 if (*tmpfilelen<strlen(tmpfn+tmplast)) {
489 xret|=VIF_BUFFTOOSMALL;
490 DeleteFileA(tmpfn);
491 } else {
492 strcpy(tmpfile,tmpfn+tmplast);
493 *tmpfilelen = strlen(tmpfn+tmplast)+1;
494 xret|=VIF_TEMPFILE;
496 } else {
497 if (INVALID_FILE_ATTRIBUTES!=GetFileAttributesA(destfn))
498 if (!DeleteFileA(destfn)) {
499 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE;
500 DeleteFileA(tmpfn);
501 LZClose(hfsrc);
502 return xret;
504 if ((!(flags & VIFF_DONTDELETEOLD)) &&
505 curdir &&
506 *curdir &&
507 lstrcmpiA(curdir,pdest)
509 char curfn[260];
511 sprintf(curfn,"%s\\%s",curdir,destfilename);
512 if (INVALID_FILE_ATTRIBUTES != GetFileAttributesA(curfn)) {
513 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
514 if (!DeleteFileA(curfn))
515 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR;
518 if (!MoveFileA(tmpfn,destfn)) {
519 xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME;
520 DeleteFileA(tmpfn);
523 LZClose(hfsrc);
524 return xret;
528 /******************************************************************************
529 * VerInstallFileW [VERSION.@]
531 DWORD WINAPI VerInstallFileW(
532 DWORD flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,
533 LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,PUINT tmpfilelen )
535 LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL;
536 DWORD ret;
537 UINT len;
539 if (srcfilename)
541 len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL );
542 if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len )))
543 WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL );
545 if (srcdir)
547 len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL );
548 if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len )))
549 WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL );
551 if (destfilename)
553 len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL );
554 if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len )))
555 WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL );
557 if (destdir)
559 len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL );
560 if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len )))
561 WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL );
563 if (curdir)
565 len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL );
566 if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len )))
567 WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL );
569 len = *tmpfilelen * sizeof(WCHAR);
570 wtmpf = HeapAlloc( GetProcessHeap(), 0, len );
571 ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len);
572 if (!ret)
573 *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen );
574 else if (ret & VIF_BUFFTOOSMALL)
575 *tmpfilelen = len; /* FIXME: not correct */
577 HeapFree( GetProcessHeap(), 0, wsrcf );
578 HeapFree( GetProcessHeap(), 0, wsrcd );
579 HeapFree( GetProcessHeap(), 0, wdestf );
580 HeapFree( GetProcessHeap(), 0, wdestd );
581 HeapFree( GetProcessHeap(), 0, wtmpf );
582 HeapFree( GetProcessHeap(), 0, wcurd );
583 return ret;