winemenubuilder.c 86 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867
  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. * Associate applications under HKCR\Applications to open any MIME type
  56. * (by associating with application/octet-stream, or how?).
  57. * Clean up fd.o MIME types when they are deleted in Windows, their icons
  58. * too. Very hard - once we associate them with fd.o, we can't tell whether
  59. * they are ours or not, and the extension <-> MIME type mapping isn't
  60. * one-to-one either.
  61. * Wine's HKCR is broken - it doesn't merge HKCU\Software\Classes, so apps
  62. * that write associations there won't associate (#17019).
  63. */
  64. #include <ctype.h>
  65. #include <stdio.h>
  66. #include <string.h>
  67. #include <errno.h>
  68. #include <stdarg.h>
  69. #define COBJMACROS
  70. #define NONAMELESSUNION
  71. #include <windows.h>
  72. #include <winternl.h>
  73. #include <shlobj.h>
  74. #include <objidl.h>
  75. #include <shlguid.h>
  76. #include <appmgmt.h>
  77. #include <tlhelp32.h>
  78. #include <intshcut.h>
  79. #include <shlwapi.h>
  80. #include <initguid.h>
  81. #include <wincodec.h>
  82. #include "wine/debug.h"
  83. #include "wine/list.h"
  84. #include "wine/rbtree.h"
  85. WINE_DEFAULT_DEBUG_CHANNEL(menubuilder);
  86. #define in_desktop_dir(csidl) ((csidl)==CSIDL_DESKTOPDIRECTORY || \
  87. (csidl)==CSIDL_COMMON_DESKTOPDIRECTORY)
  88. #define in_startmenu(csidl) ((csidl)==CSIDL_STARTMENU || \
  89. (csidl)==CSIDL_COMMON_STARTMENU)
  90. #define IS_OPTION_TRUE(ch) \
  91. ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
  92. /* link file formats */
  93. #include "pshpack1.h"
  94. typedef struct
  95. {
  96. BYTE bWidth;
  97. BYTE bHeight;
  98. BYTE bColorCount;
  99. BYTE bReserved;
  100. WORD wPlanes;
  101. WORD wBitCount;
  102. DWORD dwBytesInRes;
  103. WORD nID;
  104. } GRPICONDIRENTRY;
  105. typedef struct
  106. {
  107. WORD idReserved;
  108. WORD idType;
  109. WORD idCount;
  110. GRPICONDIRENTRY idEntries[1];
  111. } GRPICONDIR;
  112. typedef struct
  113. {
  114. BYTE bWidth;
  115. BYTE bHeight;
  116. BYTE bColorCount;
  117. BYTE bReserved;
  118. WORD wPlanes;
  119. WORD wBitCount;
  120. DWORD dwBytesInRes;
  121. DWORD dwImageOffset;
  122. } ICONDIRENTRY;
  123. typedef struct
  124. {
  125. WORD idReserved;
  126. WORD idType;
  127. WORD idCount;
  128. } ICONDIR;
  129. typedef struct
  130. {
  131. WORD offset;
  132. WORD length;
  133. WORD flags;
  134. WORD id;
  135. WORD handle;
  136. WORD usage;
  137. } NE_NAMEINFO;
  138. typedef struct
  139. {
  140. WORD type_id;
  141. WORD count;
  142. DWORD resloader;
  143. } NE_TYPEINFO;
  144. #define NE_RSCTYPE_ICON 0x8003
  145. #define NE_RSCTYPE_GROUP_ICON 0x800e
  146. #include "poppack.h"
  147. typedef struct
  148. {
  149. HRSRC *pResInfo;
  150. int nIndex;
  151. } ENUMRESSTRUCT;
  152. struct xdg_mime_type
  153. {
  154. WCHAR *mimeType;
  155. WCHAR *glob;
  156. struct list entry;
  157. };
  158. struct rb_string_entry
  159. {
  160. WCHAR *string;
  161. struct wine_rb_entry entry;
  162. };
  163. static WCHAR *xdg_menu_dir;
  164. static WCHAR *xdg_data_dir;
  165. static WCHAR xdg_desktop_dir[MAX_PATH];
  166. /* Utility routines */
  167. static unsigned short crc16(const WCHAR *string)
  168. {
  169. unsigned short crc = 0;
  170. int i, j, xor_poly;
  171. for (i = 0; string[i] != 0; i++)
  172. {
  173. WCHAR c = string[i];
  174. for (j = 0; j < 16; c >>= 1, j++)
  175. {
  176. xor_poly = (c ^ crc) & 1;
  177. crc >>= 1;
  178. if (xor_poly)
  179. crc ^= 0xa001;
  180. }
  181. }
  182. return crc;
  183. }
  184. static void *xmalloc( size_t size )
  185. {
  186. void *ret = HeapAlloc( GetProcessHeap(), 0, size );
  187. if (!ret)
  188. {
  189. ERR( "out of memory\n" );
  190. ExitProcess(1);
  191. }
  192. return ret;
  193. }
  194. static void *xrealloc( void *ptr, size_t size )
  195. {
  196. if (!ptr) return xmalloc( size );
  197. ptr = HeapReAlloc( GetProcessHeap(), 0, ptr, size );
  198. if (!ptr)
  199. {
  200. ERR( "out of memory\n" );
  201. ExitProcess(1);
  202. }
  203. return ptr;
  204. }
  205. static WCHAR *xwcsdup( const WCHAR *str )
  206. {
  207. WCHAR *ret;
  208. if (!str) return NULL;
  209. ret = xmalloc( (lstrlenW(str) + 1) * sizeof(WCHAR) );
  210. lstrcpyW( ret, str );
  211. return ret;
  212. }
  213. static void heap_free( void *ptr )
  214. {
  215. HeapFree( GetProcessHeap(), 0, ptr );
  216. }
  217. static WCHAR * WINAPIV heap_wprintf(const WCHAR *format, ...)
  218. {
  219. va_list args;
  220. int size = 4096;
  221. WCHAR *buffer;
  222. int n;
  223. while (1)
  224. {
  225. buffer = xmalloc(size * sizeof(WCHAR));
  226. va_start(args, format);
  227. n = _vsnwprintf(buffer, size, format, args);
  228. va_end(args);
  229. if (n == -1)
  230. size *= 2;
  231. else if (n >= size)
  232. size = n + 1;
  233. else
  234. return buffer;
  235. heap_free(buffer);
  236. }
  237. }
  238. static int winemenubuilder_rb_string_compare(const void *key, const struct wine_rb_entry *entry)
  239. {
  240. const struct rb_string_entry *t = WINE_RB_ENTRY_VALUE(entry, const struct rb_string_entry, entry);
  241. return wcscmp((WCHAR *)key, t->string);
  242. }
  243. static void winemenubuilder_rb_destroy(struct wine_rb_entry *entry, void *context)
  244. {
  245. struct rb_string_entry *t = WINE_RB_ENTRY_VALUE(entry, struct rb_string_entry, entry);
  246. heap_free(t->string);
  247. heap_free(t);
  248. }
  249. static BOOL create_directories(WCHAR *directory)
  250. {
  251. WCHAR *p = PathSkipRootW( directory );
  252. for ( ; p && *p; p++)
  253. {
  254. if (*p == '\\')
  255. {
  256. *p = 0;
  257. CreateDirectoryW( directory, NULL );
  258. *p = '\\';
  259. }
  260. }
  261. return CreateDirectoryW( directory, NULL ) || GetLastError() == ERROR_ALREADY_EXISTS;
  262. }
  263. static char* wchars_to_utf8_chars(LPCWSTR string)
  264. {
  265. char *ret;
  266. INT size = WideCharToMultiByte(CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL);
  267. ret = xmalloc(size);
  268. WideCharToMultiByte(CP_UTF8, 0, string, -1, ret, size, NULL, NULL);
  269. return ret;
  270. }
  271. static WCHAR* utf8_chars_to_wchars(LPCSTR string)
  272. {
  273. WCHAR *ret;
  274. INT size = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0);
  275. ret = xmalloc(size * sizeof(WCHAR));
  276. MultiByteToWideChar(CP_UTF8, 0, string, -1, ret, size);
  277. return ret;
  278. }
  279. static char *wchars_to_xml_text(const WCHAR *string)
  280. {
  281. int i, pos;
  282. char *text = wchars_to_utf8_chars( string );
  283. char *ret = xmalloc( 6 * strlen(text) + 1 );
  284. for (i = pos = 0; text[i]; i++)
  285. {
  286. if (text[i] == '&')
  287. pos += sprintf(ret + pos, "&amp;");
  288. else if (text[i] == '<')
  289. pos += sprintf(ret + pos, "&lt;");
  290. else if (text[i] == '>')
  291. pos += sprintf(ret + pos, "&gt;");
  292. else if (text[i] == '\'')
  293. pos += sprintf(ret + pos, "&apos;");
  294. else if (text[i] == '"')
  295. pos += sprintf(ret + pos, "&quot;");
  296. else
  297. ret[pos++] = text[i];
  298. }
  299. heap_free( text );
  300. ret[pos] = 0;
  301. return ret;
  302. }
  303. /* Icon extraction routines
  304. *
  305. * FIXME: should use PrivateExtractIcons and friends
  306. * FIXME: should not use stdio
  307. */
  308. static HRESULT convert_to_native_icon(IStream *icoFile, int *indices, int numIndices,
  309. const CLSID *outputFormat, const WCHAR *outputFileName)
  310. {
  311. IWICImagingFactory *factory = NULL;
  312. IWICBitmapDecoder *decoder = NULL;
  313. IWICBitmapEncoder *encoder = NULL;
  314. IStream *outputFile = NULL;
  315. int i;
  316. HRESULT hr = E_FAIL;
  317. hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
  318. &IID_IWICImagingFactory, (void**)&factory);
  319. if (FAILED(hr))
  320. {
  321. WINE_ERR("error 0x%08X creating IWICImagingFactory\n", hr);
  322. goto end;
  323. }
  324. hr = IWICImagingFactory_CreateDecoderFromStream(factory, icoFile, NULL,
  325. WICDecodeMetadataCacheOnDemand, &decoder);
  326. if (FAILED(hr))
  327. {
  328. WINE_ERR("error 0x%08X creating IWICBitmapDecoder\n", hr);
  329. goto end;
  330. }
  331. hr = CoCreateInstance(outputFormat, NULL, CLSCTX_INPROC_SERVER,
  332. &IID_IWICBitmapEncoder, (void**)&encoder);
  333. if (FAILED(hr))
  334. {
  335. WINE_ERR("error 0x%08X creating bitmap encoder\n", hr);
  336. goto end;
  337. }
  338. hr = SHCreateStreamOnFileW(outputFileName, STGM_CREATE | STGM_WRITE, &outputFile);
  339. if (FAILED(hr))
  340. {
  341. WINE_ERR("error 0x%08X creating output file %s\n", hr, wine_dbgstr_w(outputFileName));
  342. goto end;
  343. }
  344. hr = IWICBitmapEncoder_Initialize(encoder, outputFile, WICBitmapEncoderNoCache);
  345. if (FAILED(hr))
  346. {
  347. WINE_ERR("error 0x%08X initializing encoder\n", hr);
  348. goto end;
  349. }
  350. for (i = 0; i < numIndices; i++)
  351. {
  352. IWICBitmapFrameDecode *sourceFrame = NULL;
  353. IWICBitmapSource *sourceBitmap = NULL;
  354. IWICBitmapFrameEncode *dstFrame = NULL;
  355. IPropertyBag2 *options = NULL;
  356. UINT width, height;
  357. hr = IWICBitmapDecoder_GetFrame(decoder, indices[i], &sourceFrame);
  358. if (FAILED(hr))
  359. {
  360. WINE_ERR("error 0x%08X getting frame %d\n", hr, indices[i]);
  361. goto endloop;
  362. }
  363. hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, (IWICBitmapSource*)sourceFrame, &sourceBitmap);
  364. if (FAILED(hr))
  365. {
  366. WINE_ERR("error 0x%08X converting bitmap to 32bppBGRA\n", hr);
  367. goto endloop;
  368. }
  369. hr = IWICBitmapEncoder_CreateNewFrame(encoder, &dstFrame, &options);
  370. if (FAILED(hr))
  371. {
  372. WINE_ERR("error 0x%08X creating encoder frame\n", hr);
  373. goto endloop;
  374. }
  375. hr = IWICBitmapFrameEncode_Initialize(dstFrame, options);
  376. if (FAILED(hr))
  377. {
  378. WINE_ERR("error 0x%08X initializing encoder frame\n", hr);
  379. goto endloop;
  380. }
  381. hr = IWICBitmapSource_GetSize(sourceBitmap, &width, &height);
  382. if (FAILED(hr))
  383. {
  384. WINE_ERR("error 0x%08X getting source bitmap size\n", hr);
  385. goto endloop;
  386. }
  387. hr = IWICBitmapFrameEncode_SetSize(dstFrame, width, height);
  388. if (FAILED(hr))
  389. {
  390. WINE_ERR("error 0x%08X setting destination bitmap size\n", hr);
  391. goto endloop;
  392. }
  393. hr = IWICBitmapFrameEncode_SetResolution(dstFrame, 96, 96);
  394. if (FAILED(hr))
  395. {
  396. WINE_ERR("error 0x%08X setting destination bitmap resolution\n", hr);
  397. goto endloop;
  398. }
  399. hr = IWICBitmapFrameEncode_WriteSource(dstFrame, sourceBitmap, NULL);
  400. if (FAILED(hr))
  401. {
  402. WINE_ERR("error 0x%08X copying bitmaps\n", hr);
  403. goto endloop;
  404. }
  405. hr = IWICBitmapFrameEncode_Commit(dstFrame);
  406. if (FAILED(hr))
  407. {
  408. WINE_ERR("error 0x%08X committing frame\n", hr);
  409. goto endloop;
  410. }
  411. endloop:
  412. if (sourceFrame)
  413. IWICBitmapFrameDecode_Release(sourceFrame);
  414. if (sourceBitmap)
  415. IWICBitmapSource_Release(sourceBitmap);
  416. if (dstFrame)
  417. IWICBitmapFrameEncode_Release(dstFrame);
  418. if (options)
  419. IPropertyBag2_Release(options);
  420. }
  421. hr = IWICBitmapEncoder_Commit(encoder);
  422. if (FAILED(hr))
  423. {
  424. WINE_ERR("error 0x%08X committing encoder\n", hr);
  425. goto end;
  426. }
  427. end:
  428. if (factory)
  429. IWICImagingFactory_Release(factory);
  430. if (decoder)
  431. IWICBitmapDecoder_Release(decoder);
  432. if (encoder)
  433. IWICBitmapEncoder_Release(encoder);
  434. if (outputFile)
  435. IStream_Release(outputFile);
  436. return hr;
  437. }
  438. struct IconData16 {
  439. BYTE *fileBytes;
  440. DWORD fileSize;
  441. NE_TYPEINFO *iconResources;
  442. WORD alignmentShiftCount;
  443. };
  444. static int populate_module16_icons(struct IconData16 *iconData16, GRPICONDIR *grpIconDir, ICONDIRENTRY *iconDirEntries, BYTE *icons, SIZE_T *iconOffset)
  445. {
  446. int i, j;
  447. int validEntries = 0;
  448. for (i = 0; i < grpIconDir->idCount; i++)
  449. {
  450. BYTE *iconPtr = (BYTE*)iconData16->iconResources;
  451. NE_NAMEINFO *matchingIcon = NULL;
  452. iconPtr += sizeof(NE_TYPEINFO);
  453. for (j = 0; j < iconData16->iconResources->count; j++)
  454. {
  455. NE_NAMEINFO *iconInfo = (NE_NAMEINFO*)iconPtr;
  456. if ((iconPtr + sizeof(NE_NAMEINFO)) > (iconData16->fileBytes + iconData16->fileSize))
  457. {
  458. WINE_WARN("file too small for icon NE_NAMEINFO\n");
  459. break;
  460. }
  461. if (iconInfo->id == (0x8000 | grpIconDir->idEntries[i].nID))
  462. {
  463. matchingIcon = iconInfo;
  464. break;
  465. }
  466. iconPtr += sizeof(NE_NAMEINFO);
  467. }
  468. if (matchingIcon == NULL)
  469. continue;
  470. if (((matchingIcon->offset << iconData16->alignmentShiftCount) + grpIconDir->idEntries[i].dwBytesInRes) > iconData16->fileSize)
  471. {
  472. WINE_WARN("file too small for icon contents\n");
  473. break;
  474. }
  475. iconDirEntries[validEntries].bWidth = grpIconDir->idEntries[i].bWidth;
  476. iconDirEntries[validEntries].bHeight = grpIconDir->idEntries[i].bHeight;
  477. iconDirEntries[validEntries].bColorCount = grpIconDir->idEntries[i].bColorCount;
  478. iconDirEntries[validEntries].bReserved = grpIconDir->idEntries[i].bReserved;
  479. iconDirEntries[validEntries].wPlanes = grpIconDir->idEntries[i].wPlanes;
  480. iconDirEntries[validEntries].wBitCount = grpIconDir->idEntries[i].wBitCount;
  481. iconDirEntries[validEntries].dwBytesInRes = grpIconDir->idEntries[i].dwBytesInRes;
  482. iconDirEntries[validEntries].dwImageOffset = *iconOffset;
  483. validEntries++;
  484. memcpy(&icons[*iconOffset], &iconData16->fileBytes[matchingIcon->offset << iconData16->alignmentShiftCount], grpIconDir->idEntries[i].dwBytesInRes);
  485. *iconOffset += grpIconDir->idEntries[i].dwBytesInRes;
  486. }
  487. return validEntries;
  488. }
  489. static int populate_module_icons(HMODULE hModule, GRPICONDIR *grpIconDir, ICONDIRENTRY *iconDirEntries, BYTE *icons, SIZE_T *iconOffset)
  490. {
  491. int i;
  492. int validEntries = 0;
  493. for (i = 0; i < grpIconDir->idCount; i++)
  494. {
  495. HRSRC hResInfo;
  496. LPCWSTR lpName = MAKEINTRESOURCEW(grpIconDir->idEntries[i].nID);
  497. if ((hResInfo = FindResourceW(hModule, lpName, (LPCWSTR)RT_ICON)))
  498. {
  499. HGLOBAL hResData;
  500. if ((hResData = LoadResource(hModule, hResInfo)))
  501. {
  502. BITMAPINFO *pIcon;
  503. DWORD size = min( grpIconDir->idEntries[i].dwBytesInRes, ((IMAGE_RESOURCE_DATA_ENTRY *)hResInfo)->Size );
  504. if ((pIcon = LockResource(hResData)))
  505. {
  506. iconDirEntries[validEntries].bWidth = grpIconDir->idEntries[i].bWidth;
  507. iconDirEntries[validEntries].bHeight = grpIconDir->idEntries[i].bHeight;
  508. iconDirEntries[validEntries].bColorCount = grpIconDir->idEntries[i].bColorCount;
  509. iconDirEntries[validEntries].bReserved = grpIconDir->idEntries[i].bReserved;
  510. iconDirEntries[validEntries].wPlanes = grpIconDir->idEntries[i].wPlanes;
  511. iconDirEntries[validEntries].wBitCount = grpIconDir->idEntries[i].wBitCount;
  512. iconDirEntries[validEntries].dwBytesInRes = size;
  513. iconDirEntries[validEntries].dwImageOffset = *iconOffset;
  514. validEntries++;
  515. memcpy(&icons[*iconOffset], pIcon, size);
  516. *iconOffset += size;
  517. }
  518. FreeResource(hResData);
  519. }
  520. }
  521. }
  522. return validEntries;
  523. }
  524. static IStream *add_module_icons_to_stream(struct IconData16 *iconData16, HMODULE hModule, GRPICONDIR *grpIconDir)
  525. {
  526. int i;
  527. SIZE_T iconsSize = 0;
  528. BYTE *icons = NULL;
  529. ICONDIRENTRY *iconDirEntries = NULL;
  530. IStream *stream = NULL;
  531. HRESULT hr = E_FAIL;
  532. ULONG bytesWritten;
  533. ICONDIR iconDir;
  534. SIZE_T iconOffset;
  535. int validEntries = 0;
  536. LARGE_INTEGER zero;
  537. for (i = 0; i < grpIconDir->idCount; i++)
  538. iconsSize += grpIconDir->idEntries[i].dwBytesInRes;
  539. icons = xmalloc(iconsSize);
  540. iconDirEntries = xmalloc(grpIconDir->idCount*sizeof(ICONDIRENTRY));
  541. hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
  542. if (FAILED(hr))
  543. {
  544. WINE_ERR("error creating icon stream\n");
  545. goto end;
  546. }
  547. iconOffset = 0;
  548. if (iconData16)
  549. validEntries = populate_module16_icons(iconData16, grpIconDir, iconDirEntries, icons, &iconOffset);
  550. else if (hModule)
  551. validEntries = populate_module_icons(hModule, grpIconDir, iconDirEntries, icons, &iconOffset);
  552. if (validEntries == 0)
  553. {
  554. WINE_ERR("no valid icon entries\n");
  555. goto end;
  556. }
  557. iconDir.idReserved = 0;
  558. iconDir.idType = 1;
  559. iconDir.idCount = validEntries;
  560. hr = IStream_Write(stream, &iconDir, sizeof(iconDir), &bytesWritten);
  561. if (FAILED(hr) || bytesWritten != sizeof(iconDir))
  562. {
  563. WINE_ERR("error 0x%08X writing icon stream\n", hr);
  564. goto end;
  565. }
  566. for (i = 0; i < validEntries; i++)
  567. iconDirEntries[i].dwImageOffset += sizeof(ICONDIR) + validEntries*sizeof(ICONDIRENTRY);
  568. hr = IStream_Write(stream, iconDirEntries, validEntries*sizeof(ICONDIRENTRY), &bytesWritten);
  569. if (FAILED(hr) || bytesWritten != validEntries*sizeof(ICONDIRENTRY))
  570. {
  571. WINE_ERR("error 0x%08X writing icon dir entries to stream\n", hr);
  572. goto end;
  573. }
  574. hr = IStream_Write(stream, icons, iconOffset, &bytesWritten);
  575. if (FAILED(hr) || bytesWritten != iconOffset)
  576. {
  577. WINE_ERR("error 0x%08X writing icon images to stream\n", hr);
  578. goto end;
  579. }
  580. zero.QuadPart = 0;
  581. hr = IStream_Seek(stream, zero, STREAM_SEEK_SET, NULL);
  582. end:
  583. heap_free(icons);
  584. heap_free(iconDirEntries);
  585. if (FAILED(hr) && stream != NULL)
  586. {
  587. IStream_Release(stream);
  588. stream = NULL;
  589. }
  590. return stream;
  591. }
  592. static HRESULT open_module16_icon(LPCWSTR szFileName, int nIndex, IStream **ppStream)
  593. {
  594. HANDLE hFile = INVALID_HANDLE_VALUE;
  595. HANDLE hFileMapping = NULL;
  596. DWORD fileSize;
  597. BYTE *fileBytes = NULL;
  598. IMAGE_DOS_HEADER *dosHeader;
  599. IMAGE_OS2_HEADER *neHeader;
  600. BYTE *rsrcTab;
  601. NE_TYPEINFO *iconGroupResources;
  602. NE_TYPEINFO *iconResources;
  603. NE_NAMEINFO *iconDirPtr;
  604. GRPICONDIR *iconDir;
  605. WORD alignmentShiftCount;
  606. struct IconData16 iconData16;
  607. HRESULT hr = E_FAIL;
  608. hFile = CreateFileW(szFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
  609. OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL);
  610. if (hFile == INVALID_HANDLE_VALUE)
  611. {
  612. WINE_WARN("opening %s failed with error %d\n", wine_dbgstr_w(szFileName), GetLastError());
  613. goto end;
  614. }
  615. hFileMapping = CreateFileMappingW(hFile, NULL, PAGE_READONLY | SEC_COMMIT, 0, 0, NULL);
  616. if (hFileMapping == NULL)
  617. {
  618. WINE_WARN("CreateFileMapping failed, error %d\n", GetLastError());
  619. goto end;
  620. }
  621. fileSize = GetFileSize(hFile, NULL);
  622. fileBytes = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
  623. if (fileBytes == NULL)
  624. {
  625. WINE_WARN("MapViewOfFile failed, error %d\n", GetLastError());
  626. goto end;
  627. }
  628. dosHeader = (IMAGE_DOS_HEADER*)fileBytes;
  629. if (sizeof(IMAGE_DOS_HEADER) >= fileSize || dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
  630. {
  631. WINE_WARN("file too small for MZ header\n");
  632. goto end;
  633. }
  634. neHeader = (IMAGE_OS2_HEADER*)(fileBytes + dosHeader->e_lfanew);
  635. if ((((BYTE*)neHeader) + sizeof(IMAGE_OS2_HEADER)) > (fileBytes + fileSize) ||
  636. neHeader->ne_magic != IMAGE_OS2_SIGNATURE)
  637. {
  638. WINE_WARN("file too small for NE header\n");
  639. goto end;
  640. }
  641. rsrcTab = ((BYTE*)neHeader) + neHeader->ne_rsrctab;
  642. if ((rsrcTab + 2) > (fileBytes + fileSize))
  643. {
  644. WINE_WARN("file too small for resource table\n");
  645. goto end;
  646. }
  647. alignmentShiftCount = *(WORD*)rsrcTab;
  648. rsrcTab += 2;
  649. iconGroupResources = NULL;
  650. iconResources = NULL;
  651. for (;;)
  652. {
  653. NE_TYPEINFO *neTypeInfo = (NE_TYPEINFO*)rsrcTab;
  654. if ((rsrcTab + sizeof(NE_TYPEINFO)) > (fileBytes + fileSize))
  655. {
  656. WINE_WARN("file too small for resource table\n");
  657. goto end;
  658. }
  659. if (neTypeInfo->type_id == 0)
  660. break;
  661. else if (neTypeInfo->type_id == NE_RSCTYPE_GROUP_ICON)
  662. iconGroupResources = neTypeInfo;
  663. else if (neTypeInfo->type_id == NE_RSCTYPE_ICON)
  664. iconResources = neTypeInfo;
  665. rsrcTab += sizeof(NE_TYPEINFO) + neTypeInfo->count*sizeof(NE_NAMEINFO);
  666. }
  667. if (iconGroupResources == NULL)
  668. {
  669. WINE_WARN("no group icon resource type found\n");
  670. goto end;
  671. }
  672. if (iconResources == NULL)
  673. {
  674. WINE_WARN("no icon resource type found\n");
  675. goto end;
  676. }
  677. if (nIndex >= iconGroupResources->count)
  678. {
  679. WINE_WARN("icon index out of range\n");
  680. goto end;
  681. }
  682. iconDirPtr = (NE_NAMEINFO*)(((BYTE*)iconGroupResources) + sizeof(NE_TYPEINFO) + nIndex*sizeof(NE_NAMEINFO));
  683. if ((((BYTE*)iconDirPtr) + sizeof(NE_NAMEINFO)) > (fileBytes + fileSize))
  684. {
  685. WINE_WARN("file too small for icon group NE_NAMEINFO\n");
  686. goto end;
  687. }
  688. iconDir = (GRPICONDIR*)(fileBytes + (iconDirPtr->offset << alignmentShiftCount));
  689. if ((((BYTE*)iconDir) + sizeof(GRPICONDIR) + iconDir->idCount*sizeof(GRPICONDIRENTRY)) > (fileBytes + fileSize))
  690. {
  691. WINE_WARN("file too small for GRPICONDIR\n");
  692. goto end;
  693. }
  694. iconData16.fileBytes = fileBytes;
  695. iconData16.fileSize = fileSize;
  696. iconData16.iconResources = iconResources;
  697. iconData16.alignmentShiftCount = alignmentShiftCount;
  698. *ppStream = add_module_icons_to_stream(&iconData16, NULL, iconDir);
  699. if (*ppStream)
  700. hr = S_OK;
  701. end:
  702. if (hFile != INVALID_HANDLE_VALUE)
  703. CloseHandle(hFile);
  704. if (hFileMapping != NULL)
  705. CloseHandle(hFileMapping);
  706. if (fileBytes != NULL)
  707. UnmapViewOfFile(fileBytes);
  708. return hr;
  709. }
  710. static BOOL CALLBACK EnumResNameProc(HMODULE hModule, LPCWSTR lpszType, LPWSTR lpszName, LONG_PTR lParam)
  711. {
  712. ENUMRESSTRUCT *sEnumRes = (ENUMRESSTRUCT *) lParam;
  713. if (!sEnumRes->nIndex--)
  714. {
  715. *sEnumRes->pResInfo = FindResourceW(hModule, lpszName, (LPCWSTR)RT_GROUP_ICON);
  716. return FALSE;
  717. }
  718. else
  719. return TRUE;
  720. }
  721. static HRESULT open_module_icon(LPCWSTR szFileName, int nIndex, IStream **ppStream)
  722. {
  723. HMODULE hModule;
  724. HRSRC hResInfo;
  725. HGLOBAL hResData;
  726. GRPICONDIR *pIconDir;
  727. ENUMRESSTRUCT sEnumRes;
  728. HRESULT hr = E_FAIL;
  729. hModule = LoadLibraryExW(szFileName, 0, LOAD_LIBRARY_AS_DATAFILE);
  730. if (!hModule)
  731. {
  732. if (GetLastError() == ERROR_BAD_EXE_FORMAT)
  733. return open_module16_icon(szFileName, nIndex, ppStream);
  734. else
  735. {
  736. WINE_WARN("LoadLibraryExW (%s) failed, error %d\n",
  737. wine_dbgstr_w(szFileName), GetLastError());
  738. return HRESULT_FROM_WIN32(GetLastError());
  739. }
  740. }
  741. if (nIndex < 0)
  742. {
  743. hResInfo = FindResourceW(hModule, MAKEINTRESOURCEW(-nIndex), (LPCWSTR)RT_GROUP_ICON);
  744. WINE_TRACE("FindResourceW (%s) called, return %p, error %d\n",
  745. wine_dbgstr_w(szFileName), hResInfo, GetLastError());
  746. }
  747. else
  748. {
  749. hResInfo=NULL;
  750. sEnumRes.pResInfo = &hResInfo;
  751. sEnumRes.nIndex = nIndex;
  752. if (!EnumResourceNamesW(hModule, (LPCWSTR)RT_GROUP_ICON,
  753. EnumResNameProc, (LONG_PTR)&sEnumRes) &&
  754. sEnumRes.nIndex != -1)
  755. {
  756. WINE_TRACE("EnumResourceNamesW failed, error %d\n", GetLastError());
  757. }
  758. }
  759. if (hResInfo)
  760. {
  761. if ((hResData = LoadResource(hModule, hResInfo)))
  762. {
  763. if ((pIconDir = LockResource(hResData)))
  764. {
  765. *ppStream = add_module_icons_to_stream(0, hModule, pIconDir);
  766. if (*ppStream)
  767. hr = S_OK;
  768. }
  769. FreeResource(hResData);
  770. }
  771. }
  772. else
  773. {
  774. WINE_WARN("found no icon\n");
  775. FreeLibrary(hModule);
  776. return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
  777. }
  778. FreeLibrary(hModule);
  779. return hr;
  780. }
  781. static HRESULT read_ico_direntries(IStream *icoStream, ICONDIRENTRY **ppIconDirEntries, int *numEntries)
  782. {
  783. ICONDIR iconDir;
  784. ULONG bytesRead;
  785. HRESULT hr;
  786. *ppIconDirEntries = NULL;
  787. hr = IStream_Read(icoStream, &iconDir, sizeof(ICONDIR), &bytesRead);
  788. if (FAILED(hr) || bytesRead != sizeof(ICONDIR) ||
  789. (iconDir.idReserved != 0) || (iconDir.idType != 1))
  790. {
  791. WINE_WARN("Invalid ico file format (hr=0x%08X, bytesRead=%d)\n", hr, bytesRead);
  792. hr = E_FAIL;
  793. goto end;
  794. }
  795. *numEntries = iconDir.idCount;
  796. *ppIconDirEntries = xmalloc(sizeof(ICONDIRENTRY)*iconDir.idCount);
  797. hr = IStream_Read(icoStream, *ppIconDirEntries, sizeof(ICONDIRENTRY)*iconDir.idCount, &bytesRead);
  798. if (FAILED(hr) || bytesRead != sizeof(ICONDIRENTRY)*iconDir.idCount)
  799. {
  800. if (SUCCEEDED(hr)) hr = E_FAIL;
  801. goto end;
  802. }
  803. end:
  804. if (FAILED(hr))
  805. heap_free(*ppIconDirEntries);
  806. return hr;
  807. }
  808. static HRESULT validate_ico(IStream **ppStream, ICONDIRENTRY **ppIconDirEntries, int *numEntries)
  809. {
  810. HRESULT hr;
  811. hr = read_ico_direntries(*ppStream, ppIconDirEntries, numEntries);
  812. if (SUCCEEDED(hr))
  813. {
  814. if (*numEntries)
  815. return hr;
  816. heap_free(*ppIconDirEntries);
  817. *ppIconDirEntries = NULL;
  818. }
  819. IStream_Release(*ppStream);
  820. *ppStream = NULL;
  821. return E_FAIL;
  822. }
  823. static HRESULT write_native_icon(IStream *iconStream, ICONDIRENTRY *pIconDirEntry,
  824. int numEntries, const WCHAR *icon_name)
  825. {
  826. int nMax = 0, nMaxBits = 0;
  827. int nIndex = 0;
  828. int i;
  829. LARGE_INTEGER position;
  830. HRESULT hr;
  831. for (i = 0; i < numEntries; i++)
  832. {
  833. WINE_TRACE("[%d]: %d x %d @ %d\n", i, pIconDirEntry[i].bWidth, pIconDirEntry[i].bHeight, pIconDirEntry[i].wBitCount);
  834. if (pIconDirEntry[i].wBitCount >= nMaxBits &&
  835. (pIconDirEntry[i].bHeight * pIconDirEntry[i].bWidth) >= nMax)
  836. {
  837. nIndex = i;
  838. nMax = pIconDirEntry[i].bHeight * pIconDirEntry[i].bWidth;
  839. nMaxBits = pIconDirEntry[i].wBitCount;
  840. }
  841. }
  842. WINE_TRACE("Selected: %d\n", nIndex);
  843. position.QuadPart = 0;
  844. hr = IStream_Seek(iconStream, position, STREAM_SEEK_SET, NULL);
  845. if (FAILED(hr)) return hr;
  846. return convert_to_native_icon(iconStream, &nIndex, 1, &CLSID_WICPngEncoder, icon_name);
  847. }
  848. static WCHAR* assoc_query(ASSOCSTR assocStr, LPCWSTR name, LPCWSTR extra)
  849. {
  850. HRESULT hr;
  851. WCHAR *value = NULL;
  852. DWORD size = 0;
  853. hr = AssocQueryStringW(0, assocStr, name, extra, NULL, &size);
  854. if (SUCCEEDED(hr))
  855. {
  856. value = xmalloc(size * sizeof(WCHAR));
  857. hr = AssocQueryStringW(0, assocStr, name, extra, value, &size);
  858. if (FAILED(hr))
  859. {
  860. heap_free(value);
  861. value = NULL;
  862. }
  863. }
  864. return value;
  865. }
  866. static HRESULT open_file_type_icon(LPCWSTR szFileName, IStream **ppStream)
  867. {
  868. WCHAR *extension;
  869. WCHAR *icon = NULL;
  870. WCHAR *comma;
  871. WCHAR *executable = NULL;
  872. int index = 0;
  873. HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
  874. extension = wcsrchr(szFileName, '.');
  875. if (extension == NULL)
  876. goto end;
  877. icon = assoc_query(ASSOCSTR_DEFAULTICON, extension, NULL);
  878. if (icon)
  879. {
  880. comma = wcsrchr(icon, ',');
  881. if (comma)
  882. {
  883. *comma = 0;
  884. index = wcstol(comma + 1, NULL, 10);
  885. }
  886. hr = open_module_icon(icon, index, ppStream);
  887. }
  888. else
  889. {
  890. executable = assoc_query(ASSOCSTR_EXECUTABLE, extension, L"open");
  891. if (executable)
  892. hr = open_module_icon(executable, 0, ppStream);
  893. }
  894. end:
  895. heap_free(icon);
  896. heap_free(executable);
  897. return hr;
  898. }
  899. static HRESULT open_default_icon(IStream **ppStream)
  900. {
  901. return open_module_icon(L"user32", -(INT_PTR)IDI_WINLOGO, ppStream);
  902. }
  903. static HRESULT open_icon(LPCWSTR filename, int index, BOOL bWait, IStream **ppStream, ICONDIRENTRY **ppIconDirEntries, int *numEntries)
  904. {
  905. HRESULT hr;
  906. hr = open_module_icon(filename, index, ppStream);
  907. if (FAILED(hr))
  908. {
  909. if(bWait && hr == HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND))
  910. {
  911. WINE_WARN("Can't find file: %s, give a chance to parent process to create it\n",
  912. wine_dbgstr_w(filename));
  913. return hr;
  914. }
  915. else
  916. {
  917. /* This might be a raw .ico file */
  918. hr = SHCreateStreamOnFileW(filename, STGM_READ, ppStream);
  919. }
  920. }
  921. if (SUCCEEDED(hr))
  922. hr = validate_ico(ppStream, ppIconDirEntries, numEntries);
  923. if (FAILED(hr))
  924. {
  925. hr = open_file_type_icon(filename, ppStream);
  926. if (SUCCEEDED(hr))
  927. hr = validate_ico(ppStream, ppIconDirEntries, numEntries);
  928. }
  929. if (FAILED(hr) && !bWait)
  930. {
  931. hr = open_default_icon(ppStream);
  932. if (SUCCEEDED(hr))
  933. hr = validate_ico(ppStream, ppIconDirEntries, numEntries);
  934. }
  935. return hr;
  936. }
  937. static WCHAR *compute_native_identifier(int exeIndex, LPCWSTR icoPathW, LPCWSTR filename)
  938. {
  939. unsigned short crc;
  940. const WCHAR *basename, *ext;
  941. if (filename) return xwcsdup( filename );
  942. crc = crc16(icoPathW);
  943. basename = wcsrchr(icoPathW, '\\');
  944. if (basename == NULL) basename = icoPathW;
  945. else basename++;
  946. ext = wcsrchr(basename, '.');
  947. if (!ext) ext = basename + lstrlenW(basename);
  948. return heap_wprintf(L"%04X_%.*s.%d", crc, (int)(ext - basename), basename, exeIndex);
  949. }
  950. static void refresh_icon_cache(const WCHAR *iconsDir)
  951. {
  952. WCHAR buffer[MAX_PATH];
  953. /* The icon theme spec only requires the mtime on the "toplevel"
  954. * directory (whatever that is) to be changed for a refresh,
  955. * but on GNOME you have to create a file in that directory
  956. * instead. Creating a file also works on KDE, Xfce and LXDE.
  957. */
  958. GetTempFileNameW( iconsDir, L"icn", 0, buffer );
  959. DeleteFileW( buffer );
  960. }
  961. static HRESULT platform_write_icon(IStream *icoStream, ICONDIRENTRY *iconDirEntries,
  962. int numEntries, int exeIndex, LPCWSTR icoPathW,
  963. const WCHAR *destFilename, WCHAR **nativeIdentifier)
  964. {
  965. int i;
  966. WCHAR *iconsDir;
  967. HRESULT hr = S_OK;
  968. LARGE_INTEGER zero;
  969. *nativeIdentifier = compute_native_identifier(exeIndex, icoPathW, destFilename);
  970. iconsDir = heap_wprintf(L"%s\\icons\\hicolor", xdg_data_dir);
  971. for (i = 0; i < numEntries; i++)
  972. {
  973. int bestIndex = i;
  974. int j;
  975. BOOLEAN duplicate = FALSE;
  976. int w, h;
  977. WCHAR *iconDir;
  978. WCHAR *pngPath;
  979. WINE_TRACE("[%d]: %d x %d @ %d\n", i, iconDirEntries[i].bWidth,
  980. iconDirEntries[i].bHeight, iconDirEntries[i].wBitCount);
  981. for (j = 0; j < i; j++)
  982. {
  983. if (iconDirEntries[j].bWidth == iconDirEntries[i].bWidth &&
  984. iconDirEntries[j].bHeight == iconDirEntries[i].bHeight)
  985. {
  986. duplicate = TRUE;
  987. break;
  988. }
  989. }
  990. if (duplicate)
  991. continue;
  992. for (j = i + 1; j < numEntries; j++)
  993. {
  994. if (iconDirEntries[j].bWidth == iconDirEntries[i].bWidth &&
  995. iconDirEntries[j].bHeight == iconDirEntries[i].bHeight &&
  996. iconDirEntries[j].wBitCount >= iconDirEntries[bestIndex].wBitCount)
  997. {
  998. bestIndex = j;
  999. }
  1000. }
  1001. WINE_TRACE("Selected: %d\n", bestIndex);
  1002. w = iconDirEntries[bestIndex].bWidth ? iconDirEntries[bestIndex].bWidth : 256;
  1003. h = iconDirEntries[bestIndex].bHeight ? iconDirEntries[bestIndex].bHeight : 256;
  1004. iconDir = heap_wprintf(L"%s\\%dx%d\\apps", iconsDir, w, h);
  1005. create_directories(iconDir);
  1006. pngPath = heap_wprintf(L"%s\\%s.png", iconDir, *nativeIdentifier);
  1007. zero.QuadPart = 0;
  1008. hr = IStream_Seek(icoStream, zero, STREAM_SEEK_SET, NULL);
  1009. if (SUCCEEDED(hr))
  1010. hr = convert_to_native_icon(icoStream, &bestIndex, 1, &CLSID_WICPngEncoder, pngPath);
  1011. heap_free(iconDir);
  1012. heap_free(pngPath);
  1013. }
  1014. refresh_icon_cache(iconsDir);
  1015. heap_free(iconsDir);
  1016. return hr;
  1017. }
  1018. /* extract an icon from an exe or icon file; helper for IPersistFile_fnSave */
  1019. static WCHAR *extract_icon(LPCWSTR icoPathW, int index, const WCHAR *destFilename, BOOL bWait)
  1020. {
  1021. IStream *stream = NULL;
  1022. ICONDIRENTRY *pIconDirEntries = NULL;
  1023. int numEntries;
  1024. HRESULT hr;
  1025. WCHAR *nativeIdentifier = NULL;
  1026. WINE_TRACE("path=[%s] index=%d destFilename=[%s]\n", wine_dbgstr_w(icoPathW), index, wine_dbgstr_w(destFilename));
  1027. hr = open_icon(icoPathW, index, bWait, &stream, &pIconDirEntries, &numEntries);
  1028. if (FAILED(hr))
  1029. {
  1030. WINE_WARN("opening icon %s index %d failed, hr=0x%08X\n", wine_dbgstr_w(icoPathW), index, hr);
  1031. goto end;
  1032. }
  1033. hr = platform_write_icon(stream, pIconDirEntries, numEntries, index, icoPathW, destFilename, &nativeIdentifier);
  1034. if (FAILED(hr))
  1035. WINE_WARN("writing icon failed, error 0x%08X\n", hr);
  1036. end:
  1037. if (stream)
  1038. IStream_Release(stream);
  1039. heap_free(pIconDirEntries);
  1040. if (FAILED(hr))
  1041. {
  1042. heap_free(nativeIdentifier);
  1043. nativeIdentifier = NULL;
  1044. }
  1045. return nativeIdentifier;
  1046. }
  1047. static HKEY open_menus_reg_key(void)
  1048. {
  1049. HKEY assocKey;
  1050. DWORD ret;
  1051. ret = RegCreateKeyW(HKEY_CURRENT_USER, L"Software\\Wine\\MenuFiles", &assocKey);
  1052. if (ret == ERROR_SUCCESS)
  1053. return assocKey;
  1054. SetLastError(ret);
  1055. return NULL;
  1056. }
  1057. static DWORD register_menus_entry(const WCHAR *menu_file, const WCHAR *windows_file)
  1058. {
  1059. HKEY hkey;
  1060. DWORD ret;
  1061. hkey = open_menus_reg_key();
  1062. if (hkey)
  1063. {
  1064. ret = RegSetValueExW(hkey, menu_file, 0, REG_SZ, (const BYTE*)windows_file,
  1065. (lstrlenW(windows_file) + 1) * sizeof(WCHAR));
  1066. RegCloseKey(hkey);
  1067. }
  1068. else
  1069. ret = GetLastError();
  1070. return ret;
  1071. }
  1072. /* This escapes reserved characters in .desktop files' Exec keys. */
  1073. static LPSTR escape(LPCWSTR arg)
  1074. {
  1075. int i, j;
  1076. WCHAR *escaped_string;
  1077. char *utf8_string;
  1078. escaped_string = xmalloc((4 * lstrlenW(arg) + 1) * sizeof(WCHAR));
  1079. for (i = j = 0; arg[i]; i++)
  1080. {
  1081. switch (arg[i])
  1082. {
  1083. case '\\':
  1084. escaped_string[j++] = '\\';
  1085. escaped_string[j++] = '\\';
  1086. escaped_string[j++] = '\\';
  1087. escaped_string[j++] = '\\';
  1088. break;
  1089. case ' ':
  1090. case '\t':
  1091. case '\n':
  1092. case '"':
  1093. case '\'':
  1094. case '>':
  1095. case '<':
  1096. case '~':
  1097. case '|':
  1098. case '&':
  1099. case ';':
  1100. case '$':
  1101. case '*':
  1102. case '?':
  1103. case '#':
  1104. case '(':
  1105. case ')':
  1106. case '`':
  1107. escaped_string[j++] = '\\';
  1108. escaped_string[j++] = '\\';
  1109. /* fall through */
  1110. default:
  1111. escaped_string[j++] = arg[i];
  1112. break;
  1113. }
  1114. }
  1115. escaped_string[j] = 0;
  1116. utf8_string = wchars_to_utf8_chars(escaped_string);
  1117. heap_free(escaped_string);
  1118. return utf8_string;
  1119. }
  1120. static BOOL write_desktop_entry(const WCHAR *link, const WCHAR *location, const WCHAR *linkname,
  1121. const WCHAR *path, const WCHAR *args, const WCHAR *descr,
  1122. const WCHAR *workdir, const WCHAR *icon, const WCHAR *wmclass)
  1123. {
  1124. FILE *file;
  1125. char *workdir_unix;
  1126. int needs_chmod = FALSE;
  1127. const WCHAR *name;
  1128. const WCHAR *prefix = _wgetenv( L"WINECONFIGDIR" );
  1129. WINE_TRACE("(%s,%s,%s,%s,%s,%s,%s,%s,%s)\n", wine_dbgstr_w(link), wine_dbgstr_w(location),
  1130. wine_dbgstr_w(linkname), wine_dbgstr_w(path), wine_dbgstr_w(args),
  1131. wine_dbgstr_w(descr), wine_dbgstr_w(workdir), wine_dbgstr_w(icon),
  1132. wine_dbgstr_w(wmclass));
  1133. name = PathFindFileNameW( linkname );
  1134. if (!location)
  1135. {
  1136. location = heap_wprintf(L"%s\\%s.desktop", xdg_desktop_dir, name);
  1137. needs_chmod = TRUE;
  1138. }
  1139. file = _wfopen( location, L"wb" );
  1140. if (file == NULL)
  1141. return FALSE;
  1142. fprintf(file, "[Desktop Entry]\n");
  1143. fprintf(file, "Name=%s\n", wchars_to_utf8_chars(name));
  1144. fprintf(file, "Exec=" );
  1145. if (prefix)
  1146. {
  1147. char *path = wine_get_unix_file_name( prefix );
  1148. fprintf(file, "env WINEPREFIX=\"%s\" ", path);
  1149. heap_free( path );
  1150. }
  1151. fprintf(file, "wine %s", escape(path));
  1152. if (args) fprintf(file, " %s", escape(args) );
  1153. fputc( '\n', file );
  1154. fprintf(file, "Type=Application\n");
  1155. fprintf(file, "StartupNotify=true\n");
  1156. if (descr && *descr)
  1157. fprintf(file, "Comment=%s\n", wchars_to_utf8_chars(descr));
  1158. if (workdir && *workdir && (workdir_unix = wine_get_unix_file_name(workdir)))
  1159. fprintf(file, "Path=%s\n", workdir_unix);
  1160. if (icon && *icon)
  1161. fprintf(file, "Icon=%s\n", wchars_to_utf8_chars(icon));
  1162. if (wmclass && *wmclass)
  1163. fprintf(file, "StartupWMClass=%s\n", wchars_to_utf8_chars(wmclass));
  1164. fclose(file);
  1165. if (needs_chmod)
  1166. {
  1167. const char *argv[] = { "chmod", "+x", wine_get_unix_file_name(location), NULL };
  1168. __wine_unix_spawnvp( (char **)argv, FALSE );
  1169. }
  1170. if (link)
  1171. {
  1172. DWORD ret = register_menus_entry(location, link);
  1173. if (ret != ERROR_SUCCESS)
  1174. return FALSE;
  1175. }
  1176. return TRUE;
  1177. }
  1178. static BOOL write_directory_entry(const WCHAR *directory, const WCHAR *location)
  1179. {
  1180. FILE *file;
  1181. WINE_TRACE("(%s,%s)\n", wine_dbgstr_w(directory), wine_dbgstr_w(location));
  1182. file = _wfopen( location, L"wb" );
  1183. if (file == NULL)
  1184. return FALSE;
  1185. fprintf(file, "[Desktop Entry]\n");
  1186. fprintf(file, "Type=Directory\n");
  1187. if (wcscmp(directory, L"wine") == 0)
  1188. {
  1189. fprintf(file, "Name=Wine\n");
  1190. fprintf(file, "Icon=wine\n");
  1191. }
  1192. else
  1193. {
  1194. fprintf(file, "Name=%s\n", wchars_to_utf8_chars(directory));
  1195. fprintf(file, "Icon=folder\n");
  1196. }
  1197. fclose(file);
  1198. return TRUE;
  1199. }
  1200. static BOOL write_menu_file(const WCHAR *windows_link, const WCHAR *link)
  1201. {
  1202. WCHAR tempfilename[MAX_PATH];
  1203. FILE *tempfile = NULL;
  1204. WCHAR *filename, *lastEntry, *menuPath;
  1205. int i;
  1206. int count = 0;
  1207. BOOL ret = FALSE;
  1208. WINE_TRACE("(%s)\n", wine_dbgstr_w(link));
  1209. GetTempFileNameW( xdg_menu_dir, L"mnu", 0, tempfilename );
  1210. if (!(tempfile = _wfopen( tempfilename, L"wb" ))) return FALSE;
  1211. fprintf(tempfile, "<!DOCTYPE Menu PUBLIC \"-//freedesktop//DTD Menu 1.0//EN\"\n");
  1212. fprintf(tempfile, "\"http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd\">\n");
  1213. fprintf(tempfile, "<Menu>\n");
  1214. fprintf(tempfile, " <Name>Applications</Name>\n");
  1215. filename = heap_wprintf(L"wine\\%s.desktop", link);
  1216. lastEntry = filename;
  1217. for (i = 0; filename[i]; i++)
  1218. {
  1219. if (filename[i] == '\\')
  1220. {
  1221. WCHAR *dir_file_name;
  1222. const char *prefix = count ? "" : "wine-";
  1223. filename[i] = 0;
  1224. fprintf(tempfile, " <Menu>\n");
  1225. fprintf(tempfile, " <Name>%s%s</Name>\n",
  1226. prefix, wchars_to_xml_text(filename));
  1227. fprintf(tempfile, " <Directory>%s%s.directory</Directory>\n",
  1228. prefix, wchars_to_xml_text(filename));
  1229. dir_file_name = heap_wprintf(L"%s\\desktop-directories\\%s%s.directory",
  1230. xdg_data_dir, count ? L"" : L"wine-", filename);
  1231. if (GetFileAttributesW( dir_file_name ) == INVALID_FILE_ATTRIBUTES)
  1232. write_directory_entry(lastEntry, dir_file_name);
  1233. heap_free(dir_file_name);
  1234. filename[i] = '-';
  1235. lastEntry = &filename[i+1];
  1236. ++count;
  1237. }
  1238. }
  1239. filename[i] = 0;
  1240. fprintf(tempfile, " <Include>\n");
  1241. fprintf(tempfile, " <Filename>%s</Filename>\n", wchars_to_xml_text(filename));
  1242. fprintf(tempfile, " </Include>\n");
  1243. for (i = 0; i < count; i++)
  1244. fprintf(tempfile, " </Menu>\n");
  1245. fprintf(tempfile, "</Menu>\n");
  1246. menuPath = heap_wprintf(L"%s\\%s", xdg_menu_dir, filename);
  1247. lstrcpyW(menuPath + lstrlenW(menuPath) - lstrlenW(L".desktop"), L".menu");
  1248. fclose(tempfile);
  1249. ret = MoveFileExW( tempfilename, menuPath, MOVEFILE_REPLACE_EXISTING );
  1250. if (ret)
  1251. register_menus_entry(menuPath, windows_link);
  1252. else
  1253. DeleteFileW( tempfilename );
  1254. heap_free(filename);
  1255. heap_free(menuPath);
  1256. return ret;
  1257. }
  1258. static BOOL write_menu_entry(const WCHAR *windows_link, const WCHAR *link, const WCHAR *path, const WCHAR *args,
  1259. const WCHAR *descr, const WCHAR *workdir, const WCHAR *icon, const WCHAR *wmclass)
  1260. {
  1261. WCHAR *desktopPath;
  1262. WCHAR *desktopDir;
  1263. WCHAR *filename = NULL;
  1264. BOOL ret = TRUE;
  1265. WINE_TRACE("(%s, %s, %s, %s, %s, %s, %s, %s)\n", wine_dbgstr_w(windows_link), wine_dbgstr_w(link),
  1266. wine_dbgstr_w(path), wine_dbgstr_w(args), wine_dbgstr_w(descr),
  1267. wine_dbgstr_w(workdir), wine_dbgstr_w(icon), wine_dbgstr_w(wmclass));
  1268. desktopPath = heap_wprintf(L"%s\\applications\\wine\\%s.desktop", xdg_data_dir, link);
  1269. desktopDir = wcsrchr(desktopPath, '\\');
  1270. *desktopDir = 0;
  1271. if (!create_directories(desktopPath))
  1272. {
  1273. WINE_WARN("couldn't make parent directories for %s\n", wine_dbgstr_w(desktopPath));
  1274. ret = FALSE;
  1275. goto end;
  1276. }
  1277. *desktopDir = '\\';
  1278. if (!write_desktop_entry(windows_link, desktopPath, link, path, args, descr, workdir, icon, wmclass))
  1279. {
  1280. WINE_WARN("couldn't make desktop entry %s\n", wine_dbgstr_w(desktopPath));
  1281. ret = FALSE;
  1282. goto end;
  1283. }
  1284. if (!write_menu_file(windows_link, link))
  1285. {
  1286. WINE_WARN("couldn't make menu file %s\n", wine_dbgstr_w(filename));
  1287. ret = FALSE;
  1288. }
  1289. end:
  1290. heap_free(desktopPath);
  1291. heap_free(filename);
  1292. return ret;
  1293. }
  1294. /***********************************************************************
  1295. * get_link_location
  1296. *
  1297. * returns TRUE if successful
  1298. * *loc will contain CS_DESKTOPDIRECTORY, CS_STARTMENU, CS_STARTUP etc.
  1299. * *relative will contain the address of a heap-allocated copy of the portion
  1300. * of the filename that is within the specified location, in unix form
  1301. */
  1302. static BOOL get_link_location( LPCWSTR linkfile, DWORD *loc, WCHAR **relative )
  1303. {
  1304. WCHAR filename[MAX_PATH], shortfilename[MAX_PATH], buffer[MAX_PATH];
  1305. DWORD len, i, filelen;
  1306. const DWORD locations[] = {
  1307. CSIDL_STARTUP, CSIDL_DESKTOPDIRECTORY, CSIDL_STARTMENU,
  1308. CSIDL_COMMON_STARTUP, CSIDL_COMMON_DESKTOPDIRECTORY,
  1309. CSIDL_COMMON_STARTMENU };
  1310. WINE_TRACE("%s\n", wine_dbgstr_w(linkfile));
  1311. filelen=GetFullPathNameW( linkfile, MAX_PATH, shortfilename, NULL );
  1312. if (filelen==0 || filelen>MAX_PATH)
  1313. return FALSE;
  1314. WINE_TRACE("%s\n", wine_dbgstr_w(shortfilename));
  1315. /* the CSLU Toolkit uses a short path name when creating .lnk files;
  1316. * expand or our hardcoded list won't match.
  1317. */
  1318. filelen=GetLongPathNameW(shortfilename, filename, MAX_PATH);
  1319. if (filelen==0 || filelen>MAX_PATH)
  1320. return FALSE;
  1321. WINE_TRACE("%s\n", wine_dbgstr_w(filename));
  1322. for( i=0; i<ARRAY_SIZE( locations ); i++ )
  1323. {
  1324. if (!SHGetSpecialFolderPathW( 0, buffer, locations[i], FALSE ))
  1325. continue;
  1326. len = lstrlenW(buffer);
  1327. if (len >= MAX_PATH)
  1328. continue; /* We've just trashed memory! Hopefully we are OK */
  1329. if (len > filelen || filename[len]!='\\')
  1330. continue;
  1331. if (wcsnicmp( filename, buffer, len )) continue;
  1332. /* return the remainder of the string and link type */
  1333. *loc = locations[i];
  1334. *relative = xwcsdup( filename + len + 1 );
  1335. PathRemoveExtensionW( *relative );
  1336. return TRUE;
  1337. }
  1338. return FALSE;
  1339. }
  1340. /* gets the target path directly or through MSI */
  1341. static HRESULT get_cmdline( IShellLinkW *sl, LPWSTR szPath, DWORD pathSize,
  1342. LPWSTR szArgs, DWORD argsSize)
  1343. {
  1344. IShellLinkDataList *dl = NULL;
  1345. EXP_DARWIN_LINK *dar = NULL;
  1346. HRESULT hr;
  1347. szPath[0] = 0;
  1348. szArgs[0] = 0;
  1349. hr = IShellLinkW_GetPath( sl, szPath, pathSize, NULL, SLGP_RAWPATH );
  1350. if (hr == S_OK && szPath[0])
  1351. {
  1352. IShellLinkW_GetArguments( sl, szArgs, argsSize );
  1353. return hr;
  1354. }
  1355. hr = IShellLinkW_QueryInterface( sl, &IID_IShellLinkDataList, (LPVOID*) &dl );
  1356. if (FAILED(hr))
  1357. return hr;
  1358. hr = IShellLinkDataList_CopyDataBlock( dl, EXP_DARWIN_ID_SIG, (LPVOID*) &dar );
  1359. if (SUCCEEDED(hr))
  1360. {
  1361. WCHAR* szCmdline;
  1362. DWORD cmdSize;
  1363. cmdSize=0;
  1364. hr = CommandLineFromMsiDescriptor( dar->szwDarwinID, NULL, &cmdSize );
  1365. if (hr == ERROR_SUCCESS)
  1366. {
  1367. cmdSize++;
  1368. szCmdline = xmalloc(cmdSize*sizeof(WCHAR) );
  1369. hr = CommandLineFromMsiDescriptor( dar->szwDarwinID, szCmdline, &cmdSize );
  1370. WINE_TRACE(" command : %s\n", wine_dbgstr_w(szCmdline));
  1371. if (hr == ERROR_SUCCESS)
  1372. {
  1373. WCHAR *s, *d;
  1374. int bcount = 0;
  1375. BOOL in_quotes = FALSE;
  1376. /* Extract the application path */
  1377. s=szCmdline;
  1378. d=szPath;
  1379. while (*s)
  1380. {
  1381. if ((*s==0x0009 || *s==0x0020) && !in_quotes)
  1382. {
  1383. /* skip the remaining spaces */
  1384. do {
  1385. s++;
  1386. } while (*s==0x0009 || *s==0x0020);
  1387. break;
  1388. }
  1389. else if (*s==0x005c)
  1390. {
  1391. /* '\\' */
  1392. *d++=*s++;
  1393. bcount++;
  1394. }
  1395. else if (*s==0x0022)
  1396. {
  1397. /* '"' */
  1398. if ((bcount & 1)==0)
  1399. {
  1400. /* Preceded by an even number of '\', this is
  1401. * half that number of '\', plus a quote which
  1402. * we erase.
  1403. */
  1404. d-=bcount/2;
  1405. in_quotes=!in_quotes;
  1406. s++;
  1407. }
  1408. else
  1409. {
  1410. /* Preceded by an odd number of '\', this is
  1411. * half that number of '\' followed by a '"'
  1412. */
  1413. d=d-bcount/2-1;
  1414. *d++='"';
  1415. s++;
  1416. }
  1417. bcount=0;
  1418. }
  1419. else
  1420. {
  1421. /* a regular character */
  1422. *d++=*s++;
  1423. bcount=0;
  1424. }
  1425. if ((d-szPath) == pathSize)
  1426. {
  1427. /* Keep processing the path till we get to the
  1428. * arguments, but 'stand still'
  1429. */
  1430. d--;
  1431. }
  1432. }
  1433. /* Close the application path */
  1434. *d=0;
  1435. lstrcpynW(szArgs, s, argsSize);
  1436. }
  1437. heap_free(szCmdline );
  1438. }
  1439. LocalFree( dar );
  1440. }
  1441. IShellLinkDataList_Release( dl );
  1442. return hr;
  1443. }
  1444. static WCHAR *slashes_to_minuses(const WCHAR *string)
  1445. {
  1446. int i;
  1447. WCHAR *ret = xwcsdup(string);
  1448. for (i = 0; ret[i]; i++) if (ret[i] == '/') ret[i] = '-';
  1449. return ret;
  1450. }
  1451. static BOOL next_line(FILE *file, char **line, int *size)
  1452. {
  1453. int pos = 0;
  1454. char *cr;
  1455. if (*line == NULL)
  1456. {
  1457. *size = 4096;
  1458. *line = xmalloc(*size);
  1459. }
  1460. while (*line != NULL)
  1461. {
  1462. if (fgets(&(*line)[pos], *size - pos, file) == NULL)
  1463. {
  1464. heap_free(*line);
  1465. *line = NULL;
  1466. if (feof(file))
  1467. return TRUE;
  1468. return FALSE;
  1469. }
  1470. pos = strlen(*line);
  1471. cr = strchr(*line, '\n');
  1472. if (cr == NULL)
  1473. {
  1474. (*size) *= 2;
  1475. *line = xrealloc(*line, *size);
  1476. }
  1477. else
  1478. {
  1479. *cr = 0;
  1480. return TRUE;
  1481. }
  1482. }
  1483. return FALSE;
  1484. }
  1485. static BOOL add_mimes(const WCHAR *dir, struct list *mime_types)
  1486. {
  1487. WCHAR *globs_filename;
  1488. BOOL ret = TRUE;
  1489. FILE *globs_file;
  1490. globs_filename = heap_wprintf(L"%s\\mime\\globs", dir);
  1491. globs_file = _wfopen( globs_filename, L"r" );
  1492. if (globs_file) /* doesn't have to exist */
  1493. {
  1494. char *line = NULL;
  1495. int size = 0;
  1496. while (ret && (ret = next_line(globs_file, &line, &size)) && line)
  1497. {
  1498. char *pos;
  1499. struct xdg_mime_type *mime_type_entry = NULL;
  1500. if (line[0] != '#' && (pos = strchr(line, ':')))
  1501. {
  1502. mime_type_entry = xmalloc(sizeof(struct xdg_mime_type));
  1503. *pos = 0;
  1504. mime_type_entry->mimeType = utf8_chars_to_wchars(line);
  1505. mime_type_entry->glob = utf8_chars_to_wchars(pos + 1);
  1506. list_add_tail(mime_types, &mime_type_entry->entry);
  1507. }
  1508. }
  1509. heap_free(line);
  1510. fclose(globs_file);
  1511. }
  1512. heap_free(globs_filename);
  1513. return ret;
  1514. }
  1515. static void free_native_mime_types(struct list *native_mime_types)
  1516. {
  1517. struct xdg_mime_type *mime_type_entry, *mime_type_entry2;
  1518. LIST_FOR_EACH_ENTRY_SAFE(mime_type_entry, mime_type_entry2, native_mime_types, struct xdg_mime_type, entry)
  1519. {
  1520. list_remove(&mime_type_entry->entry);
  1521. heap_free(mime_type_entry->glob);
  1522. heap_free(mime_type_entry->mimeType);
  1523. heap_free(mime_type_entry);
  1524. }
  1525. }
  1526. static BOOL build_native_mime_types(struct list *mime_types)
  1527. {
  1528. WCHAR *dirs, *dir, *dos_name, *ctx, *p;
  1529. BOOL ret;
  1530. if (_wgetenv( L"XDG_DATA_DIRS" ))
  1531. dirs = xwcsdup( _wgetenv( L"XDG_DATA_DIRS" ));
  1532. else
  1533. dirs = xwcsdup( L"/usr/local/share/:/usr/share/" );
  1534. ret = add_mimes(xdg_data_dir, mime_types);
  1535. if (ret)
  1536. {
  1537. for (dir = wcstok( dirs, L":", &ctx ); dir; dir = wcstok( NULL, L":", &ctx ))
  1538. {
  1539. dos_name = heap_wprintf( L"\\\\?\\unix%s", dir );
  1540. for (p = dos_name; *p; p++) if (*p == '/') *p = '\\';
  1541. if (p > dos_name + 9 && p[-1] == '\\') p[-1] = 0;
  1542. ret = add_mimes(dos_name, mime_types);
  1543. heap_free( dos_name );
  1544. if (!ret)
  1545. break;
  1546. }
  1547. }
  1548. heap_free(dirs);
  1549. if (!ret)
  1550. free_native_mime_types(mime_types);
  1551. return ret;
  1552. }
  1553. static WCHAR *freedesktop_mime_type_for_extension(struct list *native_mime_types,
  1554. const WCHAR *extensionW)
  1555. {
  1556. struct xdg_mime_type *mime_type_entry;
  1557. int matchLength = 0;
  1558. const WCHAR* match = NULL;
  1559. LIST_FOR_EACH_ENTRY(mime_type_entry, native_mime_types, struct xdg_mime_type, entry)
  1560. {
  1561. if (PathMatchSpecW( extensionW, mime_type_entry->glob ))
  1562. {
  1563. if (match == NULL || matchLength < lstrlenW(mime_type_entry->glob))
  1564. {
  1565. match = mime_type_entry->mimeType;
  1566. matchLength = lstrlenW(mime_type_entry->glob);
  1567. }
  1568. }
  1569. }
  1570. return match ? xwcsdup(match) : NULL;
  1571. }
  1572. static WCHAR *reg_enum_keyW(HKEY key, DWORD index)
  1573. {
  1574. WCHAR *subkey;
  1575. DWORD size = 1024 * sizeof(WCHAR);
  1576. LSTATUS ret;
  1577. for (;;)
  1578. {
  1579. subkey = xmalloc(size);
  1580. ret = RegEnumKeyExW(key, index, subkey, &size, NULL, NULL, NULL, NULL);
  1581. if (ret == ERROR_SUCCESS)
  1582. {
  1583. return subkey;
  1584. }
  1585. if (ret != ERROR_MORE_DATA)
  1586. {
  1587. heap_free(subkey);
  1588. return NULL;
  1589. }
  1590. size *= 2;
  1591. heap_free(subkey);
  1592. }
  1593. }
  1594. static WCHAR* reg_get_valW(HKEY key, LPCWSTR subkey, LPCWSTR name)
  1595. {
  1596. DWORD size;
  1597. if (RegGetValueW(key, subkey, name, RRF_RT_REG_SZ, NULL, NULL, &size) == ERROR_SUCCESS)
  1598. {
  1599. WCHAR *ret = xmalloc(size);
  1600. if (RegGetValueW(key, subkey, name, RRF_RT_REG_SZ, NULL, ret, &size) == ERROR_SUCCESS)
  1601. return ret;
  1602. heap_free(ret);
  1603. }
  1604. return NULL;
  1605. }
  1606. static HKEY open_associations_reg_key(void)
  1607. {
  1608. HKEY assocKey;
  1609. if (RegCreateKeyW(HKEY_CURRENT_USER, L"Software\\Wine\\FileOpenAssociations", &assocKey) == ERROR_SUCCESS)
  1610. return assocKey;
  1611. return NULL;
  1612. }
  1613. static BOOL has_association_changed(LPCWSTR extensionW, const WCHAR *mimeType, const WCHAR *progId,
  1614. const WCHAR *appName, const WCHAR *openWithIcon)
  1615. {
  1616. HKEY assocKey;
  1617. BOOL ret;
  1618. if ((assocKey = open_associations_reg_key()))
  1619. {
  1620. WCHAR *value;
  1621. ret = FALSE;
  1622. value = reg_get_valW(assocKey, extensionW, L"MimeType");
  1623. if (!value || wcscmp(value, mimeType))
  1624. ret = TRUE;
  1625. heap_free(value);
  1626. value = reg_get_valW(assocKey, extensionW, L"ProgID");
  1627. if (!value || wcscmp(value, progId))
  1628. ret = TRUE;
  1629. heap_free(value);
  1630. value = reg_get_valW(assocKey, extensionW, L"AppName");
  1631. if (!value || wcscmp(value, appName))
  1632. ret = TRUE;
  1633. heap_free(value);
  1634. value = reg_get_valW(assocKey, extensionW, L"OpenWithIcon");
  1635. if ((openWithIcon && !value) ||
  1636. (!openWithIcon && value) ||
  1637. (openWithIcon && value && wcscmp(value, openWithIcon)))
  1638. ret = TRUE;
  1639. heap_free(value);
  1640. RegCloseKey(assocKey);
  1641. }
  1642. else
  1643. {
  1644. WINE_ERR("error opening associations registry key\n");
  1645. ret = FALSE;
  1646. }
  1647. return ret;
  1648. }
  1649. static void update_association(LPCWSTR extension, const WCHAR *mimeType, const WCHAR *progId,
  1650. const WCHAR *appName, const WCHAR *desktopFile, const WCHAR *openWithIcon)
  1651. {
  1652. HKEY assocKey = NULL;
  1653. HKEY subkey = NULL;
  1654. assocKey = open_associations_reg_key();
  1655. if (assocKey == NULL)
  1656. {
  1657. WINE_ERR("could not open file associations key\n");
  1658. goto done;
  1659. }
  1660. if (RegCreateKeyW(assocKey, extension, &subkey) != ERROR_SUCCESS)
  1661. {
  1662. WINE_ERR("could not create extension subkey\n");
  1663. goto done;
  1664. }
  1665. RegSetValueExW(subkey, L"MimeType", 0, REG_SZ, (const BYTE*) mimeType, (lstrlenW(mimeType) + 1) * sizeof(WCHAR));
  1666. RegSetValueExW(subkey, L"ProgID", 0, REG_SZ, (const BYTE*) progId, (lstrlenW(progId) + 1) * sizeof(WCHAR));
  1667. RegSetValueExW(subkey, L"AppName", 0, REG_SZ, (const BYTE*) appName, (lstrlenW(appName) + 1) * sizeof(WCHAR));
  1668. RegSetValueExW(subkey, L"DesktopFile", 0, REG_SZ, (const BYTE*) desktopFile, (lstrlenW(desktopFile) + 1) * sizeof(WCHAR));
  1669. if (openWithIcon)
  1670. RegSetValueExW(subkey, L"OpenWithIcon", 0, REG_SZ, (const BYTE*) openWithIcon, (lstrlenW(openWithIcon) + 1) * sizeof(WCHAR));
  1671. else
  1672. RegDeleteValueW(subkey, L"OpenWithIcon");
  1673. done:
  1674. RegCloseKey(assocKey);
  1675. RegCloseKey(subkey);
  1676. }
  1677. static BOOL cleanup_associations(void)
  1678. {
  1679. HKEY assocKey;
  1680. BOOL hasChanged = FALSE;
  1681. if ((assocKey = open_associations_reg_key()))
  1682. {
  1683. int i = 0;
  1684. for (;;)
  1685. {
  1686. WCHAR *extensionW;
  1687. WCHAR *command;
  1688. if (!(extensionW = reg_enum_keyW(assocKey, i)))
  1689. break;
  1690. if (!(command = assoc_query(ASSOCSTR_COMMAND, extensionW, L"open")))
  1691. {
  1692. WCHAR *desktopFile = reg_get_valW(assocKey, extensionW, L"DesktopFile");
  1693. if (desktopFile)
  1694. {
  1695. WINE_TRACE("removing file type association for %s\n", wine_dbgstr_w(extensionW));
  1696. DeleteFileW(desktopFile);
  1697. }
  1698. RegDeleteKeyW(assocKey, extensionW);
  1699. hasChanged = TRUE;
  1700. heap_free(desktopFile);
  1701. }
  1702. else
  1703. {
  1704. i++;
  1705. heap_free(command);
  1706. }
  1707. heap_free(extensionW);
  1708. }
  1709. RegCloseKey(assocKey);
  1710. }
  1711. else
  1712. WINE_ERR("could not open file associations key\n");
  1713. return hasChanged;
  1714. }
  1715. static BOOL write_freedesktop_mime_type_entry(const WCHAR *packages_dir, const WCHAR *dot_extension,
  1716. const WCHAR *mime_type, const WCHAR *comment)
  1717. {
  1718. BOOL ret = FALSE;
  1719. WCHAR *filename;
  1720. FILE *packageFile;
  1721. WINE_TRACE("writing MIME type %s, extension=%s, comment=%s\n", wine_dbgstr_w(mime_type),
  1722. wine_dbgstr_w(dot_extension), wine_dbgstr_w(comment));
  1723. filename = heap_wprintf(L"%s\\x-wine-extension-%s.xml", packages_dir, dot_extension + 1);
  1724. packageFile = _wfopen( filename, L"wb" );
  1725. if (packageFile)
  1726. {
  1727. fprintf(packageFile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
  1728. fprintf(packageFile, "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n");
  1729. fprintf(packageFile, " <mime-type type=\"%s\">\n", wchars_to_xml_text(mime_type));
  1730. fprintf(packageFile, " <glob pattern=\"*%s\"/>\n", wchars_to_xml_text(dot_extension));
  1731. if (comment) fprintf(packageFile, " <comment>%s</comment>\n", wchars_to_xml_text(comment));
  1732. fprintf(packageFile, " </mime-type>\n");
  1733. fprintf(packageFile, "</mime-info>\n");
  1734. ret = TRUE;
  1735. fclose(packageFile);
  1736. }
  1737. else
  1738. WINE_ERR("error writing file %s\n", debugstr_w(filename));
  1739. heap_free(filename);
  1740. return ret;
  1741. }
  1742. static BOOL is_extension_banned(LPCWSTR extension)
  1743. {
  1744. /* These are managed through external tools like wine.desktop, to evade malware created file type associations */
  1745. if (!wcsicmp(extension, L".com") ||
  1746. !wcsicmp(extension, L".exe") ||
  1747. !wcsicmp(extension, L".msi"))
  1748. return TRUE;
  1749. return FALSE;
  1750. }
  1751. static WCHAR *get_special_mime_type(LPCWSTR extension)
  1752. {
  1753. if (!wcsicmp(extension, L".lnk"))
  1754. return xwcsdup(L"application/x-ms-shortcut");
  1755. return NULL;
  1756. }
  1757. static BOOL write_freedesktop_association_entry(const WCHAR *desktopPath, const WCHAR *friendlyAppName,
  1758. const WCHAR *mimeType, const WCHAR *progId,
  1759. const WCHAR *openWithIcon)
  1760. {
  1761. BOOL ret = FALSE;
  1762. FILE *desktop;
  1763. const WCHAR *prefix = _wgetenv( L"WINECONFIGDIR" );
  1764. WINE_TRACE("friendlyAppName=%s, MIME type %s, progID=%s, icon=%s to file %s\n",
  1765. wine_dbgstr_w(friendlyAppName), wine_dbgstr_w(mimeType),
  1766. wine_dbgstr_w(progId), wine_dbgstr_w(openWithIcon), wine_dbgstr_w(desktopPath));
  1767. desktop = _wfopen( desktopPath, L"wb" );
  1768. if (desktop)
  1769. {
  1770. fprintf(desktop, "[Desktop Entry]\n");
  1771. fprintf(desktop, "Type=Application\n");
  1772. fprintf(desktop, "Name=%s\n", wchars_to_utf8_chars(friendlyAppName));
  1773. fprintf(desktop, "MimeType=%s;\n", wchars_to_utf8_chars(mimeType));
  1774. if (prefix)
  1775. {
  1776. char *path = wine_get_unix_file_name( prefix );
  1777. fprintf(desktop, "Exec=env WINEPREFIX=\"%s\" wine start /ProgIDOpen %s %%f\n", path, escape(progId));
  1778. heap_free( path );
  1779. }
  1780. else
  1781. fprintf(desktop, "Exec=wine start /ProgIDOpen %s %%f\n", escape(progId));
  1782. fprintf(desktop, "NoDisplay=true\n");
  1783. fprintf(desktop, "StartupNotify=true\n");
  1784. if (openWithIcon)
  1785. fprintf(desktop, "Icon=%s\n", wchars_to_utf8_chars(openWithIcon));
  1786. ret = TRUE;
  1787. fclose(desktop);
  1788. }
  1789. else
  1790. WINE_ERR("error writing association file %s\n", wine_dbgstr_w(desktopPath));
  1791. return ret;
  1792. }
  1793. static BOOL generate_associations(const WCHAR *packages_dir, const WCHAR *applications_dir)
  1794. {
  1795. struct wine_rb_tree mimeProgidTree = { winemenubuilder_rb_string_compare };
  1796. struct list nativeMimeTypes = LIST_INIT(nativeMimeTypes);
  1797. int i;
  1798. BOOL hasChanged = FALSE;
  1799. if (!build_native_mime_types(&nativeMimeTypes))
  1800. {
  1801. WINE_ERR("could not build native MIME types\n");
  1802. return FALSE;
  1803. }
  1804. for (i = 0; ; i++)
  1805. {
  1806. WCHAR *extensionW;
  1807. if (!(extensionW = reg_enum_keyW(HKEY_CLASSES_ROOT, i)))
  1808. break;
  1809. if (extensionW[0] == '.' && !is_extension_banned(extensionW))
  1810. {
  1811. WCHAR *commandW = NULL;
  1812. WCHAR *executableW = NULL;
  1813. WCHAR *openWithIcon = NULL;
  1814. WCHAR *friendlyDocNameW = NULL;
  1815. WCHAR *iconW = NULL;
  1816. WCHAR *contentTypeW = NULL;
  1817. WCHAR *mimeType = NULL;
  1818. const WCHAR *friendlyAppName;
  1819. WCHAR *progIdW = NULL;
  1820. WCHAR *mimeProgId = NULL;
  1821. struct rb_string_entry *entry;
  1822. wcslwr(extensionW);
  1823. friendlyDocNameW = assoc_query(ASSOCSTR_FRIENDLYDOCNAME, extensionW, NULL);
  1824. iconW = assoc_query(ASSOCSTR_DEFAULTICON, extensionW, NULL);
  1825. contentTypeW = assoc_query(ASSOCSTR_CONTENTTYPE, extensionW, NULL);
  1826. if (contentTypeW)
  1827. wcslwr(contentTypeW);
  1828. mimeType = freedesktop_mime_type_for_extension(&nativeMimeTypes, extensionW);
  1829. if (mimeType == NULL)
  1830. {
  1831. if (contentTypeW != NULL && wcschr(contentTypeW, '/'))
  1832. mimeType = xwcsdup(contentTypeW);
  1833. else if (!(mimeType = get_special_mime_type(extensionW)))
  1834. mimeType = heap_wprintf(L"application/x-wine-extension-%s", &extensionW[1]);
  1835. /* GNOME seems to ignore the <icon> tag in MIME packages,
  1836. * and the default name is more intuitive anyway.
  1837. */
  1838. if (iconW)
  1839. {
  1840. WCHAR *flattened_mime = slashes_to_minuses(mimeType);
  1841. int index = 0;
  1842. WCHAR *comma = wcsrchr(iconW, ',');
  1843. if (comma)
  1844. {
  1845. *comma = 0;
  1846. index = wcstol(comma + 1, NULL, 10);
  1847. }
  1848. extract_icon(iconW, index, flattened_mime, FALSE);
  1849. heap_free(flattened_mime);
  1850. }
  1851. write_freedesktop_mime_type_entry(packages_dir, extensionW, mimeType, friendlyDocNameW);
  1852. hasChanged = TRUE;
  1853. }
  1854. commandW = assoc_query(ASSOCSTR_COMMAND, extensionW, L"open");
  1855. if (commandW == NULL)
  1856. /* no command => no application is associated */
  1857. goto end;
  1858. executableW = assoc_query(ASSOCSTR_EXECUTABLE, extensionW, L"open");
  1859. if (executableW)
  1860. openWithIcon = compute_native_identifier(0, executableW, NULL);
  1861. friendlyAppName = assoc_query(ASSOCSTR_FRIENDLYAPPNAME, extensionW, L"open");
  1862. if (!friendlyAppName) friendlyAppName = L"A Wine application";
  1863. progIdW = reg_get_valW(HKEY_CLASSES_ROOT, extensionW, NULL);
  1864. if (!progIdW) goto end; /* no progID => not a file type association */
  1865. /* Do not allow duplicate ProgIDs for a MIME type, it causes unnecessary duplication in Open dialogs */
  1866. mimeProgId = heap_wprintf(L"%s=>%s", mimeType, progIdW);
  1867. if (wine_rb_get(&mimeProgidTree, mimeProgId))
  1868. {
  1869. heap_free(mimeProgId);
  1870. goto end;
  1871. }
  1872. entry = xmalloc(sizeof(struct rb_string_entry));
  1873. entry->string = mimeProgId;
  1874. if (wine_rb_put(&mimeProgidTree, mimeProgId, &entry->entry))
  1875. {
  1876. WINE_ERR("error updating rb tree\n");
  1877. goto end;
  1878. }
  1879. if (has_association_changed(extensionW, mimeType, progIdW, friendlyAppName, openWithIcon))
  1880. {
  1881. WCHAR *desktopPath = heap_wprintf(L"%s\\wine-extension-%s.desktop",
  1882. applications_dir, extensionW + 1 );
  1883. if (write_freedesktop_association_entry(desktopPath, friendlyAppName, mimeType, progIdW, openWithIcon))
  1884. {
  1885. hasChanged = TRUE;
  1886. update_association(extensionW, mimeType, progIdW, friendlyAppName, desktopPath, openWithIcon);
  1887. }
  1888. heap_free(desktopPath);
  1889. }
  1890. if (hasChanged && openWithIcon) extract_icon(executableW, 0, openWithIcon, FALSE);
  1891. end:
  1892. heap_free(commandW);
  1893. heap_free(executableW);
  1894. heap_free(openWithIcon);
  1895. heap_free(friendlyDocNameW);
  1896. heap_free(iconW);
  1897. heap_free(contentTypeW);
  1898. heap_free(mimeType);
  1899. heap_free(progIdW);
  1900. }
  1901. heap_free(extensionW);
  1902. }
  1903. wine_rb_destroy(&mimeProgidTree, winemenubuilder_rb_destroy, NULL);
  1904. free_native_mime_types(&nativeMimeTypes);
  1905. return hasChanged;
  1906. }
  1907. static BOOL InvokeShellLinker( IShellLinkW *sl, LPCWSTR link, BOOL bWait )
  1908. {
  1909. WCHAR *icon_name, *link_name;
  1910. WCHAR szTmp[INFOTIPSIZE];
  1911. WCHAR szDescription[INFOTIPSIZE], szPath[MAX_PATH], szWorkDir[MAX_PATH];
  1912. WCHAR szArgs[INFOTIPSIZE], szIconPath[MAX_PATH], szWMClass[MAX_PATH];
  1913. int iIconId = 0, r = -1;
  1914. DWORD csidl = -1;
  1915. HANDLE hsem = NULL;
  1916. if ( !link )
  1917. {
  1918. WINE_ERR("Link name is null\n");
  1919. return FALSE;
  1920. }
  1921. if( !get_link_location( link, &csidl, &link_name ) )
  1922. {
  1923. WINE_WARN("Unknown link location %s. Ignoring.\n",wine_dbgstr_w(link));
  1924. return TRUE;
  1925. }
  1926. if (!in_desktop_dir(csidl) && !in_startmenu(csidl))
  1927. {
  1928. WINE_WARN("Not under desktop or start menu. Ignoring.\n");
  1929. return TRUE;
  1930. }
  1931. WINE_TRACE("Link : %s\n", wine_dbgstr_w(link_name));
  1932. szTmp[0] = 0;
  1933. IShellLinkW_GetWorkingDirectory( sl, szTmp, MAX_PATH );
  1934. ExpandEnvironmentStringsW(szTmp, szWorkDir, MAX_PATH);
  1935. WINE_TRACE("workdir : %s\n", wine_dbgstr_w(szWorkDir));
  1936. szTmp[0] = 0;
  1937. IShellLinkW_GetDescription( sl, szTmp, INFOTIPSIZE );
  1938. ExpandEnvironmentStringsW(szTmp, szDescription, INFOTIPSIZE);
  1939. WINE_TRACE("description: %s\n", wine_dbgstr_w(szDescription));
  1940. get_cmdline( sl, szTmp, MAX_PATH, szArgs, INFOTIPSIZE);
  1941. ExpandEnvironmentStringsW(szTmp, szPath, MAX_PATH);
  1942. WINE_TRACE("path : %s\n", wine_dbgstr_w(szPath));
  1943. WINE_TRACE("args : %s\n", wine_dbgstr_w(szArgs));
  1944. szTmp[0] = 0;
  1945. IShellLinkW_GetIconLocation( sl, szTmp, MAX_PATH, &iIconId );
  1946. ExpandEnvironmentStringsW(szTmp, szIconPath, MAX_PATH);
  1947. WINE_TRACE("icon file : %s\n", wine_dbgstr_w(szIconPath) );
  1948. szWMClass[0] = 0;
  1949. if( !szPath[0] )
  1950. {
  1951. LPITEMIDLIST pidl = NULL;
  1952. IShellLinkW_GetIDList( sl, &pidl );
  1953. if( pidl && SHGetPathFromIDListW( pidl, szPath ) )
  1954. WINE_TRACE("pidl path : %s\n", wine_dbgstr_w(szPath));
  1955. }
  1956. /* extract the icon */
  1957. if( szIconPath[0] )
  1958. icon_name = extract_icon( szIconPath , iIconId, NULL, bWait );
  1959. else
  1960. icon_name = extract_icon( szPath, iIconId, NULL, bWait );
  1961. /* fail - try once again after parent process exit */
  1962. if( !icon_name )
  1963. {
  1964. if (bWait)
  1965. {
  1966. WINE_WARN("Unable to extract icon, deferring.\n");
  1967. goto cleanup;
  1968. }
  1969. WINE_ERR("failed to extract icon from %s\n",
  1970. wine_dbgstr_w( szIconPath[0] ? szIconPath : szPath ));
  1971. }
  1972. /* check the path */
  1973. if( szPath[0] )
  1974. {
  1975. /* FIXME: Use AppUserModelID if present. */
  1976. WCHAR *p = PathFindFileNameW(szPath);
  1977. if (p)
  1978. {
  1979. lstrcpyW(szWMClass, p);
  1980. CharLowerW(szWMClass);
  1981. }
  1982. }
  1983. /* building multiple menus concurrently has race conditions */
  1984. hsem = CreateSemaphoreA( NULL, 1, 1, "winemenubuilder_semaphore");
  1985. if( WAIT_OBJECT_0 != MsgWaitForMultipleObjects( 1, &hsem, FALSE, INFINITE, QS_ALLINPUT ) )
  1986. {
  1987. WINE_ERR("failed wait for semaphore\n");
  1988. goto cleanup;
  1989. }
  1990. if (in_desktop_dir(csidl))
  1991. {
  1992. if (csidl == CSIDL_COMMON_DESKTOPDIRECTORY || !szPath[0])
  1993. r = !write_desktop_entry(link, NULL, link_name, link, NULL,
  1994. szDescription, szWorkDir, icon_name, szWMClass);
  1995. else
  1996. r = !write_desktop_entry(NULL, NULL, link_name, szPath, szArgs,
  1997. szDescription, szWorkDir, icon_name, szWMClass);
  1998. }
  1999. else
  2000. r = !write_menu_entry(link, link_name, link, NULL, szDescription, szWorkDir, icon_name, szWMClass);
  2001. ReleaseSemaphore( hsem, 1, NULL );
  2002. cleanup:
  2003. if (hsem) CloseHandle( hsem );
  2004. heap_free(icon_name );
  2005. heap_free(link_name );
  2006. if (r && !bWait)
  2007. WINE_ERR("failed to build the menu\n" );
  2008. return ( r == 0 );
  2009. }
  2010. static BOOL InvokeShellLinkerForURL( IUniformResourceLocatorW *url, LPCWSTR link, BOOL bWait )
  2011. {
  2012. WCHAR *link_name, *icon_name = NULL;
  2013. DWORD csidl = -1;
  2014. LPWSTR urlPath = NULL;
  2015. HRESULT hr;
  2016. HANDLE hSem = NULL;
  2017. BOOL ret = TRUE;
  2018. int r = -1;
  2019. IPropertySetStorage *pPropSetStg;
  2020. IPropertyStorage *pPropStg;
  2021. PROPSPEC ps[2];
  2022. PROPVARIANT pv[2];
  2023. BOOL has_icon = FALSE;
  2024. if ( !link )
  2025. {
  2026. WINE_ERR("Link name is null\n");
  2027. return TRUE;
  2028. }
  2029. if( !get_link_location( link, &csidl, &link_name ) )
  2030. {
  2031. WINE_WARN("Unknown link location %s. Ignoring.\n",wine_dbgstr_w(link));
  2032. return TRUE;
  2033. }
  2034. if (!in_desktop_dir(csidl) && !in_startmenu(csidl))
  2035. {
  2036. WINE_WARN("Not under desktop or start menu. Ignoring.\n");
  2037. ret = TRUE;
  2038. goto cleanup;
  2039. }
  2040. WINE_TRACE("Link : %s\n", wine_dbgstr_w(link_name));
  2041. hr = url->lpVtbl->GetURL(url, &urlPath);
  2042. if (FAILED(hr))
  2043. {
  2044. ret = TRUE;
  2045. goto cleanup;
  2046. }
  2047. WINE_TRACE("path : %s\n", wine_dbgstr_w(urlPath));
  2048. ps[0].ulKind = PRSPEC_PROPID;
  2049. ps[0].u.propid = PID_IS_ICONFILE;
  2050. ps[1].ulKind = PRSPEC_PROPID;
  2051. ps[1].u.propid = PID_IS_ICONINDEX;
  2052. hr = url->lpVtbl->QueryInterface(url, &IID_IPropertySetStorage, (void **) &pPropSetStg);
  2053. if (SUCCEEDED(hr))
  2054. {
  2055. hr = IPropertySetStorage_Open(pPropSetStg, &FMTID_Intshcut, STGM_READ | STGM_SHARE_EXCLUSIVE, &pPropStg);
  2056. if (SUCCEEDED(hr))
  2057. {
  2058. hr = IPropertyStorage_ReadMultiple(pPropStg, 2, ps, pv);
  2059. if (SUCCEEDED(hr))
  2060. {
  2061. if (pv[0].vt == VT_LPWSTR && pv[0].pwszVal && pv[0].pwszVal[0])
  2062. {
  2063. has_icon = TRUE;
  2064. icon_name = extract_icon( pv[0].pwszVal, pv[1].iVal, NULL, bWait );
  2065. WINE_TRACE("URL icon path: %s icon index: %d icon name: %s\n", wine_dbgstr_w(pv[0].pwszVal), pv[1].iVal, debugstr_w(icon_name));
  2066. }
  2067. PropVariantClear(&pv[0]);
  2068. PropVariantClear(&pv[1]);
  2069. }
  2070. IPropertyStorage_Release(pPropStg);
  2071. }
  2072. IPropertySetStorage_Release(pPropSetStg);
  2073. }
  2074. /* fail - try once again after parent process exit */
  2075. if( has_icon && !icon_name )
  2076. {
  2077. if (bWait)
  2078. {
  2079. WINE_WARN("Unable to extract icon, deferring.\n");
  2080. ret = FALSE;
  2081. goto cleanup;
  2082. }
  2083. WINE_ERR("failed to extract icon from %s\n",
  2084. wine_dbgstr_w( pv[0].pwszVal ));
  2085. }
  2086. hSem = CreateSemaphoreA( NULL, 1, 1, "winemenubuilder_semaphore");
  2087. if( WAIT_OBJECT_0 != MsgWaitForMultipleObjects( 1, &hSem, FALSE, INFINITE, QS_ALLINPUT ) )
  2088. {
  2089. WINE_ERR("failed wait for semaphore\n");
  2090. goto cleanup;
  2091. }
  2092. if (in_desktop_dir(csidl))
  2093. r = !write_desktop_entry(NULL, NULL, link_name, L"start.exe", urlPath, NULL, NULL, icon_name, NULL);
  2094. else
  2095. r = !write_menu_entry(link, link_name, L"start.exe", urlPath, NULL, NULL, icon_name, NULL);
  2096. ret = (r == 0);
  2097. ReleaseSemaphore(hSem, 1, NULL);
  2098. cleanup:
  2099. if (hSem)
  2100. CloseHandle(hSem);
  2101. heap_free(icon_name );
  2102. heap_free(link_name);
  2103. CoTaskMemFree( urlPath );
  2104. return ret;
  2105. }
  2106. static BOOL WaitForParentProcess( void )
  2107. {
  2108. PROCESSENTRY32 procentry;
  2109. HANDLE hsnapshot = NULL, hprocess = NULL;
  2110. DWORD ourpid = GetCurrentProcessId();
  2111. BOOL ret = FALSE, rc;
  2112. WINE_TRACE("Waiting for parent process\n");
  2113. if ((hsnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 )) ==
  2114. INVALID_HANDLE_VALUE)
  2115. {
  2116. WINE_ERR("CreateToolhelp32Snapshot failed, error %d\n", GetLastError());
  2117. goto done;
  2118. }
  2119. procentry.dwSize = sizeof(PROCESSENTRY32);
  2120. rc = Process32First( hsnapshot, &procentry );
  2121. while (rc)
  2122. {
  2123. if (procentry.th32ProcessID == ourpid) break;
  2124. rc = Process32Next( hsnapshot, &procentry );
  2125. }
  2126. if (!rc)
  2127. {
  2128. WINE_WARN("Unable to find current process id %d when listing processes\n", ourpid);
  2129. goto done;
  2130. }
  2131. if ((hprocess = OpenProcess( SYNCHRONIZE, FALSE, procentry.th32ParentProcessID )) ==
  2132. NULL)
  2133. {
  2134. WINE_WARN("OpenProcess failed pid=%d, error %d\n", procentry.th32ParentProcessID,
  2135. GetLastError());
  2136. goto done;
  2137. }
  2138. if (MsgWaitForMultipleObjects( 1, &hprocess, FALSE, INFINITE, QS_ALLINPUT ) == WAIT_OBJECT_0)
  2139. ret = TRUE;
  2140. else
  2141. WINE_ERR("Unable to wait for parent process, error %d\n", GetLastError());
  2142. done:
  2143. if (hprocess) CloseHandle( hprocess );
  2144. if (hsnapshot) CloseHandle( hsnapshot );
  2145. return ret;
  2146. }
  2147. static BOOL Process_Link( LPCWSTR linkname, BOOL bWait )
  2148. {
  2149. IShellLinkW *sl;
  2150. IPersistFile *pf;
  2151. HRESULT r;
  2152. WCHAR fullname[MAX_PATH];
  2153. DWORD len;
  2154. WINE_TRACE("%s, wait %d\n", wine_dbgstr_w(linkname), bWait);
  2155. if( !linkname[0] )
  2156. {
  2157. WINE_ERR("link name missing\n");
  2158. return FALSE;
  2159. }
  2160. len=GetFullPathNameW( linkname, MAX_PATH, fullname, NULL );
  2161. if (len==0 || len>MAX_PATH)
  2162. {
  2163. WINE_ERR("couldn't get full path of link file\n");
  2164. return FALSE;
  2165. }
  2166. r = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
  2167. &IID_IShellLinkW, (LPVOID *) &sl );
  2168. if( FAILED( r ) )
  2169. {
  2170. WINE_ERR("No IID_IShellLink\n");
  2171. return FALSE;
  2172. }
  2173. r = IShellLinkW_QueryInterface( sl, &IID_IPersistFile, (LPVOID*) &pf );
  2174. if( FAILED( r ) )
  2175. {
  2176. WINE_ERR("No IID_IPersistFile\n");
  2177. return FALSE;
  2178. }
  2179. r = IPersistFile_Load( pf, fullname, STGM_READ );
  2180. if( SUCCEEDED( r ) )
  2181. {
  2182. /* If something fails (eg. Couldn't extract icon)
  2183. * wait for parent process and try again
  2184. */
  2185. if( ! InvokeShellLinker( sl, fullname, bWait ) && bWait )
  2186. {
  2187. WaitForParentProcess();
  2188. InvokeShellLinker( sl, fullname, FALSE );
  2189. }
  2190. }
  2191. else
  2192. {
  2193. WINE_ERR("unable to load %s\n", wine_dbgstr_w(linkname));
  2194. }
  2195. IPersistFile_Release( pf );
  2196. IShellLinkW_Release( sl );
  2197. return !r;
  2198. }
  2199. static BOOL Process_URL( LPCWSTR urlname, BOOL bWait )
  2200. {
  2201. IUniformResourceLocatorW *url;
  2202. IPersistFile *pf;
  2203. HRESULT r;
  2204. WCHAR fullname[MAX_PATH];
  2205. DWORD len;
  2206. WINE_TRACE("%s, wait %d\n", wine_dbgstr_w(urlname), bWait);
  2207. if( !urlname[0] )
  2208. {
  2209. WINE_ERR("URL name missing\n");
  2210. return FALSE;
  2211. }
  2212. len=GetFullPathNameW( urlname, MAX_PATH, fullname, NULL );
  2213. if (len==0 || len>MAX_PATH)
  2214. {
  2215. WINE_ERR("couldn't get full path of URL file\n");
  2216. return FALSE;
  2217. }
  2218. r = CoCreateInstance( &CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER,
  2219. &IID_IUniformResourceLocatorW, (LPVOID *) &url );
  2220. if( FAILED( r ) )
  2221. {
  2222. WINE_ERR("No IID_IUniformResourceLocatorW\n");
  2223. return FALSE;
  2224. }
  2225. r = url->lpVtbl->QueryInterface( url, &IID_IPersistFile, (LPVOID*) &pf );
  2226. if( FAILED( r ) )
  2227. {
  2228. WINE_ERR("No IID_IPersistFile\n");
  2229. return FALSE;
  2230. }
  2231. r = IPersistFile_Load( pf, fullname, STGM_READ );
  2232. if( SUCCEEDED( r ) )
  2233. {
  2234. /* If something fails (eg. Couldn't extract icon)
  2235. * wait for parent process and try again
  2236. */
  2237. if( ! InvokeShellLinkerForURL( url, fullname, bWait ) && bWait )
  2238. {
  2239. WaitForParentProcess();
  2240. InvokeShellLinkerForURL( url, fullname, FALSE );
  2241. }
  2242. }
  2243. IPersistFile_Release( pf );
  2244. url->lpVtbl->Release( url );
  2245. return !r;
  2246. }
  2247. static void RefreshFileTypeAssociations(void)
  2248. {
  2249. HANDLE hSem = NULL;
  2250. WCHAR *mime_dir;
  2251. WCHAR *packages_dir;
  2252. WCHAR *applications_dir;
  2253. BOOL hasChanged;
  2254. hSem = CreateSemaphoreA( NULL, 1, 1, "winemenubuilder_semaphore");
  2255. if( WAIT_OBJECT_0 != MsgWaitForMultipleObjects( 1, &hSem, FALSE, INFINITE, QS_ALLINPUT ) )
  2256. {
  2257. WINE_ERR("failed wait for semaphore\n");
  2258. CloseHandle(hSem);
  2259. return;
  2260. }
  2261. mime_dir = heap_wprintf(L"%s\\mime", xdg_data_dir);
  2262. packages_dir = heap_wprintf(L"%s\\packages", mime_dir);
  2263. create_directories(packages_dir);
  2264. applications_dir = heap_wprintf(L"%s\\applications", xdg_data_dir);
  2265. create_directories(applications_dir);
  2266. hasChanged = generate_associations(packages_dir, applications_dir);
  2267. hasChanged |= cleanup_associations();
  2268. if (hasChanged)
  2269. {
  2270. const char *argv[3];
  2271. argv[0] = "update-mime-database";
  2272. argv[1] = wine_get_unix_file_name(mime_dir);
  2273. argv[2] = NULL;
  2274. __wine_unix_spawnvp( (char **)argv, FALSE );
  2275. argv[0] = "update-desktop-database";
  2276. argv[1] = wine_get_unix_file_name(applications_dir);
  2277. __wine_unix_spawnvp( (char **)argv, FALSE );
  2278. }
  2279. ReleaseSemaphore(hSem, 1, NULL);
  2280. CloseHandle(hSem);
  2281. heap_free(mime_dir);
  2282. heap_free(packages_dir);
  2283. heap_free(applications_dir);
  2284. }
  2285. static void cleanup_menus(void)
  2286. {
  2287. HKEY hkey;
  2288. hkey = open_menus_reg_key();
  2289. if (hkey)
  2290. {
  2291. int i;
  2292. LSTATUS lret = ERROR_SUCCESS;
  2293. for (i = 0; lret == ERROR_SUCCESS; )
  2294. {
  2295. WCHAR *value = NULL;
  2296. WCHAR *data = NULL;
  2297. DWORD valueSize = 4096;
  2298. DWORD dataSize = 4096;
  2299. while (1)
  2300. {
  2301. value = xmalloc(valueSize * sizeof(WCHAR));
  2302. data = xmalloc(dataSize * sizeof(WCHAR));
  2303. lret = RegEnumValueW(hkey, i, value, &valueSize, NULL, NULL, (BYTE*)data, &dataSize);
  2304. if (lret != ERROR_MORE_DATA)
  2305. break;
  2306. valueSize *= 2;
  2307. dataSize *= 2;
  2308. heap_free(value);
  2309. heap_free(data);
  2310. value = data = NULL;
  2311. }
  2312. if (lret == ERROR_SUCCESS)
  2313. {
  2314. if (GetFileAttributesW( data ) == INVALID_FILE_ATTRIBUTES)
  2315. {
  2316. WINE_TRACE("removing menu related file %s\n", debugstr_w(value));
  2317. DeleteFileW( value );
  2318. RegDeleteValueW(hkey, value);
  2319. }
  2320. else
  2321. i++;
  2322. }
  2323. else if (lret != ERROR_NO_MORE_ITEMS)
  2324. WINE_ERR("error %d reading registry\n", lret);
  2325. heap_free(value);
  2326. heap_free(data);
  2327. }
  2328. RegCloseKey(hkey);
  2329. }
  2330. }
  2331. static void thumbnail_lnk(LPCWSTR lnkPath, LPCWSTR outputPath)
  2332. {
  2333. char *utf8lnkPath = NULL;
  2334. WCHAR *winLnkPath = NULL;
  2335. IShellLinkW *shellLink = NULL;
  2336. IPersistFile *persistFile = NULL;
  2337. WCHAR szTmp[MAX_PATH];
  2338. WCHAR szPath[MAX_PATH];
  2339. WCHAR szArgs[INFOTIPSIZE];
  2340. WCHAR szIconPath[MAX_PATH];
  2341. int iconId;
  2342. IStream *stream = NULL;
  2343. ICONDIRENTRY *pIconDirEntries = NULL;
  2344. int numEntries;
  2345. HRESULT hr;
  2346. utf8lnkPath = wchars_to_utf8_chars(lnkPath);
  2347. winLnkPath = wine_get_dos_file_name(utf8lnkPath);
  2348. if (winLnkPath == NULL)
  2349. {
  2350. WINE_ERR("could not convert %s to DOS path\n", utf8lnkPath);
  2351. goto end;
  2352. }
  2353. hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
  2354. &IID_IShellLinkW, (LPVOID*)&shellLink);
  2355. if (FAILED(hr))
  2356. {
  2357. WINE_ERR("could not create IShellLinkW, error 0x%08X\n", hr);
  2358. goto end;
  2359. }
  2360. hr = IShellLinkW_QueryInterface(shellLink, &IID_IPersistFile, (LPVOID)&persistFile);
  2361. if (FAILED(hr))
  2362. {
  2363. WINE_ERR("could not query IPersistFile, error 0x%08X\n", hr);
  2364. goto end;
  2365. }
  2366. hr = IPersistFile_Load(persistFile, winLnkPath, STGM_READ);
  2367. if (FAILED(hr))
  2368. {
  2369. WINE_ERR("could not read .lnk, error 0x%08X\n", hr);
  2370. goto end;
  2371. }
  2372. get_cmdline(shellLink, szTmp, MAX_PATH, szArgs, INFOTIPSIZE);
  2373. ExpandEnvironmentStringsW(szTmp, szPath, MAX_PATH);
  2374. szTmp[0] = 0;
  2375. IShellLinkW_GetIconLocation(shellLink, szTmp, MAX_PATH, &iconId);
  2376. ExpandEnvironmentStringsW(szTmp, szIconPath, MAX_PATH);
  2377. if(!szPath[0])
  2378. {
  2379. LPITEMIDLIST pidl = NULL;
  2380. IShellLinkW_GetIDList(shellLink, &pidl);
  2381. if (pidl && SHGetPathFromIDListW(pidl, szPath))
  2382. WINE_TRACE("pidl path : %s\n", wine_dbgstr_w(szPath));
  2383. }
  2384. if (szIconPath[0])
  2385. {
  2386. hr = open_icon(szIconPath, iconId, FALSE, &stream, &pIconDirEntries, &numEntries);
  2387. if (SUCCEEDED(hr))
  2388. hr = write_native_icon(stream, pIconDirEntries, numEntries, outputPath);
  2389. }
  2390. else
  2391. {
  2392. hr = open_icon(szPath, iconId, FALSE, &stream, &pIconDirEntries, &numEntries);
  2393. if (SUCCEEDED(hr))
  2394. hr = write_native_icon(stream, pIconDirEntries, numEntries, outputPath);
  2395. }
  2396. end:
  2397. heap_free(utf8lnkPath);
  2398. heap_free(winLnkPath);
  2399. if (shellLink != NULL)
  2400. IShellLinkW_Release(shellLink);
  2401. if (persistFile != NULL)
  2402. IPersistFile_Release(persistFile);
  2403. if (stream != NULL)
  2404. IStream_Release(stream);
  2405. heap_free(pIconDirEntries);
  2406. }
  2407. static WCHAR *next_token( LPWSTR *p )
  2408. {
  2409. LPWSTR token = NULL, t = *p;
  2410. if( !t )
  2411. return NULL;
  2412. while( t && !token )
  2413. {
  2414. switch( *t )
  2415. {
  2416. case ' ':
  2417. t++;
  2418. continue;
  2419. case '"':
  2420. /* unquote the token */
  2421. token = ++t;
  2422. t = wcschr( token, '"' );
  2423. if( t )
  2424. *t++ = 0;
  2425. break;
  2426. case 0:
  2427. t = NULL;
  2428. break;
  2429. default:
  2430. token = t;
  2431. t = wcschr( token, ' ' );
  2432. if( t )
  2433. *t++ = 0;
  2434. break;
  2435. }
  2436. }
  2437. *p = t;
  2438. return token;
  2439. }
  2440. static BOOL init_xdg(void)
  2441. {
  2442. WCHAR *p;
  2443. HRESULT hr = SHGetFolderPathW(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, xdg_desktop_dir);
  2444. if (FAILED(hr)) return FALSE;
  2445. if ((p = _wgetenv( L"XDG_CONFIG_HOME" )))
  2446. xdg_menu_dir = heap_wprintf( L"\\??\\unix%s/menus/applications-merged", p );
  2447. else
  2448. xdg_menu_dir = heap_wprintf( L"%s/.config/menus/applications-merged", _wgetenv(L"WINEHOMEDIR") );
  2449. for (p = xdg_menu_dir; *p; p++) if (*p == '/') *p = '\\';
  2450. xdg_menu_dir[1] = '\\'; /* change \??\ to \\?\ */
  2451. create_directories(xdg_menu_dir);
  2452. if ((p = _wgetenv( L"XDG_DATA_HOME" )))
  2453. xdg_data_dir = heap_wprintf( L"\\??\\unix%s", p );
  2454. else
  2455. xdg_data_dir = heap_wprintf( L"%s/.local/share", _wgetenv(L"WINEHOMEDIR") );
  2456. for (p = xdg_data_dir; *p; p++) if (*p == '/') *p = '\\';
  2457. xdg_data_dir[1] = '\\'; /* change \??\ to \\?\ */
  2458. p = heap_wprintf( L"%s\\desktop-directories", xdg_data_dir );
  2459. create_directories(p);
  2460. heap_free(p);
  2461. return TRUE;
  2462. }
  2463. static BOOL associations_enabled(void)
  2464. {
  2465. BOOL ret = TRUE;
  2466. HKEY hkey;
  2467. BYTE buf[32];
  2468. DWORD len;
  2469. if ((hkey = open_associations_reg_key()))
  2470. {
  2471. len = sizeof(buf);
  2472. if (!RegQueryValueExA(hkey, "Enable", NULL, NULL, buf, &len))
  2473. ret = IS_OPTION_TRUE(buf[0]);
  2474. RegCloseKey( hkey );
  2475. }
  2476. return ret;
  2477. }
  2478. /***********************************************************************
  2479. *
  2480. * wWinMain
  2481. */
  2482. int PASCAL wWinMain (HINSTANCE hInstance, HINSTANCE prev, LPWSTR cmdline, int show)
  2483. {
  2484. LPWSTR token = NULL, p;
  2485. BOOL bWait = FALSE;
  2486. BOOL bURL = FALSE;
  2487. HRESULT hr;
  2488. int ret = 0;
  2489. if (!init_xdg())
  2490. return 1;
  2491. hr = CoInitialize(NULL);
  2492. if (FAILED(hr))
  2493. {
  2494. WINE_ERR("could not initialize COM, error 0x%08X\n", hr);
  2495. return 1;
  2496. }
  2497. for( p = cmdline; p && *p; )
  2498. {
  2499. token = next_token( &p );
  2500. if( !token )
  2501. break;
  2502. if( !wcscmp( token, L"-a" ) )
  2503. {
  2504. if (associations_enabled())
  2505. RefreshFileTypeAssociations();
  2506. continue;
  2507. }
  2508. if( !wcscmp( token, L"-r" ) )
  2509. {
  2510. cleanup_menus();
  2511. continue;
  2512. }
  2513. if( !wcscmp( token, L"-w" ) )
  2514. bWait = TRUE;
  2515. else if ( !wcscmp( token, L"-u" ) )
  2516. bURL = TRUE;
  2517. else if ( !wcscmp( token, L"-t" ) )
  2518. {
  2519. WCHAR *lnkFile = next_token( &p );
  2520. if (lnkFile)
  2521. {
  2522. WCHAR *outputFile = next_token( &p );
  2523. if (outputFile)
  2524. thumbnail_lnk(lnkFile, outputFile);
  2525. }
  2526. }
  2527. else if( token[0] == '-' )
  2528. {
  2529. WINE_ERR( "unknown option %s\n", wine_dbgstr_w(token) );
  2530. }
  2531. else
  2532. {
  2533. BOOL bRet;
  2534. if (bURL)
  2535. bRet = Process_URL( token, bWait );
  2536. else
  2537. bRet = Process_Link( token, bWait );
  2538. if (!bRet)
  2539. {
  2540. WINE_ERR( "failed to build menu item for %s\n", wine_dbgstr_w(token) );
  2541. ret = 1;
  2542. }
  2543. }
  2544. }
  2545. CoUninitialize();
  2546. return ret;
  2547. }