Replace functions which called once with their bodies
[pidgin-git.git] / libpurple / win32 / win32dep.c
blobd30ef12391bc8ac3e8c78cbe805884d7665c4371
1 /*
2 * purple
4 * File: win32dep.c
5 * Date: June, 2002
6 * Description: Windows dependant code for Purple
8 * Copyright (C) 2002-2003, Herman Bloggs <hermanator12002@yahoo.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
25 #include "internal.h"
26 #include <winuser.h>
28 #include "debug.h"
29 #include "glibcompat.h"
30 #include "notify.h"
32 #define MAX_PATH_LEN 2048
35 * LOCALS
37 static char *app_data_dir = NULL, *bin_dir = NULL, *data_dir = NULL,
38 *lib_dir = NULL, *locale_dir = NULL, *sysconf_dir = NULL,
39 *cert_dir = NULL;
41 static HINSTANCE libpurpledll_hInstance = NULL;
44 * PUBLIC CODE
47 /* Escape windows dir separators. This is needed when paths are saved,
48 and on being read back have their '\' chars used as an escape char.
49 Returns an allocated string which needs to be freed.
51 char *wpurple_escape_dirsep(const char *filename) {
52 int sepcount = 0;
53 const char *tmp = filename;
54 char *ret;
55 int cnt = 0;
57 g_return_val_if_fail(filename != NULL, NULL);
59 while(*tmp) {
60 if(*tmp == '\\')
61 sepcount++;
62 tmp++;
64 ret = g_malloc0(strlen(filename) + sepcount + 1);
65 while(*filename) {
66 ret[cnt] = *filename;
67 if(*filename == '\\')
68 ret[++cnt] = '\\';
69 filename++;
70 cnt++;
72 ret[cnt] = '\0';
73 return ret;
76 /* Determine whether the specified dll contains the specified procedure.
77 If so, load it (if not already loaded). */
78 FARPROC wpurple_find_and_loadproc(const char *dllname, const char *procedure) {
79 HMODULE hmod;
80 BOOL did_load = FALSE;
81 FARPROC proc = 0;
83 wchar_t *wc_dllname = g_utf8_to_utf16(dllname, -1, NULL, NULL, NULL);
85 if(!(hmod = GetModuleHandleW(wc_dllname))) {
86 if (purple_debug_is_verbose())
87 purple_debug_info("wpurple", "%s not already loaded; loading it...\n", dllname);
88 if(!(hmod = LoadLibraryW(wc_dllname))) {
89 purple_debug_error("wpurple", "Could not load: %s (%s)\n", dllname,
90 g_win32_error_message(GetLastError()));
91 g_free(wc_dllname);
92 return NULL;
94 else
95 did_load = TRUE;
98 g_free(wc_dllname);
99 wc_dllname = NULL;
101 if((proc = GetProcAddress(hmod, procedure))) {
102 if (purple_debug_is_verbose()) {
103 purple_debug_info("wpurple", "This version of %s contains %s\n",
104 dllname, procedure);
106 return proc;
108 else {
109 purple_debug_warning("wpurple", "Function %s not found in dll %s\n",
110 procedure, dllname);
111 if(did_load) {
112 /* unload dll */
113 FreeLibrary(hmod);
115 return NULL;
119 /* Determine Purple Paths during Runtime */
121 /* Get paths to special Windows folders. */
122 gchar *wpurple_get_special_folder(int folder_type) {
123 gchar *retval = NULL;
124 wchar_t utf_16_dir[MAX_PATH + 1];
126 if (SUCCEEDED(SHGetFolderPathW(NULL, folder_type, NULL,
127 SHGFP_TYPE_CURRENT, utf_16_dir))) {
128 retval = g_utf16_to_utf8(utf_16_dir, -1, NULL, NULL, NULL);
131 return retval;
134 const char *wpurple_bin_dir(void) {
135 static gboolean initialized = FALSE;
137 if (!initialized) {
138 char *tmp = NULL;
139 wchar_t winstall_dir[MAXPATHLEN];
141 /* We might use g_win32_get_package_installation_directory_of_module
142 * here, but we won't because this routine strips bin or lib
143 * part of the path.
145 if (GetModuleFileNameW(libpurpledll_hInstance, winstall_dir,
146 MAXPATHLEN) > 0) {
147 tmp = g_utf16_to_utf8(winstall_dir, -1,
148 NULL, NULL, NULL);
151 if (tmp == NULL) {
152 tmp = g_win32_error_message(GetLastError());
153 purple_debug_error("wpurple",
154 "GetModuleFileName error: %s\n", tmp);
155 g_free(tmp);
156 return NULL;
157 } else {
158 bin_dir = g_path_get_dirname(tmp);
159 g_free(tmp);
160 initialized = TRUE;
164 return bin_dir;
167 static gchar *
168 wpurple_install_relative_path(const gchar *abspath)
170 const gchar *bindir = WIN32_FHS_BINDIR;
171 const gchar *relpath;
172 int i, last_dirsep = -1, bin_esc_cnt;
173 gchar *ret;
174 GString *bin_esc;
176 g_return_val_if_fail(bindir != NULL, NULL);
177 g_return_val_if_fail(bindir[0] != '\0', NULL);
178 g_return_val_if_fail(abspath != NULL, NULL);
179 g_return_val_if_fail(abspath[0] != '\0', NULL);
181 /* let's find the common prefix of those paths */
182 for (i = 0; bindir[i] == abspath[i]; i++) {
183 if (bindir[i] == '\0')
184 break;
185 if (bindir[i] == '\\' || bindir[i] == '/')
186 last_dirsep = i;
188 if (bindir[i] == '\0' && (abspath[i] == '\\' || abspath[i] == '/'))
189 last_dirsep = i;
190 if (abspath[i] == '\0' && (bindir[i] == '\\' || bindir[i] == '/'))
191 last_dirsep = i;
193 /* there is no common prefix, return absolute path */
194 if (last_dirsep == -1)
195 return g_strdup(abspath);
197 /* let's check, how many dirs we need to go up to the common prefix */
198 bin_esc_cnt = 0;
199 for (i = last_dirsep; bindir[i]; i++) {
200 if (bindir[i] != '\\' && bindir[i] != '/')
201 continue;
202 if (bindir[i + 1] == '\0') /* trailing dir separator */
203 break;
204 bin_esc_cnt++;
206 bin_esc = g_string_new("");
207 for (i = 0; i < bin_esc_cnt; i++)
208 g_string_append(bin_esc, ".." G_DIR_SEPARATOR_S);
210 /* now, we need to go back deeper into the directory tree */
211 relpath = &abspath[last_dirsep];
212 if (relpath[0] != '\0')
213 relpath++;
215 /* - enter bin dir
216 * - escape it to the common prefix
217 * - dive into the abspath dir
219 ret = g_build_filename(wpurple_bin_dir(), bin_esc->str, relpath, NULL);
220 g_string_free(bin_esc, TRUE);
222 purple_debug_misc("wpurple", "wpurple_install_relative_path(%s) = %s",
223 abspath, ret);
225 return ret;
228 const char *
229 wpurple_data_dir(void) {
230 static gboolean initialized = FALSE;
231 if (initialized)
232 return data_dir;
233 data_dir = wpurple_install_relative_path(WIN32_FHS_DATADIR);
234 initialized = TRUE;
236 return data_dir;
240 const char *wpurple_lib_dir(const char *subdir)
242 static gboolean initialized = FALSE;
243 static gchar subpath[MAX_PATH_LEN];
245 if (!initialized) {
246 lib_dir = wpurple_install_relative_path(WIN32_FHS_LIBDIR);
247 initialized = TRUE;
250 if (subdir == NULL)
251 return lib_dir;
253 g_snprintf(subpath, sizeof(subpath),
254 "%s" G_DIR_SEPARATOR_S "%s", lib_dir, subdir);
255 return subpath;
258 const char *wpurple_locale_dir(void) {
259 static gboolean initialized = FALSE;
261 if (!initialized) {
262 locale_dir = wpurple_install_relative_path(WIN32_FHS_LOCALEDIR);
263 initialized = TRUE;
266 return locale_dir;
269 const char *wpurple_home_dir(void) {
271 if (!app_data_dir) {
272 /* Set app data dir, used by purple_home_dir */
273 const char *newenv = g_getenv("PURPLEHOME");
274 if (newenv)
275 app_data_dir = g_strdup(newenv);
276 else {
277 app_data_dir = wpurple_get_special_folder(CSIDL_APPDATA);
278 if (!app_data_dir)
279 app_data_dir = g_strdup("C:");
281 purple_debug_info("wpurple", "Purple settings dir: %s\n",
282 app_data_dir);
285 return app_data_dir;
288 const char *wpurple_sysconf_dir(void)
290 static gboolean initialized = FALSE;
292 if (!initialized) {
293 sysconf_dir = wpurple_install_relative_path(WIN32_FHS_SYSCONFDIR);
294 initialized = TRUE;
297 return sysconf_dir;
300 /* Miscellaneous */
302 gboolean wpurple_write_reg_string(HKEY rootkey, const char *subkey, const char *valname,
303 const char *value) {
304 HKEY reg_key;
305 gboolean success = FALSE;
307 wchar_t *wc_subkey = g_utf8_to_utf16(subkey, -1, NULL,
308 NULL, NULL);
310 if(RegOpenKeyExW(rootkey, wc_subkey, 0,
311 KEY_SET_VALUE, &reg_key) == ERROR_SUCCESS) {
312 wchar_t *wc_valname = NULL;
314 if (valname)
315 wc_valname = g_utf8_to_utf16(valname, -1,
316 NULL, NULL, NULL);
318 if(value) {
319 wchar_t *wc_value = g_utf8_to_utf16(value, -1,
320 NULL, NULL, NULL);
321 int len = (wcslen(wc_value) * sizeof(wchar_t)) + 1;
322 if(RegSetValueExW(reg_key, wc_valname, 0, REG_SZ,
323 (LPBYTE)wc_value, len
324 ) == ERROR_SUCCESS)
325 success = TRUE;
326 g_free(wc_value);
327 } else
328 if(RegDeleteValueW(reg_key, wc_valname) == ERROR_SUCCESS)
329 success = TRUE;
331 g_free(wc_valname);
333 g_free(wc_subkey);
335 if(reg_key != NULL)
336 RegCloseKey(reg_key);
338 return success;
341 static HKEY _reg_open_key(HKEY rootkey, const char *subkey, REGSAM access) {
342 HKEY reg_key = NULL;
343 LONG rv;
345 wchar_t *wc_subkey = g_utf8_to_utf16(subkey, -1, NULL,
346 NULL, NULL);
347 rv = RegOpenKeyExW(rootkey, wc_subkey, 0, access, &reg_key);
349 g_free(wc_subkey);
351 if (rv != ERROR_SUCCESS) {
352 char *errmsg = g_win32_error_message(rv);
353 purple_debug_error("wpurple", "Could not open reg key '%s' subkey '%s'.\nMessage: (%ld) %s\n",
354 ((rootkey == HKEY_LOCAL_MACHINE) ? "HKLM" :
355 (rootkey == HKEY_CURRENT_USER) ? "HKCU" :
356 (rootkey == HKEY_CLASSES_ROOT) ? "HKCR" : "???"),
357 subkey, rv, errmsg);
358 g_free(errmsg);
361 return reg_key;
364 static gboolean _reg_read(HKEY reg_key, const char *valname, LPDWORD type, LPBYTE data, LPDWORD data_len) {
365 LONG rv;
367 wchar_t *wc_valname = NULL;
368 if (valname)
369 wc_valname = g_utf8_to_utf16(valname, -1, NULL, NULL, NULL);
370 rv = RegQueryValueExW(reg_key, wc_valname, 0, type, data, data_len);
371 g_free(wc_valname);
373 if (rv != ERROR_SUCCESS) {
374 char *errmsg = g_win32_error_message(rv);
375 purple_debug_error("wpurple", "Could not read from reg key value '%s'.\nMessage: (%ld) %s\n",
376 valname, rv, errmsg);
377 g_free(errmsg);
380 return (rv == ERROR_SUCCESS);
383 gboolean wpurple_reg_val_exists(HKEY rootkey, const char *subkey, const char *valname)
385 HKEY hkey;
386 LONG retv;
387 DWORD index;
388 wchar_t name_buffer[100];
389 BOOL exists = FALSE;
390 wchar_t *wc_valname = NULL;
391 wchar_t *wc_subkey;
393 if (subkey == NULL)
394 return FALSE;
396 wc_subkey = g_utf8_to_utf16(subkey, -1, NULL, NULL, NULL);
397 retv = RegOpenKeyExW(rootkey, wc_subkey, 0, KEY_ENUMERATE_SUB_KEYS, &hkey);
398 g_free(wc_subkey);
400 if (retv != ERROR_SUCCESS)
401 return FALSE;
403 if (valname[0] == '\0' || valname == NULL) {
404 RegCloseKey(hkey);
405 return TRUE;
408 wc_valname = g_utf8_to_utf16(valname, -1, NULL, NULL, NULL);
409 index = 0;
410 while (TRUE)
412 DWORD name_size = sizeof(name_buffer);
413 retv = RegEnumValueW(hkey, index++, name_buffer, &name_size,
414 NULL, NULL, NULL, NULL);
415 if (retv != ERROR_SUCCESS)
416 break;
417 name_size /= sizeof(wchar_t);
418 if (wcsncmp(name_buffer, wc_valname, name_size) == 0) {
419 exists = TRUE;
420 break;
423 g_free(wc_valname);
425 RegCloseKey(hkey);
426 return exists;
429 gboolean wpurple_read_reg_dword(HKEY rootkey, const char *subkey, const char *valname, LPDWORD result) {
431 DWORD type;
432 DWORD nbytes;
433 HKEY reg_key = _reg_open_key(rootkey, subkey, KEY_QUERY_VALUE);
434 gboolean success = FALSE;
436 if(reg_key) {
437 if(_reg_read(reg_key, valname, &type, (LPBYTE)result, &nbytes))
438 success = TRUE;
439 RegCloseKey(reg_key);
442 return success;
445 char *wpurple_read_reg_string(HKEY rootkey, const char *subkey, const char *valname) {
447 DWORD type;
448 DWORD nbytes;
449 HKEY reg_key = _reg_open_key(rootkey, subkey, KEY_QUERY_VALUE);
450 char *result = NULL;
452 if(reg_key) {
453 if(_reg_read(reg_key, valname, &type, NULL, &nbytes) && type == REG_SZ) {
454 LPBYTE data = (LPBYTE) g_new(wchar_t, ((nbytes + 1) / sizeof(wchar_t)) + 1);
456 if(_reg_read(reg_key, valname, &type, data, &nbytes)) {
457 wchar_t *wc_temp = (wchar_t*) data;
458 wc_temp[nbytes / sizeof(wchar_t)] = '\0';
459 result = g_utf16_to_utf8(wc_temp, -1,
460 NULL, NULL, NULL);
462 g_free(data);
464 RegCloseKey(reg_key);
467 return result;
470 int wpurple_input_pipe(int pipefd[2])
472 SOCKET sock_server, sock_client, sock_server_established;
473 struct sockaddr_in saddr_in;
474 struct sockaddr * const saddr_p = (struct sockaddr *)&saddr_in;
475 int saddr_len = sizeof(struct sockaddr_in);
476 u_long arg;
477 fd_set select_set;
478 char succ = 1;
480 sock_server = sock_client = sock_server_established = INVALID_SOCKET;
482 purple_debug_misc("wpurple", "wpurple_input_pipe(%p[%d,%d])\n", pipefd,
483 pipefd[0], pipefd[1]);
485 /* create client and passive server sockets */
486 sock_server = socket(AF_INET, SOCK_STREAM, 0);
487 sock_client = socket(AF_INET, SOCK_STREAM, 0);
488 succ = (sock_server != INVALID_SOCKET || sock_client != INVALID_SOCKET);
490 /* set created sockets into nonblocking mode */
491 arg = 1;
492 succ = (succ &&
493 ioctlsocket(sock_server, FIONBIO, &arg) != SOCKET_ERROR);
494 arg = 1;
495 succ = (succ &&
496 ioctlsocket(sock_client, FIONBIO, &arg) != SOCKET_ERROR);
498 /* listen on server socket */
499 memset(&saddr_in, 0, saddr_len);
500 saddr_in.sin_family = AF_INET;
501 saddr_in.sin_port = 0;
502 saddr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
503 succ = (succ &&
504 bind(sock_server, saddr_p, saddr_len) != SOCKET_ERROR &&
505 listen(sock_server, 1) != SOCKET_ERROR &&
506 getsockname(sock_server, saddr_p, &saddr_len) != SOCKET_ERROR);
508 /* request a connection from client to server socket */
509 succ = (succ &&
510 connect(sock_client, saddr_p, saddr_len) == SOCKET_ERROR &&
511 WSAGetLastError() == WSAEWOULDBLOCK);
513 /* ensure, that server socket is readable */
514 if (succ)
516 FD_ZERO(&select_set);
517 FD_SET(sock_server, &select_set);
519 succ = (succ &&
520 select(0, &select_set, NULL, NULL, NULL) != SOCKET_ERROR &&
521 FD_ISSET(sock_server, &select_set));
523 /* accept (establish) connection from client socket */
524 if (succ)
526 sock_server_established =
527 accept(sock_server, saddr_p, &saddr_len);
528 succ = (sock_server_established != INVALID_SOCKET);
531 /* ensure, that client socket is writable */
532 if (succ)
534 FD_ZERO(&select_set);
535 FD_SET(sock_client, &select_set);
537 succ = (succ &&
538 select(0, NULL, &select_set, NULL, NULL) != SOCKET_ERROR &&
539 FD_ISSET(sock_client, &select_set));
541 /* set sockets into blocking mode */
542 arg = 0;
543 succ = (succ &&
544 ioctlsocket(sock_client, FIONBIO, &arg) != SOCKET_ERROR);
545 arg = 0;
546 succ = (succ &&
547 ioctlsocket(sock_server_established, FIONBIO, &arg)
548 != SOCKET_ERROR);
550 /* we don't need (passive) server socket anymore */
551 if (sock_server != INVALID_SOCKET)
552 closesocket(sock_server);
554 if (succ)
556 purple_debug_misc("wpurple",
557 "wpurple_input_pipe created pipe [%" G_GUINTPTR_FORMAT
558 ",%" G_GUINTPTR_FORMAT "]\n",
559 sock_client, sock_server_established);
560 pipefd[0] = sock_client; /* for reading */
561 pipefd[1] = sock_server_established; /* for writing */
562 return 0;
564 else
566 purple_debug_error("wpurple", "wpurple_input_pipe failed\n");
567 if (sock_client != INVALID_SOCKET)
568 closesocket(sock_client);
569 if (sock_server_established != INVALID_SOCKET)
570 closesocket(sock_server_established);
571 errno = EMFILE;
572 return -1;
576 void wpurple_init(void) {
577 WORD wVersionRequested;
578 WSADATA wsaData;
580 if (purple_debug_is_verbose())
581 purple_debug_misc("wpurple", "wpurple_init start\n");
583 purple_debug_info("wpurple", "libpurple version: " DISPLAY_VERSION "\n");
584 purple_debug_info("wpurple", "Glib: %u.%u.%u\n",
585 glib_major_version, glib_minor_version, glib_micro_version);
587 /* Winsock init */
588 wVersionRequested = MAKEWORD(2, 2);
589 WSAStartup(wVersionRequested, &wsaData);
591 /* Confirm that the winsock DLL supports 2.2 */
592 /* Note that if the DLL supports versions greater than
593 2.2 in addition to 2.2, it will still return 2.2 in
594 wVersion since that is the version we requested. */
595 if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
596 purple_debug_error("wpurple", "Could not find a usable WinSock DLL. Oh well.\n");
597 WSACleanup();
600 if (purple_debug_is_verbose())
601 purple_debug_misc("wpurple", "wpurple_init end\n");
604 /* Windows Cleanup */
606 void wpurple_cleanup(void) {
607 purple_debug_info("wpurple", "wpurple_cleanup\n");
609 /* winsock cleanup */
610 WSACleanup();
612 g_free(app_data_dir);
613 g_free(bin_dir);
614 g_free(data_dir);
615 g_free(lib_dir);
616 g_free(locale_dir);
617 g_free(sysconf_dir);
618 g_free(cert_dir);
620 app_data_dir = NULL;
621 bin_dir = NULL;
622 data_dir = NULL;
623 lib_dir = NULL;
624 locale_dir = NULL;
625 sysconf_dir = NULL;
626 cert_dir = NULL;
628 libpurpledll_hInstance = NULL;
631 /* DLL initializer */
632 /* suppress gcc "no previous prototype" warning */
633 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);
634 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
635 libpurpledll_hInstance = hinstDLL;
636 return TRUE;