Revert "winecrt0: Make the main() and wmain() entry points cdecl."
[wine.git] / programs / winebrowser / main.c
blob7c82d674f663698f8bc7e8c7c50d6fa64aa08d06
1 /*
2 * winebrowser - winelib app to launch native OS browser or mail client.
4 * Copyright (C) 2004 Chris Morgan
5 * Copyright (C) 2005 Hans Leidekker
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * NOTES:
22 * Winebrowser is a winelib application that will start the appropriate
23 * native browser or mail client for a wine installation that lacks a
24 * windows browser/mail client. For example, you will be able to open
25 * URLs via native mozilla if no browser has yet been installed in wine.
27 * The application to launch is chosen from a default set or, if set,
28 * taken from a registry key.
30 * The argument may be a regular Windows file name, a file URL, an
31 * URL or a mailto URL. In the first three cases the argument
32 * will be fed to a web browser. In the last case the argument is fed
33 * to a mail client. A mailto URL is composed as follows:
35 * mailto:[E-MAIL]?subject=[TOPIC]&cc=[E-MAIL]&bcc=[E-MAIL]&body=[TEXT]
38 #define WIN32_LEAN_AND_MEAN
39 #define COBJMACROS
41 #include "config.h"
42 #include "wine/port.h"
43 #include "wine/debug.h"
44 #include "wine/unicode.h"
46 #include <windows.h>
47 #include <shlwapi.h>
48 #include <shellapi.h>
49 #include <urlmon.h>
50 #include <ddeml.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <errno.h>
55 WINE_DEFAULT_DEBUG_CHANNEL(winebrowser);
57 typedef LPSTR (*CDECL wine_get_unix_file_name_t)(LPCWSTR unixname);
59 static const WCHAR browser_key[] =
60 {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
61 'W','i','n','e','B','r','o','w','s','e','r',0};
63 static char *strdup_unixcp( const WCHAR *str )
65 char *ret;
66 int len = WideCharToMultiByte( CP_UNIXCP, 0, str, -1, NULL, 0, NULL, NULL );
67 if ((ret = HeapAlloc( GetProcessHeap(), 0, len )))
68 WideCharToMultiByte( CP_UNIXCP, 0, str, -1, ret, len, NULL, NULL );
69 return ret;
72 /* try to launch a unix app from a comma separated string of app names */
73 static int launch_app( const WCHAR *candidates, const WCHAR *argv1 )
75 char *cmdline;
76 int i, count;
77 char **argv_new;
79 if (!(cmdline = strdup_unixcp( argv1 ))) return 1;
81 while (*candidates)
83 WCHAR **args = CommandLineToArgvW( candidates, &count );
85 if (!(argv_new = HeapAlloc( GetProcessHeap(), 0, (count + 2) * sizeof(*argv_new) ))) break;
86 for (i = 0; i < count; i++) argv_new[i] = strdup_unixcp( args[i] );
87 argv_new[count] = cmdline;
88 argv_new[count + 1] = NULL;
90 TRACE( "Trying" );
91 for (i = 0; i <= count; i++) TRACE( " %s", wine_dbgstr_a( argv_new[i] ));
92 TRACE( "\n" );
94 _spawnvp( _P_OVERLAY, argv_new[0], (const char **)argv_new ); /* only returns on error */
95 for (i = 0; i < count; i++) HeapFree( GetProcessHeap(), 0, argv_new[i] );
96 HeapFree( GetProcessHeap(), 0, argv_new );
97 candidates += strlenW( candidates ) + 1; /* grab the next app */
99 WINE_ERR( "could not find a suitable app to open %s\n", debugstr_w( argv1 ));
101 HeapFree( GetProcessHeap(), 0, cmdline );
102 return 1;
105 static LSTATUS get_commands( HKEY key, const WCHAR *value, WCHAR *buffer, DWORD size )
107 DWORD type;
108 LSTATUS res;
110 size -= sizeof(WCHAR);
111 if (!(res = RegQueryValueExW( key, value, 0, &type, (LPBYTE)buffer, &size )) && (type == REG_SZ))
113 /* convert to REG_MULTI_SZ type */
114 WCHAR *p = buffer;
115 p[strlenW(p) + 1] = 0;
116 while ((p = strchrW( p, ',' ))) *p++ = 0;
118 return res;
121 static int open_http_url( const WCHAR *url )
123 #ifdef __APPLE__
124 static const WCHAR defaultbrowsers[] =
125 { '/','u','s','r','/','b','i','n','/','o','p','e','n',0,0 };
126 #else
127 static const WCHAR defaultbrowsers[] =
128 {'x','d','g','-','o','p','e','n',0,
129 'f','i','r','e','f','o','x',0,
130 'k','o','n','q','u','e','r','o','r',0,
131 'm','o','z','i','l','l','a',0,
132 'n','e','t','s','c','a','p','e',0,
133 'g','a','l','e','o','n',0,
134 'o','p','e','r','a',0,
135 'd','i','l','l','o',0,0};
136 #endif
137 static const WCHAR browsersW[] =
138 {'B','r','o','w','s','e','r','s',0};
140 WCHAR browsers[256];
141 HKEY key;
142 LONG r;
144 /* @@ Wine registry key: HKCU\Software\Wine\WineBrowser */
145 if (!(r = RegOpenKeyW( HKEY_CURRENT_USER, browser_key, &key )))
147 r = get_commands( key, browsersW, browsers, sizeof(browsers) );
148 RegCloseKey( key );
150 if (r != ERROR_SUCCESS)
151 memcpy( browsers, defaultbrowsers, sizeof(defaultbrowsers) );
153 return launch_app( browsers, url );
156 static int open_mailto_url( const WCHAR *url )
158 #ifdef __APPLE__
159 static const WCHAR defaultmailers[] =
160 { '/','u','s','r','/','b','i','n','/','o','p','e','n',0,0 };
161 #else
162 static const WCHAR defaultmailers[] =
163 {'x','d','g','-','e','m','a','i','l',0,
164 'm','o','z','i','l','l','a','-','t','h','u','n','d','e','r','b','i','r','d',0,
165 't','h','u','n','d','e','r','b','i','r','d',0,
166 'e','v','o','l','u','t','i','o','n',0,0 };
167 #endif
168 static const WCHAR mailersW[] =
169 {'M','a','i','l','e','r','s',0};
171 WCHAR mailers[256];
172 HKEY key;
173 LONG r;
175 /* @@ Wine registry key: HKCU\Software\Wine\WineBrowser */
176 if (!(r = RegOpenKeyW( HKEY_CURRENT_USER, browser_key, &key )))
178 r = get_commands( key, mailersW, mailers, sizeof(mailers) );
179 RegCloseKey( key );
181 if (r != ERROR_SUCCESS)
182 memcpy( mailers, defaultmailers, sizeof(defaultmailers) );
184 return launch_app( mailers, url );
187 static int open_invalid_url( const WCHAR *url )
189 static const WCHAR httpW[] =
190 {'h','t','t','p',':','/','/',0};
192 WCHAR *url_prefixed;
193 int ret;
195 url_prefixed = HeapAlloc( GetProcessHeap(), 0, (ARRAY_SIZE(httpW) + strlenW( url )) * sizeof(WCHAR) );
196 if (!url_prefixed)
198 WINE_ERR("Out of memory\n");
199 return 1;
202 strcpyW( url_prefixed, httpW );
203 strcatW( url_prefixed, url );
205 ret = open_http_url( url_prefixed );
206 HeapFree( GetProcessHeap(), 0, url_prefixed );
207 return ret;
210 /*****************************************************************************
211 * DDE helper functions.
214 static WCHAR *ddeString = NULL;
215 static HSZ hszTopic = 0, hszReturn = 0;
216 static DWORD ddeInst = 0;
218 /* Dde callback, save the execute or request string for processing */
219 static HDDEDATA CALLBACK ddeCb(UINT uType, UINT uFmt, HCONV hConv,
220 HSZ hsz1, HSZ hsz2, HDDEDATA hData,
221 ULONG_PTR dwData1, ULONG_PTR dwData2)
223 DWORD size = 0, ret = 0;
225 WINE_TRACE("dde_cb: %04x, %04x, %p, %p, %p, %p, %08lx, %08lx\n",
226 uType, uFmt, hConv, hsz1, hsz2, hData, dwData1, dwData2);
228 switch (uType)
230 case XTYP_CONNECT:
231 if (!DdeCmpStringHandles(hsz1, hszTopic))
232 return (HDDEDATA)TRUE;
233 return (HDDEDATA)FALSE;
235 case XTYP_EXECUTE:
236 if (!(size = DdeGetData(hData, NULL, 0, 0)))
237 WINE_ERR("DdeGetData returned zero size of execute string\n");
238 else if (!(ddeString = HeapAlloc(GetProcessHeap(), 0, size)))
239 WINE_ERR("Out of memory\n");
240 else if (DdeGetData(hData, (LPBYTE)ddeString, size, 0) != size)
241 WINE_WARN("DdeGetData did not return %d bytes\n", size);
242 DdeFreeDataHandle(hData);
243 return (HDDEDATA)DDE_FACK;
245 case XTYP_REQUEST:
246 ret = -3; /* error */
247 if (!(size = DdeQueryStringW(ddeInst, hsz2, NULL, 0, CP_WINUNICODE)))
248 WINE_ERR("DdeQueryString returned zero size of request string\n");
249 else if (!(ddeString = HeapAlloc(GetProcessHeap(), 0, (size + 1) * sizeof(WCHAR))))
250 WINE_ERR("Out of memory\n");
251 else if (DdeQueryStringW(ddeInst, hsz2, ddeString, size + 1, CP_WINUNICODE) != size)
252 WINE_WARN("DdeQueryString did not return %d characters\n", size);
253 else
254 ret = -2; /* acknowledgment */
255 return DdeCreateDataHandle(ddeInst, (LPBYTE)&ret, sizeof(ret), 0,
256 hszReturn, CF_TEXT, 0);
258 default:
259 return NULL;
263 static WCHAR *get_url_from_dde(void)
265 static const WCHAR szApplication[] = {'I','E','x','p','l','o','r','e',0};
266 static const WCHAR szTopic[] = {'W','W','W','_','O','p','e','n','U','R','L',0};
267 static const WCHAR szReturn[] = {'R','e','t','u','r','n',0};
269 HSZ hszApplication = 0;
270 UINT_PTR timer = 0;
271 int rc;
272 WCHAR *ret = NULL;
274 rc = DdeInitializeW(&ddeInst, ddeCb, CBF_SKIP_ALLNOTIFICATIONS | CBF_FAIL_ADVISES | CBF_FAIL_POKES, 0);
275 if (rc != DMLERR_NO_ERROR)
277 WINE_ERR("Unable to initialize DDE, DdeInitialize returned %d\n", rc);
278 goto done;
281 hszApplication = DdeCreateStringHandleW(ddeInst, szApplication, CP_WINUNICODE);
282 if (!hszApplication)
284 WINE_ERR("Unable to initialize DDE, DdeCreateStringHandle failed\n");
285 goto done;
288 hszTopic = DdeCreateStringHandleW(ddeInst, szTopic, CP_WINUNICODE);
289 if (!hszTopic)
291 WINE_ERR("Unable to initialize DDE, DdeCreateStringHandle failed\n");
292 goto done;
295 hszReturn = DdeCreateStringHandleW(ddeInst, szReturn, CP_WINUNICODE);
296 if (!hszReturn)
298 WINE_ERR("Unable to initialize DDE, DdeCreateStringHandle failed\n");
299 goto done;
302 if (!DdeNameService(ddeInst, hszApplication, 0, DNS_REGISTER))
304 WINE_ERR("Unable to initialize DDE, DdeNameService failed\n");
305 goto done;
308 timer = SetTimer(NULL, 0, 5000, NULL);
309 if (!timer)
311 WINE_ERR("SetTimer failed to create timer\n");
312 goto done;
315 while (!ddeString)
317 MSG msg;
318 if (!GetMessageW(&msg, NULL, 0, 0)) break;
319 if (msg.message == WM_TIMER) break;
320 DispatchMessageW(&msg);
323 if (ddeString)
325 if (*ddeString == '"')
327 WCHAR *endquote = strchrW(ddeString + 1, '"');
328 if (!endquote)
330 WINE_ERR("Unable to retrieve URL from string %s\n", wine_dbgstr_w(ddeString));
331 goto done;
333 *endquote = 0;
334 ret = ddeString+1;
336 else
337 ret = ddeString;
340 done:
341 if (timer) KillTimer(NULL, timer);
342 if (ddeInst)
344 if (hszTopic && hszApplication) DdeNameService(ddeInst, hszApplication, 0, DNS_UNREGISTER);
345 if (hszReturn) DdeFreeStringHandle(ddeInst, hszReturn);
346 if (hszTopic) DdeFreeStringHandle(ddeInst, hszTopic);
347 if (hszApplication) DdeFreeStringHandle(ddeInst, hszApplication);
348 DdeUninitialize(ddeInst);
350 return ret;
353 static WCHAR *encode_unix_path(const char *src)
355 const char *tmp_src;
356 WCHAR *dst, *tmp_dst;
357 const char safe_chars[] = "/-_.~@&=+$,:";
358 const char hex_digits[] = "0123456789ABCDEF";
359 const WCHAR schema[] = {'f','i','l','e',':','/','/',0};
360 int len = ARRAY_SIZE(schema);
362 tmp_src = src;
364 while (*tmp_src != 0)
366 if ((*tmp_src >= 'a' && *tmp_src <= 'z') ||
367 (*tmp_src >= 'A' && *tmp_src <= 'Z') ||
368 (*tmp_src >= '0' && *tmp_src <= '9') ||
369 strchr(safe_chars, *tmp_src))
370 len += 1;
371 else
372 len += 3;
373 tmp_src++;
376 dst = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
378 if (!dst)
379 return NULL;
381 strcpyW(dst, schema);
383 tmp_src = src;
384 tmp_dst = dst + strlenW(dst);
386 while (*tmp_src != 0)
388 if ((*tmp_src >= 'a' && *tmp_src <= 'z') ||
389 (*tmp_src >= 'A' && *tmp_src <= 'Z') ||
390 (*tmp_src >= '0' && *tmp_src <= '9') ||
391 strchr(safe_chars, *tmp_src))
393 *tmp_dst++ = *tmp_src;
395 else
397 *tmp_dst++ = '%';
398 *tmp_dst++ = hex_digits[*(unsigned char*)(tmp_src) / 16];
399 *tmp_dst++ = hex_digits[*tmp_src & 0xf];
401 tmp_src++;
404 *tmp_dst = 0;
406 return dst;
409 static WCHAR *convert_file_uri(IUri *uri)
411 wine_get_unix_file_name_t wine_get_unix_file_name_ptr;
412 struct stat dummy;
413 WCHAR *new_path;
414 char *unixpath;
415 BSTR filename;
416 HRESULT hres;
418 /* check if the argument is a local file */
419 wine_get_unix_file_name_ptr = (wine_get_unix_file_name_t)
420 GetProcAddress( GetModuleHandleA( "KERNEL32" ), "wine_get_unix_file_name" );
421 if(!wine_get_unix_file_name_ptr)
422 return NULL;
424 hres = IUri_GetPath(uri, &filename);
425 if(FAILED(hres))
426 return NULL;
428 WINE_TRACE("Windows path: %s\n", wine_dbgstr_w(filename));
430 unixpath = wine_get_unix_file_name_ptr(filename);
431 SysFreeString(filename);
432 if(unixpath && stat(unixpath, &dummy) >= 0) {
433 WINE_TRACE("Unix path: %s\n", wine_dbgstr_a(unixpath));
434 new_path = encode_unix_path(unixpath);
435 HeapFree(GetProcessHeap(), 0, unixpath);
436 }else {
437 WINE_WARN("File %s does not exist\n", wine_dbgstr_a(unixpath));
438 HeapFree(GetProcessHeap(), 0, unixpath);
439 new_path = NULL;
442 WINE_TRACE("New path: %s\n", wine_dbgstr_w(new_path));
444 return new_path;
447 /*****************************************************************************
448 * Main entry point. This is a console application so we have a wmain() not a
449 * winmain().
451 int wmain(int argc, WCHAR *argv[])
453 static const WCHAR nohomeW[] = {'-','n','o','h','o','m','e',0};
455 WCHAR *url = argv[1];
456 BSTR display_uri = NULL;
457 DWORD scheme;
458 IUri *uri;
459 HRESULT hres;
460 int ret = 1;
462 /* DDE used only if -nohome is specified; avoids delay in printing usage info
463 * when no parameters are passed */
464 if (url && !strcmpiW( url, nohomeW ))
465 url = argc > 2 ? argv[2] : get_url_from_dde();
467 if (!url) {
468 WINE_ERR( "Usage: winebrowser URL\n" );
469 return -1;
472 hres = CreateUri(url, Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME|Uri_CREATE_FILE_USE_DOS_PATH, 0, &uri);
473 if(FAILED(hres)) {
474 WINE_ERR("Failed to parse URL %s, treating as HTTP\n", wine_dbgstr_w(url));
475 ret = open_invalid_url(url);
476 HeapFree(GetProcessHeap(), 0, ddeString);
477 return ret;
480 HeapFree(GetProcessHeap(), 0, ddeString);
481 IUri_GetScheme(uri, &scheme);
483 if(scheme == URL_SCHEME_FILE) {
484 display_uri = convert_file_uri(uri);
485 if(!display_uri) {
486 WINE_ERR("Failed to convert file URL to unix path\n");
490 if (!display_uri)
491 hres = IUri_GetDisplayUri(uri, &display_uri);
492 IUri_Release(uri);
493 if(FAILED(hres))
494 return -1;
496 WINE_TRACE("opening %s\n", wine_dbgstr_w(display_uri));
498 if(scheme == URL_SCHEME_MAILTO)
499 ret = open_mailto_url(display_uri);
500 else
501 /* let the browser decide how to handle the given url */
502 ret = open_http_url(display_uri);
504 SysFreeString(display_uri);
505 return ret;