Replace functions which called once with their bodies
[pidgin-git.git] / libpurple / plugins / keyrings / wincred.c
blob075e88e869281e3fd22e609f49741a3b68a6a1b9
1 /**
2 * @file wincred.c Passwords storage using Windows credentials
3 * @ingroup plugins
4 */
6 /* purple
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program ; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
27 #include "internal.h"
28 #include <purple.h>
30 #include <wincred.h>
32 #define WINCRED_NAME N_("Windows credentials")
33 #define WINCRED_SUMMARY N_("Store passwords using Windows credentials")
34 #define WINCRED_DESCRIPTION N_("This plugin stores passwords using Windows " \
35 "credentials.")
36 #define WINCRED_AUTHORS {"Tomek Wasilczyk <twasilczyk@pidgin.im>", NULL}
37 #define WINCRED_ID "keyring-wincred"
38 #define WINCRED_DOMAIN (g_quark_from_static_string(WINCRED_ID))
40 #define WINCRED_MAX_TARGET_NAME 256
42 static PurpleKeyring *keyring_handler = NULL;
44 static gunichar2 *
45 wincred_get_target_name(PurpleAccount *account)
47 gchar target_name_utf8[WINCRED_MAX_TARGET_NAME];
48 gunichar2 *target_name_utf16;
50 g_return_val_if_fail(account != NULL, NULL);
52 g_snprintf(target_name_utf8, WINCRED_MAX_TARGET_NAME, "libpurple_%s_%s",
53 purple_account_get_protocol_id(account),
54 purple_account_get_username(account));
56 target_name_utf16 = g_utf8_to_utf16(target_name_utf8, -1,
57 NULL, NULL, NULL);
59 if (target_name_utf16 == NULL) {
60 purple_debug_fatal("keyring-wincred", "Couldn't convert target "
61 "name\n");
64 return target_name_utf16;
67 static void
68 wincred_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
69 gpointer data)
71 GError *error = NULL;
72 gunichar2 *target_name = NULL;
73 gchar *password;
74 PCREDENTIALW credential;
76 g_return_if_fail(account != NULL);
78 target_name = wincred_get_target_name(account);
79 g_return_if_fail(target_name != NULL);
81 if (!CredReadW(target_name, CRED_TYPE_GENERIC, 0, &credential)) {
82 DWORD error_code = GetLastError();
84 if (error_code == ERROR_NOT_FOUND) {
85 if (purple_debug_is_verbose()) {
86 purple_debug_misc("keyring-wincred",
87 "No password found for account %s\n",
88 purple_account_get_username(account));
90 error = g_error_new(PURPLE_KEYRING_ERROR,
91 PURPLE_KEYRING_ERROR_NOPASSWORD,
92 _("Password not found."));
93 } else if (error_code == ERROR_NO_SUCH_LOGON_SESSION) {
94 purple_debug_error("keyring-wincred",
95 "Cannot read password, no valid logon "
96 "session\n");
97 error = g_error_new(PURPLE_KEYRING_ERROR,
98 PURPLE_KEYRING_ERROR_ACCESSDENIED,
99 _("Cannot read password, no valid logon "
100 "session."));
101 } else {
102 purple_debug_error("keyring-wincred",
103 "Cannot read password, error %lx\n",
104 error_code);
105 error = g_error_new(PURPLE_KEYRING_ERROR,
106 PURPLE_KEYRING_ERROR_BACKENDFAIL,
107 _("Cannot read password (error %lx)."), error_code);
110 if (cb != NULL)
111 cb(account, NULL, error, data);
112 g_error_free(error);
113 return;
116 password = g_utf16_to_utf8((gunichar2*)credential->CredentialBlob,
117 credential->CredentialBlobSize / sizeof(gunichar2),
118 NULL, NULL, NULL);
120 memset(credential->CredentialBlob, 0, credential->CredentialBlobSize);
121 CredFree(credential);
123 if (password == NULL) {
124 purple_debug_error("keyring-wincred",
125 "Cannot convert password\n");
126 error = g_error_new(PURPLE_KEYRING_ERROR,
127 PURPLE_KEYRING_ERROR_BACKENDFAIL,
128 _("Cannot read password (unicode error)."));
129 } else {
130 purple_debug_misc("keyring-wincred",
131 _("Got password for account %s.\n"),
132 purple_account_get_username(account));
135 if (cb != NULL)
136 cb(account, password, error, data);
137 if (error != NULL)
138 g_error_free(error);
140 purple_str_wipe(password);
143 static void
144 wincred_save(PurpleAccount *account, const gchar *password,
145 PurpleKeyringSaveCallback cb, gpointer data)
147 GError *error = NULL;
148 gunichar2 *target_name = NULL;
149 gunichar2 *username_utf16 = NULL;
150 gunichar2 *password_utf16 = NULL;
151 CREDENTIALW credential;
153 g_return_if_fail(account != NULL);
155 target_name = wincred_get_target_name(account);
156 g_return_if_fail(target_name != NULL);
158 if (password == NULL) {
159 if (CredDeleteW(target_name, CRED_TYPE_GENERIC, 0)) {
160 purple_debug_misc("keyring-wincred", "Password for "
161 "account %s removed\n",
162 purple_account_get_username(account));
163 } else {
164 DWORD error_code = GetLastError();
166 if (error_code == ERROR_NOT_FOUND) {
167 if (purple_debug_is_verbose()) {
168 purple_debug_misc("keyring-wincred",
169 "Password for account %s was already "
170 "removed.\n",
171 purple_account_get_username(account));
173 } else if (error_code == ERROR_NO_SUCH_LOGON_SESSION) {
174 purple_debug_error("keyring-wincred",
175 "Cannot remove password, no valid "
176 "logon session\n");
177 error = g_error_new(PURPLE_KEYRING_ERROR,
178 PURPLE_KEYRING_ERROR_ACCESSDENIED,
179 _("Cannot remove password, no valid "
180 "logon session."));
181 } else {
182 purple_debug_error("keyring-wincred",
183 "Cannot remove password, error %lx\n",
184 error_code);
185 error = g_error_new(PURPLE_KEYRING_ERROR,
186 PURPLE_KEYRING_ERROR_BACKENDFAIL,
187 _("Cannot remove password (error %lx)."),
188 error_code);
192 if (cb != NULL)
193 cb(account, error, data);
194 if (error != NULL)
195 g_error_free(error);
196 return;
199 username_utf16 = g_utf8_to_utf16(purple_account_get_username(account),
200 -1, NULL, NULL, NULL);
201 password_utf16 = g_utf8_to_utf16(password, -1, NULL, NULL, NULL);
203 if (username_utf16 == NULL || password_utf16 == NULL) {
204 g_free(username_utf16);
205 purple_utf16_wipe(password_utf16);
207 purple_debug_fatal("keyring-wincred", "Couldn't convert "
208 "username or password\n");
209 g_return_if_reached();
212 memset(&credential, 0, sizeof(CREDENTIALW));
213 credential.Type = CRED_TYPE_GENERIC;
214 credential.TargetName = target_name;
215 credential.CredentialBlobSize = purple_utf16_size(password_utf16) - 2;
216 credential.CredentialBlob = (LPBYTE)password_utf16;
217 credential.Persist = CRED_PERSIST_LOCAL_MACHINE;
218 credential.UserName = username_utf16;
220 if (!CredWriteW(&credential, 0)) {
221 DWORD error_code = GetLastError();
223 if (error_code == ERROR_NO_SUCH_LOGON_SESSION) {
224 purple_debug_error("keyring-wincred",
225 "Cannot store password, no valid logon "
226 "session\n");
227 error = g_error_new(PURPLE_KEYRING_ERROR,
228 PURPLE_KEYRING_ERROR_ACCESSDENIED,
229 _("Cannot remove password, no valid logon "
230 "session."));
231 } else {
232 purple_debug_error("keyring-wincred",
233 "Cannot store password, error %lx\n",
234 error_code);
235 error = g_error_new(PURPLE_KEYRING_ERROR,
236 PURPLE_KEYRING_ERROR_BACKENDFAIL,
237 _("Cannot store password (error %lx)."), error_code);
239 } else {
240 purple_debug_misc("keyring-wincred",
241 "Password updated for account %s.\n",
242 purple_account_get_username(account));
245 g_free(target_name);
246 g_free(username_utf16);
247 purple_utf16_wipe(password_utf16);
249 if (cb != NULL)
250 cb(account, error, data);
251 if (error != NULL)
252 g_error_free(error);
255 static PurplePluginInfo *
256 plugin_query(GError **error)
258 const gchar * const authors[] = WINCRED_AUTHORS;
260 return purple_plugin_info_new(
261 "id", WINCRED_ID,
262 "name", WINCRED_NAME,
263 "version", DISPLAY_VERSION,
264 "category", N_("Keyring"),
265 "summary", WINCRED_SUMMARY,
266 "description", WINCRED_DESCRIPTION,
267 "authors", authors,
268 "website", PURPLE_WEBSITE,
269 "abi-version", PURPLE_ABI_VERSION,
270 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL,
271 NULL
275 static gboolean
276 plugin_load(PurplePlugin *plugin, GError **error)
278 keyring_handler = purple_keyring_new();
280 purple_keyring_set_name(keyring_handler, _(WINCRED_NAME));
281 purple_keyring_set_id(keyring_handler, WINCRED_ID);
282 purple_keyring_set_read_password(keyring_handler, wincred_read);
283 purple_keyring_set_save_password(keyring_handler, wincred_save);
285 purple_keyring_register(keyring_handler);
287 return TRUE;
290 static gboolean
291 plugin_unload(PurplePlugin *plugin, GError **error)
293 if (purple_keyring_get_inuse() == keyring_handler) {
294 g_set_error(error, WINCRED_DOMAIN, 0, "The keyring is currently "
295 "in use.");
296 purple_debug_warning("keyring-wincred",
297 "keyring in use, cannot unload\n");
298 return FALSE;
301 purple_keyring_unregister(keyring_handler);
302 purple_keyring_free(keyring_handler);
303 keyring_handler = NULL;
305 return TRUE;
308 PURPLE_PLUGIN_INIT(wincred_keyring, plugin_query, plugin_load, plugin_unload);