← Back to team overview

ubuntu-touch-coreapps-reviewers team mailing list archive

[Merge] lp:~carlos-mazieri/ubuntu-filemanager-app/samba-browsing-04 into lp:ubuntu-filemanager-app

 

Carlos Jose Mazieri has proposed merging lp:~carlos-mazieri/ubuntu-filemanager-app/samba-browsing-04 into lp:ubuntu-filemanager-app with lp:~carlos-mazieri/ubuntu-filemanager-app/samba-browsing-03 as a prerequisite.

Commit message:
Intoduces the SmbUtil class which provides the basic open/close/listing operations for Samba items.


Requested reviews:
  Ubuntu File Manager Developers (ubuntu-filemanager-dev)

For more details, see:
https://code.launchpad.net/~carlos-mazieri/ubuntu-filemanager-app/samba-browsing-04/+merge/252220

Intoduces the SmbUtil class which provides the basic open/close/listing operations for Samba items.

it comes separated in a "qsambaclient" directory which is intended to be a generic Qt Samba client library.
-- 
Your team Ubuntu File Manager Developers is requested to review the proposed merge of lp:~carlos-mazieri/ubuntu-filemanager-app/samba-browsing-04 into lp:ubuntu-filemanager-app.
=== modified file 'src/plugin/folderlistmodel/CMakeLists.txt'
--- src/plugin/folderlistmodel/CMakeLists.txt	2015-03-08 12:43:13 +0000
+++ src/plugin/folderlistmodel/CMakeLists.txt	2015-03-08 12:43:13 +0000
@@ -3,6 +3,8 @@
     disk
     trash   
     net
+    smb 
+    smb/qsambaclient/src
 )
 
 set(PLUGIN_DIR org/nemomobile/folderlistmodel)
@@ -55,8 +57,12 @@
     trash/trashiteminfo.h
     trash/trashlocation.cpp
     trash/trashlocation.h          
+    smb/qsambaclient/src/smbutil.cpp
+    smb/qsambaclient/src/smbutil.h
     net/netauthenticationdata.cpp
-    net/netauthenticationdata.h   
+    net/netauthenticationdata.h  
+    net/netutil.cpp
+    net/netutil.h
 )
 
 add_library(nemofolderlistmodel MODULE
@@ -65,6 +71,24 @@
 
 qt5_use_modules(nemofolderlistmodel Gui Qml Quick Widgets)
 
+## samba requires libsmbclient
+find_path(SAMBA_INCLUDE_DIR 
+          NAMES libsmbclient.h 
+          HINTS /usr/include/smbclient /usr/include/samba /usr/include/samba-3.0 /usr/include/samba-4.0
+          )
+find_library(SAMBA_LIBRARIES NAMES smbclient )
+message(STATUS "samba include=${SAMBA_INCLUDE_DIR}")
+message(STATUS "samba lib=${SAMBA_LIBRARIES}=${SAMBA_LIBRARIES}")
+
+if(SAMBA_INCLUDE_DIR AND SAMBA_LIBRARIES)
+   message(STATUS "Found samba: include=${SAMBA_INCLUDE_DIR}  library=${SAMBA_LIBRARIES}")
+   INCLUDE_DIRECTORIES(${SAMBA_INCLUDE_DIR})
+   TARGET_LINK_LIBRARIES(nemofolderlistmodel ${SAMBA_LIBRARIES})
+else(SAMBA_INCLUDE_DIR AND SAMBA_LIBRARIES)
+   message(FATAL_ERROR "Could not find Samba libsmbclient")
+endif(SAMBA_INCLUDE_DIR AND SAMBA_LIBRARIES)
+mark_as_advanced(SAMBA_INCLUDE_DIR SAMBA_LIBRARIES)
+## end samba confiuration
 
 # Copy the plugin, the qmldir file and other assets to the build dir for running in QtCreator
 if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")

=== modified file 'src/plugin/folderlistmodel/folderlistmodel.pri'
--- src/plugin/folderlistmodel/folderlistmodel.pri	2015-03-08 12:43:13 +0000
+++ src/plugin/folderlistmodel/folderlistmodel.pri	2015-03-08 12:43:13 +0000
@@ -40,8 +40,8 @@
 HEADERS +=  $$PWD/trash/qtrashdir.h       $$PWD/trash/trashiteminfo.h    \
             $$PWD/trash/qtrashutilinfo.h  $$PWD/trash/trashlocation.h
 
-SOURCES +=  $$PWD/net/netauthenticationdata.cpp
-HEADERS +=  $$PWD/net/netauthenticationdata.h
+SOURCES +=  $$PWD/net/netutil.cpp $$PWD/net/netauthenticationdata.cpp
+HEADERS +=  $$PWD/net/netutil.h   $$PWD/net/netauthenticationdata.h
 
 INCLUDEPATH  += $$PWD $$PWD/trash $$PWD/disk $$PWD/net
 

=== modified file 'src/plugin/folderlistmodel/location.cpp'
--- src/plugin/folderlistmodel/location.cpp	2015-03-08 12:43:13 +0000
+++ src/plugin/folderlistmodel/location.cpp	2015-03-08 12:43:13 +0000
@@ -43,6 +43,8 @@
 #include "ioworkerthread.h"
 #include "netauthenticationdata.h"
 
+#include <QDebug>
+
 Q_GLOBAL_STATIC(IOWorkerThread, ioWorkerThread)
 
 

=== modified file 'src/plugin/folderlistmodel/locationurl.cpp'
--- src/plugin/folderlistmodel/locationurl.cpp	2014-05-02 10:42:48 +0000
+++ src/plugin/folderlistmodel/locationurl.cpp	2015-03-08 12:43:13 +0000
@@ -23,8 +23,9 @@
 
 const QString LocationUrl::TrashRootURL("trash:///");
 const QString LocationUrl::DiskRootURL("file:///");
+const QString LocationUrl::SmbURL("smb://");
+const QString LocationUrl::CifsURL("cifs://");
 #if 0
-QString LocationURL::SmbURL("smb://");
 QString LocationURL::FishURL("fish:///");
 #endif
 

=== modified file 'src/plugin/folderlistmodel/locationurl.h'
--- src/plugin/folderlistmodel/locationurl.h	2014-05-02 10:42:48 +0000
+++ src/plugin/folderlistmodel/locationurl.h	2015-03-08 12:43:13 +0000
@@ -29,8 +29,9 @@
 public:
     static const   QString DiskRootURL;
     static const   QString TrashRootURL;
+    static const   QString SmbURL;
+    static const   QString CifsURL;
 #if 0
-    static const   QString SmbURL;
     static const   QString FishURL;
 #endif
 private:

=== added file 'src/plugin/folderlistmodel/net/netutil.cpp'
--- src/plugin/folderlistmodel/net/netutil.cpp	1970-01-01 00:00:00 +0000
+++ src/plugin/folderlistmodel/net/netutil.cpp	2015-03-08 12:43:13 +0000
@@ -0,0 +1,89 @@
+/**************************************************************************
+ *
+ * Copyright 2014 Canonical Ltd.
+ * Copyright 2014 Carlos J Mazieri <carlos.mazieri@xxxxxxxxx>
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * File: netutil.cpp
+ * Date: 29/11/2014
+ */
+
+#include "netutil.h"
+#include <QHostAddress>
+#include <QHostInfo>
+#include <QUrl>
+
+#include <QDebug>
+
+NetUtil::NetUtil()
+{
+}
+
+
+QString NetUtil::normalizeHostName(const QString& name)
+{
+    QString host(name.toLower());
+    bool isLoopBack = false;
+    QHostInfo info = QHostInfo::fromName(host);
+    // take advantage of network with Bonjour/Avahi
+    // as winbind looks like harder to configure or does not work
+    if (info.error() == QHostInfo::HostNotFound)
+    {
+        host += QLatin1String(".local");
+        info = QHostInfo::fromName(host);
+    }
+    if (info.error() == QHostInfo::NoError)
+    {
+        host = info.hostName();
+        QList<QHostAddress> addrs  = info.addresses();
+        int counter = addrs.count();
+        while (!isLoopBack && counter--)
+        {
+            isLoopBack = addrs.at(counter).isLoopback();
+        }
+    }
+    if (isLoopBack)
+    {
+         host = QLatin1String("localhost");
+    }
+    return host;
+}
+
+/*!
+ * \brief NetUtil::urlConvertHostnameToIP()  Tries to convert an url like protocol://hostname/blavbla to protocol://ip-address/blavbla
+ * \param url
+ * \return the url using IP numbers or an empty string saying that was not possible to get its IP number
+ */
+QString NetUtil::urlConvertHostnameToIP(const QString &url)
+{
+    QString ret;
+    QUrl tmpUrl(url);
+    if (tmpUrl.isValid() && !tmpUrl.host().isEmpty() && tmpUrl.host() != QLatin1String("localhost"))
+    {
+       QString host = tmpUrl.host();
+       QHostInfo info = QHostInfo::fromName(host);
+       if (info.error() == QHostInfo::HostNotFound)
+       {
+           // take advantage of network with Bonjour/Avahi
+           // as winbind looks like harder to configure or does not work
+           info = QHostInfo::fromName(host + QLatin1String(".local"));
+       }
+       if (info.error() == QHostInfo::NoError)
+       {
+           tmpUrl.setHost(info.addresses().at(0).toString());
+           ret = tmpUrl.toString();
+       }
+    }
+    return ret;
+}

=== added file 'src/plugin/folderlistmodel/net/netutil.h'
--- src/plugin/folderlistmodel/net/netutil.h	1970-01-01 00:00:00 +0000
+++ src/plugin/folderlistmodel/net/netutil.h	2015-03-08 12:43:13 +0000
@@ -0,0 +1,36 @@
+/**************************************************************************
+ *
+ * Copyright 2014 Canonical Ltd.
+ * Copyright 2014 Carlos J Mazieri <carlos.mazieri@xxxxxxxxx>
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * File: netutil.h
+ * Date: 29/11/2014
+ */
+
+#ifndef NETUTIL_H
+#define NETUTIL_H
+
+#include <QString>
+
+class NetUtil
+{
+private:
+    NetUtil();
+public:
+    static QString normalizeHostName(const QString& name);
+    static QString urlConvertHostnameToIP(const QString& url);
+};
+
+#endif // NETUTIL_H

=== added directory 'src/plugin/folderlistmodel/smb'
=== added directory 'src/plugin/folderlistmodel/smb/qsambaclient'
=== added file 'src/plugin/folderlistmodel/smb/qsambaclient/qsambaclient.pri'
--- src/plugin/folderlistmodel/smb/qsambaclient/qsambaclient.pri	1970-01-01 00:00:00 +0000
+++ src/plugin/folderlistmodel/smb/qsambaclient/qsambaclient.pri	2015-03-08 12:43:13 +0000
@@ -0,0 +1,14 @@
+
+SOURCES +=   $$PWD/src/smbutil.cpp 
+             
+
+
+HEADERS +=   $$PWD/src/smbutil.h 
+                     
+
+QT        *= core network             
+
+CONFIG    *= link_pkgconfig
+PKGCONFIG *= smbclient
+
+INCLUDEPATH += $$PWD/src

=== added directory 'src/plugin/folderlistmodel/smb/qsambaclient/src'
=== added file 'src/plugin/folderlistmodel/smb/qsambaclient/src/smbutil.cpp'
--- src/plugin/folderlistmodel/smb/qsambaclient/src/smbutil.cpp	1970-01-01 00:00:00 +0000
+++ src/plugin/folderlistmodel/smb/qsambaclient/src/smbutil.cpp	2015-03-08 12:43:13 +0000
@@ -0,0 +1,704 @@
+/**************************************************************************
+ *
+ * Copyright 2014 Canonical Ltd.
+ * Copyright 2014 Carlos J Mazieri <carlos.mazieri@xxxxxxxxx>
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * File: smbutil.cpp
+ * Date: 20/11/2014
+ */
+
+#include "smbutil.h"
+#include "locationurl.h"
+#include "netutil.h"
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <errno.h>
+
+#include <QUrl>
+#include <QDebug>
+#include <QRegExp>
+
+// set debug level at compilation time
+#ifndef SMB_DEBUG_LEVEL
+#define SMB_DEBUG_LEVEL 0
+#endif
+
+
+#if defined(SHOW_MESSAGES)
+#  define DBG(more_items) qDebug() << Q_FUNC_INFO  more_items
+#else
+#define DBG(none)
+#endif
+
+static QByteArray   s_user("guest");
+static QByteArray   s_passwd;
+static QByteArray   s_workGroup("WORKGROUP");
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::SmbUtil() This is the default constructor that provides the default authentication method
+ *
+ *  The user is the current user, password is "passwd" and the authentication function is \ref authenticateCallBack()
+ */
+SmbUtil::SmbUtil()
+{
+   init(::qgetenv("USER"), QLatin1String("passwd"), &SmbUtil::authenticateCallBack);
+}
+
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::SmbUtil() This constructor accepts an \a user and \a password for authentication
+ *
+ *   The authentication function is \ref authenticateCallBack()
+ *
+ * \param authUser
+ * \param authPassword
+ */
+SmbUtil::SmbUtil(const QString& authUser, const QString& authPassword)
+{
+    init(authUser, authPassword, &SmbUtil::authenticateCallBack);
+}
+
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::SmbUtil() This constructor accepts another authentication function other than the default
+ *
+ *  No users no password is providedm the function should be able to provide everything
+ *
+ * \param ptAuthenticateCallBack
+ */
+SmbUtil::SmbUtil(Smb::AuthenticationFunction ptAuthenticateCallBack):
+         m_authCallBack(ptAuthenticateCallBack)
+{
+
+}
+
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::SmbUtil() This is the more complete constructor where user and password can come from the \a smbUrl
+ * \param smbUrl
+ * \param fn
+ */
+SmbUtil::SmbUtil(const QUrl& smbUrl, Smb::AuthenticationFunction fn)
+{
+    m_authCallBack  = fn ? fn : &SmbUtil::authenticateCallBack;
+    if (!smbUrl.userName().isEmpty())
+    {
+        init(smbUrl.userName(), smbUrl.password(), m_authCallBack);
+    }
+}
+
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::~SmbUtil() destructor
+ */
+SmbUtil::~SmbUtil()
+{   
+}
+
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::authenticateCallBack() Default authentication function, it uses static variables to keep user/password
+ * \param server
+ * \param share
+ * \param wrkgrp
+ * \param wrkgrplen
+ * \param user
+ * \param userlen
+ * \param passwd
+ * \param passwdlen
+ */
+void SmbUtil::authenticateCallBack( const char *server,
+                                    const char *share,
+                                    char *wrkgrp,
+                                    int wrkgrplen,
+                                    char *user,
+                                    int userlen,
+                                    char *passwd,
+                                    int passwdlen)
+{
+    Q_UNUSED(server);
+    Q_UNUSED(share);
+    DBG(<< "server:" << server << "share:" << share << "wrkgrp:" << wrkgrp << "user:" << user << "passwd:" << passwd);
+
+#if 0
+    //this may not be necessary
+    ::strncpy(wrkgrp, s_workGroup.constData(), --wrkgrplen);
+#else
+    Q_UNUSED(wrkgrp);
+    Q_UNUSED(wrkgrplen);
+#endif
+
+ // check some environment variables to help test authentication
+#if defined(REGRESSION_TEST_QSAMBACLIENT)
+    QByteArray  env = ::qgetenv("SMB_DEFAULT_USER");
+    if (env.size() > 0)
+    {
+        s_user = env;
+    }
+    env = ::qgetenv("SMB_DEFAULT_PASSWORD");
+    if (env.size() > 0)
+    {
+        s_passwd = env;
+    }
+#endif
+
+    ::strncpy(user,   s_user.constData(),      --userlen);
+    ::strncpy(passwd, s_passwd.constData(),    --passwdlen);
+}
+
+
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::init() Just stores user/password and authentication function
+ * \param user
+ * \param password
+ * \param fn
+ */
+void SmbUtil::init(const QString &user, const QString &password, Smb::AuthenticationFunction fn)
+{
+    s_user   = user.toLocal8Bit();
+    s_passwd = password.toLocal8Bit();
+    m_authCallBack = fn;
+}
+
+
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::createContext() It creates a SMB context which is necessary to all operations
+ *
+ *     It sets the current authentication function callback
+ *
+ * \return the context created
+ */
+Smb::Context SmbUtil::createContext()
+{
+    Smb::Context ctx = smbc_new_context();
+    if (ctx)
+    {
+        smbc_setDebug(ctx, SMB_DEBUG_LEVEL);                 
+        smbc_setFunctionAuthData(ctx, m_authCallBack);
+        if (smbc_init_context(ctx) == NULL)
+        {
+            smbc_free_context(ctx, 1);
+            ctx = 0;
+        }
+    }
+    DBG(<< "ctx:" << ctx);
+    return ctx;
+}
+
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::deleteContext() Just deletes a context created by \ref createContext()
+ * \param context
+ */
+void SmbUtil::deleteContext(Smb::Context context)
+{   
+    smbc_getFunctionPurgeCachedServers(context)(context);
+    smbc_free_context(context, 1);
+    DBG();
+}
+
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::openFile() opens a file
+ * \param context
+ * \param smb_path  it must point to a file full pathname
+ * \param flags
+ * \param mode
+ * \return the FileHandler or NULL when it is not possible to open the file
+ */
+Smb::FileHandler
+SmbUtil::openFile(Smb::Context context, const QString &smb_path, int flags , mode_t mode)
+{   
+    Smb::FileHandler fd = ::smbc_getFunctionOpen(context)
+                            (context, smb_path.toLocal8Bit().constData(), flags, mode);
+
+    if (fd == 0 && errno != EISDIR)
+    {
+        QString ipUrl = NetUtil::urlConvertHostnameToIP(smb_path);
+        if (!ipUrl.isEmpty())
+        {
+            fd = ::smbc_getFunctionOpen(context)
+                    (context, ipUrl.toLocal8Bit().constData(), flags, mode);
+        }
+    }
+    if (fd == 0)
+    {
+        qWarning() << Q_FUNC_INFO << "errno:" << errno << smb_path;
+    }
+    return fd;
+}
+
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::openDir() opens a directory
+ * \param context
+ * \param smb_string it must point to a directory full pathname
+ * \return  the FileHandler or NULL when it is not possible to open the directory
+ */
+Smb::FileHandler
+SmbUtil::openDir(Smb::Context context, const QString &smb_string)
+{ 
+  Smb::FileHandler fd = ::smbc_getFunctionOpendir(context)
+                             (context, smb_string.toLocal8Bit().constData());
+
+  if (fd == 0)
+  {
+      //try to use an IP address if possible
+       QString ipUrl = NetUtil::urlConvertHostnameToIP(smb_string);
+       if (!ipUrl.isEmpty())
+       {
+           fd = ::smbc_getFunctionOpendir(context)
+                                        (context, ipUrl.toLocal8Bit().constData());
+       }
+  }
+  if (fd == 0)
+  {
+      qWarning() << Q_FUNC_INFO << "errno:" << errno << smb_string;
+  }
+  return fd;
+}
+
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::closeHandle() closes a open FileHandler created by \ref openDir() or \ref openFile()
+ * \param context
+ * \param fd
+ */
+void SmbUtil::closeHandle(Smb::Context context, Smb::FileHandler fd)
+{
+    if (fd)
+    {
+        ::smbc_getFunctionClose(context)(context, fd);
+    }
+}
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::setAuthenticationCallback() Just sets the authentication function
+ * \param fn
+ */
+void SmbUtil::setAuthenticationCallback(Smb::AuthenticationFunction fn)
+{
+    m_authCallBack = fn;
+}
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::getStatInfo() It gets information about files and directories, similar to POSIX stat(2)
+ *
+ * It looks like smbclient brings no information for directories, it works only for files, in this case the caller
+ * must set valid information in the struct stat.
+ *
+ * The distintion between files and directories is made by \ref openDir() and \ref openFile(), as just one
+ *  of them should open the \a smb_path.
+ * For directories which it is supposed to have a content it is necessary to know if it is a
+ *   host/share/workgroup or a single directory, for this the \ref guessDirType() is used.
+ *
+ * \param smb_path  it must point to a file full pathname file or directory
+ * \param st        pointer to a struct stat which will receive the information
+ * \return   one of the \ref StatReturn
+ */
+SmbUtil::StatReturn
+SmbUtil::getStatInfo(const QString &smb_path, struct stat* st)
+{   
+    Smb::Context context = createContext();       
+    Q_ASSERT(context);
+    ::memset(st, 0 , sizeof(struct stat));
+    StatReturn ret   = StatInvalid;
+    int slashes = smb_path.count(QDir::separator());
+    Smb::FileHandler fd = 0;
+    //  smb:// -> slahes=2   smb/workgroup -> slahes=2 smb://host/share -> slashes=3
+    if ((fd=openDir(context, smb_path)))
+    {       
+        if ((ret = guessDirType(context,fd)) == StatDir && slashes == 3)
+        {
+                ret  = StatShare;
+        }
+        if (slashes > 2 && (ret == StatShare || ret == StatDir))
+        {
+          /* smbc_getFunctionFstatdir does not work
+            ret = static_cast<StatReturn>(::smbc_getFunctionFstatdir(context)(context,fd, st));
+          */
+            QString ipUrl = NetUtil::urlConvertHostnameToIP(smb_path);
+            if (ipUrl.isEmpty())
+            {
+                ipUrl = smb_path;
+            }
+            (void)static_cast<StatReturn> (::smbc_getFunctionStat(context)(context,ipUrl.toLocal8Bit().constData(), st));
+        }
+    }
+    else
+    if (errno != EACCES && errno != ECONNREFUSED )
+    {
+        if ((fd = openFile(context,smb_path)))
+        {
+            ret =  static_cast<StatReturn> (::smbc_getFunctionFstat(context)(context,fd, st));
+        }
+    }
+
+    if (fd)
+    {
+        closeHandle(context, fd);
+    }
+    else
+    {
+        qDebug() << Q_FUNC_INFO << "path:" << smb_path << "errno:" << errno << strerror(errno);
+        switch(errno)
+        {
+           case EACCES:
+                ret = StatNoAccess;
+                break;
+           case ENOENT:
+           case ENODEV:
+           case ECONNREFUSED:
+                ret = StatDoesNotExist;
+                break;
+           default:
+                break;
+        }
+    }
+    deleteContext(context);
+    return ret;
+}
+
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::guessDirType() gets the first directory item to guess the content type
+ * \param context
+ * \param fd an already opened FileHandler of a directory
+ * \return the item type in \ref StatReturn
+ */
+SmbUtil::StatReturn
+SmbUtil::guessDirType(Smb::Context context, Smb::FileHandler fd)
+{
+    struct smbc_dirent	*dirent=0;
+    StatReturn ret = StatDone;
+    while (ret == StatDone &&
+           (dirent = smbc_getFunctionReaddir(context)(context, fd)) )
+    {
+        if (!dirent->name[0] && dirent->smbc_type != SMBC_SERVER )
+        {
+            continue;
+        }
+        switch(dirent->smbc_type)
+        {
+            //current item is a Host
+            case SMBC_FILE_SHARE:                
+                 ret = StatHost;
+            break;
+            //current item is a Workgroup
+            case SMBC_SERVER:
+                 ret = StatWorkgroup;
+            break;
+            //current item is Root smb://
+            case SMBC_WORKGROUP:
+            break;
+            //ignore system shares
+            case SMBC_PRINTER_SHARE:
+            case SMBC_COMMS_SHARE:
+            case SMBC_IPC_SHARE:
+            break;
+            //current item is Common directory
+            // or a share, shares are handdled by the caller
+            default:
+                 ret = StatDir;
+            break;
+        }
+    }
+    return ret;
+}
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::listContent() Just lists the content of a directory/share/workgroup/hostname
+ * \param smb_path it must point to full pathname directory/share/workgroup/hostname
+ * \param recursive
+ * \param filters a QDir like filter
+ * \return The string list that matches \a filters
+ */
+QStringList SmbUtil::listContent(QString smb_path, bool recursive, QDir::Filters filters , const QStringList &filterNames)
+{
+    QStringList content;
+    Smb::Context context = createContext();
+    Q_ASSERT(context);
+    Smb::FileHandler fd = openDir(context,smb_path);
+    if (fd)
+    {
+        struct smbc_dirent	*dirent = 0;
+        const char *cur_name        = 0;
+        while ((dirent = smbc_getFunctionReaddir(context)(context, fd)) )
+        {           
+            //first check for hidden files
+            if (!(filters & QDir::Hidden) && dirent->name[0] == '.')
+            {
+                continue;
+            }           
+            if ( !dirent->name[0] && dirent->smbc_type != SMBC_SERVER)
+            {
+                //it may be a libsmbclient bug                
+                continue;
+            }
+            cur_name = dirent->name;
+            QString path;
+            bool itemHasContent = false;
+            switch(dirent->smbc_type)
+            {
+               case SMBC_PRINTER_SHARE:
+               case SMBC_COMMS_SHARE:
+               case SMBC_IPC_SHARE:
+                    continue;
+                    break;
+               case SMBC_WORKGROUP:
+               case SMBC_SERVER:                    
+                    itemHasContent = true;
+                    path = LocationUrl::SmbURL;
+                    if (dirent->smbc_type == SMBC_SERVER)
+                    {
+                         QString goodHostName = findSmBServer(*dirent);
+                         //path += NetUtil::normalizeHostName(goodHostName);
+                         path += goodHostName;
+                    }
+                    else
+                    {
+                        path += cur_name;
+                    }
+                    break;
+               case SMBC_DIR:                   
+                    if (filters & QDir::Dirs)
+                    {
+                        bool isDot     = ::strcmp(".", cur_name) == 0;
+                        bool isDotDot  = ::strcmp("..", cur_name) == 0;
+                        if(      !((filters & QDir::NoDot)    && isDot)
+                              && !((filters & QDir::NoDotDot) && isDotDot) )
+                        {
+                             path = smb_path + QDir::separator() + cur_name;
+                             if (!isDot && !isDotDot)
+                             {
+                                 itemHasContent = true;
+                             }
+                        }
+                    }
+                    break;
+               case SMBC_FILE:
+               case SMBC_LINK:
+                    if (filters & QDir::Files)
+                    {                     
+                        path = smb_path + QDir::separator() + cur_name;
+                    }
+                    break;
+               case SMBC_FILE_SHARE:                   
+                    if (checkValidShareName(cur_name))
+                    {
+                        itemHasContent = true;
+                        path = smb_path + QDir::separator() + cur_name;
+                    }
+                    break;
+            }//switch
+            if (!path.isEmpty())
+            {
+                if (filterNames.isEmpty() || namesMatchFilter(cur_name, filterNames))
+                {
+                    content.append(path);
+                }
+                if (recursive && itemHasContent )
+                {
+                    content += listContent(path, true, filters, filterNames);
+                }
+            }
+        }//while
+        closeHandle(context, fd);
+    }//if (fd)
+    else
+    {
+        qDebug() << Q_FUNC_INFO << "could not open directory" << smb_path << "errno:" << errno;
+    }
+    deleteContext(context);
+    return content;
+}
+
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::lisShares() Brings the list of all available file shares in the network
+ * \return all available file shares
+ */
+QStringList SmbUtil::lisShares()
+{
+    return walkForShares(LocationUrl::SmbURL);
+}
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::walkForShares() Just a helper function that can be recursive, called by  \ref lisShares()
+ * \param smb_path
+ * \return list of shares from a single hostname
+ */
+QStringList SmbUtil::walkForShares(QString smb_path)
+{
+    QStringList content;
+    Smb::Context context = createContext();
+    Q_ASSERT(context);
+    Smb::FileHandler fd = openDir(context,smb_path);
+    if (fd)
+    {
+        struct smbc_dirent	*dirent = 0;
+        const char *cur_name        = 0;
+        QString path;
+        while ((dirent = smbc_getFunctionReaddir(context)(context, fd)))
+        {          
+            cur_name = dirent->name;
+            if ( !dirent->name[0] && dirent->smbc_type != SMBC_SERVER)
+            {
+                //it may be a libsmbclient bug               
+                    continue;             
+            }
+            switch(dirent->smbc_type)
+            {
+               case SMBC_WORKGROUP:
+               case SMBC_SERVER:
+                    path = LocationUrl::SmbURL;
+                    if (dirent->smbc_type == SMBC_SERVER)
+                    {
+                        QString goodHostName = findSmBServer(*dirent);
+                        //path += NetUtil::normalizeHostName(goodHostName);
+                        path += goodHostName;
+                    }
+                    else
+                    {
+                        path += cur_name;
+                    }
+                    content += walkForShares(path);
+                    break;
+               case SMBC_FILE_SHARE:                   
+                    if (checkValidShareName(cur_name))
+                    {
+                        path = smb_path + QDir::separator() + cur_name;
+                        content.append(path);
+                    }
+                    break;
+            }//switch
+        }//while
+    }//if (fd)
+    deleteContext(context);
+    return content;
+}
+
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::checkValidShareName()  Helper function to ignore some system shares that should not contain any file
+ *
+ *   It is used for \ref lisShares() and \ref listContent()
+ *
+ * \param shareName
+ * \return TRUE if the share has a good name (should contain files), FALSE if it is supposed to be a system share
+ */
+bool SmbUtil::checkValidShareName(const char *shareName)
+{
+    if (::strcmp(shareName, "print$") == 0)
+    {
+      return false;
+    }
+
+    return true;
+}
+
+
+//===============================================================================================
+/*!
+ * \brief SmbUtil::getStatvfsInfo
+ * \param smb_path it must point to a file full pathname file or directory
+ * \param st pointer to a struct statvfs
+ * \return StatDone in case of success or StatInvalid.
+ */
+SmbUtil::StatReturn
+SmbUtil::getStatvfsInfo(const QString &smb_path, struct statvfs *st)
+{
+    Smb::Context context = createContext();
+    Q_ASSERT(context);
+    ::memset(st, 0 , sizeof(struct statvfs));
+    StatReturn ret   = StatInvalid;
+    Smb::FileHandler fd = openDir(context,smb_path);
+    if (fd == 0)
+    {
+        fd = openFile(context, smb_path);
+    }
+    if (fd)
+    {
+        ret = static_cast<StatReturn> (::smbc_getFunctionFstatVFS(context)(context,fd, st));
+        closeHandle(context, fd);
+    }
+    deleteContext(context);
+    return ret;
+}
+
+
+bool SmbUtil::namesMatchFilter(const QString & str, const QStringList &filterNames)
+{
+    bool ret = true;
+    int counter = filterNames.count();
+    while (ret && counter--)
+    {
+        QRegExp regExp(filterNames.at(counter), Qt::CaseSensitive, QRegExp::Wildcard);
+        ret = regExp.exactMatch(str);
+    }
+    return ret;
+}
+
+/*!
+ * \brief SmbUtil::findSmBServer() Helper function to find the server name
+ * \param dirent smbc_dirent & dirent result of smbc_getFunctionReaddir()
+ *
+ *   1. Some smbclient versions (or host configuration) bring dirent.name empty when browsing localhost
+ *
+ *   2. When dirent.name brings the hostname, usually it is limited to 16 characters which will not reacheable in the network
+ *      in this case try to get the name from the comment
+ *
+ * \return the hostname
+ */
+QString SmbUtil::findSmBServer(const smbc_dirent & dirent)
+{
+    QString host("localhost");
+    if (dirent.name[0] != 0)
+    {
+        QString name(dirent.name);
+        host = name;
+        QString comment(dirent.comment);
+        if (!comment.isEmpty())
+        {
+            QString fullName = comment.split(QLatin1Char(' '), QString::SkipEmptyParts).first();
+            if (!fullName.isEmpty() &&  fullName.startsWith(name), Qt::CaseSensitive)
+            {
+                host = fullName;
+            }
+        }
+    }
+    return host.toLower();
+}

=== added file 'src/plugin/folderlistmodel/smb/qsambaclient/src/smbutil.h'
--- src/plugin/folderlistmodel/smb/qsambaclient/src/smbutil.h	1970-01-01 00:00:00 +0000
+++ src/plugin/folderlistmodel/smb/qsambaclient/src/smbutil.h	2015-03-08 12:43:13 +0000
@@ -0,0 +1,130 @@
+/**************************************************************************
+ *
+ * Copyright 2014 Canonical Ltd.
+ * Copyright 2014 Carlos J Mazieri <carlos.mazieri@xxxxxxxxx>
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * File: smbutil.h
+ * Date: 20/11/2014
+ */
+
+#ifndef SMBUTIL_H
+#define SMBUTIL_H
+
+#include <libsmbclient.h>
+
+#include <QStringList>
+#include <QDir>
+
+class QUrl;
+class NetAuthenticationData;
+class NetAuthenticationDataList;
+struct stat;
+
+
+namespace Smb
+{
+ typedef SMBCCTX     * Context;
+ typedef SMBCFILE    * FileHandler; 
+ typedef void (*AuthenticationFunction) (const char *server,
+                                         const char *share,
+                                         char *wrkgrp,
+                                         int wrkgrplen,
+                                         char *user,
+                                         int userlen,
+                                         char *passwd,
+                                         int passwdlen);
+}
+
+
+/*!
+ * \brief The SmbUtil class provides the interface through the libsmbclient functions
+ *
+ * Some documentation can found at:
+ * \link  http://www.samba.org/samba/docs/man/manpages-3/libsmbclient.7.html
+ * \link  https://github.com/Zentyal/samba/tree/master/examples/libsmbclient
+ *
+ */
+class SmbUtil
+{
+public:
+     SmbUtil();
+     SmbUtil(Smb::AuthenticationFunction fn);
+     SmbUtil(const QString& authUser, const QString& authPassword);
+     SmbUtil(const QUrl& smbUrl, Smb::AuthenticationFunction fn = 0);  //may have smb:://user::password@host/..
+     ~SmbUtil();
+
+public:
+     enum StatReturn
+     {
+         StatInvalid = -3,
+         StatDoesNotExist= -2,
+         StatNoAccess= -1,
+         StatDone=0,      // already done
+         StatDir,
+         StatHost,
+         StatWorkgroup,
+         StatShare
+     };
+
+public:
+    Smb::Context     createContext();
+    void             deleteContext(Smb::Context context);
+    void             setAuthenticationCallback(Smb::AuthenticationFunction fn);
+    StatReturn       getStatInfo(const QString &smb_path, struct stat *st);
+    StatReturn       getStatvfsInfo(const QString& smb_path, struct statvfs *st);
+    Smb::FileHandler openDir(Smb::Context context, const QString& smb_string);
+    Smb::FileHandler openFile(Smb::Context context,const QString& smb_path,
+                              int flags = O_RDONLY, mode_t mode = 0);
+    void             closeHandle(Smb::Context context, Smb::FileHandler fd);
+    QStringList      lisShares();
+    QStringList      listContent(QString smb_path,
+                                 bool recursive = false,
+                                 QDir::Filters filters = QDir::AllEntries | QDir::NoDotAndDotDot,
+                                 const QStringList& filterNames = QStringList());
+
+
+private:   
+    StatReturn      guessDirType(Smb::Context context, Smb::FileHandler fd);
+    bool            checkValidShareName(const char *shareName);
+    QStringList     walkForShares(QString smb_path);
+    QString         findSmBServer(const smbc_dirent&);
+
+
+private:
+    static void     authenticateCallBack(
+                                const char  *server,
+                                const char  *share,
+                                char        *wrkgrp,
+                                int         wrkgrplen,
+                                char        *user,
+                                int         userlen,
+                                char        *passwd,
+                                int         passwdlen);
+
+protected:
+    void            init(const QString& user, const QString& password, Smb::AuthenticationFunction fn);
+    bool            namesMatchFilter(const QString& str, const QStringList& filterNames);
+
+private:  
+   Smb::AuthenticationFunction   m_authCallBack;
+
+
+#if defined(REGRESSION_TEST_QSAMBACLIENT)
+    friend class TestQSambaSuite;
+#endif
+
+};
+
+#endif // SMBUTIL_H


Follow ups