~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/programs/winemenubuilder/winemenubuilder.c

Version: ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  1 /*
  2  * Helper program to build unix menu entries
  3  *
  4  * Copyright 1997 Marcus Meissner
  5  * Copyright 1998 Juergen Schmied
  6  * Copyright 2003 Mike McCormack for CodeWeavers
  7  * Copyright 2004 Dmitry Timoshkov
  8  * Copyright 2005 Bill Medland
  9  * Copyright 2008 Damjan Jovanovic
 10  *
 11  * This library is free software; you can redistribute it and/or
 12  * modify it under the terms of the GNU Lesser General Public
 13  * License as published by the Free Software Foundation; either
 14  * version 2.1 of the License, or (at your option) any later version.
 15  *
 16  * This library is distributed in the hope that it will be useful,
 17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19  * Lesser General Public License for more details.
 20  *
 21  * You should have received a copy of the GNU Lesser General Public
 22  * License along with this library; if not, write to the Free Software
 23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 24  *
 25  *
 26  *  This program is used to replicate the Windows desktop and start menu
 27  * into the native desktop's copies.  Desktop entries are merged directly
 28  * into the native desktop.  The Windows Start Menu corresponds to a Wine
 29  * entry within the native "start" menu and replicates the whole tree
 30  * structure of the Windows Start Menu.  Currently it does not differentiate
 31  * between the user's desktop/start menu and the "All Users" copies.
 32  *
 33  *  This program will read a Windows shortcut file using the IShellLink
 34  * interface, then create a KDE/Gnome menu entry for the shortcut.
 35  *
 36  *  winemenubuilder [ -w ] <shortcut.lnk>
 37  *
 38  *  If the -w parameter is passed, and the shortcut cannot be created,
 39  * this program will wait for the parent process to finish and then try
 40  * again. This covers the case when a ShortCut is created before the
 41  * executable containing its icon.
 42  *
 43  * TODO
 44  *  Handle data lnk files. There is no icon in the file; the icon is in 
 45  * the handler for the file type (or pointed to by the lnk file).  Also it 
 46  * might be better to use a native handler (e.g. a native acroread for pdf
 47  * files).  
 48  *  Differentiate between the user's entries and the "All Users" entries.
 49  * If it is possible to add the desktop files to the native system's
 50  * shared location for an "All Users" entry then do so.  As a suggestion the
 51  * shared menu Wine base could be writable to the wine group, or a wineadm 
 52  * group.
 53  *  Clean up fd.o menu icons and .directory files when the menu is deleted
 54  * in Windows.
 55  *  Generate icons for file open handlers to go into the "Open with..."
 56  * list. What does Windows use, the default icon for the .EXE file? It's
 57  * not in the registry.
 58  *  Associate applications under HKCR\Applications to open any MIME type
 59  * (by associating with application/octet-stream, or how?).
 60  *  Clean up fd.o MIME types when they are deleted in Windows, their icons
 61  * too. Very hard - once we associate them with fd.o, we can't tell whether
 62  * they are ours or not, and the extension <-> MIME type mapping isn't
 63  * one-to-one either.
 64  *  Wine's HKCR is broken - it doesn't merge HKCU\Software\Classes, so apps
 65  * that write associations there won't associate (#17019).
 66  */
 67 
 68 #include "config.h"
 69 #include "wine/port.h"
 70 
 71 #include <ctype.h>
 72 #include <stdio.h>
 73 #include <string.h>
 74 #ifdef HAVE_UNISTD_H
 75 #include <unistd.h>
 76 #endif
 77 #include <errno.h>
 78 #include <stdarg.h>
 79 #ifdef HAVE_FNMATCH_H
 80 #include <fnmatch.h>
 81 #endif
 82 
 83 #define COBJMACROS
 84 
 85 #include <windows.h>
 86 #include <shlobj.h>
 87 #include <objidl.h>
 88 #include <shlguid.h>
 89 #include <appmgmt.h>
 90 #include <tlhelp32.h>
 91 #include <intshcut.h>
 92 #include <shlwapi.h>
 93 
 94 #include "wine/unicode.h"
 95 #include "wine/debug.h"
 96 #include "wine/library.h"
 97 #include "wine/list.h"
 98 #include "wine.xpm"
 99 
100 #ifdef HAVE_PNG_H
101 #undef FAR
102 #include <png.h>
103 #endif
104 
105 WINE_DEFAULT_DEBUG_CHANNEL(menubuilder);
106 
107 #define in_desktop_dir(csidl) ((csidl)==CSIDL_DESKTOPDIRECTORY || \
108                                (csidl)==CSIDL_COMMON_DESKTOPDIRECTORY)
109 #define in_startmenu(csidl)   ((csidl)==CSIDL_STARTMENU || \
110                                (csidl)==CSIDL_COMMON_STARTMENU)
111         
112 /* link file formats */
113 
114 #include "pshpack1.h"
115 
116 typedef struct
117 {
118     BYTE bWidth;
119     BYTE bHeight;
120     BYTE bColorCount;
121     BYTE bReserved;
122     WORD wPlanes;
123     WORD wBitCount;
124     DWORD dwBytesInRes;
125     WORD nID;
126 } GRPICONDIRENTRY;
127 
128 typedef struct
129 {
130     WORD idReserved;
131     WORD idType;
132     WORD idCount;
133     GRPICONDIRENTRY idEntries[1];
134 } GRPICONDIR;
135 
136 typedef struct
137 {
138     BYTE bWidth;
139     BYTE bHeight;
140     BYTE bColorCount;
141     BYTE bReserved;
142     WORD wPlanes;
143     WORD wBitCount;
144     DWORD dwBytesInRes;
145     DWORD dwImageOffset;
146 } ICONDIRENTRY;
147 
148 typedef struct
149 {
150     WORD idReserved;
151     WORD idType;
152     WORD idCount;
153 } ICONDIR;
154 
155 
156 #include "poppack.h"
157 
158 typedef struct
159 {
160         HRSRC *pResInfo;
161         int   nIndex;
162 } ENUMRESSTRUCT;
163 
164 struct xdg_mime_type
165 {
166     char *mimeType;
167     char *glob;
168     struct list entry;
169 };
170 
171 static char *xdg_config_dir;
172 static char *xdg_data_dir;
173 static char *xdg_desktop_dir;
174 
175 /* Icon extraction routines
176  *
177  * FIXME: should use PrivateExtractIcons and friends
178  * FIXME: should not use stdio
179  */
180 
181 #define MASK(x,y) (pAND[(x) / 8 + (nHeight - (y) - 1) * nANDWidthBytes] & (1 << (7 - (x) % 8)))
182 
183 /* PNG-specific code */
184 #ifdef SONAME_LIBPNG
185 
186 static void *libpng_handle;
187 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
188 MAKE_FUNCPTR(png_create_info_struct);
189 MAKE_FUNCPTR(png_create_write_struct);
190 MAKE_FUNCPTR(png_destroy_write_struct);
191 MAKE_FUNCPTR(png_init_io);
192 MAKE_FUNCPTR(png_set_bgr);
193 MAKE_FUNCPTR(png_set_text);
194 MAKE_FUNCPTR(png_set_IHDR);
195 MAKE_FUNCPTR(png_write_end);
196 MAKE_FUNCPTR(png_write_info);
197 MAKE_FUNCPTR(png_write_row);
198 #undef MAKE_FUNCPTR
199 
200 static void *load_libpng(void)
201 {
202     if ((libpng_handle = wine_dlopen(SONAME_LIBPNG, RTLD_NOW, NULL, 0)) != NULL)
203     {
204 #define LOAD_FUNCPTR(f) \
205     if((p##f = wine_dlsym(libpng_handle, #f, NULL, 0)) == NULL) { \
206         libpng_handle = NULL; \
207         return NULL; \
208     }
209         LOAD_FUNCPTR(png_create_info_struct);
210         LOAD_FUNCPTR(png_create_write_struct);
211         LOAD_FUNCPTR(png_destroy_write_struct);
212         LOAD_FUNCPTR(png_init_io);
213         LOAD_FUNCPTR(png_set_bgr);
214         LOAD_FUNCPTR(png_set_IHDR);
215         LOAD_FUNCPTR(png_set_text);
216         LOAD_FUNCPTR(png_write_end);
217         LOAD_FUNCPTR(png_write_info);
218         LOAD_FUNCPTR(png_write_row);
219 #undef LOAD_FUNCPTR
220     }
221     return libpng_handle;
222 }
223 
224 static BOOL SaveIconResAsPNG(const BITMAPINFO *pIcon, const char *png_filename, LPCWSTR commentW)
225 {
226     static const char comment_key[] = "Created from";
227     FILE *fp;
228     png_structp png_ptr;
229     png_infop info_ptr;
230     png_text comment;
231     int nXORWidthBytes, nANDWidthBytes, color_type = 0, i, j;
232     BYTE *row, *copy = NULL;
233     const BYTE *pXOR, *pAND = NULL;
234     int nWidth  = pIcon->bmiHeader.biWidth;
235     int nHeight = pIcon->bmiHeader.biHeight;
236     int nBpp    = pIcon->bmiHeader.biBitCount;
237 
238     switch (nBpp)
239     {
240     case 32:
241         color_type |= PNG_COLOR_MASK_ALPHA;
242         /* fall through */
243     case 24:
244         color_type |= PNG_COLOR_MASK_COLOR;
245         break;
246     default:
247         return FALSE;
248     }
249 
250     if (!libpng_handle && !load_libpng())
251     {
252         WINE_WARN("Unable to load libpng\n");
253         return FALSE;
254     }
255 
256     if (!(fp = fopen(png_filename, "w")))
257     {
258         WINE_ERR("unable to open '%s' for writing: %s\n", png_filename, strerror(errno));
259         return FALSE;
260     }
261 
262     nXORWidthBytes = 4 * ((nWidth * nBpp + 31) / 32);
263     nANDWidthBytes = 4 * ((nWidth + 31 ) / 32);
264     pXOR = (const BYTE*) pIcon + sizeof(BITMAPINFOHEADER) + pIcon->bmiHeader.biClrUsed * sizeof(RGBQUAD);
265     if (nHeight > nWidth)
266     {
267         nHeight /= 2;
268         pAND = pXOR + nHeight * nXORWidthBytes;
269     }
270 
271     /* Apply mask if present */
272     if (pAND)
273     {
274         RGBQUAD bgColor;
275 
276         /* copy bytes before modifying them */
277         copy = HeapAlloc( GetProcessHeap(), 0, nHeight * nXORWidthBytes );
278         memcpy( copy, pXOR, nHeight * nXORWidthBytes );
279         pXOR = copy;
280 
281         /* image and mask are upside down reversed */
282         row = copy + (nHeight - 1) * nXORWidthBytes;
283 
284         /* top left corner */
285         bgColor.rgbRed   = row[0];
286         bgColor.rgbGreen = row[1];
287         bgColor.rgbBlue  = row[2];
288         bgColor.rgbReserved = 0;
289 
290         for (i = 0; i < nHeight; i++, row -= nXORWidthBytes)
291             for (j = 0; j < nWidth; j++, row += nBpp >> 3)
292                 if (MASK(j, i))
293                 {
294                     RGBQUAD *pixel = (RGBQUAD *)row;
295                     pixel->rgbBlue  = bgColor.rgbBlue;
296                     pixel->rgbGreen = bgColor.rgbGreen;
297                     pixel->rgbRed   = bgColor.rgbRed;
298                     if (nBpp == 32)
299                         pixel->rgbReserved = bgColor.rgbReserved;
300                 }
301     }
302 
303     comment.text = NULL;
304 
305     if (!(png_ptr = ppng_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) ||
306         !(info_ptr = ppng_create_info_struct(png_ptr)))
307         goto error;
308 
309     if (setjmp(png_jmpbuf(png_ptr)))
310     {
311         /* All future errors jump here */
312         WINE_ERR("png error\n");
313         goto error;
314     }
315 
316     ppng_init_io(png_ptr, fp);
317     ppng_set_IHDR(png_ptr, info_ptr, nWidth, nHeight, 8,
318                   color_type,
319                   PNG_INTERLACE_NONE,
320                   PNG_COMPRESSION_TYPE_DEFAULT,
321                   PNG_FILTER_TYPE_DEFAULT);
322 
323     /* Set comment */
324     comment.compression = PNG_TEXT_COMPRESSION_NONE;
325     comment.key = (png_charp)comment_key;
326     i = WideCharToMultiByte(CP_UNIXCP, 0, commentW, -1, NULL, 0, NULL, NULL);
327     comment.text = HeapAlloc(GetProcessHeap(), 0, i);
328     WideCharToMultiByte(CP_UNIXCP, 0, commentW, -1, comment.text, i, NULL, NULL);
329     comment.text_length = i - 1;
330     ppng_set_text(png_ptr, info_ptr, &comment, 1);
331 
332 
333     ppng_write_info(png_ptr, info_ptr);
334     ppng_set_bgr(png_ptr);
335     for (i = nHeight - 1; i >= 0 ; i--)
336         ppng_write_row(png_ptr, (png_bytep)pXOR + nXORWidthBytes * i);
337     ppng_write_end(png_ptr, info_ptr);
338 
339     ppng_destroy_write_struct(&png_ptr, &info_ptr);
340     if (png_ptr) ppng_destroy_write_struct(&png_ptr, NULL);
341     fclose(fp);
342     HeapFree(GetProcessHeap(), 0, copy);
343     HeapFree(GetProcessHeap(), 0, comment.text);
344     return TRUE;
345 
346  error:
347     if (png_ptr) ppng_destroy_write_struct(&png_ptr, NULL);
348     fclose(fp);
349     unlink(png_filename);
350     HeapFree(GetProcessHeap(), 0, copy);
351     HeapFree(GetProcessHeap(), 0, comment.text);
352     return FALSE;
353 }
354 #endif /* SONAME_LIBPNG */
355 
356 static BOOL SaveIconResAsXPM(const BITMAPINFO *pIcon, const char *szXPMFileName, LPCWSTR commentW)
357 {
358     FILE *fXPMFile;
359     int nHeight;
360     int nXORWidthBytes;
361     int nANDWidthBytes;
362     BOOL b8BitColors;
363     int nColors;
364     const BYTE *pXOR;
365     const BYTE *pAND;
366     BOOL aColorUsed[256] = {0};
367     int nColorsUsed = 0;
368     int i,j;
369     char *comment;
370 
371     if (!((pIcon->bmiHeader.biBitCount == 4) || (pIcon->bmiHeader.biBitCount == 8)))
372     {
373         WINE_FIXME("Unsupported color depth %d-bit\n", pIcon->bmiHeader.biBitCount);
374         return FALSE;
375     }
376 
377     if (!(fXPMFile = fopen(szXPMFileName, "w")))
378     {
379         WINE_TRACE("unable to open '%s' for writing: %s\n", szXPMFileName, strerror(errno));
380         return FALSE;
381     }
382 
383     i = WideCharToMultiByte(CP_UNIXCP, 0, commentW, -1, NULL, 0, NULL, NULL);
384     comment = HeapAlloc(GetProcessHeap(), 0, i);
385     WideCharToMultiByte(CP_UNIXCP, 0, commentW, -1, comment, i, NULL, NULL);
386 
387     nHeight = pIcon->bmiHeader.biHeight / 2;
388     nXORWidthBytes = 4 * ((pIcon->bmiHeader.biWidth * pIcon->bmiHeader.biBitCount / 32)
389                           + ((pIcon->bmiHeader.biWidth * pIcon->bmiHeader.biBitCount % 32) > 0));
390     nANDWidthBytes = 4 * ((pIcon->bmiHeader.biWidth / 32)
391                           + ((pIcon->bmiHeader.biWidth % 32) > 0));
392     b8BitColors = pIcon->bmiHeader.biBitCount == 8;
393     nColors = pIcon->bmiHeader.biClrUsed ? pIcon->bmiHeader.biClrUsed
394         : 1 << pIcon->bmiHeader.biBitCount;
395     pXOR = (const BYTE*) pIcon + sizeof (BITMAPINFOHEADER) + (nColors * sizeof (RGBQUAD));
396     pAND = pXOR + nHeight * nXORWidthBytes;
397 
398 #define COLOR(x,y) (b8BitColors ? pXOR[(x) + (nHeight - (y) - 1) * nXORWidthBytes] : (x) % 2 ? pXOR[(x) / 2 + (nHeight - (y) - 1) * nXORWidthBytes] & 0xF : (pXOR[(x) / 2 + (nHeight - (y) - 1) * nXORWidthBytes] & 0xF0) >> 4)
399 
400     for (i = 0; i < nHeight; i++) {
401         for (j = 0; j < pIcon->bmiHeader.biWidth; j++) {
402             if (!aColorUsed[COLOR(j,i)] && !MASK(j,i))
403             {
404                 aColorUsed[COLOR(j,i)] = TRUE;
405                 nColorsUsed++;
406             }
407         }
408     }
409 
410     if (fprintf(fXPMFile, "/* XPM */\n/* %s */\nstatic char *icon[] = {\n", comment) <= 0)
411         goto error;
412     if (fprintf(fXPMFile, "\"%d %d %d %d\",\n",
413                 (int) pIcon->bmiHeader.biWidth, nHeight, nColorsUsed + 1, 2) <=0)
414         goto error;
415 
416     for (i = 0; i < nColors; i++) {
417         if (aColorUsed[i])
418             if (fprintf(fXPMFile, "\"%.2X c #%.2X%.2X%.2X\",\n", i, pIcon->bmiColors[i].rgbRed,
419                         pIcon->bmiColors[i].rgbGreen, pIcon->bmiColors[i].rgbBlue) <= 0)
420                 goto error;
421     }
422     if (fprintf(fXPMFile, "\"   c None\"") <= 0)
423         goto error;
424 
425     for (i = 0; i < nHeight; i++)
426     {
427         if (fprintf(fXPMFile, ",\n\"") <= 0)
428             goto error;
429         for (j = 0; j < pIcon->bmiHeader.biWidth; j++)
430         {
431             if MASK(j,i)
432                 {
433                     if (fprintf(fXPMFile, "  ") <= 0)
434                         goto error;
435                 }
436             else
437                 if (fprintf(fXPMFile, "%.2X", COLOR(j,i)) <= 0)
438                     goto error;
439         }
440         if (fprintf(fXPMFile, "\"") <= 0)
441             goto error;
442     }
443     if (fprintf(fXPMFile, "};\n") <= 0)
444         goto error;
445 
446 #undef MASK
447 #undef COLOR
448 
449     HeapFree(GetProcessHeap(), 0, comment);
450     fclose(fXPMFile);
451     return TRUE;
452 
453  error:
454     HeapFree(GetProcessHeap(), 0, comment);
455     fclose(fXPMFile);
456     unlink( szXPMFileName );
457     return FALSE;
458 }
459 
460 static BOOL CALLBACK EnumResNameProc(HMODULE hModule, LPCWSTR lpszType, LPWSTR lpszName, LONG_PTR lParam)
461 {
462     ENUMRESSTRUCT *sEnumRes = (ENUMRESSTRUCT *) lParam;
463 
464     if (!sEnumRes->nIndex--)
465     {
466         *sEnumRes->pResInfo = FindResourceW(hModule, lpszName, (LPCWSTR)RT_GROUP_ICON);
467         return FALSE;
468     }
469     else
470         return TRUE;
471 }
472 
473 static BOOL extract_icon32(LPCWSTR szFileName, int nIndex, char *szXPMFileName)
474 {
475     HMODULE hModule;
476     HRSRC hResInfo;
477     LPCWSTR lpName = NULL;
478     HGLOBAL hResData;
479     GRPICONDIR *pIconDir;
480     BITMAPINFO *pIcon;
481     ENUMRESSTRUCT sEnumRes;
482     int nMax = 0;
483     int nMaxBits = 0;
484     int i;
485     BOOL ret = FALSE;
486 
487     hModule = LoadLibraryExW(szFileName, 0, LOAD_LIBRARY_AS_DATAFILE);
488     if (!hModule)
489     {
490         WINE_WARN("LoadLibraryExW (%s) failed, error %d\n",
491                  wine_dbgstr_w(szFileName), GetLastError());
492         return FALSE;
493     }
494 
495     if (nIndex < 0)
496     {
497         hResInfo = FindResourceW(hModule, MAKEINTRESOURCEW(-nIndex), (LPCWSTR)RT_GROUP_ICON);
498         WINE_TRACE("FindResourceW (%s) called, return %p, error %d\n",
499                    wine_dbgstr_w(szFileName), hResInfo, GetLastError());
500     }
501     else
502     {
503         hResInfo=NULL;
504         sEnumRes.pResInfo = &hResInfo;
505         sEnumRes.nIndex = nIndex;
506         if (!EnumResourceNamesW(hModule, (LPCWSTR)RT_GROUP_ICON,
507                                 EnumResNameProc, (LONG_PTR)&sEnumRes) &&
508             sEnumRes.nIndex != -1)
509         {
510             WINE_TRACE("EnumResourceNamesW failed, error %d\n", GetLastError());
511         }
512     }
513 
514     if (hResInfo)
515     {
516         if ((hResData = LoadResource(hModule, hResInfo)))
517         {
518             if ((pIconDir = LockResource(hResData)))
519             {
520                 for (i = 0; i < pIconDir->idCount; i++)
521                 {
522                     if (pIconDir->idEntries[i].wBitCount >= nMaxBits)
523                     {
524                         if ((pIconDir->idEntries[i].bHeight * pIconDir->idEntries[i].bWidth) >= nMax)
525                         {
526                             lpName = MAKEINTRESOURCEW(pIconDir->idEntries[i].nID);
527                             nMax = pIconDir->idEntries[i].bHeight * pIconDir->idEntries[i].bWidth;
528                             nMaxBits = pIconDir->idEntries[i].wBitCount;
529                         }
530                     }               
531                 }
532             }
533 
534             FreeResource(hResData);
535         }
536     }
537     else
538     {
539         WINE_WARN("found no icon\n");
540         FreeLibrary(hModule);
541         return FALSE;
542     }
543  
544     if ((hResInfo = FindResourceW(hModule, lpName, (LPCWSTR)RT_ICON)))
545     {
546         if ((hResData = LoadResource(hModule, hResInfo)))
547         {
548             if ((pIcon = LockResource(hResData)))
549             {
550 #ifdef SONAME_LIBPNG
551                 if (SaveIconResAsPNG(pIcon, szXPMFileName, szFileName))
552                     ret = TRUE;
553                 else
554 #endif
555                 {
556                     memcpy(szXPMFileName + strlen(szXPMFileName) - 3, "xpm", 3);
557                     if (SaveIconResAsXPM(pIcon, szXPMFileName, szFileName))
558                         ret = TRUE;
559                 }
560             }
561 
562             FreeResource(hResData);
563         }
564     }
565 
566     FreeLibrary(hModule);
567     return ret;
568 }
569 
570 static BOOL ExtractFromEXEDLL(LPCWSTR szFileName, int nIndex, char *szXPMFileName)
571 {
572     if (!extract_icon32(szFileName, nIndex, szXPMFileName) /*&&
573         !extract_icon16(szFileName, szXPMFileName)*/)
574         return FALSE;
575     return TRUE;
576 }
577 
578 static int ExtractFromICO(LPCWSTR szFileName, char *szXPMFileName)
579 {
580     FILE *fICOFile = NULL;
581     ICONDIR iconDir;
582     ICONDIRENTRY *pIconDirEntry = NULL;
583     int nMax = 0, nMaxBits = 0;
584     int nIndex = 0;
585     void *pIcon = NULL;
586     int i;
587     char *filename = NULL;
588 
589     filename = wine_get_unix_file_name(szFileName);
590     if (!(fICOFile = fopen(filename, "r")))
591     {
592         WINE_TRACE("unable to open '%s' for reading: %s\n", filename, strerror(errno));
593         goto error;
594     }
595 
596     if (fread(&iconDir, sizeof (ICONDIR), 1, fICOFile) != 1 ||
597         (iconDir.idReserved != 0) || (iconDir.idType != 1))
598     {
599         WINE_WARN("Invalid ico file format\n");
600         goto error;
601     }
602 
603     if ((pIconDirEntry = HeapAlloc(GetProcessHeap(), 0, iconDir.idCount * sizeof (ICONDIRENTRY))) == NULL)
604         goto error;
605     if (fread(pIconDirEntry, sizeof (ICONDIRENTRY), iconDir.idCount, fICOFile) != iconDir.idCount)
606         goto error;
607 
608     for (i = 0; i < iconDir.idCount; i++)
609     {
610         WINE_TRACE("[%d]: %d x %d @ %d\n", i, pIconDirEntry[i].bWidth, pIconDirEntry[i].bHeight, pIconDirEntry[i].wBitCount);
611         if (pIconDirEntry[i].wBitCount >= nMaxBits &&
612             (pIconDirEntry[i].bHeight * pIconDirEntry[i].bWidth) >= nMax)
613         {
614             nIndex = i;
615             nMax = pIconDirEntry[i].bHeight * pIconDirEntry[i].bWidth;
616             nMaxBits = pIconDirEntry[i].wBitCount;
617         }
618     }
619     WINE_TRACE("Selected: %d\n", nIndex);
620 
621     if ((pIcon = HeapAlloc(GetProcessHeap(), 0, pIconDirEntry[nIndex].dwBytesInRes)) == NULL)
622         goto error;
623     if (fseek(fICOFile, pIconDirEntry[nIndex].dwImageOffset, SEEK_SET))
624         goto error;
625     if (fread(pIcon, pIconDirEntry[nIndex].dwBytesInRes, 1, fICOFile) != 1)
626         goto error;
627 
628 
629     /* Prefer PNG over XPM */
630 #ifdef SONAME_LIBPNG
631     if (!SaveIconResAsPNG(pIcon, szXPMFileName, szFileName))
632 #endif
633     {
634         memcpy(szXPMFileName + strlen(szXPMFileName) - 3, "xpm", 3);
635         if (!SaveIconResAsXPM(pIcon, szXPMFileName, szFileName))
636             goto error;
637     }
638 
639     HeapFree(GetProcessHeap(), 0, pIcon);
640     HeapFree(GetProcessHeap(), 0, pIconDirEntry);
641     fclose(fICOFile);
642     HeapFree(GetProcessHeap(), 0, filename);
643     return 1;
644 
645  error:
646     HeapFree(GetProcessHeap(), 0, pIcon);
647     HeapFree(GetProcessHeap(), 0, pIconDirEntry);
648     if (fICOFile) fclose(fICOFile);
649     HeapFree(GetProcessHeap(), 0, filename);
650     return 0;
651 }
652 
653 static BOOL create_default_icon( const char *filename, const char* comment )
654 {
655     FILE *fXPM;
656     unsigned int i;
657 
658     if (!(fXPM = fopen(filename, "w"))) return FALSE;
659     if (fprintf(fXPM, "/* XPM */\n/* %s */\nstatic char * icon[] = {", comment) <= 0)
660         goto error;
661     for (i = 0; i < sizeof(wine_xpm)/sizeof(wine_xpm[0]); i++) {
662         if (fprintf( fXPM, "\n\"%s\",", wine_xpm[i]) <= 0)
663             goto error;
664     }
665     if (fprintf( fXPM, "};\n" ) <=0)
666         goto error;
667     fclose( fXPM );
668     return TRUE;
669  error:
670     fclose( fXPM );
671     unlink( filename );
672     return FALSE;
673 
674 }
675 
676 static unsigned short crc16(const char* string)
677 {
678     unsigned short crc = 0;
679     int i, j, xor_poly;
680 
681     for (i = 0; string[i] != 0; i++)
682     {
683         char c = string[i];
684         for (j = 0; j < 8; c >>= 1, j++)
685         {
686             xor_poly = (c ^ crc) & 1;
687             crc >>= 1;
688             if (xor_poly)
689                 crc ^= 0xa001;
690         }
691     }
692     return crc;
693 }
694 
695 static char *strdupA( const char *str )
696 {
697     char *ret;
698 
699     if (!str) return NULL;
700     if ((ret = HeapAlloc( GetProcessHeap(), 0, strlen(str) + 1 ))) strcpy( ret, str );
701     return ret;
702 }
703 
704 static char* heap_printf(const char *format, ...)
705 {
706     va_list args;
707     int size = 4096;
708     char *buffer, *ret;
709     int n;
710 
711     va_start(args, format);
712     while (1)
713     {
714         buffer = HeapAlloc(GetProcessHeap(), 0, size);
715         if (buffer == NULL)
716             break;
717         n = vsnprintf(buffer, size, format, args);
718         if (n == -1)
719             size *= 2;
720         else if (n >= size)
721             size = n + 1;
722         else
723             break;
724         HeapFree(GetProcessHeap(), 0, buffer);
725     }
726     va_end(args);
727     if (!buffer) return NULL;
728     ret = HeapReAlloc(GetProcessHeap(), 0, buffer, strlen(buffer) + 1 );
729     if (!ret) ret = buffer;
730     return ret;
731 }
732 
733 static void write_xml_text(FILE *file, const char *text)
734 {
735     int i;
736     for (i = 0; text[i]; i++)
737     {
738         if (text[i] == '&')
739             fputs("&amp;", file);
740         else if (text[i] == '<')
741             fputs("&lt;", file);
742         else if (text[i] == '>')
743             fputs("&gt;", file);
744         else if (text[i] == '\'')
745             fputs("&apos;", file);
746         else if (text[i] == '"')
747             fputs("&quot;", file);
748         else
749             fputc(text[i], file);
750     }
751 }
752 
753 static BOOL create_directories(char *directory)
754 {
755     BOOL ret = TRUE;
756     int i;
757 
758     for (i = 0; directory[i]; i++)
759     {
760         if (i > 0 && directory[i] == '/')
761         {
762             directory[i] = 0;
763             mkdir(directory, 0777);
764             directory[i] = '/';
765         }
766     }
767     if (mkdir(directory, 0777) && errno != EEXIST)
768        ret = FALSE;
769 
770     return ret;
771 }
772 
773 /* extract an icon from an exe or icon file; helper for IPersistFile_fnSave */
774 static char *extract_icon( LPCWSTR path, int index, const char *destFilename, BOOL bWait )
775 {
776     unsigned short crc;
777     char *iconsdir = NULL, *ico_path = NULL, *ico_name, *xpm_path = NULL;
778     char* s;
779     int n;
780 
781     /* Where should we save the icon? */
782     WINE_TRACE("path=[%s] index=%d\n", wine_dbgstr_w(path), index);
783     iconsdir = heap_printf("%s/icons", xdg_data_dir);
784     if (iconsdir)
785     {
786         if (mkdir(iconsdir, 0777) && errno != EEXIST)
787         {
788             WINE_WARN("couldn't make icons directory %s\n", wine_dbgstr_a(iconsdir));
789             goto end;
790         }
791     }
792     else
793     {
794         WINE_TRACE("no icon created\n");
795         return NULL;
796     }
797     
798     /* Determine the icon base name */
799     n = WideCharToMultiByte(CP_UNIXCP, 0, path, -1, NULL, 0, NULL, NULL);
800     ico_path = HeapAlloc(GetProcessHeap(), 0, n);
801     WideCharToMultiByte(CP_UNIXCP, 0, path, -1, ico_path, n, NULL, NULL);
802     s=ico_name=ico_path;
803     while (*s!='\0') {
804         if (*s=='/' || *s=='\\') {
805             *s='\\';
806             ico_name=s;
807         } else {
808             *s=tolower(*s);
809         }
810         s++;
811     }
812     if (*ico_name=='\\') *ico_name++='\0';
813     s=strrchr(ico_name,'.');
814     if (s) *s='\0';
815 
816     /* Compute the source-path hash */
817     crc=crc16(ico_path);
818 
819     /* Try to treat the source file as an exe */
820     if (destFilename)
821         xpm_path=heap_printf("%s/%s.png",iconsdir,destFilename);
822     else
823         xpm_path=heap_printf("%s/%04x_%s.%d.png",iconsdir,crc,ico_name,index);
824     if (xpm_path == NULL)
825     {
826         WINE_ERR("could not extract icon %s, out of memory\n", wine_dbgstr_a(ico_name));
827         return NULL;
828     }
829 
830     if (ExtractFromEXEDLL( path, index, xpm_path ))
831         goto end;
832 
833     /* Must be something else, ignore the index in that case */
834     if (destFilename)
835         sprintf(xpm_path,"%s/%s.png",iconsdir,destFilename);
836     else
837         sprintf(xpm_path,"%s/%04x_%s.png",iconsdir,crc,ico_name);
838     if (ExtractFromICO( path, xpm_path))
839         goto end;
840     if (!bWait)
841     {
842         if (destFilename)
843             sprintf(xpm_path,"%s/%s.xpm",iconsdir,destFilename);
844         else
845             sprintf(xpm_path,"%s/%04x_%s.xpm",iconsdir,crc,ico_name);
846         if (create_default_icon( xpm_path, ico_path ))
847             goto end;
848     }
849 
850     HeapFree( GetProcessHeap(), 0, xpm_path );
851     xpm_path=NULL;
852 
853  end:
854     HeapFree(GetProcessHeap(), 0, iconsdir);
855     HeapFree(GetProcessHeap(), 0, ico_path);
856     return xpm_path;
857 }
858 
859 static HKEY open_menus_reg_key(void)
860 {
861     static const WCHAR Software_Wine_FileOpenAssociationsW[] = {
862         'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','M','e','n','u','F','i','l','e','s',0};
863     HKEY assocKey;
864     if (RegCreateKeyW(HKEY_CURRENT_USER, Software_Wine_FileOpenAssociationsW, &assocKey) == ERROR_SUCCESS)
865         return assocKey;
866     return NULL;
867 }
868 
869 static BOOL write_desktop_entry(const char *unix_link, const char *location, const char *linkname,
870                                 const char *path, const char *args, const char *descr,
871                                 const char *workdir, const char *icon)
872 {
873     FILE *file;
874 
875     WINE_TRACE("(%s,%s,%s,%s,%s,%s,%s,%s)\n", wine_dbgstr_a(unix_link), wine_dbgstr_a(location),
876                wine_dbgstr_a(linkname), wine_dbgstr_a(path), wine_dbgstr_a(args),
877                wine_dbgstr_a(descr), wine_dbgstr_a(workdir), wine_dbgstr_a(icon));
878 
879     file = fopen(location, "w");
880     if (file == NULL)
881         return FALSE;
882 
883     fprintf(file, "[Desktop Entry]\n");
884     fprintf(file, "Name=%s\n", linkname);
885     fprintf(file, "Exec=env WINEPREFIX=\"%s\" wine \"%s\" %s\n",
886             wine_get_config_dir(), path, args);
887     fprintf(file, "Type=Application\n");
888     fprintf(file, "StartupNotify=true\n");
889     if (descr && lstrlenA(descr))
890         fprintf(file, "Comment=%s\n", descr);
891     if (workdir && lstrlenA(workdir))
892         fprintf(file, "Path=%s\n", workdir);
893     if (icon && lstrlenA(icon))
894         fprintf(file, "Icon=%s\n", icon);
895 
896     fclose(file);
897 
898     if (unix_link)
899     {
900         HKEY hkey = open_menus_reg_key();
901         if (hkey)
902         {
903             RegSetValueExA(hkey, location, 0, REG_SZ, (BYTE*) unix_link, lstrlenA(unix_link) + 1);
904             RegCloseKey(hkey);
905         }
906         else
907             return FALSE;
908     }
909 
910     return TRUE;
911 }
912 
913 static BOOL write_directory_entry(const char *directory, const char *location)
914 {
915     FILE *file;
916 
917     WINE_TRACE("(%s,%s)\n", wine_dbgstr_a(directory), wine_dbgstr_a(location));
918 
919     file = fopen(location, "w");
920     if (file == NULL)
921         return FALSE;
922 
923     fprintf(file, "[Desktop Entry]\n");
924     fprintf(file, "Type=Directory\n");
925     if (strcmp(directory, "wine") == 0)
926     {
927         fprintf(file, "Name=Wine\n");
928         fprintf(file, "Icon=wine\n");
929     }
930     else
931     {
932         fprintf(file, "Name=%s\n", directory);
933         fprintf(file, "Icon=folder\n");
934     }
935 
936     fclose(file);
937     return TRUE;
938 }
939 
940 static BOOL write_menu_file(const char *unix_link, const char *filename)
941 {
942     char *tempfilename;
943     FILE *tempfile = NULL;
944     char *lastEntry;
945     char *name = NULL;
946     char *menuPath = NULL;
947     int i;
948     int count = 0;
949     BOOL ret = FALSE;
950 
951     WINE_TRACE("(%s)\n", wine_dbgstr_a(filename));
952 
953     while (1)
954     {
955         tempfilename = heap_printf("%s/wine-menu-XXXXXX", xdg_config_dir);
956         if (tempfilename)
957         {
958             int tempfd = mkstemps(tempfilename, 0);
959             if (tempfd >= 0)
960             {
961                 tempfile = fdopen(tempfd, "w");
962                 if (tempfile)
963                     break;
964                 close(tempfd);
965                 goto end;
966             }
967             else if (errno == EEXIST)
968             {
969                 HeapFree(GetProcessHeap(), 0, tempfilename);
970                 continue;
971             }
972             HeapFree(GetProcessHeap(), 0, tempfilename);
973         }
974         return FALSE;
975     }
976 
977     fprintf(tempfile, "<!DOCTYPE Menu PUBLIC \"-//freedesktop//DTD Menu 1.0//EN\"\n");
978     fprintf(tempfile, "\"http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd\">\n");
979     fprintf(tempfile, "<Menu>\n");
980     fprintf(tempfile, "  <Name>Applications</Name>\n");
981 
982     name = HeapAlloc(GetProcessHeap(), 0, lstrlenA(filename) + 1);
983     if (name == NULL) goto end;
984     lastEntry = name;
985     for (i = 0; filename[i]; i++)
986     {
987         name[i] = filename[i];
988         if (filename[i] == '/')
989         {
990             char *dir_file_name;
991             struct stat st;
992             name[i] = 0;
993             fprintf(tempfile, "  <Menu>\n");
994             fprintf(tempfile, "    <Name>%s%s</Name>\n", count ? "" : "wine-", name);
995             fprintf(tempfile, "    <Directory>%s%s.directory</Directory>\n", count ? "" : "wine-", name);
996             dir_file_name = heap_printf("%s/desktop-directories/%s%s.directory",
997                 xdg_data_dir, count ? "" : "wine-", name);
998             if (dir_file_name)
999             {
1000                 if (stat(dir_file_name, &st) != 0 && errno == ENOENT)
1001                     write_directory_entry(lastEntry, dir_file_name);
1002                 HeapFree(GetProcessHeap(), 0, dir_file_name);
1003             }
1004             name[i] = '-';
1005             lastEntry = &name[i+1];
1006             ++count;
1007         }
1008     }
1009     name[i] = 0;
1010 
1011     fprintf(tempfile, "    <Include>\n");
1012     fprintf(tempfile, "      <Filename>%s</Filename>\n", name);
1013     fprintf(tempfile, "    </Include>\n");
1014     for (i = 0; i < count; i++)
1015          fprintf(tempfile, "  </Menu>\n");
1016     fprintf(tempfile, "</Menu>\n");
1017 
1018     menuPath = heap_printf("%s/%s", xdg_config_dir, name);
1019     if (menuPath == NULL) goto end;
1020     strcpy(menuPath + strlen(menuPath) - strlen(".desktop"), ".menu");
1021     ret = TRUE;
1022 
1023 end:
1024     if (tempfile)
1025         fclose(tempfile);
1026     if (ret)
1027         ret = (rename(tempfilename, menuPath) == 0);
1028     if (!ret && tempfilename)
1029         remove(tempfilename);
1030     HeapFree(GetProcessHeap(), 0, tempfilename);
1031     if (ret)
1032     {
1033         HKEY hkey = open_menus_reg_key();
1034         if (hkey)
1035         {
1036             RegSetValueExA(hkey, menuPath, 0, REG_SZ, (BYTE*) unix_link, lstrlenA(unix_link) + 1);
1037             RegCloseKey(hkey);
1038         }
1039     }
1040     HeapFree(GetProcessHeap(), 0, name);
1041     HeapFree(GetProcessHeap(), 0, menuPath);
1042     return ret;
1043 }
1044 
1045 static BOOL write_menu_entry(const char *unix_link, const char *link, const char *path, const char *args,
1046                              const char *descr, const char *workdir, const char *icon)
1047 {
1048     const char *linkname;
1049     char *desktopPath = NULL;
1050     char *desktopDir;
1051     char *filename = NULL;
1052     BOOL ret = TRUE;
1053 
1054     WINE_TRACE("(%s, %s, %s, %s, %s, %s, %s)\n", wine_dbgstr_a(unix_link), wine_dbgstr_a(link),
1055                wine_dbgstr_a(path), wine_dbgstr_a(args), wine_dbgstr_a(descr),
1056                wine_dbgstr_a(workdir), wine_dbgstr_a(icon));
1057 
1058     linkname = strrchr(link, '/');
1059     if (linkname == NULL)
1060         linkname = link;
1061     else
1062         ++linkname;
1063 
1064     desktopPath = heap_printf("%s/applications/wine/%s.desktop", xdg_data_dir, link);
1065     if (!desktopPath)
1066     {
1067         WINE_WARN("out of memory creating menu entry\n");
1068         ret = FALSE;
1069         goto end;
1070     }
1071     desktopDir = strrchr(desktopPath, '/');
1072     *desktopDir = 0;
1073     if (!create_directories(desktopPath))
1074     {
1075         WINE_WARN("couldn't make parent directories for %s\n", wine_dbgstr_a(desktopPath));
1076         ret = FALSE;
1077         goto end;
1078     }
1079     *desktopDir = '/';
1080     if (!write_desktop_entry(unix_link, desktopPath, linkname, path, args, descr, workdir, icon))
1081     {
1082         WINE_WARN("couldn't make desktop entry %s\n", wine_dbgstr_a(desktopPath));
1083         ret = FALSE;
1084         goto end;
1085     }
1086 
1087     filename = heap_printf("wine/%s.desktop", link);
1088     if (!filename || !write_menu_file(unix_link, filename))
1089     {
1090         WINE_WARN("couldn't make menu file %s\n", wine_dbgstr_a(filename));
1091         ret = FALSE;
1092     }
1093 
1094 end:
1095     HeapFree(GetProcessHeap(), 0, desktopPath);
1096     HeapFree(GetProcessHeap(), 0, filename);
1097     return ret;
1098 }
1099 
1100 /* This escapes \ in filenames */
1101 static LPSTR escape(LPCWSTR arg)
1102 {
1103     LPSTR narg, x;
1104     LPCWSTR esc;
1105     int len = 0, n;
1106 
1107     esc = arg;
1108     while((esc = strchrW(esc, '\\')))
1109     {
1110         esc++;
1111         len++;
1112     }
1113 
1114     len += WideCharToMultiByte(CP_UNIXCP, 0, arg, -1, NULL, 0, NULL, NULL);
1115     narg = HeapAlloc(GetProcessHeap(), 0, len);
1116 
1117     x = narg;
1118     while (*arg)
1119     {
1120         n = WideCharToMultiByte(CP_UNIXCP, 0, arg, 1, x, len, NULL, NULL);
1121         x += n;
1122         len -= n;
1123         if (*arg == '\\')
1124             *x++='\\'; /* escape \ */
1125         arg++;
1126     }
1127     *x = 0;
1128     return narg;
1129 }
1130 
1131 /* Return a heap-allocated copy of the unix format difference between the two
1132  * Windows-format paths.
1133  * locn is the owning location
1134  * link is within locn
1135  */
1136 static char *relative_path( LPCWSTR link, LPCWSTR locn )
1137 {
1138     char *unix_locn, *unix_link;
1139     char *relative = NULL;
1140 
1141     unix_locn = wine_get_unix_file_name(locn);
1142     unix_link = wine_get_unix_file_name(link);
1143     if (unix_locn && unix_link)
1144     {
1145         size_t len_unix_locn, len_unix_link;
1146         len_unix_locn = strlen (unix_locn);
1147         len_unix_link = strlen (unix_link);
1148         if (len_unix_locn < len_unix_link && memcmp (unix_locn, unix_link, len_unix_locn) == 0 && unix_link[len_unix_locn] == '/')
1149         {
1150             size_t len_rel;
1151             char *p = strrchr (unix_link + len_unix_locn, '/');
1152             p = strrchr (p, '.');
1153             if (p)
1154             {
1155                 *p = '\0';
1156                 len_unix_link = p - unix_link;
1157             }
1158             len_rel = len_unix_link - len_unix_locn;
1159             relative = HeapAlloc(GetProcessHeap(), 0, len_rel);
1160             if (relative)
1161             {
1162                 memcpy (relative, unix_link + len_unix_locn + 1, len_rel);
1163             }
1164         }
1165     }
1166     if (!relative)
1167         WINE_WARN("Could not separate the relative link path of %s in %s\n", wine_dbgstr_w(link), wine_dbgstr_w(locn));
1168     HeapFree(GetProcessHeap(), 0, unix_locn);
1169     HeapFree(GetProcessHeap(), 0, unix_link);
1170     return relative;
1171 }
1172 
1173 /***********************************************************************
1174  *
1175  *           GetLinkLocation
1176  *
1177  * returns TRUE if successful
1178  * *loc will contain CS_DESKTOPDIRECTORY, CS_STARTMENU, CS_STARTUP etc.
1179  * *relative will contain the address of a heap-allocated copy of the portion
1180  * of the filename that is within the specified location, in unix form
1181  */
1182 static BOOL GetLinkLocation( LPCWSTR linkfile, DWORD *loc, char **relative )
1183 {
1184     WCHAR filename[MAX_PATH], shortfilename[MAX_PATH], buffer[MAX_PATH];
1185     DWORD len, i, r, filelen;
1186     const DWORD locations[] = {
1187         CSIDL_STARTUP, CSIDL_DESKTOPDIRECTORY, CSIDL_STARTMENU,
1188         CSIDL_COMMON_STARTUP, CSIDL_COMMON_DESKTOPDIRECTORY,
1189         CSIDL_COMMON_STARTMENU };
1190 
1191     WINE_TRACE("%s\n", wine_dbgstr_w(linkfile));
1192     filelen=GetFullPathNameW( linkfile, MAX_PATH, shortfilename, NULL );
1193     if (filelen==0 || filelen>MAX_PATH)
1194         return FALSE;
1195 
1196     WINE_TRACE("%s\n", wine_dbgstr_w(shortfilename));
1197 
1198     /* the CSLU Toolkit uses a short path name when creating .lnk files;
1199      * expand or our hardcoded list won't match.
1200      */
1201     filelen=GetLongPathNameW(shortfilename, filename, MAX_PATH);
1202     if (filelen==0 || filelen>MAX_PATH)
1203         return FALSE;
1204 
1205     WINE_TRACE("%s\n", wine_dbgstr_w(filename));
1206 
1207     for( i=0; i<sizeof(locations)/sizeof(locations[0]); i++ )
1208     {
1209         if (!SHGetSpecialFolderPathW( 0, buffer, locations[i], FALSE ))
1210             continue;
1211 
1212         len = lstrlenW(buffer);
1213         if (len >= MAX_PATH)
1214             continue; /* We've just trashed memory! Hopefully we are OK */
1215 
1216         if (len > filelen || filename[len]!='\\')
1217             continue;
1218         /* do a lstrcmpinW */
1219         filename[len] = 0;
1220         r = lstrcmpiW( filename, buffer );
1221         filename[len] = '\\';
1222         if ( r )
1223             continue;
1224 
1225         /* return the remainder of the string and link type */
1226         *loc = locations[i];
1227         *relative = relative_path (filename, buffer);
1228         return (*relative != NULL);
1229     }
1230 
1231     return FALSE;
1232 }
1233 
1234 /* gets the target path directly or through MSI */
1235 static HRESULT get_cmdline( IShellLinkW *sl, LPWSTR szPath, DWORD pathSize,
1236                             LPWSTR szArgs, DWORD argsSize)
1237 {
1238     IShellLinkDataList *dl = NULL;
1239     EXP_DARWIN_LINK *dar = NULL;
1240     HRESULT hr;
1241 
1242     szPath[0] = 0;
1243     szArgs[0] = 0;
1244 
1245     hr = IShellLinkW_GetPath( sl, szPath, pathSize, NULL, SLGP_RAWPATH );
1246     if (hr == S_OK && szPath[0])
1247     {
1248         IShellLinkW_GetArguments( sl, szArgs, argsSize );
1249         return hr;
1250     }
1251 
1252     hr = IShellLinkW_QueryInterface( sl, &IID_IShellLinkDataList, (LPVOID*) &dl );
1253     if (FAILED(hr))
1254         return hr;
1255 
1256     hr = IShellLinkDataList_CopyDataBlock( dl, EXP_DARWIN_ID_SIG, (LPVOID*) &dar );
1257     if (SUCCEEDED(hr))
1258     {
1259         WCHAR* szCmdline;
1260         DWORD cmdSize;
1261 
1262         cmdSize=0;
1263         hr = CommandLineFromMsiDescriptor( dar->szwDarwinID, NULL, &cmdSize );
1264         if (hr == ERROR_SUCCESS)
1265         {
1266             cmdSize++;
1267             szCmdline = HeapAlloc( GetProcessHeap(), 0, cmdSize*sizeof(WCHAR) );
1268             hr = CommandLineFromMsiDescriptor( dar->szwDarwinID, szCmdline, &cmdSize );
1269             WINE_TRACE("      command    : %s\n", wine_dbgstr_w(szCmdline));
1270             if (hr == ERROR_SUCCESS)
1271             {
1272                 WCHAR *s, *d;
1273                 int bcount, in_quotes;
1274 
1275                 /* Extract the application path */
1276                 bcount=0;
1277                 in_quotes=0;
1278                 s=szCmdline;
1279                 d=szPath;
1280                 while (*s)
1281                 {
1282                     if ((*s==0x0009 || *s==0x0020) && !in_quotes)
1283                     {
1284                         /* skip the remaining spaces */
1285                         do {
1286                             s++;
1287                         } while (*s==0x0009 || *s==0x0020);
1288                         break;
1289                     }
1290                     else if (*s==0x005c)
1291                     {
1292                         /* '\\' */
1293                         *d++=*s++;
1294                         bcount++;
1295                     }
1296                     else if (*s==0x0022)
1297                     {
1298                         /* '"' */
1299                         if ((bcount & 1)==0)
1300                         {
1301                             /* Preceded by an even number of '\', this is
1302                              * half that number of '\', plus a quote which
1303                              * we erase.
1304                              */
1305                             d-=bcount/2;
1306                             in_quotes=!in_quotes;
1307                             s++;
1308                         }
1309                         else
1310                         {
1311                             /* Preceded by an odd number of '\', this is
1312                              * half that number of '\' followed by a '"'
1313                              */
1314                             d=d-bcount/2-1;
1315                             *d++='"';
1316                             s++;
1317                         }
1318                         bcount=0;
1319                     }
1320                     else
1321                     {
1322                         /* a regular character */
1323                         *d++=*s++;
1324                         bcount=0;
1325                     }
1326                     if ((d-szPath) == pathSize)
1327                     {
1328                         /* Keep processing the path till we get to the
1329                          * arguments, but 'stand still'
1330                          */
1331                         d--;
1332                     }
1333                 }
1334                 /* Close the application path */
1335                 *d=0;
1336 
1337                 lstrcpynW(szArgs, s, argsSize);
1338             }
1339             HeapFree( GetProcessHeap(), 0, szCmdline );
1340         }
1341         LocalFree( dar );
1342     }
1343 
1344     IShellLinkDataList_Release( dl );
1345     return hr;
1346 }
1347 
1348 static WCHAR* assoc_query(ASSOCSTR assocStr, LPCWSTR name, LPCWSTR extra)
1349 {
1350     HRESULT hr;
1351     WCHAR *value = NULL;
1352     DWORD size = 0;
1353     hr = AssocQueryStringW(0, assocStr, name, extra, NULL, &size);
1354     if (SUCCEEDED(hr))
1355     {
1356         value = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
1357         if (value)
1358         {
1359             hr = AssocQueryStringW(0, assocStr, name, extra, value, &size);
1360             if (FAILED(hr))
1361             {
1362                 HeapFree(GetProcessHeap(), 0, value);
1363                 value = NULL;
1364             }
1365         }
1366     }
1367     return value;
1368 }
1369 
1370 static char* wchars_to_utf8_chars(LPCWSTR string)
1371 {
1372     char *ret;
1373     INT size = WideCharToMultiByte(CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL);
1374     ret = HeapAlloc(GetProcessHeap(), 0, size);
1375     if (ret)
1376         WideCharToMultiByte(CP_UTF8, 0, string, -1, ret, size, NULL, NULL);
1377     return ret;
1378 }
1379 
1380 static char *slashes_to_minuses(const char *string)
1381 {
1382     int i;
1383     char *ret = HeapAlloc(GetProcessHeap(), 0, lstrlenA(string) + 1);
1384     if (ret)
1385     {
1386         for (i = 0; string[i]; i++)
1387         {
1388             if (string[i] == '/')
1389                 ret[i] = '-';
1390             else
1391                 ret[i] = string[i];
1392         }
1393         ret[i] = 0;
1394         return ret;
1395     }
1396     return NULL;
1397 }
1398 
1399 static BOOL next_line(FILE *file, char **line, int *size)
1400 {
1401     int pos = 0;
1402     char *cr;
1403     if (*line == NULL)
1404     {
1405         *size = 4096;
1406         *line = HeapAlloc(GetProcessHeap(), 0, *size);
1407     }
1408     while (*line != NULL)
1409     {
1410         if (fgets(&(*line)[pos], *size - pos, file) == NULL)
1411         {
1412             HeapFree(GetProcessHeap(), 0, *line);
1413             *line = NULL;
1414             if (feof(file))
1415                 return TRUE;
1416             return FALSE;
1417         }
1418         pos = strlen(*line);
1419         cr = strchr(*line, '\n');
1420         if (cr == NULL)
1421         {
1422             char *line2;
1423             (*size) *= 2;
1424             line2 = HeapReAlloc(GetProcessHeap(), 0, *line, *size);
1425             if (line2)
1426                 *line = line2;
1427             else
1428             {
1429                 HeapFree(GetProcessHeap(), 0, *line);
1430                 *line = NULL;
1431             }
1432         }
1433         else
1434         {
1435             *cr = 0;
1436             return TRUE;
1437         }
1438     }
1439     return FALSE;
1440 }
1441 
1442 static BOOL add_mimes(const char *xdg_data_dir, struct list *mime_types)
1443 {
1444     char *globs_filename = NULL;
1445     BOOL ret = TRUE;
1446     globs_filename = heap_printf("%s/mime/globs", xdg_data_dir);
1447     if (globs_filename)
1448     {
1449         FILE *globs_file = fopen(globs_filename, "r");
1450         if (globs_file) /* doesn't have to exist */
1451         {
1452             char *line = NULL;
1453             int size = 0;
1454             while (ret && (ret = next_line(globs_file, &line, &size)) && line)
1455             {
1456                 char *pos;
1457                 struct xdg_mime_type *mime_type_entry = NULL;
1458                 if (line[0] != '#' && (pos = strchr(line, ':')))
1459                 {
1460                     mime_type_entry = HeapAlloc(GetProcessHeap(), 0, sizeof(struct xdg_mime_type));
1461                     if (mime_type_entry)
1462                     {
1463                         *pos = 0;
1464                         mime_type_entry->mimeType = strdupA(line);
1465                         mime_type_entry->glob = strdupA(pos + 1);
1466                         if (mime_type_entry->mimeType && mime_type_entry->glob)
1467                             list_add_tail(mime_types, &mime_type_entry->entry);
1468                         else
1469                         {
1470                             HeapFree(GetProcessHeap(), 0, mime_type_entry->mimeType);
1471                             HeapFree(GetProcessHeap(), 0, mime_type_entry->glob);
1472                             HeapFree(GetProcessHeap(), 0, mime_type_entry);
1473                             ret = FALSE;
1474                         }
1475                     }
1476                     else
1477                         ret = FALSE;
1478                 }
1479             }
1480             HeapFree(GetProcessHeap(), 0, line);
1481             fclose(globs_file);
1482         }
1483         HeapFree(GetProcessHeap(), 0, globs_filename);
1484     }
1485     else
1486         ret = FALSE;
1487     return ret;
1488 }
1489 
1490 static void free_native_mime_types(struct list *native_mime_types)
1491 {
1492     struct xdg_mime_type *mime_type_entry, *mime_type_entry2;
1493 
1494     LIST_FOR_EACH_ENTRY_SAFE(mime_type_entry, mime_type_entry2, native_mime_types, struct xdg_mime_type, entry)
1495     {
1496         list_remove(&mime_type_entry->entry);
1497         HeapFree(GetProcessHeap(), 0, mime_type_entry->glob);
1498         HeapFree(GetProcessHeap(), 0, mime_type_entry->mimeType);
1499         HeapFree(GetProcessHeap(), 0, mime_type_entry);
1500     }
1501     HeapFree(GetProcessHeap(), 0, native_mime_types);
1502 }
1503 
1504 static BOOL build_native_mime_types(const char *xdg_data_home, struct list **mime_types)
1505 {
1506     char *xdg_data_dirs;
1507     BOOL ret;
1508 
1509     *mime_types = NULL;
1510 
1511     xdg_data_dirs = getenv("XDG_DATA_DIRS");
1512     if (xdg_data_dirs == NULL)
1513         xdg_data_dirs = heap_printf("/usr/local/share/:/usr/share/");
1514     else
1515         xdg_data_dirs = strdupA(xdg_data_dirs);
1516 
1517     if (xdg_data_dirs)
1518     {
1519         *mime_types = HeapAlloc(GetProcessHeap(), 0, sizeof(struct list));
1520         if (*mime_types)
1521         {
1522             const char *begin;
1523             char *end;
1524 
1525             list_init(*mime_types);
1526             ret = add_mimes(xdg_data_home, *mime_types);
1527             if (ret)
1528             {
1529                 for (begin = xdg_data_dirs; (end = strchr(begin, ':')); begin = end + 1)
1530                 {
1531                     *end = '\0';
1532                     ret = add_mimes(begin, *mime_types);
1533                     *end = ':';
1534                     if (!ret)
1535                         break;
1536                 }
1537                 if (ret)
1538                     ret = add_mimes(begin, *mime_types);
1539             }
1540         }
1541         else
1542             ret = FALSE;
1543         HeapFree(GetProcessHeap(), 0, xdg_data_dirs);
1544     }
1545     else
1546         ret = FALSE;
1547     if (!ret && *mime_types)
1548     {
1549         free_native_mime_types(*mime_types);
1550         *mime_types = NULL;
1551     }
1552     return ret;
1553 }
1554 
1555 static BOOL match_glob(struct list *native_mime_types, const char *extension,
1556                        char **match)
1557 {
1558 #ifdef HAVE_FNMATCH
1559     struct xdg_mime_type *mime_type_entry;
1560     int matchLength = 0;
1561 
1562     *match = NULL;
1563 
1564     LIST_FOR_EACH_ENTRY(mime_type_entry, native_mime_types, struct xdg_mime_type, entry)
1565     {
1566         if (fnmatch(mime_type_entry->glob, extension, 0) == 0)
1567         {
1568             if (*match == NULL || matchLength < strlen(mime_type_entry->glob))
1569             {
1570                 *match = mime_type_entry->mimeType;
1571                 matchLength = strlen(mime_type_entry->glob);
1572             }
1573         }
1574     }
1575 
1576     if (*match != NULL)
1577     {
1578         *match = strdupA(*match);
1579         if (*match == NULL)
1580             return FALSE;
1581     }
1582 #else
1583     *match = NULL;
1584 #endif
1585     return TRUE;
1586 }
1587 
1588 static BOOL freedesktop_mime_type_for_extension(struct list *native_mime_types,
1589                                                 const char *extensionA,
1590                                                 LPCWSTR extensionW,
1591                                                 char **mime_type)
1592 {
1593     WCHAR *lower_extensionW;
1594     INT len;
1595     BOOL ret = match_glob(native_mime_types, extensionA, mime_type);
1596     if (ret == FALSE || *mime_type != NULL)
1597         return ret;
1598     len = strlenW(extensionW);
1599     lower_extensionW = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
1600     if (lower_extensionW)
1601     {
1602         char *lower_extensionA;
1603         memcpy(lower_extensionW, extensionW, (len + 1)*sizeof(WCHAR));
1604         strlwrW(lower_extensionW);
1605         lower_extensionA = wchars_to_utf8_chars(lower_extensionW);
1606         if (lower_extensionA)
1607         {
1608             ret = match_glob(native_mime_types, lower_extensionA, mime_type);
1609             HeapFree(GetProcessHeap(), 0, lower_extensionA);
1610         }
1611         else
1612         {
1613             ret = FALSE;
1614             WINE_FIXME("out of memory\n");
1615         }
1616         HeapFree(GetProcessHeap(), 0, lower_extensionW);
1617     }
1618     else
1619     {
1620         ret = FALSE;
1621         WINE_FIXME("out of memory\n");
1622     }
1623     return ret;
1624 }
1625 
1626 static CHAR* reg_get_valA(HKEY key, LPCSTR subkey, LPCSTR name)
1627 {
1628     DWORD size;
1629     if (RegGetValueA(key, subkey, name, RRF_RT_REG_SZ, NULL, NULL, &size) == ERROR_SUCCESS)
1630     {
1631         CHAR *ret = HeapAlloc(GetProcessHeap(), 0, size);
1632         if (ret)
1633         {
1634             if (RegGetValueA(key, subkey, name, RRF_RT_REG_SZ, NULL, ret, &size) == ERROR_SUCCESS)
1635                 return ret;
1636         }
1637         HeapFree(GetProcessHeap(), 0, ret);
1638     }
1639     return NULL;
1640 }
1641 
1642 static WCHAR* reg_get_valW(HKEY key, LPCWSTR subkey, LPCWSTR name)
1643 {
1644     DWORD size;
1645     if (RegGetValueW(key, subkey, name, RRF_RT_REG_SZ, NULL, NULL, &size) == ERROR_SUCCESS)
1646     {
1647         WCHAR *ret = HeapAlloc(GetProcessHeap(), 0, size);
1648         if (ret)
1649         {
1650             if (RegGetValueW(key, subkey, name, RRF_RT_REG_SZ, NULL, ret, &size) == ERROR_SUCCESS)
1651                 return ret;
1652         }
1653         HeapFree(GetProcessHeap(), 0, ret);
1654     }
1655     return NULL;
1656 }
1657 
1658 static HKEY open_associations_reg_key(void)
1659 {
1660     static const WCHAR Software_Wine_FileOpenAssociationsW[] = {
1661         'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','F','i','l','e','O','p','e','n','A','s','s','o','c','i','a','t','i','o','n','s',0};
1662     HKEY assocKey;
1663     if (RegCreateKeyW(HKEY_CURRENT_USER, Software_Wine_FileOpenAssociationsW, &assocKey) == ERROR_SUCCESS)
1664         return assocKey;
1665     return NULL;
1666 }
1667 
1668 static BOOL has_association_changed(LPCSTR extensionA, LPCWSTR extensionW, LPCSTR mimeType, LPCWSTR progId, LPCSTR appName, LPCWSTR docName)
1669 {
1670     static const WCHAR ProgIDW[] = {'P','r','o','g','I','D',0};
1671     static const WCHAR DocNameW[] = {'D','o','c','N','a','m','e',0};
1672     HKEY assocKey;
1673     BOOL ret;
1674 
1675     if ((assocKey = open_associations_reg_key()))
1676     {
1677         CHAR *valueA;
1678         WCHAR *value;
1679 
1680         ret = FALSE;
1681 
1682         valueA = reg_get_valA(assocKey, extensionA, "MimeType");
1683         if (!valueA || lstrcmpA(valueA, mimeType))
1684             ret = TRUE;
1685         HeapFree(GetProcessHeap(), 0, valueA);
1686 
1687         value = reg_get_valW(assocKey, extensionW, ProgIDW);
1688         if (!value || strcmpW(value, progId))
1689             ret = TRUE;
1690         HeapFree(GetProcessHeap(), 0, value);
1691 
1692         valueA = reg_get_valA(assocKey, extensionA, "AppName");
1693         if (!valueA || lstrcmpA(valueA, appName))
1694             ret = TRUE;
1695         HeapFree(GetProcessHeap(), 0, valueA);
1696 
1697         value = reg_get_valW(assocKey, extensionW, DocNameW);
1698         if (docName && (!value || strcmpW(value, docName)))
1699             ret = TRUE;
1700         HeapFree(GetProcessHeap(), 0, value);
1701 
1702         RegCloseKey(assocKey);
1703     }
1704     else
1705     {
1706         WINE_ERR("error opening associations registry key\n");
1707         ret = FALSE;
1708     }
1709     return ret;
1710 }
1711 
1712 static void update_association(LPCWSTR extension, LPCSTR mimeType, LPCWSTR progId, LPCSTR appName, LPCWSTR docName, LPCSTR desktopFile)
1713 {
1714     static const WCHAR ProgIDW[] = {'P','r','o','g','I','D',0};
1715     static const WCHAR DocNameW[] = {'D','o','c','N','a','m','e',0};
1716     HKEY assocKey;
1717 
1718     if ((assocKey = open_associations_reg_key()))
1719     {
1720         HKEY subkey;
1721         if (RegCreateKeyW(assocKey, extension, &subkey) == ERROR_SUCCESS)
1722         {
1723             RegSetValueExA(subkey, "MimeType", 0, REG_SZ, (BYTE*) mimeType, lstrlenA(mimeType) + 1);
1724             RegSetValueExW(subkey, ProgIDW, 0, REG_SZ, (BYTE*) progId, (lstrlenW(progId) + 1) * sizeof(WCHAR));
1725             RegSetValueExA(subkey, "AppName", 0, REG_SZ, (BYTE*) appName, lstrlenA(appName) + 1);
1726             if (docName)
1727                 RegSetValueExW(subkey, DocNameW, 0, REG_SZ, (BYTE*) docName, (lstrlenW(docName) + 1) * sizeof(WCHAR));
1728             RegSetValueExA(subkey, "DesktopFile", 0, REG_SZ, (BYTE*) desktopFile, (lstrlenA(desktopFile) + 1));
1729             RegCloseKey(subkey);
1730         }
1731         else
1732             WINE_ERR("could not create extension subkey\n");
1733         RegCloseKey(assocKey);
1734     }
1735     else
1736         WINE_ERR("could not open file associations key\n");
1737 }
1738 
1739 static BOOL cleanup_associations(void)
1740 {
1741     static const WCHAR openW[] = {'o','p','e','n',0};
1742     HKEY assocKey;
1743     BOOL hasChanged = FALSE;
1744     if ((assocKey = open_associations_reg_key()))
1745     {
1746         int i;
1747         BOOL done = FALSE;
1748         for (i = 0; !done; i++)
1749         {
1750             WCHAR *extensionW = NULL;
1751             char *extensionA = NULL;
1752             DWORD size = 1024;
1753             LSTATUS ret;
1754 
1755             do
1756             {
1757                 HeapFree(GetProcessHeap(), 0, extensionW);
1758                 extensionW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
1759                 if (extensionW == NULL)
1760                 {
1761                     WINE_ERR("out of memory\n");
1762                     ret = ERROR_OUTOFMEMORY;
1763                     break;
1764                 }
1765                 ret = RegEnumKeyExW(assocKey, i, extensionW, &size, NULL, NULL, NULL, NULL);
1766                 size *= 2;
1767             } while (ret == ERROR_MORE_DATA);
1768 
1769             if (ret == ERROR_SUCCESS)
1770             {
1771                 WCHAR *command;
1772                 extensionA = wchars_to_utf8_chars(extensionW);
1773                 if (extensionA == NULL)
1774                 {
1775                     WINE_ERR("out of memory\n");
1776                     done = TRUE;
1777                     goto end;
1778                 }
1779                 command = assoc_query(ASSOCSTR_COMMAND, extensionW, openW);
1780                 if (command == NULL)
1781                 {
1782                     char *desktopFile = reg_get_valA(assocKey, extensionA, "DesktopFile");
1783                     if (desktopFile)
1784                     {
1785                         WINE_TRACE("removing file type association for %s\n", wine_dbgstr_a(extensionA));
1786                         remove(desktopFile);
1787                     }
1788                     RegDeleteKeyW(assocKey, extensionW);
1789                     hasChanged = TRUE;
1790                     HeapFree(GetProcessHeap(), 0, desktopFile);
1791                 }
1792                 HeapFree(GetProcessHeap(), 0, command);
1793             }
1794             else
1795             {
1796                 if (ret != ERROR_NO_MORE_ITEMS)
1797                     WINE_ERR("error %d while reading registry\n", ret);
1798                 done = TRUE;
1799             }
1800         end:
1801             HeapFree(GetProcessHeap(), 0, extensionA);
1802             HeapFree(GetProcessHeap(), 0, extensionW);
1803         }
1804         RegCloseKey(assocKey);
1805     }
1806     else
1807         WINE_ERR("could not open file associations key\n");
1808     return hasChanged;
1809 }
1810 
1811 static BOOL write_freedesktop_mime_type_entry(const char *packages_dir, const char *dot_extension,
1812                                               const char *mime_type, const char *comment)
1813 {
1814     BOOL ret = FALSE;
1815     char *filename;
1816 
1817     WINE_TRACE("writing MIME type %s, extension=%s, comment=%s\n", wine_dbgstr_a(mime_type),
1818                wine_dbgstr_a(dot_extension), wine_dbgstr_a(comment));
1819 
1820     filename = heap_printf("%s/x-wine-extension-%s.xml", packages_dir, &dot_extension[1]);
1821     if (filename)
1822     {
1823         FILE *packageFile = fopen(filename, "w");
1824         if (packageFile)
1825         {
1826             fprintf(packageFile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1827             fprintf(packageFile, "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n");
1828             fprintf(packageFile, "  <mime-type type=\"");
1829             write_xml_text(packageFile, mime_type);
1830             fprintf(packageFile, "\">\n");
1831             fprintf(packageFile, "    <glob pattern=\"*");
1832             write_xml_text(packageFile, dot_extension);
1833             fprintf(packageFile, "\"/>\n");
1834             if (comment)
1835             {
1836                 fprintf(packageFile, "    <comment>");
1837                 write_xml_text(packageFile, comment);
1838                 fprintf(packageFile, "</comment>\n");
1839             }
1840             fprintf(packageFile, "  </mime-type>\n");
1841             fprintf(packageFile, "</mime-info>\n");
1842             ret = TRUE;
1843             fclose(packageFile);
1844         }
1845         else
1846             WINE_ERR("error writing file %s\n", filename);
1847         HeapFree(GetProcessHeap(), 0, filename);
1848     }
1849     else
1850         WINE_ERR("out of memory\n");
1851     return ret;
1852 }
1853 
1854 static BOOL is_extension_blacklisted(LPCWSTR extension)
1855 {
1856     /* These are managed through external tools like wine.desktop, to evade malware created file type associations */
1857     static const WCHAR comW[] = {'.','c','o','m',0};
1858     static const WCHAR exeW[] = {'.','e','x','e',0};
1859     static const WCHAR msiW[] = {'.','m','s','i',0};
1860 
1861     if (!strcmpiW(extension, comW) ||
1862         !strcmpiW(extension, exeW) ||
1863         !strcmpiW(extension, msiW))
1864         return TRUE;
1865     return FALSE;
1866 }
1867 
1868 static BOOL write_freedesktop_association_entry(const char *desktopPath, const char *dot_extension,
1869                                                 const char *friendlyAppName, const char *mimeType,
1870                                                 const char *progId)
1871 {
1872     BOOL ret = FALSE;
1873     FILE *desktop;
1874 
1875     WINE_TRACE("writing association for file type %s, friendlyAppName=%s, MIME type %s, progID=%s, to file %s\n",
1876                wine_dbgstr_a(dot_extension), wine_dbgstr_a(friendlyAppName), wine_dbgstr_a(mimeType),
1877                wine_dbgstr_a(progId), wine_dbgstr_a(desktopPath));
1878 
1879     desktop = fopen(desktopPath, "w");
1880     if (desktop)
1881     {
1882         fprintf(desktop, "[Desktop Entry]\n");
1883         fprintf(desktop, "Type=Application\n");
1884         fprintf(desktop, "Name=%s\n", friendlyAppName);
1885         fprintf(desktop, "MimeType=%s\n", mimeType);
1886         fprintf(desktop, "Exec=wine start /ProgIDOpen %s %%f\n", progId);
1887         fprintf(desktop, "NoDisplay=true\n");
1888         fprintf(desktop, "StartupNotify=true\n");
1889         ret = TRUE;
1890         fclose(desktop);
1891     }
1892     else
1893         WINE_ERR("error writing association file %s\n", wine_dbgstr_a(desktopPath));
1894     return ret;
1895 }
1896 
1897 static BOOL generate_associations(const char *xdg_data_home, const char *packages_dir, const char *applications_dir)
1898 {
1899     static const WCHAR openW[] = {'o','p','e','n',0};
1900     struct list *nativeMimeTypes = NULL;
1901     LSTATUS ret = 0;
1902     int i;
1903     BOOL hasChanged = FALSE;
1904 
1905     if (!build_native_mime_types(xdg_data_home, &nativeMimeTypes))
1906     {
1907         WINE_ERR("could not build native MIME types\n");
1908         return FALSE;
1909     }
1910 
1911     for (i = 0; ; i++)
1912     {
1913         WCHAR *extensionW = NULL;
1914         DWORD size = 1024;
1915 
1916         do
1917         {
1918             HeapFree(GetProcessHeap(), 0, extensionW);
1919             extensionW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
1920             if (extensionW == NULL)
1921             {
1922                 WINE_ERR("out of memory\n");
1923                 ret = ERROR_OUTOFMEMORY;
1924                 break;
1925             }
1926             ret = RegEnumKeyExW(HKEY_CLASSES_ROOT, i, extensionW, &size, NULL, NULL, NULL, NULL);
1927             size *= 2;
1928         } while (ret == ERROR_MORE_DATA);
1929 
1930         if (ret == ERROR_SUCCESS && extensionW[0] == '.' && !is_extension_blacklisted(extensionW))
1931         {
1932             char *extensionA = NULL;
1933             WCHAR *commandW = NULL;
1934             WCHAR *friendlyDocNameW = NULL;
1935             char *friendlyDocNameA = NULL;
1936             WCHAR *iconW = NULL;
1937             char *iconA = NULL;
1938             WCHAR *contentTypeW = NULL;
1939             char *mimeTypeA = NULL;
1940             WCHAR *friendlyAppNameW = NULL;
1941             char *friendlyAppNameA = NULL;
1942             WCHAR *progIdW = NULL;
1943             char *progIdA = NULL;
1944 
1945             extensionA = wchars_to_utf8_chars(extensionW);
1946             if (extensionA == NULL)
1947             {
1948                 WINE_ERR("out of memory\n");
1949                 goto end;
1950             }
1951 
1952             friendlyDocNameW = assoc_query(ASSOCSTR_FRIENDLYDOCNAME, extensionW, NULL);
1953             if (friendlyDocNameW)
1954             {
1955                 friendlyDocNameA = wchars_to_utf8_chars(friendlyDocNameW);
1956                 if (friendlyDocNameA == NULL)
1957                 {
1958                     WINE_ERR("out of memory\n");
1959                     goto end;
1960                 }
1961             }
1962 
1963             iconW = assoc_query(ASSOCSTR_DEFAULTICON, extensionW, NULL);
1964 
1965             contentTypeW = assoc_query(ASSOCSTR_CONTENTTYPE, extensionW, NULL);
1966 
1967             if (!freedesktop_mime_type_for_extension(nativeMimeTypes, extensionA, extensionW, &mimeTypeA))
1968                 goto end;
1969 
1970             if (mimeTypeA == NULL)
1971             {
1972                 if (contentTypeW != NULL && strchrW(contentTypeW, '/'))
1973                     mimeTypeA = wchars_to_utf8_chars(contentTypeW);
1974                 else
1975                     mimeTypeA = heap_printf("application/x-wine-extension-%s", &extensionA[1]);
1976 
1977                 if (mimeTypeA != NULL)
1978                 {
1979                     /* Gnome seems to ignore the <icon> tag in MIME packages,
1980                      * and the default name is more intuitive anyway.
1981                      */
1982                     if (iconW)
1983                     {
1984                         char *flattened_mime = slashes_to_minuses(mimeTypeA);
1985                         if (flattened_mime)
1986                         {
1987                             int index = 0;
1988                             WCHAR *comma = strrchrW(iconW, ',');
1989                             if (comma)
1990                             {
1991                                 *comma = 0;
1992                                 index = atoiW(comma + 1);
1993                             }
1994                             iconA = extract_icon(iconW, index, flattened_mime, FALSE);
1995                             HeapFree(GetProcessHeap(), 0, flattened_mime);
1996                         }
1997                     }
1998 
1999                     write_freedesktop_mime_type_entry(packages_dir, extensionA, mimeTypeA, friendlyDocNameA);
2000                     hasChanged = TRUE;
2001                 }
2002                 else
2003                 {
2004                     WINE_FIXME("out of memory\n");
2005                     goto end;
2006                 }
2007             }
2008 
2009             commandW = assoc_query(ASSOCSTR_COMMAND, extensionW, openW);
2010             if (commandW == NULL)
2011                 /* no command => no application is associated */
2012                 goto end;
2013 
2014             friendlyAppNameW = assoc_query(ASSOCSTR_FRIENDLYAPPNAME, extensionW, NULL);
2015             if (friendlyAppNameW)
2016             {
2017                 friendlyAppNameA = wchars_to_utf8_chars(friendlyAppNameW);
2018                 if (friendlyAppNameA == NULL)
2019                 {
2020                     WINE_ERR("out of memory\n");
2021                     goto end;
2022                 }
2023             }
2024             else
2025             {
2026                 friendlyAppNameA = heap_printf("A Wine application");
2027                 if (friendlyAppNameA == NULL)
2028                 {
2029                     WINE_ERR("out of memory\n");
2030                     goto end;
2031                 }
2032             }
2033 
2034             progIdW = reg_get_valW(HKEY_CLASSES_ROOT, extensionW, NULL);
2035             if (progIdW)
2036             {
2037                 progIdA = wchars_to_utf8_chars(progIdW);
2038                 if (progIdA == NULL)
2039                 {
2040                     WINE_ERR("out of memory\n");
2041                     goto end;
2042                 }
2043             }
2044             else
2045                 goto end; /* no progID => not a file type association */
2046 
2047             if (has_association_changed(extensionA, extensionW, mimeTypeA, progIdW, friendlyAppNameA, friendlyDocNameW))
2048             {
2049                 char *desktopPath = heap_printf("%s/wine-extension-%s.desktop", applications_dir, &extensionA[1]);
2050                 if (desktopPath)
2051                 {
2052                     if (write_freedesktop_association_entry(desktopPath, extensionA, friendlyAppNameA, mimeTypeA, progIdA))
2053                     {
2054                         hasChanged = TRUE;
2055                         update_association(extensionW, mimeTypeA, progIdW, friendlyAppNameA, friendlyDocNameW, desktopPath);
2056                     }
2057                     HeapFree(GetProcessHeap(), 0, desktopPath);
2058                 }
2059             }
2060 
2061         end:
2062             HeapFree(GetProcessHeap(), 0, extensionA);
2063             HeapFree(GetProcessHeap(), 0, commandW);
2064             HeapFree(GetProcessHeap(), 0, friendlyDocNameW);
2065             HeapFree(GetProcessHeap(), 0, friendlyDocNameA);
2066             HeapFree(GetProcessHeap(), 0, iconW);
2067             HeapFree(GetProcessHeap(), 0, iconA);
2068             HeapFree(GetProcessHeap(), 0, contentTypeW);
2069             HeapFree(GetProcessHeap(), 0, mimeTypeA);
2070             HeapFree(GetProcessHeap(), 0, friendlyAppNameW);
2071             HeapFree(GetProcessHeap(), 0, friendlyAppNameA);
2072             HeapFree(GetProcessHeap(), 0, progIdW);
2073             HeapFree(GetProcessHeap(), 0, progIdA);
2074         }
2075         HeapFree(GetProcessHeap(), 0, extensionW);
2076         if (ret != ERROR_SUCCESS)
2077             break;
2078     }
2079 
2080     free_native_mime_types(nativeMimeTypes);
2081     return hasChanged;
2082 }
2083 
2084 static BOOL InvokeShellLinker( IShellLinkW *sl, LPCWSTR link, BOOL bWait )
2085 {
2086     static const WCHAR startW[] = {'\\','c','o','m','m','a','n','d',
2087                                    '\\','s','t','a','r','t','.','e','x','e',0};
2088     char *link_name = NULL, *icon_name = NULL, *work_dir = NULL;
2089     char *escaped_path = NULL, *escaped_args = NULL, *escaped_description = NULL;
2090     WCHAR szTmp[INFOTIPSIZE];
2091     WCHAR szDescription[INFOTIPSIZE], szPath[MAX_PATH], szWorkDir[MAX_PATH];
2092     WCHAR szArgs[INFOTIPSIZE], szIconPath[MAX_PATH];
2093     int iIconId = 0, r = -1;
2094     DWORD csidl = -1;
2095     HANDLE hsem = NULL;
2096     char *unix_link = NULL;
2097 
2098     if ( !link )
2099     {
2100         WINE_ERR("Link name is null\n");
2101         return FALSE;
2102     }
2103 
2104     if( !GetLinkLocation( link, &csidl, &link_name ) )
2105     {
2106         WINE_WARN("Unknown link location %s. Ignoring.\n",wine_dbgstr_w(link));
2107         return TRUE;
2108     }
2109     if (!in_desktop_dir(csidl) && !in_startmenu(csidl))
2110     {
2111         WINE_WARN("Not under desktop or start menu. Ignoring.\n");
2112         return TRUE;
2113     }
2114     WINE_TRACE("Link       : %s\n", wine_dbgstr_a(link_name));
2115 
2116     szTmp[0] = 0;
2117     IShellLinkW_GetWorkingDirectory( sl, szTmp, MAX_PATH );
2118     ExpandEnvironmentStringsW(szTmp, szWorkDir, MAX_PATH);
2119     WINE_TRACE("workdir    : %s\n", wine_dbgstr_w(szWorkDir));
2120 
2121     szTmp[0] = 0;
2122     IShellLinkW_GetDescription( sl, szTmp, INFOTIPSIZE );
2123     ExpandEnvironmentStringsW(szTmp, szDescription, INFOTIPSIZE);
2124     WINE_TRACE("description: %s\n", wine_dbgstr_w(szDescription));
2125 
2126     get_cmdline( sl, szPath, MAX_PATH, szArgs, INFOTIPSIZE);
2127     WINE_TRACE("path       : %s\n", wine_dbgstr_w(szPath));
2128     WINE_TRACE("args       : %s\n", wine_dbgstr_w(szArgs));
2129 
2130     szTmp[0] = 0;
2131     IShellLinkW_GetIconLocation( sl, szTmp, MAX_PATH, &iIconId );
2132     ExpandEnvironmentStringsW(szTmp, szIconPath, MAX_PATH);
2133     WINE_TRACE("icon file  : %s\n", wine_dbgstr_w(szIconPath) );
2134 
2135     if( !szPath[0] )
2136     {
2137         LPITEMIDLIST pidl = NULL;
2138         IShellLinkW_GetIDList( sl, &pidl );
2139         if( pidl && SHGetPathFromIDListW( pidl, szPath ) )
2140             WINE_TRACE("pidl path  : %s\n", wine_dbgstr_w(szPath));
2141     }
2142 
2143     /* extract the icon */
2144     if( szIconPath[0] )
2145         icon_name = extract_icon( szIconPath , iIconId, NULL, bWait );
2146     else
2147         icon_name = extract_icon( szPath, iIconId, NULL, bWait );
2148 
2149     /* fail - try once again after parent process exit */
2150     if( !icon_name )
2151     {
2152         if (bWait)
2153         {
2154             WINE_WARN("Unable to extract icon, deferring.\n");
2155             goto cleanup;
2156         }
2157         WINE_ERR("failed to extract icon from %s\n",
2158                  wine_dbgstr_w( szIconPath[0] ? szIconPath : szPath ));
2159     }
2160 
2161     unix_link = wine_get_unix_file_name(link);
2162     if (unix_link == NULL)
2163     {
2164         WINE_WARN("couldn't find unix path of %s\n", wine_dbgstr_w(link));
2165         goto cleanup;
2166     }
2167 
2168     /* check the path */
2169     if( szPath[0] )
2170     {
2171         static const WCHAR exeW[] = {'.','e','x','e',0};
2172         WCHAR *p;
2173 
2174         /* check for .exe extension */
2175         if (!(p = strrchrW( szPath, '.' )) ||
2176             strchrW( p, '\\' ) || strchrW( p, '/' ) ||
2177             lstrcmpiW( p, exeW ))
2178         {
2179             /* Not .exe - use 'start.exe' to launch this file */
2180             p = szArgs + lstrlenW(szPath) + 2;
2181             if (szArgs[0])
2182             {
2183                 p[0] = ' ';
2184                 memmove( p+1, szArgs, min( (lstrlenW(szArgs) + 1) * sizeof(szArgs[0]),
2185                                            sizeof(szArgs) - (p + 1 - szArgs) * sizeof(szArgs[0]) ) );
2186             }
2187             else
2188                 p[0] = 0;
2189 
2190             szArgs[0] = '"';
2191             lstrcpyW(szArgs + 1, szPath);
2192             p[-1] = '"';
2193 
2194             GetWindowsDirectoryW(szPath, MAX_PATH);
2195             lstrcatW(szPath, startW);
2196         }
2197 
2198         /* convert app working dir */
2199         if (szWorkDir[0])
2200             work_dir = wine_get_unix_file_name( szWorkDir );
2201     }
2202     else
2203     {
2204         /* if there's no path... try run the link itself */
2205         lstrcpynW(szArgs, link, MAX_PATH);
2206         GetWindowsDirectoryW(szPath, MAX_PATH);
2207         lstrcatW(szPath, startW);
2208     }
2209 
2210     /* escape the path and parameters */
2211     escaped_path = escape(szPath);
2212     escaped_args = escape(szArgs);
2213     escaped_description = escape(szDescription);
2214 
2215     /* building multiple menus concurrently has race conditions */
2216     hsem = CreateSemaphoreA( NULL, 1, 1, "winemenubuilder_semaphore");
2217     if( WAIT_OBJECT_0 != MsgWaitForMultipleObjects( 1, &hsem, FALSE, INFINITE, QS_ALLINPUT ) )
2218     {
2219         WINE_ERR("failed wait for semaphore\n");
2220         goto cleanup;
2221     }
2222 
2223     if (in_desktop_dir(csidl))
2224     {
2225         char *location;
2226         const char *lastEntry;
2227         lastEntry = strrchr(link_name, '/');
2228         if (lastEntry == NULL)
2229             lastEntry = link_name;
2230         else
2231             ++lastEntry;
2232         location = heap_printf("%s/%s.desktop", xdg_desktop_dir, lastEntry);
2233         if (location)
2234         {
2235             r = !write_desktop_entry(NULL, location, lastEntry, escaped_path, escaped_args, escaped_description, work_dir, icon_name);
2236             HeapFree(GetProcessHeap(), 0, location);
2237         }
2238     }
2239     else
2240         r = !write_menu_entry(unix_link, link_name, escaped_path, escaped_args, escaped_description, work_dir, icon_name);
2241 
2242     ReleaseSemaphore( hsem, 1, NULL );
2243 
2244 cleanup:
2245     if (hsem) CloseHandle( hsem );
2246     HeapFree( GetProcessHeap(), 0, icon_name );
2247     HeapFree( GetProcessHeap(), 0, work_dir );
2248     HeapFree( GetProcessHeap(), 0, link_name );
2249     HeapFree( GetProcessHeap(), 0, escaped_args );
2250     HeapFree( GetProcessHeap(), 0, escaped_path );
2251     HeapFree( GetProcessHeap(), 0, escaped_description );
2252     HeapFree( GetProcessHeap(), 0, unix_link);
2253 
2254     if (r && !bWait)
2255         WINE_ERR("failed to build the menu\n" );
2256 
2257     return ( r == 0 );
2258 }
2259 
2260 static BOOL InvokeShellLinkerForURL( IUniformResourceLocatorW *url, LPCWSTR link, BOOL bWait )
2261 {
2262     char *link_name = NULL;
2263     DWORD csidl = -1;
2264     LPWSTR urlPath;
2265     char *escaped_urlPath = NULL;
2266     HRESULT hr;
2267     HANDLE hSem = NULL;
2268     BOOL ret = TRUE;
2269     int r = -1;
2270     char *unix_link = NULL;
2271 
2272     if ( !link )
2273     {
2274         WINE_ERR("Link name is null\n");
2275         return TRUE;
2276     }
2277 
2278     if( !GetLinkLocation( link, &csidl, &link_name ) )
2279     {
2280         WINE_WARN("Unknown link location %s. Ignoring.\n",wine_dbgstr_w(link));
2281         return TRUE;
2282     }
2283     if (!in_desktop_dir(csidl) && !in_startmenu(csidl))
2284     {
2285         WINE_WARN("Not under desktop or start menu. Ignoring.\n");
2286         ret = TRUE;
2287         goto cleanup;
2288     }
2289     WINE_TRACE("Link       : %s\n", wine_dbgstr_a(link_name));
2290 
2291     hr = url->lpVtbl->GetURL(url, &urlPath);
2292     if (FAILED(hr))
2293     {
2294         ret = TRUE;
2295         goto cleanup;
2296     }
2297     WINE_TRACE("path       : %s\n", wine_dbgstr_w(urlPath));
2298 
2299     unix_link = wine_get_unix_file_name(link);
2300     if (unix_link == NULL)
2301     {
2302         WINE_WARN("couldn't find unix path of %s\n", wine_dbgstr_w(link));
2303         goto cleanup;
2304     }
2305 
2306     escaped_urlPath = escape(urlPath);
2307 
2308     hSem = CreateSemaphoreA( NULL, 1, 1, "winemenubuilder_semaphore");
2309     if( WAIT_OBJECT_0 != MsgWaitForMultipleObjects( 1, &hSem, FALSE, INFINITE, QS_ALLINPUT ) )
2310     {
2311         WINE_ERR("failed wait for semaphore\n");
2312         goto cleanup;
2313     }
2314     if (in_desktop_dir(csidl))
2315     {
2316         char *location;
2317         const char *lastEntry;
2318         lastEntry = strrchr(link_name, '/');
2319         if (lastEntry == NULL)
2320             lastEntry = link_name;
2321         else
2322             ++lastEntry;
2323         location = heap_printf("%s/%s.desktop", xdg_desktop_dir, lastEntry);
2324         if (location)
2325         {
2326             r = !write_desktop_entry(NULL, location, lastEntry, "winebrowser", escaped_urlPath, NULL, NULL, NULL);
2327             HeapFree(GetProcessHeap(), 0, location);
2328         }
2329     }
2330     else
2331         r = !write_menu_entry(unix_link, link_name, "winebrowser", escaped_urlPath, NULL, NULL, NULL);
2332     ret = (r != 0);
2333     ReleaseSemaphore(hSem, 1, NULL);
2334 
2335 cleanup:
2336     if (hSem)
2337         CloseHandle(hSem);
2338     HeapFree(GetProcessHeap(), 0, link_name);
2339     CoTaskMemFree( urlPath );
2340     HeapFree(GetProcessHeap(), 0, escaped_urlPath);
2341     HeapFree(GetProcessHeap(), 0, unix_link);
2342     return ret;
2343 }
2344 
2345 static BOOL WaitForParentProcess( void )
2346 {
2347     PROCESSENTRY32 procentry;
2348     HANDLE hsnapshot = NULL, hprocess = NULL;
2349     DWORD ourpid = GetCurrentProcessId();
2350     BOOL ret = FALSE, rc;
2351 
2352     WINE_TRACE("Waiting for parent process\n");
2353     if ((hsnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 )) ==
2354         INVALID_HANDLE_VALUE)
2355     {
2356         WINE_ERR("CreateToolhelp32Snapshot failed, error %d\n", GetLastError());
2357         goto done;
2358     }
2359 
2360     procentry.dwSize = sizeof(PROCESSENTRY32);
2361     rc = Process32First( hsnapshot, &procentry );
2362     while (rc)
2363     {
2364         if (procentry.th32ProcessID == ourpid) break;
2365         rc = Process32Next( hsnapshot, &procentry );
2366     }
2367     if (!rc)
2368     {
2369         WINE_WARN("Unable to find current process id %d when listing processes\n", ourpid);
2370         goto done;
2371     }
2372 
2373     if ((hprocess = OpenProcess( SYNCHRONIZE, FALSE, procentry.th32ParentProcessID )) ==
2374         NULL)
2375     {
2376         WINE_WARN("OpenProcess failed pid=%d, error %d\n", procentry.th32ParentProcessID,
2377                  GetLastError());
2378         goto done;
2379     }
2380 
2381     if (MsgWaitForMultipleObjects( 1, &hprocess, FALSE, INFINITE, QS_ALLINPUT ) == WAIT_OBJECT_0)
2382         ret = TRUE;
2383     else
2384         WINE_ERR("Unable to wait for parent process, error %d\n", GetLastError());
2385 
2386 done:
2387     if (hprocess) CloseHandle( hprocess );
2388     if (hsnapshot) CloseHandle( hsnapshot );
2389     return ret;
2390 }
2391 
2392 static BOOL Process_Link( LPCWSTR linkname, BOOL bWait )
2393 {
2394     IShellLinkW *sl;
2395     IPersistFile *pf;
2396     HRESULT r;
2397     WCHAR fullname[MAX_PATH];
2398     DWORD len;
2399 
2400     WINE_TRACE("%s, wait %d\n", wine_dbgstr_w(linkname), bWait);
2401 
2402     if( !linkname[0] )
2403     {
2404         WINE_ERR("link name missing\n");
2405         return 1;
2406     }
2407 
2408     len=GetFullPathNameW( linkname, MAX_PATH, fullname, NULL );
2409     if (len==0 || len>MAX_PATH)
2410     {
2411         WINE_ERR("couldn't get full path of link file\n");
2412         return 1;
2413     }
2414 
2415     r = CoInitialize( NULL );
2416     if( FAILED( r ) )
2417     {
2418         WINE_ERR("CoInitialize failed\n");
2419         return 1;
2420     }
2421 
2422     r = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
2423                           &IID_IShellLinkW, (LPVOID *) &sl );
2424     if( FAILED( r ) )
2425     {
2426         WINE_ERR("No IID_IShellLink\n");
2427         return 1;
2428     }
2429 
2430     r = IShellLinkW_QueryInterface( sl, &IID_IPersistFile, (LPVOID*) &pf );
2431     if( FAILED( r ) )
2432     {
2433         WINE_ERR("No IID_IPersistFile\n");
2434         return 1;
2435     }
2436 
2437     r = IPersistFile_Load( pf, fullname, STGM_READ );
2438     if( SUCCEEDED( r ) )
2439     {
2440         /* If something fails (eg. Couldn't extract icon)
2441          * wait for parent process and try again
2442          */
2443         if( ! InvokeShellLinker( sl, fullname, bWait ) && bWait )
2444         {
2445             WaitForParentProcess();
2446             InvokeShellLinker( sl, fullname, FALSE );
2447         }
2448     }
2449     else
2450     {
2451         WINE_ERR("unable to load %s\n", wine_dbgstr_w(linkname));
2452     }
2453 
2454     IPersistFile_Release( pf );
2455     IShellLinkW_Release( sl );
2456 
2457     CoUninitialize();
2458 
2459     return !r;
2460 }
2461 
2462 static BOOL Process_URL( LPCWSTR urlname, BOOL bWait )
2463 {
2464     IUniformResourceLocatorW *url;
2465     IPersistFile *pf;
2466     HRESULT r;
2467     WCHAR fullname[MAX_PATH];
2468     DWORD len;
2469 
2470     WINE_TRACE("%s, wait %d\n", wine_dbgstr_w(urlname), bWait);
2471 
2472     if( !urlname[0] )
2473     {
2474         WINE_ERR("URL name missing\n");
2475         return 1;
2476     }
2477 
2478     len=GetFullPathNameW( urlname, MAX_PATH, fullname, NULL );
2479     if (len==0 || len>MAX_PATH)
2480     {
2481         WINE_ERR("couldn't get full path of URL file\n");
2482         return 1;
2483     }
2484 
2485     r = CoInitialize( NULL );
2486     if( FAILED( r ) )
2487     {
2488         WINE_ERR("CoInitialize failed\n");
2489         return 1;
2490     }
2491 
2492     r = CoCreateInstance( &CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER,
2493                           &IID_IUniformResourceLocatorW, (LPVOID *) &url );
2494     if( FAILED( r ) )
2495     {
2496         WINE_ERR("No IID_IUniformResourceLocatorW\n");
2497         return 1;
2498     }
2499 
2500     r = url->lpVtbl->QueryInterface( url, &IID_IPersistFile, (LPVOID*) &pf );
2501     if( FAILED( r ) )
2502     {
2503         WINE_ERR("No IID_IPersistFile\n");
2504         return 1;
2505     }
2506     r = IPersistFile_Load( pf, fullname, STGM_READ );
2507     if( SUCCEEDED( r ) )
2508     {
2509         /* If something fails (eg. Couldn't extract icon)
2510          * wait for parent process and try again
2511          */
2512         if( ! InvokeShellLinkerForURL( url, fullname, bWait ) && bWait )
2513         {
2514             WaitForParentProcess();
2515             InvokeShellLinkerForURL( url, fullname, FALSE );
2516         }
2517     }
2518 
2519     IPersistFile_Release( pf );
2520     url->lpVtbl->Release( url );
2521 
2522     CoUninitialize();
2523 
2524     return !r;
2525 }
2526 
2527 static void RefreshFileTypeAssociations(void)
2528 {
2529     HANDLE hSem = NULL;
2530     char *mime_dir = NULL;
2531     char *packages_dir = NULL;
2532     char *applications_dir = NULL;
2533     BOOL hasChanged;
2534 
2535     hSem = CreateSemaphoreA( NULL, 1, 1, "winemenubuilder_semaphore");
2536     if( WAIT_OBJECT_0 != MsgWaitForMultipleObjects( 1, &hSem, FALSE, INFINITE, QS_ALLINPUT ) )
2537     {
2538         WINE_ERR("failed wait for semaphore\n");
2539         CloseHandle(hSem);
2540         hSem = NULL;
2541         goto end;
2542     }
2543 
2544     mime_dir = heap_printf("%s/mime", xdg_data_dir);
2545     if (mime_dir == NULL)
2546     {
2547         WINE_ERR("out of memory\n");
2548         goto end;
2549     }
2550     create_directories(mime_dir);
2551 
2552     packages_dir = heap_printf("%s/packages", mime_dir);
2553     if (packages_dir == NULL)
2554     {
2555         WINE_ERR("out of memory\n");
2556         goto end;
2557     }
2558     create_directories(packages_dir);
2559 
2560     applications_dir = heap_printf("%s/applications", xdg_data_dir);
2561     if (applications_dir == NULL)
2562     {
2563         WINE_ERR("out of memory\n");
2564         goto end;
2565     }
2566     create_directories(applications_dir);
2567 
2568     hasChanged = generate_associations(xdg_data_dir, packages_dir, applications_dir);
2569     hasChanged |= cleanup_associations();
2570     if (hasChanged)
2571     {
2572         const char *argv[3];
2573 
2574         argv[0] = "update-mime-database";
2575         argv[1] = mime_dir;
2576         argv[2] = NULL;
2577         spawnvp( _P_NOWAIT, argv[0], argv );
2578 
2579         argv[0] = "update-desktop-database";
2580         argv[1] = applications_dir;
2581         spawnvp( _P_NOWAIT, argv[0], argv );
2582     }
2583 
2584 end:
2585     if (hSem)
2586     {
2587         ReleaseSemaphore(hSem, 1, NULL);
2588         CloseHandle(hSem);
2589     }
2590     HeapFree(GetProcessHeap(), 0, mime_dir);
2591     HeapFree(GetProcessHeap(), 0, packages_dir);
2592     HeapFree(GetProcessHeap(), 0, applications_dir);
2593 }
2594 
2595 static void cleanup_menus(void)
2596 {
2597     HKEY hkey;
2598 
2599     hkey = open_menus_reg_key();
2600     if (hkey)
2601     {
2602         int i;
2603         LSTATUS lret = ERROR_SUCCESS;
2604         for (i = 0; lret == ERROR_SUCCESS; )
2605         {
2606             char *value = NULL;
2607             char *data = NULL;
2608             DWORD valueSize = 4096;
2609             DWORD dataSize = 4096;
2610             while (1)
2611             {
2612                 lret = ERROR_OUTOFMEMORY;
2613                 value = HeapAlloc(GetProcessHeap(), 0, valueSize);
2614                 if (value == NULL)
2615                     break;
2616                 data = HeapAlloc(GetProcessHeap(), 0, dataSize);
2617                 if (data == NULL)
2618                     break;
2619                 lret = RegEnumValueA(hkey, i, value, &valueSize, NULL, NULL, (BYTE*)data, &dataSize);
2620                 if (lret == ERROR_SUCCESS || lret != ERROR_MORE_DATA)
2621                     break;
2622                 valueSize *= 2;
2623                 dataSize *= 2;
2624                 HeapFree(GetProcessHeap(), 0, value);
2625                 HeapFree(GetProcessHeap(), 0, data);
2626                 value = data = NULL;
2627             }
2628             if (lret == ERROR_SUCCESS)
2629             {
2630                 struct stat filestats;
2631                 if (stat(data, &filestats) < 0 && errno == ENOENT)
2632                 {
2633                     WINE_TRACE("removing menu related file %s\n", value);
2634                     remove(value);
2635                     RegDeleteValueA(hkey, value);
2636                 }
2637                 else
2638                     i++;
2639             }
2640             else if (lret != ERROR_NO_MORE_ITEMS)
2641                 WINE_WARN("error %d reading registry\n", lret);
2642             HeapFree(GetProcessHeap(), 0, value);
2643             HeapFree(GetProcessHeap(), 0, data);
2644         }
2645         RegCloseKey(hkey);
2646     }
2647     else
2648         WINE_ERR("error opening registry key, menu cleanup failed\n");
2649 }
2650 
2651 static CHAR *next_token( LPSTR *p )
2652 {
2653     LPSTR token = NULL, t = *p;
2654 
2655     if( !t )
2656         return NULL;
2657 
2658     while( t && !token )
2659     {
2660         switch( *t )
2661         {
2662         case ' ':
2663             t++;
2664             continue;
2665         case '"':
2666             /* unquote the token */
2667             token = ++t;
2668             t = strchr( token, '"' );
2669             if( t )
2670                  *t++ = 0;
2671             break;
2672         case 0:
2673             t = NULL;
2674             break;
2675         default:
2676             token = t;
2677             t = strchr( token, ' ' );
2678             if( t )
2679                  *t++ = 0;
2680             break;
2681         }
2682     }
2683     *p = t;
2684     return token;
2685 }
2686 
2687 static BOOL init_xdg(void)
2688 {
2689     WCHAR shellDesktopPath[MAX_PATH];
2690     HRESULT hr = SHGetFolderPathW(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, shellDesktopPath);
2691     if (SUCCEEDED(hr))
2692         xdg_desktop_dir = wine_get_unix_file_name(shellDesktopPath);
2693     if (xdg_desktop_dir == NULL)
2694     {
2695         WINE_ERR("error looking up the desktop directory\n");
2696         return FALSE;
2697     }
2698 
2699     if (getenv("XDG_CONFIG_HOME"))
2700         xdg_config_dir = heap_printf("%s/menus/applications-merged", getenv("XDG_CONFIG_HOME"));
2701     else
2702         xdg_config_dir = heap_printf("%s/.config/menus/applications-merged", getenv("HOME"));
2703     if (xdg_config_dir)
2704     {
2705         create_directories(xdg_config_dir);
2706         if (getenv("XDG_DATA_HOME"))
2707             xdg_data_dir = strdupA(getenv("XDG_DATA_HOME"));
2708         else
2709             xdg_data_dir = heap_printf("%s/.local/share", getenv("HOME"));
2710         if (xdg_data_dir)
2711         {
2712             char *buffer;
2713             create_directories(xdg_data_dir);
2714             buffer = heap_printf("%s/desktop-directories", xdg_data_dir);
2715             if (buffer)
2716             {
2717                 mkdir(buffer, 0777);
2718                 HeapFree(GetProcessHeap(), 0, buffer);
2719             }
2720             return TRUE;
2721         }
2722         HeapFree(GetProcessHeap(), 0, xdg_config_dir);
2723     }
2724     WINE_ERR("out of memory\n");
2725     return FALSE;
2726 }
2727 
2728 /***********************************************************************
2729  *
2730  *           WinMain
2731  */
2732 int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
2733 {
2734     LPSTR token = NULL, p;
2735     BOOL bWait = FALSE;
2736     BOOL bURL = FALSE;
2737     int ret = 0;
2738 
2739     if (!init_xdg())
2740         return 1;
2741 
2742     for( p = cmdline; p && *p; )
2743     {
2744         token = next_token( &p );
2745         if( !token )
2746             break;
2747         if( !lstrcmpA( token, "-a" ) )
2748         {
2749             RefreshFileTypeAssociations();
2750             continue;
2751         }
2752         if( !lstrcmpA( token, "-r" ) )
2753         {
2754             cleanup_menus();
2755             continue;
2756         }
2757         if( !lstrcmpA( token, "-w" ) )
2758             bWait = TRUE;
2759         else if ( !lstrcmpA( token, "-u" ) )
2760             bURL = TRUE;
2761         else if( token[0] == '-' )
2762         {
2763             WINE_ERR( "unknown option %s\n",token);
2764         }
2765         else
2766         {
2767             WCHAR link[MAX_PATH];
2768             BOOL bRet;
2769 
2770             MultiByteToWideChar( CP_ACP, 0, token, -1, link, sizeof(link)/sizeof(WCHAR) );
2771             if (bURL)
2772                 bRet = Process_URL( link, bWait );
2773             else
2774                 bRet = Process_Link( link, bWait );
2775             if (!bRet)
2776             {
2777                 WINE_ERR( "failed to build menu item for %s\n",token);
2778                 ret = 1;
2779             }
2780         }
2781     }
2782 
2783     return ret;
2784 }
2785 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.