Added regedit unit test, a couple minor changes to regedit.
[wine] / dlls / version / install.c
1 /*
2  * Implementation of VERSION.DLL - File Installer routines
3  *
4  * Copyright 1996,1997 Marcus Meissner
5  * Copyright 1997 David Cuthbert
6  *
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.
11  *
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.
16  *
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winver.h"
29 #include "winnls.h"
30 #include "wine/unicode.h"
31 #include "winerror.h"
32 #include "lzexpand.h"
33 #include "wine/debug.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(ver);
36
37
38 /******************************************************************************
39  *   testFileExistenceA
40  *
41  *   Tests whether a given path/file combination exists.  If the file does
42  *   not exist, the return value is zero.  If it does exist, the return
43  *   value is non-zero.
44  *
45  *   Revision history
46  *      30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu)
47  *         Original implementation
48  *
49  */
50 static int testFileExistenceA( char const * path, char const * file, BOOL excl )
51 {
52     char  filename[1024];
53     int  filenamelen;
54     OFSTRUCT  fileinfo;
55
56     fileinfo.cBytes = sizeof(OFSTRUCT);
57
58     strcpy(filename, path);
59     filenamelen = strlen(filename);
60
61     /* Add a trailing \ if necessary */
62     if(filenamelen) {
63         if(filename[filenamelen - 1] != '\\')
64             strcat(filename, "\\");
65     }
66     else /* specify the current directory */
67         strcpy(filename, ".\\");
68
69     /* Create the full pathname */
70     strcat(filename, file);
71
72     return (OpenFile(filename, &fileinfo,
73                      OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
74 }
75
76 /******************************************************************************
77  *   testFileExistenceW
78  */
79 static int testFileExistenceW( const WCHAR *path, const WCHAR *file, BOOL excl )
80 {
81     char *filename;
82     DWORD pathlen, filelen;
83     int ret;
84     OFSTRUCT fileinfo;
85
86     fileinfo.cBytes = sizeof(OFSTRUCT);
87
88     pathlen = WideCharToMultiByte( CP_ACP, 0, path, -1, NULL, 0, NULL, NULL );
89     filelen = WideCharToMultiByte( CP_ACP, 0, file, -1, NULL, 0, NULL, NULL );
90     filename = HeapAlloc( GetProcessHeap(), 0, pathlen+filelen+2 );
91
92     WideCharToMultiByte( CP_ACP, 0, path, -1, filename, pathlen, NULL, NULL );
93     /* Add a trailing \ if necessary */
94     if (pathlen > 1)
95     {
96         if (filename[pathlen-2] != '\\') strcpy( &filename[pathlen-1], "\\" );
97     }
98     else /* specify the current directory */
99         strcpy(filename, ".\\");
100
101     WideCharToMultiByte( CP_ACP, 0, file, -1, filename+strlen(filename), filelen, NULL, NULL );
102
103     ret = (OpenFile(filename, &fileinfo,
104                     OF_EXIST | (excl ? OF_SHARE_EXCLUSIVE : 0)) != HFILE_ERROR);
105     HeapFree( GetProcessHeap(), 0, filename );
106     return ret;
107 }
108
109 /*****************************************************************************
110  *   VerFindFileA [VERSION.@]
111  *
112  *   Determines where to install a file based on whether it locates another
113  *   version of the file in the system.  The values VerFindFile returns are
114  *   used in a subsequent call to the VerInstallFile function.
115  *
116  *   Revision history:
117  *      30-May-1997   Dave Cuthbert (dacut@ece.cmu.edu)
118  *         Reimplementation of VerFindFile from original stub.
119  */
120 DWORD WINAPI VerFindFileA(
121     UINT flags,
122     LPCSTR lpszFilename,
123     LPCSTR lpszWinDir,
124     LPCSTR lpszAppDir,
125     LPSTR lpszCurDir,
126     UINT *lpuCurDirLen,
127     LPSTR lpszDestDir,
128     UINT *lpuDestDirLen )
129 {
130     DWORD  retval = 0;
131     const char *curDir;
132     const char *destDir;
133     unsigned int  curDirSizeReq;
134     unsigned int  destDirSizeReq;
135     char  systemDir[MAX_PATH];
136
137     /* Print out debugging information */
138     TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
139           flags, debugstr_a(lpszFilename), debugstr_a(lpszWinDir), debugstr_a(lpszAppDir),
140           lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
141           lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
142
143     /* Figure out where the file should go; shared files default to the
144        system directory */
145
146     GetSystemDirectoryA(systemDir, sizeof(systemDir));
147     curDir = "";
148     destDir = "";
149
150     if(flags & VFFF_ISSHAREDFILE)
151     {
152         destDir = systemDir;
153         /* Were we given a filename?  If so, try to find the file. */
154         if(lpszFilename)
155         {
156             if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
157             else if(lpszAppDir && testFileExistenceA(lpszAppDir, lpszFilename, FALSE))
158             {
159                 curDir = lpszAppDir;
160                 retval |= VFF_CURNEDEST;
161             }
162         }
163     }
164     else /* not a shared file */
165     {
166         if(lpszAppDir)
167         {
168             destDir = lpszAppDir;
169             if(lpszFilename)
170             {
171                 if(testFileExistenceA(destDir, lpszFilename, FALSE)) curDir = destDir;
172                 else if(testFileExistenceA(systemDir, lpszFilename, FALSE))
173                 {
174                     curDir = systemDir;
175                     retval |= VFF_CURNEDEST;
176                 }
177             }
178         }
179     }
180
181     if (lpszFilename && !testFileExistenceA(curDir, lpszFilename, TRUE))
182         retval |= VFF_FILEINUSE;
183
184     curDirSizeReq = strlen(curDir) + 1;
185     destDirSizeReq = strlen(destDir) + 1;
186
187     /* Make sure that the pointers to the size of the buffers are
188        valid; if not, do NOTHING with that buffer.  If that pointer
189        is valid, then make sure that the buffer pointer is valid, too! */
190
191     if(lpuDestDirLen && lpszDestDir)
192     {
193         if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
194         lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen);
195         *lpuDestDirLen = destDirSizeReq;
196     }
197     if(lpuCurDirLen && lpszCurDir)
198     {
199         if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
200         lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen);
201         *lpuCurDirLen = curDirSizeReq;
202     }
203
204     TRACE("ret = %lu (%s%s%s) curdir=%s destdir=%s\n", retval,
205           (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
206           (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
207           (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
208           debugstr_a(lpszCurDir), debugstr_a(lpszDestDir));
209
210     return retval;
211 }
212
213 /*****************************************************************************
214  * VerFindFileW                                         [VERSION.@]
215  */
216 DWORD WINAPI VerFindFileW( UINT flags,LPCWSTR lpszFilename,LPCWSTR lpszWinDir,
217                            LPCWSTR lpszAppDir, LPWSTR lpszCurDir,UINT *lpuCurDirLen,
218                            LPWSTR lpszDestDir,UINT *lpuDestDirLen )
219 {
220     static const WCHAR emptyW;
221     DWORD retval = 0;
222     const WCHAR *curDir;
223     const WCHAR *destDir;
224     unsigned int curDirSizeReq;
225     unsigned int destDirSizeReq;
226     WCHAR systemDir[MAX_PATH];
227
228     /* Print out debugging information */
229     TRACE("flags = %x filename=%s windir=%s appdir=%s curdirlen=%p(%u) destdirlen=%p(%u)\n",
230           flags, debugstr_w(lpszFilename), debugstr_w(lpszWinDir), debugstr_w(lpszAppDir),
231           lpuCurDirLen, lpuCurDirLen ? *lpuCurDirLen : 0,
232           lpuDestDirLen, lpuDestDirLen ? *lpuDestDirLen : 0 );
233
234     /* Figure out where the file should go; shared files default to the
235        system directory */
236
237     GetSystemDirectoryW(systemDir, sizeof(systemDir)/sizeof(WCHAR));
238     curDir = &emptyW;
239     destDir = &emptyW;
240
241     if(flags & VFFF_ISSHAREDFILE)
242     {
243         destDir = systemDir;
244         /* Were we given a filename?  If so, try to find the file. */
245         if(lpszFilename)
246         {
247             if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
248             else if(lpszAppDir && testFileExistenceW(lpszAppDir, lpszFilename, FALSE))
249             {
250                 curDir = lpszAppDir;
251                 retval |= VFF_CURNEDEST;
252             }
253         }
254     }
255     else /* not a shared file */
256     {
257         if(lpszAppDir)
258         {
259             destDir = lpszAppDir;
260             if(lpszFilename)
261             {
262                 if(testFileExistenceW(destDir, lpszFilename, FALSE)) curDir = destDir;
263                 else if(testFileExistenceW(systemDir, lpszFilename, FALSE))
264                 {
265                     curDir = systemDir;
266                     retval |= VFF_CURNEDEST;
267                 }
268             }
269         }
270     }
271
272     if (lpszFilename && !testFileExistenceW(curDir, lpszFilename, TRUE))
273         retval |= VFF_FILEINUSE;
274
275     curDirSizeReq = strlenW(curDir) + 1;
276     destDirSizeReq = strlenW(destDir) + 1;
277
278     /* Make sure that the pointers to the size of the buffers are
279        valid; if not, do NOTHING with that buffer.  If that pointer
280        is valid, then make sure that the buffer pointer is valid, too! */
281
282     if(lpuDestDirLen && lpszDestDir)
283     {
284         if (*lpuDestDirLen < destDirSizeReq) retval |= VFF_BUFFTOOSMALL;
285         lstrcpynW(lpszDestDir, destDir, *lpuDestDirLen);
286         *lpuDestDirLen = destDirSizeReq;
287     }
288     if(lpuCurDirLen && lpszCurDir)
289     {
290         if(*lpuCurDirLen < curDirSizeReq) retval |= VFF_BUFFTOOSMALL;
291         lstrcpynW(lpszCurDir, curDir, *lpuCurDirLen);
292         *lpuCurDirLen = curDirSizeReq;
293     }
294
295     TRACE("ret = %lu (%s%s%s) curdir=%s destdir=%s\n", retval,
296           (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "",
297           (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "",
298           (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : "",
299           debugstr_w(lpszCurDir), debugstr_w(lpszDestDir));
300     return retval;
301 }
302
303 static LPBYTE
304 _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) {
305     DWORD       alloclen;
306     LPBYTE      buf;
307     DWORD       ret;
308
309     alloclen = 1000;
310     buf=HeapAlloc(GetProcessHeap(), 0, alloclen);
311     if(buf == NULL) {
312         WARN("Memory exausted while fetching version info!\n");
313         return NULL;
314     }
315     while (1) {
316         ret = GetFileVersionInfoA(fn,0,alloclen,buf);
317         if (!ret) {
318             HeapFree(GetProcessHeap(), 0, buf);
319             return NULL;
320         }
321         if (alloclen<*(WORD*)buf) {
322             alloclen = *(WORD*)buf;
323             HeapFree(GetProcessHeap(), 0, buf);
324             buf = HeapAlloc(GetProcessHeap(), 0, alloclen);
325             if(buf == NULL) {
326                WARN("Memory exausted while fetching version info!\n");
327                return NULL;
328             }
329         } else {
330             *vffi = (VS_FIXEDFILEINFO*)(buf+0x14);
331             if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */
332                 *vffi = (VS_FIXEDFILEINFO*)(buf+0x28);
333             if ((*vffi)->dwSignature != VS_FFI_SIGNATURE)
334                 WARN("Bad VS_FIXEDFILEINFO signature 0x%08lx\n",(*vffi)->dwSignature);
335             return buf;
336         }
337     }
338 }
339
340 static DWORD
341 _error2vif(DWORD error) {
342     switch (error) {
343     case ERROR_ACCESS_DENIED:
344         return VIF_ACCESSVIOLATION;
345     case ERROR_SHARING_VIOLATION:
346         return VIF_SHARINGVIOLATION;
347     default:
348         return 0;
349     }
350 }
351
352
353 /******************************************************************************
354  * VerInstallFileA [VERSION.@]
355  */
356 DWORD WINAPI VerInstallFileA(
357         UINT flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir,
358         LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,UINT *tmpfilelen )
359 {
360     LPCSTR pdest;
361     char        destfn[260],tmpfn[260],srcfn[260];
362     HFILE       hfsrc,hfdst;
363     DWORD       attr,ret,xret,tmplast;
364     LPBYTE      buf1,buf2;
365     OFSTRUCT    ofs;
366
367     TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n",
368             flags,srcfilename,destfilename,srcdir,destdir,curdir,tmpfile,*tmpfilelen
369     );
370     xret = 0;
371     sprintf(srcfn,"%s\\%s",srcdir,srcfilename);
372     if (!destdir || !*destdir) pdest = srcdir;
373     else pdest = destdir;
374     sprintf(destfn,"%s\\%s",pdest,destfilename);
375     hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ);
376     if (hfsrc < 0)
377         return VIF_CANNOTREADSRC;
378     sprintf(tmpfn,"%s\\%s",pdest,destfilename);
379     tmplast=strlen(pdest)+1;
380     attr = GetFileAttributesA(tmpfn);
381     if (attr!=-1) {
382         if (attr & FILE_ATTRIBUTE_READONLY) {
383             LZClose(hfsrc);
384             return VIF_WRITEPROT;
385         }
386         /* FIXME: check if file currently in use and return VIF_FILEINUSE */
387     }
388     attr = -1;
389     if (flags & VIFF_FORCEINSTALL) {
390         if (tmpfile[0]) {
391             sprintf(tmpfn,"%s\\%s",pdest,tmpfile);
392             tmplast = strlen(pdest)+1;
393             attr = GetFileAttributesA(tmpfn);
394             /* if it exists, it has been copied by the call before.
395              * we jump over the copy part...
396              */
397         }
398     }
399     if (attr == -1) {
400         char    *s;
401
402         GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */
403         s=strrchr(tmpfn,'\\');
404         if (s)
405             tmplast = s-tmpfn;
406         else
407             tmplast = 0;
408         hfdst = OpenFile(tmpfn,&ofs,OF_CREATE);
409         if (hfdst == HFILE_ERROR) {
410             LZClose(hfsrc);
411             return VIF_CANNOTCREATE; /* | translated dos error */
412         }
413         ret = LZCopy(hfsrc,hfdst);
414         _lclose(hfdst);
415         if (((long) ret) < 0) {
416             /* translate LZ errors into VIF_xxx */
417             switch (ret) {
418             case LZERROR_BADINHANDLE:
419             case LZERROR_READ:
420             case LZERROR_BADVALUE:
421             case LZERROR_UNKNOWNALG:
422                 ret = VIF_CANNOTREADSRC;
423                 break;
424             case LZERROR_BADOUTHANDLE:
425             case LZERROR_WRITE:
426                 ret = VIF_OUTOFSPACE;
427                 break;
428             case LZERROR_GLOBALLOC:
429             case LZERROR_GLOBLOCK:
430                 ret = VIF_OUTOFMEMORY;
431                 break;
432             default: /* unknown error, should not happen */
433                 ret = 0;
434                 break;
435             }
436             if (ret) {
437                 LZClose(hfsrc);
438                 return ret;
439             }
440         }
441     }
442     xret = 0;
443     if (!(flags & VIFF_FORCEINSTALL)) {
444         VS_FIXEDFILEINFO *destvffi,*tmpvffi;
445         buf1 = _fetch_versioninfo(destfn,&destvffi);
446         if (buf1) {
447             buf2 = _fetch_versioninfo(tmpfn,&tmpvffi);
448             if (buf2) {
449                 char    *tbuf1,*tbuf2;
450                 UINT    len1,len2;
451
452                 len1=len2=40;
453
454                 /* compare file versions */
455                 if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)||
456                     ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&&
457                      (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS)
458                     )
459                 )
460                     xret |= VIF_MISMATCH|VIF_SRCOLD;
461                 /* compare filetypes and filesubtypes */
462                 if ((destvffi->dwFileType!=tmpvffi->dwFileType) ||
463                     (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype)
464                 )
465                     xret |= VIF_MISMATCH|VIF_DIFFTYPE;
466                 if (VerQueryValueA(buf1,"\\VarFileInfo\\Translation",(LPVOID*)&tbuf1,&len1) &&
467                     VerQueryValueA(buf2,"\\VarFileInfo\\Translation",(LPVOID*)&tbuf2,&len2)
468                 ) {
469                     /* irgendwas mit tbuf1 und tbuf2 machen
470                      * generiert DIFFLANG|MISMATCH
471                      */
472                 }
473                 HeapFree(GetProcessHeap(), 0, buf2);
474             } else
475                 xret=VIF_MISMATCH|VIF_SRCOLD;
476             HeapFree(GetProcessHeap(), 0, buf1);
477         }
478     }
479     if (xret) {
480         if (*tmpfilelen<strlen(tmpfn+tmplast)) {
481             xret|=VIF_BUFFTOOSMALL;
482             DeleteFileA(tmpfn);
483         } else {
484             strcpy(tmpfile,tmpfn+tmplast);
485             *tmpfilelen = strlen(tmpfn+tmplast)+1;
486             xret|=VIF_TEMPFILE;
487         }
488     } else {
489         if (-1!=GetFileAttributesA(destfn))
490             if (!DeleteFileA(destfn)) {
491                 xret|=_error2vif(GetLastError())|VIF_CANNOTDELETE;
492                 DeleteFileA(tmpfn);
493                 LZClose(hfsrc);
494                 return xret;
495             }
496         if ((!(flags & VIFF_DONTDELETEOLD))     &&
497             curdir                              &&
498             *curdir                             &&
499             lstrcmpiA(curdir,pdest)
500         ) {
501             char curfn[260];
502
503             sprintf(curfn,"%s\\%s",curdir,destfilename);
504             if (-1!=GetFileAttributesA(curfn)) {
505                 /* FIXME: check if in use ... if it is, VIF_CANNOTDELETECUR */
506                 if (!DeleteFileA(curfn))
507                     xret|=_error2vif(GetLastError())|VIF_CANNOTDELETECUR;
508             }
509         }
510         if (!MoveFileA(tmpfn,destfn)) {
511             xret|=_error2vif(GetLastError())|VIF_CANNOTRENAME;
512             DeleteFileA(tmpfn);
513         }
514     }
515     LZClose(hfsrc);
516     return xret;
517 }
518
519
520 /******************************************************************************
521  * VerInstallFileW                              [VERSION.@]
522  */
523 DWORD WINAPI VerInstallFileW(
524         UINT flags,LPCWSTR srcfilename,LPCWSTR destfilename,LPCWSTR srcdir,
525         LPCWSTR destdir,LPCWSTR curdir,LPWSTR tmpfile,UINT *tmpfilelen )
526 {
527     LPSTR wsrcf = NULL, wsrcd = NULL, wdestf = NULL, wdestd = NULL, wtmpf = NULL, wcurd = NULL;
528     DWORD ret;
529     UINT len;
530
531     if (srcfilename)
532     {
533         len = WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, NULL, 0, NULL, NULL );
534         if ((wsrcf = HeapAlloc( GetProcessHeap(), 0, len )))
535             WideCharToMultiByte( CP_ACP, 0, srcfilename, -1, wsrcf, len, NULL, NULL );
536     }
537     if (srcdir)
538     {
539         len = WideCharToMultiByte( CP_ACP, 0, srcdir, -1, NULL, 0, NULL, NULL );
540         if ((wsrcd = HeapAlloc( GetProcessHeap(), 0, len )))
541             WideCharToMultiByte( CP_ACP, 0, srcdir, -1, wsrcd, len, NULL, NULL );
542     }
543     if (destfilename)
544     {
545         len = WideCharToMultiByte( CP_ACP, 0, destfilename, -1, NULL, 0, NULL, NULL );
546         if ((wdestf = HeapAlloc( GetProcessHeap(), 0, len )))
547             WideCharToMultiByte( CP_ACP, 0, destfilename, -1, wdestf, len, NULL, NULL );
548     }
549     if (destdir)
550     {
551         len = WideCharToMultiByte( CP_ACP, 0, destdir, -1, NULL, 0, NULL, NULL );
552         if ((wdestd = HeapAlloc( GetProcessHeap(), 0, len )))
553             WideCharToMultiByte( CP_ACP, 0, destdir, -1, wdestd, len, NULL, NULL );
554     }
555     if (curdir)
556     {
557         len = WideCharToMultiByte( CP_ACP, 0, curdir, -1, NULL, 0, NULL, NULL );
558         if ((wcurd = HeapAlloc( GetProcessHeap(), 0, len )))
559             WideCharToMultiByte( CP_ACP, 0, curdir, -1, wcurd, len, NULL, NULL );
560     }
561     len = *tmpfilelen * sizeof(WCHAR);
562     wtmpf = HeapAlloc( GetProcessHeap(), 0, len );
563     ret = VerInstallFileA(flags,wsrcf,wdestf,wsrcd,wdestd,wcurd,wtmpf,&len);
564     if (!ret)
565         *tmpfilelen = MultiByteToWideChar( CP_ACP, 0, wtmpf, -1, tmpfile, *tmpfilelen );
566     else if (ret & VIF_BUFFTOOSMALL)
567         *tmpfilelen = len;  /* FIXME: not correct */
568
569     HeapFree( GetProcessHeap(), 0, wsrcf );
570     HeapFree( GetProcessHeap(), 0, wsrcd );
571     HeapFree( GetProcessHeap(), 0, wdestf );
572     HeapFree( GetProcessHeap(), 0, wdestd );
573     HeapFree( GetProcessHeap(), 0, wtmpf );
574     HeapFree( GetProcessHeap(), 0, wcurd );
575     return ret;
576 }
577