mirror of https://code.videolan.org/videolan/vlc
add vlc_credential API
This commit is contained in:
parent
868b8453fb
commit
8d49d1cd23
|
@ -30,9 +30,10 @@
|
|||
|
||||
typedef struct vlc_keystore vlc_keystore;
|
||||
typedef struct vlc_keystore_entry vlc_keystore_entry;
|
||||
typedef struct vlc_credential vlc_credential;
|
||||
|
||||
/**
|
||||
* @defgroup keystore Keystore API
|
||||
* @defgroup keystore Keystore and credential API
|
||||
* @{
|
||||
* @defgroup keystore_public Keystore public API
|
||||
* @{
|
||||
|
@ -144,6 +145,106 @@ vlc_keystore_remove(vlc_keystore *p_keystore,
|
|||
VLC_API void
|
||||
vlc_keystore_release_entries(vlc_keystore_entry *p_entries, unsigned int i_count);
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @defgroup credential Credential API
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @note init with vlc_credential_init()
|
||||
*/
|
||||
struct vlc_credential
|
||||
{
|
||||
/** url to store or to search */
|
||||
const vlc_url_t *p_url;
|
||||
/** http realm or smb domain */
|
||||
const char *psz_realm;
|
||||
/** http authtype */
|
||||
const char *psz_authtype;
|
||||
/** valid only if vlc_credential_get() returned true */
|
||||
const char *psz_username;
|
||||
/** valid only if vlc_credential_get() returned true */
|
||||
const char *psz_password;
|
||||
|
||||
/* internal */
|
||||
enum {
|
||||
GET_FROM_URL,
|
||||
GET_FROM_OPTION,
|
||||
GET_FROM_KEYSTORE,
|
||||
GET_FROM_DIALOG,
|
||||
} i_get_order;
|
||||
|
||||
vlc_keystore *p_keystore;
|
||||
vlc_keystore_entry *p_entries;
|
||||
unsigned int i_entries_count;
|
||||
|
||||
char *psz_split_realm;
|
||||
char *psz_var_username;
|
||||
char *psz_var_password;
|
||||
|
||||
char *psz_dialog_username;
|
||||
char *psz_dialog_password;
|
||||
bool b_store;
|
||||
};
|
||||
|
||||
/**
|
||||
* Init a credential struct
|
||||
*
|
||||
* @note to be cleaned with vlc_credential_clean()
|
||||
*
|
||||
* @param psz_url url to store or to search
|
||||
*/
|
||||
VLC_API void
|
||||
vlc_credential_init(vlc_credential *p_credential, const vlc_url_t *p_url);
|
||||
|
||||
/**
|
||||
* Clean a credential struct
|
||||
*/
|
||||
VLC_API void
|
||||
vlc_credential_clean(vlc_credential *p_credential);
|
||||
|
||||
/**
|
||||
* Get a username/password couple
|
||||
*
|
||||
* This will search for a credential using url, VLC options, the vlc_keystore
|
||||
* or by asking the user via dialog_Login(). This function can be called
|
||||
* indefinitely, it will first return the user/password from the url (if any),
|
||||
* then from VLC options (if any), then from the keystore (if any), and finally
|
||||
* from the dialog (if any). This function will return true as long as the user
|
||||
* fill the dialog texts and will return false when the user cancel it.
|
||||
*
|
||||
* @param p_parent the parent object (for var, keystore and dialog)
|
||||
* @param psz_option_username VLC option name for the username
|
||||
* @param psz_option_password VLC option name for the password
|
||||
* @param psz_dialog_title dialog title, if NULL, this function won't use the
|
||||
* keystore or the dialog
|
||||
* @param psz_dialog_fmt dialog text using format
|
||||
*
|
||||
* @return true if vlc_credential.psz_username and vlc_credential.psz_password
|
||||
* are valid, otherwise this function should not be called again.
|
||||
*/
|
||||
|
||||
VLC_API bool
|
||||
vlc_credential_get(vlc_credential *p_credential, vlc_object_t *p_parent,
|
||||
const char *psz_option_username,
|
||||
const char *psz_option_password,
|
||||
const char *psz_dialog_title,
|
||||
const char *psz_dialog_fmt, ...) VLC_FORMAT(6, 7);
|
||||
#define vlc_credential_get(a, b, c, d, e, f, ...) \
|
||||
vlc_credential_get(a, VLC_OBJECT(b), c, d, e, f, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Store the last dialog credential returned by vlc_credential_get()
|
||||
*
|
||||
* This function will store the credential only if it comes from the dialog and
|
||||
* if the vlc_keystore object is valid.
|
||||
*
|
||||
* @return true if credential was stored, false otherwise
|
||||
*/
|
||||
VLC_API bool
|
||||
vlc_credential_store(vlc_credential *p_credential);
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @defgroup keystore_implementation Implemented by keystore modules
|
||||
|
|
|
@ -519,6 +519,10 @@ vlc_cond_init_daytime
|
|||
vlc_cond_signal
|
||||
vlc_cond_timedwait
|
||||
vlc_cond_wait
|
||||
vlc_credential_init
|
||||
vlc_credential_clean
|
||||
vlc_credential_get
|
||||
vlc_credential_store
|
||||
vlc_sem_init
|
||||
vlc_sem_destroy
|
||||
vlc_sem_post
|
||||
|
|
|
@ -23,8 +23,10 @@
|
|||
#endif
|
||||
|
||||
#include <vlc_common.h>
|
||||
#include <vlc_dialog.h>
|
||||
#include <vlc_keystore.h>
|
||||
#include <vlc_modules.h>
|
||||
#include <vlc_url.h>
|
||||
#include <libvlc.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
@ -120,5 +122,365 @@ vlc_keystore_release_entries(vlc_keystore_entry *p_entries, unsigned int i_count
|
|||
free(p_entry->ppsz_values[j]);
|
||||
free(p_entry->p_secret);
|
||||
}
|
||||
free (p_entries);
|
||||
free(p_entries);
|
||||
}
|
||||
|
||||
static vlc_keystore_entry *
|
||||
find_closest_path(vlc_keystore_entry *p_entries, unsigned i_count,
|
||||
const char *psz_path)
|
||||
{
|
||||
vlc_keystore_entry *p_match_entry = NULL;
|
||||
size_t i_last_pathlen = 0;
|
||||
|
||||
/* Try to find the entry that has the closest path to psz_url */
|
||||
for (unsigned int i = 0; i < i_count; ++i)
|
||||
{
|
||||
vlc_keystore_entry *p_entry = &p_entries[i];
|
||||
const char *psz_entry_path = p_entry->ppsz_values[KEY_PATH];
|
||||
size_t i_entry_pathlen = strlen(psz_entry_path);
|
||||
|
||||
if (strncasecmp(psz_path, psz_entry_path, i_entry_pathlen) == 0
|
||||
&& i_entry_pathlen > i_last_pathlen)
|
||||
{
|
||||
i_last_pathlen = i_entry_pathlen;
|
||||
p_match_entry = p_entry;
|
||||
}
|
||||
}
|
||||
return p_match_entry;
|
||||
}
|
||||
|
||||
static bool
|
||||
is_credential_valid(vlc_credential *p_credential)
|
||||
{
|
||||
return p_credential->psz_username && *p_credential->psz_username != '\0'
|
||||
&& p_credential->psz_password;
|
||||
}
|
||||
|
||||
/* Default port for each protocol */
|
||||
static struct
|
||||
{
|
||||
const char * psz_protocol;
|
||||
unsigned int i_port;
|
||||
} protocol_default_ports [] = {
|
||||
{ "rtsp", 80 },
|
||||
{ "http", 80 },
|
||||
{ "https", 443 },
|
||||
{ "ftp", 21 },
|
||||
{ "sftp", 22 },
|
||||
{ "smb", 445 },
|
||||
};
|
||||
|
||||
/* Don't store a port if it's the default one */
|
||||
static bool
|
||||
protocol_is_default_port(const vlc_url_t *p_url)
|
||||
{
|
||||
for (unsigned int i = 0; i < sizeof(protocol_default_ports)
|
||||
/ sizeof(*protocol_default_ports); ++i)
|
||||
{
|
||||
if (p_url->i_port == protocol_default_ports[i].i_port
|
||||
&& strcasecmp(p_url->psz_protocol,
|
||||
protocol_default_ports[i].psz_protocol) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
protocol_is_smb(const vlc_url_t *p_url)
|
||||
{
|
||||
return strcasecmp(p_url->psz_protocol, "smb") == 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
protocol_store_path(const vlc_url_t *p_url)
|
||||
{
|
||||
return p_url->psz_path
|
||||
&& (strncasecmp(p_url->psz_protocol, "http", 4) == 0
|
||||
|| strcasecmp(p_url->psz_protocol, "rtsp") == 0
|
||||
|| protocol_is_smb(p_url));
|
||||
}
|
||||
|
||||
/* Split domain;user in userinfo */
|
||||
static void
|
||||
smb_split_domain(vlc_credential *p_credential)
|
||||
{
|
||||
char *psz_delim = strchr(p_credential->psz_username, ';');
|
||||
if (psz_delim)
|
||||
{
|
||||
size_t i_len = psz_delim - p_credential->psz_username;
|
||||
if (i_len > 0)
|
||||
{
|
||||
p_credential->psz_split_realm = strndup(p_credential->psz_username,
|
||||
i_len);
|
||||
p_credential->psz_realm = p_credential->psz_split_realm;
|
||||
}
|
||||
p_credential->psz_username = psz_delim + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
credential_find_keystore(vlc_credential *p_credential)
|
||||
{
|
||||
const vlc_url_t *p_url = p_credential->p_url;
|
||||
|
||||
const char *ppsz_values[KEY_MAX] = { 0 };
|
||||
ppsz_values[KEY_PROTOCOL] = p_url->psz_protocol;
|
||||
ppsz_values[KEY_USER] = p_credential->psz_username;
|
||||
ppsz_values[KEY_SERVER] = p_url->psz_host;
|
||||
/* don't try to match with the path */
|
||||
ppsz_values[KEY_REALM] = p_credential->psz_realm;
|
||||
ppsz_values[KEY_AUTHTYPE] = p_credential->psz_authtype;
|
||||
char psz_port[21];
|
||||
if (p_url->i_port > 0 && !protocol_is_default_port(p_url))
|
||||
{
|
||||
sprintf(psz_port, "%u", p_url->i_port);
|
||||
ppsz_values[KEY_PORT] = psz_port;
|
||||
}
|
||||
|
||||
if (p_credential->i_entries_count > 0)
|
||||
{
|
||||
vlc_keystore_release_entries(p_credential->p_entries,
|
||||
p_credential->i_entries_count);
|
||||
p_credential->i_entries_count = 0;
|
||||
}
|
||||
|
||||
p_credential->i_entries_count = vlc_keystore_find(p_credential->p_keystore,
|
||||
ppsz_values,
|
||||
&p_credential->p_entries);
|
||||
|
||||
if (p_credential->i_entries_count > 0)
|
||||
{
|
||||
vlc_keystore_entry *p_entry;
|
||||
if (protocol_store_path(p_url))
|
||||
p_entry = find_closest_path(p_credential->p_entries,
|
||||
p_credential->i_entries_count,
|
||||
p_url->psz_path);
|
||||
else
|
||||
p_entry = &p_credential->p_entries[0];
|
||||
|
||||
if (!p_entry || p_entry->p_secret[p_entry->i_secret_len - 1] != '\0')
|
||||
{
|
||||
vlc_keystore_release_entries(p_credential->p_entries,
|
||||
p_credential->i_entries_count);
|
||||
p_credential->i_entries_count = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
p_credential->psz_password = (const char *)p_entry->p_secret;
|
||||
p_credential->psz_username = p_entry->ppsz_values[KEY_USER];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vlc_credential_init(vlc_credential *p_credential, const vlc_url_t *p_url)
|
||||
{
|
||||
assert(p_credential);
|
||||
|
||||
memset(p_credential, 0, sizeof(*p_credential));
|
||||
p_credential->i_get_order = GET_FROM_URL;
|
||||
p_credential->p_url = p_url;
|
||||
}
|
||||
|
||||
void
|
||||
vlc_credential_clean(vlc_credential *p_credential)
|
||||
{
|
||||
if (p_credential->p_keystore)
|
||||
{
|
||||
if (p_credential->i_entries_count > 0)
|
||||
vlc_keystore_release_entries(p_credential->p_entries,
|
||||
p_credential->i_entries_count);
|
||||
vlc_keystore_release(p_credential->p_keystore);
|
||||
}
|
||||
|
||||
free(p_credential->psz_split_realm);
|
||||
free(p_credential->psz_var_username);
|
||||
free(p_credential->psz_var_password);
|
||||
free(p_credential->psz_dialog_username);
|
||||
free(p_credential->psz_dialog_password);
|
||||
}
|
||||
|
||||
#undef vlc_credential_get
|
||||
bool
|
||||
vlc_credential_get(vlc_credential *p_credential, vlc_object_t *p_parent,
|
||||
const char *psz_option_username,
|
||||
const char *psz_option_password,
|
||||
const char *psz_dialog_title,
|
||||
const char *psz_dialog_fmt, ...)
|
||||
{
|
||||
assert(p_credential && p_parent);
|
||||
const vlc_url_t *p_url = p_credential->p_url;
|
||||
|
||||
if (!p_url || !p_url->psz_protocol || !p_url->psz_host)
|
||||
{
|
||||
msg_Err(p_parent, "vlc_credential_get: invalid url");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Don't set username to NULL, we may want to use the last one set */
|
||||
p_credential->psz_password = NULL;
|
||||
|
||||
while (!is_credential_valid(p_credential))
|
||||
{
|
||||
/* First, fetch credential from URL (if any).
|
||||
* Secondly, fetch credential from VLC Options (if any).
|
||||
* Thirdly, fetch credential from keystore (if any) using user and realm
|
||||
* previously set by the caller, the URL or by VLC Options.
|
||||
* Finally, fetch credential from the dialog (if any). This last will be
|
||||
* repeated until user cancel the dialog. */
|
||||
|
||||
switch (p_credential->i_get_order)
|
||||
{
|
||||
case GET_FROM_URL:
|
||||
p_credential->psz_username = p_url->psz_username;
|
||||
p_credential->psz_password = p_url->psz_password;
|
||||
|
||||
if (p_credential->psz_password)
|
||||
msg_Warn(p_parent, "Password in a URI is DEPRECATED");
|
||||
|
||||
if (p_url->psz_username && protocol_is_smb(p_url))
|
||||
smb_split_domain(p_credential);
|
||||
p_credential->i_get_order++;
|
||||
break;
|
||||
|
||||
case GET_FROM_OPTION:
|
||||
free(p_credential->psz_var_username);
|
||||
free(p_credential->psz_var_password);
|
||||
p_credential->psz_var_username =
|
||||
p_credential->psz_var_password = NULL;
|
||||
|
||||
if (psz_option_username)
|
||||
p_credential->psz_var_username =
|
||||
var_InheritString(p_parent, psz_option_username);
|
||||
if (psz_option_password)
|
||||
p_credential->psz_var_password =
|
||||
var_InheritString(p_parent, psz_option_password);
|
||||
|
||||
if (p_credential->psz_var_username)
|
||||
p_credential->psz_username = p_credential->psz_var_username;
|
||||
if (p_credential->psz_var_password)
|
||||
p_credential->psz_password = p_credential->psz_var_password;
|
||||
|
||||
p_credential->i_get_order++;
|
||||
break;
|
||||
|
||||
case GET_FROM_KEYSTORE:
|
||||
if (!psz_dialog_title || !psz_dialog_fmt)
|
||||
return false;
|
||||
|
||||
if (p_credential->p_keystore == NULL)
|
||||
p_credential->p_keystore = vlc_keystore_create(p_parent);
|
||||
if (p_credential->p_keystore != NULL)
|
||||
credential_find_keystore(p_credential);
|
||||
|
||||
p_credential->i_get_order++;
|
||||
break;
|
||||
|
||||
default:
|
||||
case GET_FROM_DIALOG:
|
||||
if (!psz_dialog_title || !psz_dialog_fmt)
|
||||
return false;
|
||||
|
||||
free(p_credential->psz_dialog_username);
|
||||
free(p_credential->psz_dialog_password);
|
||||
p_credential->psz_dialog_username =
|
||||
p_credential->psz_dialog_password = NULL;
|
||||
|
||||
/* TODO: save previously saved username and print it in dialog */
|
||||
va_list ap;
|
||||
char *psz_dialog_text;
|
||||
va_start(ap, psz_dialog_fmt);
|
||||
if (vasprintf(&psz_dialog_text, psz_dialog_fmt, ap) == -1)
|
||||
{
|
||||
va_end(ap);
|
||||
return false;
|
||||
}
|
||||
va_end(ap);
|
||||
dialog_Login(p_parent, &p_credential->psz_dialog_username,
|
||||
&p_credential->psz_dialog_password,
|
||||
psz_dialog_title, psz_dialog_text);
|
||||
free(psz_dialog_text);
|
||||
if (p_credential->psz_dialog_username
|
||||
&& p_credential->psz_dialog_password)
|
||||
{
|
||||
/* TODO: add a dialog option to know if the user want to store
|
||||
* the credential */
|
||||
p_credential->b_store = true;
|
||||
p_credential->psz_username = p_credential->psz_dialog_username;
|
||||
p_credential->psz_password = p_credential->psz_dialog_password;
|
||||
|
||||
if (protocol_is_smb(p_url))
|
||||
smb_split_domain(p_credential);
|
||||
}
|
||||
else
|
||||
{
|
||||
p_credential->psz_username = p_credential->psz_password = NULL;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return is_credential_valid(p_credential);
|
||||
}
|
||||
|
||||
bool
|
||||
vlc_credential_store(vlc_credential *p_credential)
|
||||
{
|
||||
if (!p_credential->p_keystore || !p_credential->b_store)
|
||||
return false;
|
||||
|
||||
const vlc_url_t *p_url = p_credential->p_url;
|
||||
|
||||
char *psz_path = NULL;
|
||||
if (protocol_store_path(p_url) && (psz_path = strdup(p_url->psz_path)))
|
||||
{
|
||||
char *p_slash;
|
||||
if (protocol_is_smb(p_url))
|
||||
{
|
||||
/* Remove all characters after the first slash (store the share but
|
||||
* not the path) */
|
||||
p_slash = strchr(psz_path + 1, '/');
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Remove all characters after the last slash (store the path
|
||||
* without the filename) */
|
||||
p_slash = strrchr(psz_path + 1, '/');
|
||||
}
|
||||
if (p_slash && psz_path != p_slash)
|
||||
*p_slash = '\0';
|
||||
else
|
||||
{
|
||||
free(psz_path);
|
||||
psz_path = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const char *ppsz_values[KEY_MAX] = { 0 };
|
||||
ppsz_values[KEY_PROTOCOL] = p_url->psz_protocol;
|
||||
ppsz_values[KEY_USER] = p_credential->psz_username;
|
||||
ppsz_values[KEY_SERVER] = p_url->psz_host;
|
||||
ppsz_values[KEY_PATH] = psz_path;
|
||||
ppsz_values[KEY_REALM] = p_credential->psz_realm;
|
||||
ppsz_values[KEY_AUTHTYPE] = p_credential->psz_authtype;
|
||||
|
||||
char psz_port[21];
|
||||
if (p_url->i_port > 0 && !protocol_is_default_port(p_url))
|
||||
{
|
||||
sprintf(psz_port, "%u", p_url->i_port);
|
||||
ppsz_values[KEY_PORT] = psz_port;
|
||||
}
|
||||
|
||||
char *psz_label;
|
||||
if (asprintf(&psz_label, "LibVLC password for %s://%s%s",
|
||||
p_url->psz_protocol, p_url->psz_host,
|
||||
psz_path ? psz_path : "") == -1)
|
||||
return false;
|
||||
|
||||
bool b_ret = vlc_keystore_store(p_credential->p_keystore, ppsz_values,
|
||||
(const uint8_t *)p_credential->psz_password,
|
||||
-1, psz_label) == VLC_SUCCESS;
|
||||
free(psz_label);
|
||||
free(psz_path);
|
||||
return b_ret;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue