[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