Fortunes about QT and "hearing" video codecs
[vlc.git] / src / misc / keystore.c
blob62d1822b194ac93a7bbc2aa66b62e6ffc62d05ec
1 /*****************************************************************************
2 * keystore.c:
3 *****************************************************************************
4 * Copyright (C) 2015-2016 VLC authors, VideoLAN and VideoLabs
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
25 #include <vlc_common.h>
26 #include <vlc_dialog.h>
27 #include <vlc_keystore.h>
28 #include <vlc_modules.h>
29 #include <vlc_url.h>
30 #include <libvlc.h>
32 #include <assert.h>
33 #include <limits.h>
35 static vlc_keystore *
36 keystore_create(vlc_object_t *p_parent, const char *psz_name)
38 vlc_keystore *p_keystore = vlc_custom_create(p_parent, sizeof (*p_keystore),
39 "keystore");
40 if (unlikely(p_keystore == NULL))
41 return NULL;
43 p_keystore->p_module = module_need(p_keystore, "keystore", psz_name, true);
44 if (p_keystore->p_module == NULL)
46 vlc_object_release(p_keystore);
47 return NULL;
49 assert(p_keystore->pf_store);
50 assert(p_keystore->pf_find);
51 assert(p_keystore->pf_remove);
53 return p_keystore;
56 #undef vlc_keystore_create
57 vlc_keystore *
58 vlc_keystore_create(vlc_object_t *p_parent)
60 assert(p_parent);
62 char *modlist = var_InheritString(p_parent, "keystore");
63 vlc_keystore *p_keystore = keystore_create(p_parent, modlist);
65 free(modlist);
66 return p_keystore;
69 void
70 vlc_keystore_release(vlc_keystore *p_keystore)
72 assert(p_keystore);
73 module_unneed(p_keystore, p_keystore->p_module);
75 vlc_object_release(p_keystore);
78 int
79 vlc_keystore_store(vlc_keystore *p_keystore,
80 const char * const ppsz_values[KEY_MAX],
81 const uint8_t *p_secret, ssize_t i_secret_len,
82 const char *psz_label)
84 assert(p_keystore && ppsz_values && p_secret && i_secret_len);
86 if (!ppsz_values[KEY_PROTOCOL] || !ppsz_values[KEY_SERVER])
88 msg_Err(p_keystore, "invalid store request: "
89 "protocol and server should be valid");
90 return VLC_EGENERIC;
92 if (ppsz_values[KEY_PORT])
94 long int i_port = strtol(ppsz_values[KEY_PORT], NULL, 10);
95 if (i_port == LONG_MIN || i_port == LONG_MAX)
97 msg_Err(p_keystore, "invalid store request: "
98 "port is not valid number");
99 return VLC_EGENERIC;
102 if (i_secret_len < 0)
103 i_secret_len = strlen((const char *)p_secret) + 1;
104 return p_keystore->pf_store(p_keystore, ppsz_values, p_secret, i_secret_len,
105 psz_label);
108 unsigned int
109 vlc_keystore_find(vlc_keystore *p_keystore,
110 const char * const ppsz_values[KEY_MAX],
111 vlc_keystore_entry **pp_entries)
113 assert(p_keystore && ppsz_values && pp_entries);
114 return p_keystore->pf_find(p_keystore, ppsz_values, pp_entries);
117 unsigned int
118 vlc_keystore_remove(vlc_keystore *p_keystore,
119 const char *const ppsz_values[KEY_MAX])
121 assert(p_keystore && ppsz_values);
122 return p_keystore->pf_remove(p_keystore, ppsz_values);
125 void
126 vlc_keystore_release_entries(vlc_keystore_entry *p_entries, unsigned int i_count)
128 for (unsigned int i = 0; i < i_count; ++i)
129 vlc_keystore_release_entry(&p_entries[i]);
130 free(p_entries);
134 libvlc_InternalKeystoreInit(libvlc_int_t *p_libvlc)
136 assert(p_libvlc != NULL);
137 libvlc_priv_t *p_priv = libvlc_priv(p_libvlc);
139 p_priv->p_memory_keystore = keystore_create(VLC_OBJECT(p_libvlc), "memory");
140 return p_priv->p_memory_keystore != NULL ? VLC_SUCCESS : VLC_EGENERIC;
143 void
144 libvlc_InternalKeystoreClean(libvlc_int_t *p_libvlc)
146 assert(p_libvlc != NULL);
147 libvlc_priv_t *p_priv = libvlc_priv(p_libvlc);
149 if (p_priv->p_memory_keystore != NULL)
151 vlc_keystore_release(p_priv->p_memory_keystore);
152 p_priv->p_memory_keystore = NULL;
156 static vlc_keystore *
157 get_memory_keystore(vlc_object_t *p_obj)
159 return libvlc_priv(p_obj->obj.libvlc)->p_memory_keystore;
162 static vlc_keystore_entry *
163 find_closest_path(vlc_keystore_entry *p_entries, unsigned i_count,
164 const char *psz_path)
166 vlc_keystore_entry *p_match_entry = NULL;
167 size_t i_last_pathlen = 0;
168 char *psz_decoded_path = vlc_uri_decode_duplicate(psz_path);
169 if (psz_decoded_path == NULL)
170 return NULL;
172 /* Try to find the entry that has the closest path to psz_url */
173 for (unsigned int i = 0; i < i_count; ++i)
175 vlc_keystore_entry *p_entry = &p_entries[i];
176 const char *psz_entry_path = p_entry->ppsz_values[KEY_PATH];
177 if (psz_entry_path == NULL)
179 if (p_match_entry == NULL)
180 p_match_entry = p_entry;
181 continue;
183 size_t i_entry_pathlen = strlen(psz_entry_path);
185 if (strncasecmp(psz_decoded_path, psz_entry_path, i_entry_pathlen) == 0
186 && i_entry_pathlen > i_last_pathlen)
188 i_last_pathlen = i_entry_pathlen;
189 p_match_entry = p_entry;
192 free(psz_decoded_path);
193 return p_match_entry;
196 static bool
197 is_credential_valid(vlc_credential *p_credential)
199 if (p_credential->psz_username && *p_credential->psz_username != '\0'
200 && p_credential->psz_password)
201 return true;
202 p_credential->psz_password = NULL;
203 return false;
206 static bool
207 is_url_valid(const vlc_url_t *p_url)
209 return p_url && p_url->psz_protocol && p_url->psz_protocol[0]
210 && p_url->psz_host && p_url->psz_host[0];
213 /* Default port for each protocol */
214 static struct
216 const char * psz_protocol;
217 uint16_t i_port;
218 } protocol_default_ports [] = {
219 { "rtsp", 80 },
220 { "http", 80 },
221 { "https", 443 },
222 { "ftp", 21 },
223 { "sftp", 22 },
224 { "smb", 445 },
227 /* Don't store a port if it's the default one */
228 static bool
229 protocol_set_port(const vlc_url_t *p_url, char *psz_port)
231 int i_port = -1;
233 if (p_url->i_port != 0 && p_url->i_port <= UINT16_MAX)
234 i_port = p_url->i_port;
235 else
237 for (unsigned int i = 0; i < sizeof(protocol_default_ports)
238 / sizeof(*protocol_default_ports); ++i)
240 if (strcasecmp(p_url->psz_protocol,
241 protocol_default_ports[i].psz_protocol) == 0)
243 i_port = protocol_default_ports[i].i_port;
244 break;
248 if (i_port != -1)
250 sprintf(psz_port, "%" PRIu16, (uint16_t) i_port);
251 return true;
253 return false;
256 static bool
257 protocol_is_smb(const vlc_url_t *p_url)
259 return strcasecmp(p_url->psz_protocol, "smb") == 0;
262 static bool
263 protocol_store_path(const vlc_url_t *p_url)
265 return p_url->psz_path
266 && (strncasecmp(p_url->psz_protocol, "http", 4) == 0
267 || strcasecmp(p_url->psz_protocol, "rtsp") == 0
268 || protocol_is_smb(p_url));
271 /* Split domain;user in userinfo */
272 static void
273 smb_split_domain(vlc_credential *p_credential)
275 char *psz_delim = strchr(p_credential->psz_username, ';');
276 if (psz_delim)
278 size_t i_len = psz_delim - p_credential->psz_username;
279 if (i_len > 0)
281 free(p_credential->psz_split_domain);
282 p_credential->psz_split_domain =
283 strndup(p_credential->psz_username, i_len);
284 p_credential->psz_realm = p_credential->psz_split_domain;
286 p_credential->psz_username = psz_delim + 1;
290 static void
291 credential_find_keystore(vlc_credential *p_credential, vlc_keystore *p_keystore)
293 const vlc_url_t *p_url = p_credential->p_url;
295 const char *ppsz_values[KEY_MAX] = { 0 };
296 ppsz_values[KEY_PROTOCOL] = p_url->psz_protocol;
297 ppsz_values[KEY_USER] = p_credential->psz_username;
298 ppsz_values[KEY_SERVER] = p_url->psz_host;
299 /* don't try to match with the path */
300 ppsz_values[KEY_REALM] = p_credential->psz_realm;
301 ppsz_values[KEY_AUTHTYPE] = p_credential->psz_authtype;
302 char psz_port[21];
303 if (protocol_set_port(p_url, psz_port))
304 ppsz_values[KEY_PORT] = psz_port;
306 vlc_keystore_entry *p_entries;
307 unsigned int i_entries_count;
308 i_entries_count = vlc_keystore_find(p_keystore, ppsz_values, &p_entries);
310 /* Remove last entries after vlc_keystore_find call since
311 * p_credential->psz_username (default username) can be a pointer to an
312 * entry */
313 if (p_credential->i_entries_count > 0)
315 vlc_keystore_release_entries(p_credential->p_entries,
316 p_credential->i_entries_count);
317 p_credential->psz_username = NULL;
319 p_credential->p_entries = p_entries;
320 p_credential->i_entries_count = i_entries_count;
322 if (p_credential->i_entries_count > 0)
324 vlc_keystore_entry *p_entry;
326 if (protocol_store_path(p_url))
327 p_entry = find_closest_path(p_credential->p_entries,
328 p_credential->i_entries_count,
329 p_url->psz_path);
330 else
331 p_entry = &p_credential->p_entries[0];
333 if (!p_entry || p_entry->p_secret[p_entry->i_secret_len - 1] != '\0')
335 vlc_keystore_release_entries(p_credential->p_entries,
336 p_credential->i_entries_count);
337 p_credential->i_entries_count = 0;
339 else
341 p_credential->psz_password = (const char *)p_entry->p_secret;
342 p_credential->psz_username = p_entry->ppsz_values[KEY_USER];
343 p_credential->psz_realm = p_entry->ppsz_values[KEY_REALM];
344 p_credential->psz_authtype = p_entry->ppsz_values[KEY_AUTHTYPE];
345 p_credential->b_from_keystore = true;
350 void
351 vlc_credential_init(vlc_credential *p_credential, const vlc_url_t *p_url)
353 assert(p_credential);
355 memset(p_credential, 0, sizeof(*p_credential));
356 p_credential->i_get_order = GET_FROM_URL;
357 p_credential->p_url = p_url;
360 void
361 vlc_credential_clean(vlc_credential *p_credential)
363 if (p_credential->i_entries_count > 0)
364 vlc_keystore_release_entries(p_credential->p_entries,
365 p_credential->i_entries_count);
366 if (p_credential->p_keystore)
367 vlc_keystore_release(p_credential->p_keystore);
369 free(p_credential->psz_split_domain);
370 free(p_credential->psz_var_username);
371 free(p_credential->psz_var_password);
372 free(p_credential->psz_dialog_username);
373 free(p_credential->psz_dialog_password);
376 #undef vlc_credential_get
377 bool
378 vlc_credential_get(vlc_credential *p_credential, vlc_object_t *p_parent,
379 const char *psz_option_username,
380 const char *psz_option_password,
381 const char *psz_dialog_title,
382 const char *psz_dialog_fmt, ...)
384 assert(p_credential && p_parent);
385 const vlc_url_t *p_url = p_credential->p_url;
387 if (!is_url_valid(p_url))
389 msg_Err(p_parent, "vlc_credential_get: invalid url");
390 return false;
393 p_credential->b_from_keystore = false;
394 /* Don't set username to NULL, we may want to use the last one set */
395 p_credential->psz_password = NULL;
397 while (!is_credential_valid(p_credential))
399 /* First, fetch credential from URL (if any).
400 * Secondly, fetch credential from VLC Options (if any).
401 * Thirdly, fetch credential from keystore (if any) using user and realm
402 * previously set by the caller, the URL or by VLC Options.
403 * Finally, fetch credential from the dialog (if any). This last will be
404 * repeated until user cancel the dialog. */
406 switch (p_credential->i_get_order)
408 case GET_FROM_URL:
409 p_credential->psz_username = p_url->psz_username;
410 p_credential->psz_password = p_url->psz_password;
412 if (p_credential->psz_password)
413 msg_Warn(p_parent, "Password in a URI is DEPRECATED");
415 if (p_url->psz_username && protocol_is_smb(p_url))
416 smb_split_domain(p_credential);
417 p_credential->i_get_order++;
418 break;
420 case GET_FROM_OPTION:
421 free(p_credential->psz_var_username);
422 free(p_credential->psz_var_password);
423 p_credential->psz_var_username =
424 p_credential->psz_var_password = NULL;
426 if (psz_option_username)
427 p_credential->psz_var_username =
428 var_InheritString(p_parent, psz_option_username);
429 if (psz_option_password)
430 p_credential->psz_var_password =
431 var_InheritString(p_parent, psz_option_password);
433 if (p_credential->psz_var_username)
434 p_credential->psz_username = p_credential->psz_var_username;
435 if (p_credential->psz_var_password)
436 p_credential->psz_password = p_credential->psz_var_password;
438 p_credential->i_get_order++;
439 break;
441 case GET_FROM_MEMORY_KEYSTORE:
443 if (!psz_dialog_title || !psz_dialog_fmt)
444 return false;
446 vlc_keystore *p_keystore = get_memory_keystore(p_parent);
447 if (p_keystore != NULL)
448 credential_find_keystore(p_credential, p_keystore);
449 p_credential->i_get_order++;
450 break;
453 case GET_FROM_KEYSTORE:
454 if (!psz_dialog_title || !psz_dialog_fmt)
455 return false;
457 if (p_credential->p_keystore == NULL)
458 p_credential->p_keystore = vlc_keystore_create(p_parent);
459 if (p_credential->p_keystore != NULL)
460 credential_find_keystore(p_credential, p_credential->p_keystore);
462 p_credential->i_get_order++;
463 break;
465 default:
466 case GET_FROM_DIALOG:
467 if (!psz_dialog_title || !psz_dialog_fmt)
468 return false;
469 char *psz_dialog_username = NULL;
470 char *psz_dialog_password = NULL;
471 va_list ap;
472 va_start(ap, psz_dialog_fmt);
473 bool *p_store = p_credential->p_keystore != NULL ?
474 &p_credential->b_store : NULL;
475 int i_ret =
476 vlc_dialog_wait_login_va(p_parent,
477 &psz_dialog_username,
478 &psz_dialog_password, p_store,
479 p_credential->psz_username,
480 psz_dialog_title, psz_dialog_fmt, ap);
481 va_end(ap);
483 /* Free previous dialog strings after vlc_dialog_wait_login_va call
484 * since p_credential->psz_username (default username) can be a
485 * pointer to p_credential->psz_dialog_username */
486 free(p_credential->psz_dialog_username);
487 free(p_credential->psz_dialog_password);
488 p_credential->psz_dialog_username = psz_dialog_username;
489 p_credential->psz_dialog_password = psz_dialog_password;
491 if (i_ret != 1)
493 p_credential->psz_username = p_credential->psz_password = NULL;
494 return false;
497 p_credential->psz_username = p_credential->psz_dialog_username;
498 p_credential->psz_password = p_credential->psz_dialog_password;
500 if (protocol_is_smb(p_url))
501 smb_split_domain(p_credential);
503 break;
506 return is_credential_valid(p_credential);
509 #undef vlc_credential_store
510 bool
511 vlc_credential_store(vlc_credential *p_credential, vlc_object_t *p_parent)
513 if (!is_credential_valid(p_credential))
514 return false;
515 /* Don't need to store again */
516 if (p_credential->b_from_keystore)
517 return p_credential->b_from_keystore;
519 vlc_keystore *p_keystore;
520 if (p_credential->b_store)
522 /* Store in permanent keystore */
523 assert(p_credential->p_keystore != NULL);
524 p_keystore = p_credential->p_keystore;
526 else
528 /* Store in memory keystore */
529 p_keystore = get_memory_keystore(p_parent);
531 if (p_keystore == NULL)
532 return false;
534 const vlc_url_t *p_url = p_credential->p_url;
536 char *psz_path = NULL;
537 if (protocol_store_path(p_url)
538 && (psz_path = vlc_uri_decode_duplicate(p_url->psz_path)) != NULL)
540 char *p_slash;
541 if (protocol_is_smb(p_url))
543 /* Remove all characters after the first slash (store the share but
544 * not the path) */
545 p_slash = strchr(psz_path + 1, '/');
547 else
549 /* Remove all characters after the last slash (store the path
550 * without the filename) */
551 p_slash = strrchr(psz_path + 1, '/');
553 if (p_slash && psz_path != p_slash)
554 *p_slash = '\0';
557 const char *ppsz_values[KEY_MAX] = { 0 };
558 ppsz_values[KEY_PROTOCOL] = p_url->psz_protocol;
559 ppsz_values[KEY_USER] = p_credential->psz_username;
560 ppsz_values[KEY_SERVER] = p_url->psz_host;
561 ppsz_values[KEY_PATH] = psz_path;
562 ppsz_values[KEY_REALM] = p_credential->psz_realm;
563 ppsz_values[KEY_AUTHTYPE] = p_credential->psz_authtype;
565 char psz_port[21];
566 if (protocol_set_port(p_url, psz_port))
567 ppsz_values[KEY_PORT] = psz_port;
569 char *psz_label;
570 if (asprintf(&psz_label, "LibVLC password for %s://%s%s",
571 p_url->psz_protocol, p_url->psz_host,
572 psz_path ? psz_path : "") == -1)
574 free(psz_path);
575 return false;
578 const uint8_t *p_password = (const uint8_t *)
579 (p_credential->psz_password != NULL ? p_credential->psz_password : "");
581 bool b_ret = vlc_keystore_store(p_keystore, ppsz_values, p_password,
582 -1, psz_label) == VLC_SUCCESS;
583 free(psz_label);
584 free(psz_path);
585 return b_ret;