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
29 #include "glibcompat.h"
32 #define MAX_PATH_LEN 2048
37 static char *app_data_dir
= NULL
, *bin_dir
= NULL
, *data_dir
= NULL
,
38 *lib_dir
= NULL
, *locale_dir
= NULL
, *sysconf_dir
= NULL
,
41 static HINSTANCE libpurpledll_hInstance
= NULL
;
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
) {
53 const char *tmp
= filename
;
57 g_return_val_if_fail(filename
!= NULL
, NULL
);
64 ret
= g_malloc0(strlen(filename
) + sepcount
+ 1);
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
) {
80 BOOL did_load
= FALSE
;
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()));
101 if((proc
= GetProcAddress(hmod
, procedure
))) {
102 if (purple_debug_is_verbose()) {
103 purple_debug_info("wpurple", "This version of %s contains %s\n",
109 purple_debug_warning("wpurple", "Function %s not found in dll %s\n",
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
);
134 const char *wpurple_bin_dir(void) {
135 static gboolean initialized
= FALSE
;
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
145 if (GetModuleFileNameW(libpurpledll_hInstance
, winstall_dir
,
147 tmp
= g_utf16_to_utf8(winstall_dir
, -1,
152 tmp
= g_win32_error_message(GetLastError());
153 purple_debug_error("wpurple",
154 "GetModuleFileName error: %s\n", tmp
);
158 bin_dir
= g_path_get_dirname(tmp
);
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
;
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')
185 if (bindir
[i
] == '\\' || bindir
[i
] == '/')
188 if (bindir
[i
] == '\0' && (abspath
[i
] == '\\' || abspath
[i
] == '/'))
190 if (abspath
[i
] == '\0' && (bindir
[i
] == '\\' || bindir
[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 */
199 for (i
= last_dirsep
; bindir
[i
]; i
++) {
200 if (bindir
[i
] != '\\' && bindir
[i
] != '/')
202 if (bindir
[i
+ 1] == '\0') /* trailing dir separator */
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')
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",
229 wpurple_data_dir(void) {
230 static gboolean initialized
= FALSE
;
233 data_dir
= wpurple_install_relative_path(WIN32_FHS_DATADIR
);
240 const char *wpurple_lib_dir(const char *subdir
)
242 static gboolean initialized
= FALSE
;
243 static gchar subpath
[MAX_PATH_LEN
];
246 lib_dir
= wpurple_install_relative_path(WIN32_FHS_LIBDIR
);
253 g_snprintf(subpath
, sizeof(subpath
),
254 "%s" G_DIR_SEPARATOR_S
"%s", lib_dir
, subdir
);
258 const char *wpurple_locale_dir(void) {
259 static gboolean initialized
= FALSE
;
262 locale_dir
= wpurple_install_relative_path(WIN32_FHS_LOCALEDIR
);
269 const char *wpurple_home_dir(void) {
272 /* Set app data dir, used by purple_home_dir */
273 const char *newenv
= g_getenv("PURPLEHOME");
275 app_data_dir
= g_strdup(newenv
);
277 app_data_dir
= wpurple_get_special_folder(CSIDL_APPDATA
);
279 app_data_dir
= g_strdup("C:");
281 purple_debug_info("wpurple", "Purple settings dir: %s\n",
288 const char *wpurple_sysconf_dir(void)
290 static gboolean initialized
= FALSE
;
293 sysconf_dir
= wpurple_install_relative_path(WIN32_FHS_SYSCONFDIR
);
302 gboolean
wpurple_write_reg_string(HKEY rootkey
, const char *subkey
, const char *valname
,
305 gboolean success
= FALSE
;
307 wchar_t *wc_subkey
= g_utf8_to_utf16(subkey
, -1, NULL
,
310 if(RegOpenKeyExW(rootkey
, wc_subkey
, 0,
311 KEY_SET_VALUE
, ®_key
) == ERROR_SUCCESS
) {
312 wchar_t *wc_valname
= NULL
;
315 wc_valname
= g_utf8_to_utf16(valname
, -1,
319 wchar_t *wc_value
= g_utf8_to_utf16(value
, -1,
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
328 if(RegDeleteValueW(reg_key
, wc_valname
) == ERROR_SUCCESS
)
336 RegCloseKey(reg_key
);
341 static HKEY
_reg_open_key(HKEY rootkey
, const char *subkey
, REGSAM access
) {
345 wchar_t *wc_subkey
= g_utf8_to_utf16(subkey
, -1, NULL
,
347 rv
= RegOpenKeyExW(rootkey
, wc_subkey
, 0, access
, ®_key
);
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" : "???"),
364 static gboolean
_reg_read(HKEY reg_key
, const char *valname
, LPDWORD type
, LPBYTE data
, LPDWORD data_len
) {
367 wchar_t *wc_valname
= NULL
;
369 wc_valname
= g_utf8_to_utf16(valname
, -1, NULL
, NULL
, NULL
);
370 rv
= RegQueryValueExW(reg_key
, wc_valname
, 0, type
, data
, data_len
);
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
);
380 return (rv
== ERROR_SUCCESS
);
383 gboolean
wpurple_reg_val_exists(HKEY rootkey
, const char *subkey
, const char *valname
)
388 wchar_t name_buffer
[100];
390 wchar_t *wc_valname
= NULL
;
396 wc_subkey
= g_utf8_to_utf16(subkey
, -1, NULL
, NULL
, NULL
);
397 retv
= RegOpenKeyExW(rootkey
, wc_subkey
, 0, KEY_ENUMERATE_SUB_KEYS
, &hkey
);
400 if (retv
!= ERROR_SUCCESS
)
403 if (valname
[0] == '\0' || valname
== NULL
) {
408 wc_valname
= g_utf8_to_utf16(valname
, -1, NULL
, NULL
, NULL
);
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
)
417 name_size
/= sizeof(wchar_t);
418 if (wcsncmp(name_buffer
, wc_valname
, name_size
) == 0) {
429 gboolean
wpurple_read_reg_dword(HKEY rootkey
, const char *subkey
, const char *valname
, LPDWORD result
) {
433 HKEY reg_key
= _reg_open_key(rootkey
, subkey
, KEY_QUERY_VALUE
);
434 gboolean success
= FALSE
;
437 if(_reg_read(reg_key
, valname
, &type
, (LPBYTE
)result
, &nbytes
))
439 RegCloseKey(reg_key
);
445 char *wpurple_read_reg_string(HKEY rootkey
, const char *subkey
, const char *valname
) {
449 HKEY reg_key
= _reg_open_key(rootkey
, subkey
, KEY_QUERY_VALUE
);
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,
464 RegCloseKey(reg_key
);
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
);
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 */
493 ioctlsocket(sock_server
, FIONBIO
, &arg
) != SOCKET_ERROR
);
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
);
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 */
510 connect(sock_client
, saddr_p
, saddr_len
) == SOCKET_ERROR
&&
511 WSAGetLastError() == WSAEWOULDBLOCK
);
513 /* ensure, that server socket is readable */
516 FD_ZERO(&select_set
);
517 FD_SET(sock_server
, &select_set
);
520 select(0, &select_set
, NULL
, NULL
, NULL
) != SOCKET_ERROR
&&
521 FD_ISSET(sock_server
, &select_set
));
523 /* accept (establish) connection from client socket */
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 */
534 FD_ZERO(&select_set
);
535 FD_SET(sock_client
, &select_set
);
538 select(0, NULL
, &select_set
, NULL
, NULL
) != SOCKET_ERROR
&&
539 FD_ISSET(sock_client
, &select_set
));
541 /* set sockets into blocking mode */
544 ioctlsocket(sock_client
, FIONBIO
, &arg
) != SOCKET_ERROR
);
547 ioctlsocket(sock_server_established
, FIONBIO
, &arg
)
550 /* we don't need (passive) server socket anymore */
551 if (sock_server
!= INVALID_SOCKET
)
552 closesocket(sock_server
);
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 */
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
);
576 void wpurple_init(void) {
577 WORD wVersionRequested
;
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
);
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");
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 */
612 g_free(app_data_dir
);
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
;