[vlc-devel] [PATCH 05/10][RFC][WIP] cloudstorage: add access module integrating libcloudstorage

Diogo Silva dbtdsilva at gmail.com
Thu Aug 17 03:08:13 CEST 2017


This module supports multiple providers and users. At this point it is
using microhttpd and curl dependency from libcloudstorage. However,
those are replaced in the following patches.
---
 NEWS                                            |   2 +
 configure.ac                                    |   5 +
 modules/MODULES_LIST                            |   3 +-
 modules/access/Makefile.am                      |  10 +
 modules/access/cloudstorage.cpp                 |  41 ++++
 modules/access/cloudstorage/access.cpp          | 248 ++++++++++++++++++++++++
 modules/access/cloudstorage/access.h            |  53 +++++
 modules/access/cloudstorage/provider_callback.h | 114 +++++++++++
 po/POTFILES.in                                  |   4 +
 9 files changed, 479 insertions(+), 1 deletion(-)
 create mode 100644 modules/access/cloudstorage.cpp
 create mode 100644 modules/access/cloudstorage/access.cpp
 create mode 100644 modules/access/cloudstorage/access.h
 create mode 100644 modules/access/cloudstorage/provider_callback.h

diff --git a/NEWS b/NEWS
index ef31c19a83..4dcc89f127 100644
--- a/NEWS
+++ b/NEWS
@@ -56,6 +56,8 @@ Access:
  * New SAT>IP access module, to receive DVB-S via IP networks
  * Improvements on DVB scanning
  * BluRay module can open ISO over network and has full BD-J support
+ * New cloudstorage access module using libcloudstorage supporting cloud
+   services, such as Google Drive, Dropbox and others

 Decoder:
  * OMX GPU-zerocopy support for decoding and display on Android using OpenMax IL
diff --git a/configure.ac b/configure.ac
index 8487fea895..0c232c0ef2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1807,6 +1807,11 @@ dnl  liBDSM access module
 dnl
 PKG_ENABLE_MODULES_VLC([DSM], [dsm], [libdsm >= 0.2.0], [libdsm SMB/CIFS access/sd module], [auto])

+dnl
+dnl  cloud access support with libcloudstorage
+dnl
+PKG_ENABLE_MODULES_VLC([CLOUDSTORAGE], [cloudstorage], [libcloudstorage], [cloudstorage access], [auto])
+
 dnl
 dnl sftp access support
 dnl
diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST
index 9509eba71d..4d0613d4a7 100644
--- a/modules/MODULES_LIST
+++ b/modules/MODULES_LIST
@@ -1,4 +1,4 @@
-List of vlc plugins (475)
+List of vlc plugins (489)
 $Id$
  * a52: A/52 audio decoder plugin, using liba52
  * aa: Ascii art video output
@@ -80,6 +80,7 @@ $Id$
  * chroma_yuv_neon: ARM NEON video chroma conversion
  * ci_filters: CoreImage hardware-accelerated adjust/invert/posterize/sepia/sharpen filters
  * clone: Clone video filter
+ * cloudstorage: Cloud storage services module using libcloudstorage
  * colorthres:  Theshold color based on similarity to reference color Video filter
  * compressor: Dynamic range compressor
  * console_logger: Logger outputting in the terminal
diff --git a/modules/access/Makefile.am b/modules/access/Makefile.am
index dd485a2767..75e4afa885 100644
--- a/modules/access/Makefile.am
+++ b/modules/access/Makefile.am
@@ -313,6 +313,16 @@ endif

 ### Network streams ###

+libcloudstorage_plugin_la_SOURCES = \
+	access/cloudstorage.cpp \
+	access/cloudstorage/access.h access/cloudstorage/access.cpp \
+	access/cloudstorage/provider_callback.h
+libcloudstorage_plugin_la_CXXFLAGS = $(CXXFLAGS_cloudstorage) $(AM_CXXFLAGS)
+libcloudstorage_plugin_la_LIBADD = $(LIBS_cloudstorage)
+libcloudstorage_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(accessdir)'
+access_LTLIBRARIES += $(LTLIBcloudstorage)
+EXTRA_LTLIBRARIES += libcloudstorage_plugin.la
+
 libftp_plugin_la_SOURCES = access/ftp.c
 libftp_plugin_la_LIBADD = $(SOCKET_LIBS)
 access_LTLIBRARIES += libftp_plugin.la
diff --git a/modules/access/cloudstorage.cpp b/modules/access/cloudstorage.cpp
new file mode 100644
index 0000000000..152f2067c7
--- /dev/null
+++ b/modules/access/cloudstorage.cpp
@@ -0,0 +1,41 @@
+/*****************************************************************************
+ * cloudstorage.cpp: cloud access module using libcloudstorage for VLC
+ *****************************************************************************
+ * Copyright (C) 2017 VideoLabs and VideoLAN
+ *
+ * Authors: William Ung <williamung at msn.com>
+ *          Diogo Silva <dbtdsilva at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <vlc_plugin.h>
+
+#include "cloudstorage/access.h"
+
+vlc_module_begin()
+    set_shortname(N_("cloudstorage"))
+    set_capability("access", 0)
+    set_description(N_("Cloud storage services (such as Drive, Dropbox) input"
+        " using libcloudstorage"))
+    set_category(CAT_INPUT)
+    set_subcategory(SUBCAT_INPUT_ACCESS)
+    // callbacks are defined under cloudstorage/access.h
+    set_callbacks(Open, Close)
+vlc_module_end()
diff --git a/modules/access/cloudstorage/access.cpp b/modules/access/cloudstorage/access.cpp
new file mode 100644
index 0000000000..e8a48af47d
--- /dev/null
+++ b/modules/access/cloudstorage/access.cpp
@@ -0,0 +1,248 @@
+/*****************************************************************************
+ * access.cpp: cloud storage access module using libcloudstorage
+ *****************************************************************************
+ * Copyright (C) 2017 VideoLabs and VideoLAN
+ *
+ * Authors: William Ung <williamung at msn.com>
+ *          Diogo Silva <dbtdsilva at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include "access.h"
+
+#include <algorithm>
+#include <sstream>
+#include <fstream>
+#include <ICloudStorage.h>
+#include <IRequest.h>
+
+#include "provider_callback.h"
+
+using cloudstorage::ICloudStorage;
+using cloudstorage::ICloudProvider;
+using cloudstorage::IDownloadFileCallback;
+using cloudstorage::IItem;
+using cloudstorage::IRequest;
+
+static int AddItem( struct access_fsdir *, stream_t *, IItem::Pointer );
+static int GetCredentials( stream_t * );
+static int InitProvider( stream_t * );
+static int ReadDir( stream_t *, input_item_node_t * );
+static std::string ReadFile( const std::string& path );
+static int ParseMRL( stream_t * );
+
+int Open( vlc_object_t *p_this )
+{
+    int err = VLC_EGENERIC;
+    stream_t *p_access = (stream_t*) p_this;
+    access_sys_t *p_sys;
+
+    p_access->p_sys = p_sys = new access_sys_t();
+    if ( p_sys == nullptr )
+        return VLC_ENOMEM;
+
+    if ( (err = ParseMRL( p_access )) != VLC_SUCCESS )
+        goto error;
+    if ( (err = GetCredentials( p_access )) != VLC_SUCCESS )
+        goto error;
+    if ( (err = InitProvider( p_access )) != VLC_SUCCESS )
+        goto error;
+
+    if ( p_sys->current_item->type() == IItem::FileType::Directory )
+    {
+        p_access->pf_control = access_vaDirectoryControlHelper;
+        p_access->pf_readdir = ReadDir;
+        return VLC_SUCCESS;
+    }
+    else
+    {
+        p_sys->current_item = p_sys->provider->
+                getItemDataAsync( p_sys->current_item->id() )->result().right();
+        p_access->psz_url = strdup( p_sys->current_item->url().c_str() );
+        err = VLC_ACCESS_REDIRECT;
+    }
+
+error:
+    Close( p_this );
+    return err;
+}
+
+void Close( vlc_object_t *p_this )
+{
+    stream_t *p_access = (stream_t *) p_this;
+    access_sys_t *p_sys = (access_sys_t *) p_access->p_sys;
+
+    if ( p_sys != nullptr ) {
+        vlc_UrlClean( &p_sys->url );
+        delete p_sys;
+    }
+}
+
+static int ParseMRL( stream_t * p_access )
+{
+    access_sys_t *p_sys = (access_sys_t *) p_access->p_sys;
+
+    // Correct the path if there are any invalid characters like spaces and
+    // Parse the URL into a VLC object
+    char *url = vlc_uri_fixup( p_access->psz_url );
+    int error = vlc_UrlParse( &p_sys->url, url );
+    free( url );
+    if ( error != VLC_SUCCESS )
+        return error;
+
+    // If no path was specific, then it is root
+    if ( p_sys->url.psz_path == NULL )
+        p_sys->url.psz_path = strdup("/");
+    // Since there is no info to store the authenticated user, creates a dummy
+    // user and stores it temporarly under a in-memory credentials.
+    if ( p_sys->url.psz_username == NULL )
+    {
+        p_sys->memory_keystore = true;
+        p_sys->url.psz_username = strdup("dummy_user");
+    }
+
+    return VLC_SUCCESS;
+}
+
+static int GetCredentials( stream_t * p_access )
+{
+    access_sys_t *p_sys = (access_sys_t *) p_access->p_sys;
+    // Init credentials and clean within the same scope to prevent the keystore
+    // to be loaded at all times
+    vlc_credential credentials;
+    vlc_credential_init( &credentials, &p_sys->url );
+    if ( vlc_credential_get( &credentials, p_access,
+            NULL, NULL, NULL, NULL) )
+    {
+        std::string stored_value = std::string( credentials.psz_password );
+        if ( !ICloudProvider::deserializeSession(stored_value,
+                p_sys->token, p_sys->hints) )
+        {
+            msg_Warn( p_access, "Cloudstorage found invalid credentials in the "
+                    "keystore under %s@%s, it is going to overwrite them.",
+                    p_sys->url.psz_username, p_sys->url.psz_host );
+        }
+    }
+    vlc_credential_clean( &credentials );
+    return VLC_SUCCESS;
+}
+
+static int InitProvider( stream_t * p_access )
+{
+    access_sys_t *p_sys = (access_sys_t *) p_access->p_sys;
+    ICloudProvider::Hints hints;
+    std::string redirect_port = std::to_string(
+            config_GetInt( p_access, "http-port" ) );
+
+    // Get predifined port and prefix
+    hints["redirect_uri"] = "http://localhost:" + redirect_port + "/auth";
+    hints["file_url"] = "http://localhost:" + redirect_port + "/files";
+
+    // Initialize the provider
+    p_sys->provider = cloudstorage::ICloudStorage::
+            create()->provider( p_sys->url.psz_host );
+    if ( !p_sys->provider ) {
+        msg_Err( p_access, "Failed to load the given provider" );
+        return VLC_EGENERIC;
+    }
+    p_sys->provider->initialize({
+        p_sys->token,
+        std::unique_ptr<Callback>( new Callback( p_access ) ),
+        nullptr,
+        nullptr,
+        nullptr,
+        hints
+    });
+
+    msg_Dbg( p_access, "Path: %s", p_sys->url.psz_path );
+    p_sys->current_item = p_sys->provider->
+            getItemAsync( vlc_uri_decode( p_sys->url.psz_path ) )->
+            result().right();
+    if ( p_sys->current_item == nullptr )
+    {
+        msg_Err( p_access, "Item %s does not exist in the provider %s",
+                 p_sys->url.psz_path, p_sys->url.psz_host );
+        return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+static int AddItem( struct access_fsdir *p_fsdir, stream_t *p_access,
+                    IItem::Pointer item )
+{
+    std::stringstream url;
+    int i_type;
+
+    url << p_access->psz_url;
+    if ( strlen( p_access->psz_url ) == 0 ||
+         p_access->psz_url[strlen( p_access->psz_url ) - 1] != '/' )
+        url << "/";
+
+    char* url_encoded = vlc_uri_encode( item->filename().c_str() );
+    url << url_encoded;
+    free( url_encoded );
+
+    i_type = item->type() == IItem::FileType::Directory ?
+        ITEM_TYPE_DIRECTORY : ITEM_TYPE_FILE;
+
+    return access_fsdir_additem( p_fsdir, url.str().c_str(),
+            item->filename().c_str(), i_type, ITEM_NET );
+}
+
+static int ReadDir( stream_t *p_access, input_item_node_t *p_node )
+{
+    access_sys_t *p_sys = (access_sys_t *) p_access->p_sys;
+    struct access_fsdir fsdir;
+
+    ICloudProvider::ListDirectoryRequest::Pointer list_directory_request =
+            p_sys->provider->listDirectoryAsync( p_sys->current_item );
+
+    access_fsdir_init( &fsdir, p_access, p_node );
+    int error_code = VLC_SUCCESS;
+    for ( auto &i : *list_directory_request->result().right() )
+    {
+        if ( AddItem( &fsdir, p_access, i ) != VLC_SUCCESS )
+        {
+            error_code = VLC_EGENERIC;
+            break;
+        }
+    }
+    access_fsdir_finish( &fsdir, error_code == VLC_SUCCESS );
+    return error_code;
+}
+
+static std::string ReadFile( const std::string& filename )
+{
+    char * base_path = config_GetDataDir();
+
+    std::string data_filename;
+    data_filename.append( base_path );
+    free( base_path );
+    data_filename.append( DIR_SEP );
+    data_filename.append( filename );
+    std::ifstream stream( data_filename, std::ios::in | std::ios::binary );
+    if ( !stream )
+        return "";
+
+    std::string contents;
+    stream.seekg( 0, std::ios::end );
+    contents.resize( stream.tellg() );
+    stream.seekg( 0, std::ios::beg );
+    stream.read( &contents[0], contents.size() );
+    stream.close();
+
+    return contents;
+}
diff --git a/modules/access/cloudstorage/access.h b/modules/access/cloudstorage/access.h
new file mode 100644
index 0000000000..f755600aaa
--- /dev/null
+++ b/modules/access/cloudstorage/access.h
@@ -0,0 +1,53 @@
+/*****************************************************************************
+ * access.cpp: cloud storage access module using libcloudstorage
+ *****************************************************************************
+ * Copyright (C) 2017 VideoLabs and VideoLAN
+ *
+ * Authors: William Ung <williamung at msn.com>
+ *          Diogo Silva <dbtdsilva at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLC_CLOUDSTORAGE_ACCESS_H
+#define VLC_CLOUDSTORAGE_ACCESS_H
+
+#include <string>
+#include <ICloudProvider.h>
+#include <IItem.h>
+
+#include <vlc_common.h>
+#include <vlc_access.h>
+#include <vlc_input.h>
+#include <vlc_keystore.h>
+#include <vlc_url.h>
+
+int Open( vlc_object_t * );
+void Close( vlc_object_t * );
+
+struct access_sys_t
+{
+    // Parsed parameters
+    vlc_url_t url;
+    bool memory_keystore;
+
+    // Loaded on Open
+    cloudstorage::ICloudProvider::Pointer provider;
+    std::string token;
+    cloudstorage::ICloudProvider::Hints hints;
+    cloudstorage::IItem::Pointer current_item;
+};
+
+#endif
diff --git a/modules/access/cloudstorage/provider_callback.h b/modules/access/cloudstorage/provider_callback.h
new file mode 100644
index 0000000000..f400f08e55
--- /dev/null
+++ b/modules/access/cloudstorage/provider_callback.h
@@ -0,0 +1,114 @@
+/*****************************************************************************
+ * provider_callback.h: Inherit class ICallback from libcloudstorage
+ *****************************************************************************
+ * Copyright (C) 2017 VideoLabs and VideoLAN
+ *
+ * Authors: Diogo Silva <dbtdsilva at gmail.com>
+ *          William Ung <williamung at msn.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLC_CLOUDSTORAGE_PROVIDER_CALLBACK_H
+#define VLC_CLOUDSTORAGE_PROVIDER_CALLBACK_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string>
+#include <vlc_dialog.h>
+
+#include "access.h"
+
+using cloudstorage::ICloudProvider;
+using cloudstorage::EitherError;
+
+class Callback : public ICloudProvider::IAuthCallback {
+public:
+    Callback( stream_t *access ) :
+        p_access( access ), p_sys( (access_sys_t *) access->p_sys )
+    {}
+
+    Status userConsentRequired( const ICloudProvider& provider ) override
+    {
+        std::string authorize_url = provider.authorizeLibraryUrl();
+        int i_ret = vlc_spawn_browser( p_access, authorize_url.c_str(),
+            "Authentication required", "This function requires login through a "
+            "website and will open a webbrowser." );
+        if ( i_ret != 1 )
+            return Status::None;
+        msg_Info( p_access, "User ConsentRequired at : %s",
+                authorize_url.c_str() );
+        return Status::WaitForAuthorizationCode;
+    }
+
+    void done( const ICloudProvider& provider,
+               EitherError<void> error ) override
+    {
+        // An error occurred
+        if ( error.left() )
+        {
+            msg_Err( p_access, "Authorization Error %d: %s",
+                    error.left()->code_, error.left()->description_.c_str() );
+        }
+        else // No error occurred
+        {
+            accepted( provider );
+        }
+    }
+
+    void accepted( const ICloudProvider& provider )
+    {
+        vlc_credential credentials;
+        vlc_credential_init( &credentials, &p_sys->url );
+        bool found = vlc_credential_get( &credentials, p_access,
+                NULL, NULL, NULL, NULL );
+
+        p_sys->token = provider.token();
+        p_sys->hints = provider.hints();
+
+        // Store hints and token
+        std::string serialized_value = ICloudProvider::serializeSession(
+                p_sys->token, p_sys->hints );
+
+        // Store the data related with the session using the credentials API
+        credentials.b_store = !p_sys->memory_keystore;
+        credentials.psz_password = serialized_value.c_str();
+        if ( !vlc_credential_store( &credentials, p_access ) )
+        {
+            msg_Warn( p_access, "Failed to store the credentials");
+        }
+
+        // Inform about new authentications
+        if ( !found )
+        {
+            msg_Dbg( p_access, "%s (new) was authenticated at %s",
+                     p_sys->url.psz_username, p_sys->url.psz_host );
+            std::stringstream ss_user;
+            ss_user << p_sys->url.psz_username << "@" << p_sys->url.psz_host;
+            var_SetString( p_access->obj.libvlc, "cloudstorage-new-auth",
+                    ss_user.str().c_str() );
+        }
+        vlc_credential_clean( &credentials );
+        msg_Dbg( p_access, "Accepted credentials!");
+    }
+
+private:
+    stream_t *p_access;
+    access_sys_t *p_sys;
+};
+
+#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 01388cb324..d47d046f41 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -177,6 +177,10 @@ modules/access/attachment.c
 modules/access/avio.h
 modules/access/bluray.c
 modules/access/cdda.c
+modules/access/cloudstorage.cpp
+modules/access/cloudstorage/access.cpp
+modules/access/cloudstorage/access.h
+modules/access/cloudstorage/provider_callback.h
 modules/access/concat.c
 modules/access/dc1394.c
 modules/access/dcp/dcp.cpp
--
2.14.1


More information about the vlc-devel mailing list