[vlc-devel] [RFC PATCH 7/8] add vlc_credential API
Thomas Guillem
thomas at gllm.fr
Wed Dec 30 19:37:04 CET 2015
---
include/vlc_keystore.h | 52 ++++++++++
src/libvlccore.sym | 3 +
src/misc/keystore.c | 258 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 313 insertions(+)
diff --git a/include/vlc_keystore.h b/include/vlc_keystore.h
index 1a1910f..6bdfcaf 100644
--- a/include/vlc_keystore.h
+++ b/include/vlc_keystore.h
@@ -30,6 +30,7 @@
typedef struct vlc_keystore vlc_keystore;
typedef struct vlc_keystore_entry vlc_keystore_entry;
+typedef struct vlc_credential vlc_credential;
/**
* @defgroup keystore Public API
@@ -144,6 +145,57 @@ vlc_keystore_release_entries(vlc_keystore_entry *p_entries, unsigned int i_count
/**
* @}
+ * @defgroup credentials
+ * @{
+ */
+
+/**
+ * Create a credential object
+ *
+ * @note to be released with vlc_credential_release()
+ *
+ * @param p_parent the parent object used to create the keystore object
+ * @param psz_realm http realm or smb domain
+ * @param psz_authtype http authtype
+ * @param psz_dialog_title dialog title
+ * @param psz_dialog_fmt dialog text using format
+ *
+ * @return a pointer to the credential object, or NULL in case of error
+ */
+VLC_API vlc_credential *
+vlc_credential_create(vlc_object_t *p_parent, const char *psz_url,
+ const char *psz_realm, const char *psz_authtype,
+ const char *psz_dialog_title,
+ const char *psz_dialog_fmt, ...) VLC_FORMAT(6, 7);
+#define vlc_credential_create(x, a, b, c, d, e, ...) \
+ vlc_credential_create(VLC_OBJECT(x), a, b, c, d, e, __VA_ARGS__)
+
+/**
+ * Release a credential object.
+ *
+ * @param b_valid true if the credential is valid and should be stored
+ *
+ * @return true if credential was stored
+ */
+VLC_API bool
+vlc_credential_release(vlc_credential *p_credential, bool b_valid);
+
+/**
+ * Get a username/password couple
+ *
+ * This will search for a credential using the vlc_keystore API or by asking
+ * the user via dialog_Login(). This function can be called indefinitely, but
+ * vlc_keystore will be used only the first time (if there is one).
+ *
+ * @return true if ppsz_username and ppsz_password are valid, otherwise this
+ * function should not be called again.
+ */
+VLC_API bool
+vlc_credential_get(vlc_credential *p_credential,
+ const char **ppsz_username, const char **ppsz_password);
+
+/**
+ * @}
* @defgroup keystore_implementation to be implemented by keystore modules
* @{
*/
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index e624fcb..c902513 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -519,6 +519,9 @@ vlc_cond_init_daytime
vlc_cond_signal
vlc_cond_timedwait
vlc_cond_wait
+vlc_credential_create
+vlc_credential_get
+vlc_credential_release
vlc_sem_init
vlc_sem_destroy
vlc_sem_post
diff --git a/src/misc/keystore.c b/src/misc/keystore.c
index 8af13dc..3c3ecbb 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>
@@ -123,3 +125,259 @@ vlc_keystore_release_entries(vlc_keystore_entry *p_entries, unsigned int i_count
}
free (p_entries);
}
+
+struct vlc_credential
+{
+ vlc_object_t *p_parent;
+ vlc_keystore *p_keystore;
+ vlc_url_t url;
+
+ bool b_keystore_tried;
+ bool b_store;
+
+ char *psz_dialog_title;
+ char *psz_dialog_text;
+
+ char *psz_label;
+
+ char *psz_realm;
+ char *psz_authtype;
+
+ char *psz_username;
+ char *psz_password;
+};
+
+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)
+{
+ vlc_url_t *p_url = &p_credential->url;
+ vlc_keystore_entry *p_entries;
+
+ const char *ppsz_values[KEY_MAX] = { 0 };
+ ppsz_values[KEY_PROTOCOL] = p_url->psz_protocol;
+ ppsz_values[KEY_USER] = p_url->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)
+ {
+ sprintf(psz_port, "%u", p_url->i_port);
+ ppsz_values[KEY_PORT] = psz_port;
+ }
+
+ unsigned int i_count = vlc_keystore_find(p_credential->p_keystore,
+ ppsz_values, &p_entries);
+ if (i_count > 0)
+ {
+ vlc_keystore_entry *p_entry = find_closest_path(p_entries, i_count,
+ p_url->psz_path);
+ if (!p_entry)
+ {
+ vlc_keystore_release_entries(p_entries, i_count);
+ return false;
+ }
+
+ if (p_entry->p_secret[p_entry->i_secret_len -1] == '\0')
+ {
+ p_credential->psz_password = strdup((const char *)p_entry->p_secret);
+ p_credential->psz_username = strdup(p_entry->ppsz_values[KEY_USER]);
+ if (p_credential->psz_password && p_credential->psz_username)
+ {
+ vlc_keystore_release_entries(p_entries, i_count);
+ return true;
+ }
+ else
+ {
+ free(p_credential->psz_password);
+ free(p_credential->psz_username);
+ }
+ }
+
+ vlc_keystore_release_entries(p_entries, i_count);
+ }
+
+ return false;
+}
+
+static void
+vlc_credential_clean(vlc_credential *p_credential)
+{
+ vlc_object_release(p_credential->p_parent);
+
+ vlc_UrlClean(&p_credential->url);
+
+ if (p_credential->p_keystore)
+ vlc_keystore_release(p_credential->p_keystore);
+
+ free(p_credential->psz_dialog_title);
+ free(p_credential->psz_dialog_text);
+
+ free(p_credential->psz_label);
+
+ free(p_credential->psz_realm);
+ free(p_credential->psz_authtype);
+
+ free(p_credential->psz_username);
+ free(p_credential->psz_password);
+ free(p_credential);
+}
+
+#undef vlc_credential_create
+vlc_credential *
+vlc_credential_create(vlc_object_t *p_parent, const char *psz_url,
+ const char *psz_realm, const char *psz_authtype,
+ const char *psz_dialog_title,
+ const char *psz_dialog_fmt, ...)
+{
+ assert(p_parent && psz_url && psz_dialog_title && psz_dialog_fmt);
+
+ vlc_credential *p_credential = calloc(1, sizeof(vlc_credential));
+ if (!p_credential)
+ return NULL;
+
+ p_credential->p_parent = vlc_object_hold(p_parent);
+
+ p_credential->psz_dialog_title = strdup(psz_dialog_title);
+ if (!p_credential->psz_dialog_title)
+ goto error;
+
+ va_list ap;
+ va_start(ap, psz_dialog_fmt);
+ if (vasprintf(&p_credential->psz_dialog_text, psz_dialog_fmt, ap) == -1)
+ goto error;
+
+ p_credential->p_keystore = vlc_keystore_create(p_parent);
+ if (!p_credential->p_keystore)
+ {
+ /* Following initialisations are only used by the keystore */
+ return p_credential;
+ }
+
+ vlc_UrlParse(&p_credential->url, psz_url);
+ vlc_url_t *p_url = &p_credential->url;
+ if (!p_url->psz_protocol || !p_url->psz_host || !p_url->psz_path)
+ {
+ msg_Err(p_parent, "vlc_credential_find: invalid url");
+ goto error;
+ }
+ /* Remove all characters after the last slash */
+ char *p_last_slash = strrchr(p_url->psz_path, '/');
+ if (p_last_slash && p_url->psz_path != p_last_slash)
+ *p_last_slash = '\0';
+
+ if (psz_realm)
+ {
+ p_credential->psz_realm = strdup(psz_realm);
+ if (!p_credential->psz_realm)
+ goto error;
+ }
+
+ if (psz_authtype)
+ {
+ p_credential->psz_authtype = strdup(psz_authtype);
+ if (!p_credential->psz_authtype)
+ goto error;
+ }
+
+ if (asprintf(&p_credential->psz_label,
+ "LibVLC password for %s://%s%s", p_url->psz_protocol,
+ p_url->psz_host, p_url->psz_path) == -1)
+ goto error;
+
+ return p_credential;
+error:
+ vlc_credential_clean(p_credential);
+ return NULL;
+}
+
+bool
+vlc_credential_get(vlc_credential *p_credential,
+ const char **ppsz_username, const char **ppsz_password)
+{
+ free(p_credential->psz_username);
+ free(p_credential->psz_password);
+ p_credential->psz_username = p_credential->psz_password = NULL;
+
+ /* Fetch credential via the keystore only the first time */
+ if (p_credential->p_keystore && !p_credential->b_keystore_tried
+ && vlc_credential_find_keystore(p_credential))
+ {
+ p_credential->b_keystore_tried = true;
+ goto found;
+ }
+
+ /* TODO: save previously saved username and print it in dialog */
+ dialog_Login(p_credential->p_parent, &p_credential->psz_username,
+ &p_credential->psz_password, p_credential->psz_dialog_title,
+ p_credential->psz_dialog_text);
+
+ if (!p_credential->psz_username || !p_credential->psz_password)
+ return false;
+
+ /* TODO: add a dialog option to know if the user want to store the
+ * credential */
+ p_credential->b_store = true;
+
+found:
+ *ppsz_username = p_credential->psz_username;
+ *ppsz_password = p_credential->psz_password;
+ return true;
+}
+
+bool
+vlc_credential_release(vlc_credential *p_credential, bool b_valid)
+{
+ if (!p_credential->p_keystore || !p_credential->b_store || !b_valid)
+ {
+ vlc_credential_clean(p_credential);
+ return false;
+ }
+
+ vlc_url_t *p_url = &p_credential->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;
+ ppsz_values[KEY_PATH] = p_url->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;
+ }
+
+ bool b_stored =
+ vlc_keystore_store(p_credential->p_keystore, ppsz_values,
+ (const uint8_t *)p_credential->psz_password, -1,
+ p_credential->psz_label) == VLC_SUCCESS;
+ vlc_credential_clean(p_credential);
+ return b_stored;
+}
--
2.1.4
More information about the vlc-devel
mailing list