[vlc-devel] [PATCH 08/11] add vlc_credential API
Thomas Guillem
thomas at gllm.fr
Wed Jan 6 16:56:28 CET 2016
---
include/vlc_keystore.h | 109 ++++++++++++++++++-
src/libvlccore.sym | 4 +
src/misc/keystore.c | 278 ++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 389 insertions(+), 2 deletions(-)
diff --git a/include/vlc_keystore.h b/include/vlc_keystore.h
index 1cdf8e7..e12615e 100644
--- a/include/vlc_keystore.h
+++ b/include/vlc_keystore.h
@@ -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
* @{
@@ -146,6 +147,112 @@ 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;
+ /** true if the path should be stored and searched */
+ bool b_strict_path;
+ /** 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_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
+ * @param b_strict_path true if the path should be stored and searched
+ * @param psz_realm http realm or smb domain
+ * @param psz_authtype http authtype
+ */
+VLC_API void
+vlc_credential_init(vlc_credential *p_credential,
+ const vlc_url_t *p_url, bool b_strict_path,
+ const char *psz_realm, const char *psz_authtype);
+
+/**
+ * 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
* @{
*/
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index e624fcb..e1d6aba 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -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
diff --git a/src/misc/keystore.c b/src/misc/keystore.c
index b89c679..7b5e30e 100644
--- a/src/misc/keystore.c
+++ b/src/misc/keystore.c
@@ -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,279 @@ 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
+vlc_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_url->psz_username ? p_url->psz_username
+ : p_credential->psz_var_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)
+ {
+ 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 (p_credential->b_strict_path && p_url->psz_path)
+ 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;
+ return false;
+ }
+
+ p_credential->psz_password = (const char *)p_entry->p_secret;
+ p_credential->psz_username = p_entry->ppsz_values[KEY_USER];
+ return true;
+ }
+
+ return false;
+}
+
+void
+vlc_credential_init(vlc_credential *p_credential,
+ const vlc_url_t *p_url, bool b_strict_path,
+ const char *psz_realm, const char *psz_authtype)
+{
+ assert(p_credential);
+
+ memset(p_credential, 0, sizeof(*p_credential));
+ p_credential->i_get_order = GET_FROM_URL;
+ p_credential->p_url = p_url;
+ p_credential->b_strict_path = b_strict_path;
+ p_credential->psz_realm = psz_realm;
+ p_credential->psz_authtype = psz_authtype;
+}
+
+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_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);
+ bool b_found = false;
+ 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;
+ }
+
+ while (!b_found)
+ {
+ switch (p_credential->i_get_order)
+ {
+ case GET_FROM_URL:
+ if (p_url->psz_username && p_url->psz_password)
+ {
+ b_found = true;
+ p_credential->psz_username = p_url->psz_username;
+ p_credential->psz_password = p_url->psz_password;
+ }
+ 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_var_password)
+ {
+ b_found = true;
+ p_credential->psz_username = p_credential->psz_var_username;
+ 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)
+ b_found = vlc_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;
+
+ b_found = true;
+ p_credential->psz_username = p_credential->psz_dialog_username;
+ p_credential->psz_password = p_credential->psz_dialog_password;
+ }
+ else
+ return false;
+ break;
+ }
+ }
+ return b_found;
+}
+
+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 (p_credential->b_strict_path && p_url->psz_path
+ && (psz_path = strdup(p_url->psz_path)))
+ {
+ /* Remove all characters after the last slash */
+ char *p_last_slash = strrchr(psz_path, '/');
+ if (p_last_slash && psz_path != p_last_slash)
+ *p_last_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)
+ {
+ 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;
}
--
2.1.4
More information about the vlc-devel
mailing list