[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