How to install Boxee in Gentoo Linux/FileSmb.cpp

This is a modified version of FileSmb.cpp for Boxee versions 0.9.6.4578 thru 0.9.11.5591, which will compile under Gentoo. If you are using any of these versions you should be able to replace the file with this one. See Howto install Boxee in Gentoo Linux for details.

/* *     Copyright (C) 2005-2008 Team XBMC *     http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2, 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with XBMC; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * http://www.gnu.org/copyleft/gpl.html * */
 * xbmc/FileSystem/FileSmb.cpp

// FileSmb.cpp: implementation of the CFileSMB class. // //////////////////////////////////////////////////////////////////////


 * 1) include "stdafx.h"
 * 2) include "FileSmb.h"
 * 3) include "GUIPassword.h"
 * 4) include "SMBDirectory.h"
 * 5) include "Util.h"
 * 6) ifndef _LINUX
 * 7) include "Win32Exception.h"
 * 8) include "lib/libsmb/xbLibSmb.h"
 * 9) else
 * 10) include "../lib/libsmb/libsmbclient.h"
 * 11) endif
 * 12) include "../utils/Network.h"
 * 13) include "Settings.h"


 * 1) include "Application.h"

using namespace XFILE; using namespace DIRECTORY;


 * 1) define SMB_READ_CACHE_SIZE (96*1024)

void xb_smbc_log(const char* msg) { CLog::Log(LOGINFO, "%s%s", "smb: ", msg); }

void xb_smbc_auth(const char *srv, const char *shr, char *wg, int wglen,                 char *un, int unlen, char *pw, int pwlen) { return ; }

smbc_get_cached_srv_fn orig_cache;

SMBCSRV* xb_smbc_cache(SMBCCTX* c, const char* server, const char* share, const char* workgroup, const char* username) { return orig_cache(c, server, share, workgroup, username); }

CSMB::CSMB {   m_context = NULL; }

CSMB::~CSMB { Deinit; }

void CSMB::Deinit { CSingleLock lock(*this); CLog::Log(LOGINFO,"SMB: DEinitializing package");

/* samba goes loco if deinited while it has some files opened */ if (m_context) {   try {     smbc_set_context(NULL); smbc_free_context(m_context, 1); }   catch(win32_exception e)    { e.writelog(__FUNCTION__); }   catch(...) {     CLog::Log(LOGERROR,"exception on CSMB::Deinit. errno: %d", errno); }   m_context = NULL; } }
 * 1) ifndef _LINUX
 * 1) else
 * 1) endif

void CSMB::Init { CSingleLock lock(*this); static bool bInitialized = false; if (!bInitialized) {   CLog::Log(LOGINFO,"SMB: initializing package"); smbc_init(xb_smbc_auth, 0); bInitialized = true; } if (!m_context) {   // set the log function set_log_callback(xb_smbc_log);
 * 1) ifdef _WIN32
 * 1) endif

// setup our context m_context = smbc_new_context;

// smbc_setDebug(m_context, 0); // smbc_setFunctionAuthData(m_context, xb_smbc_auth); // orig_cache = smbc_getFunctionGetCachedServer(m_context); // smbc_setFunctionGetCachedServer(m_context, xb_smbc_cache); // smbc_setOptionOneSharePerServer(m_context, false); // smbc_setOptionBrowseMaxLmbCount(m_context, 0); // smbc_setTimeout(m_context, g_advancedSettings.m_sambaclienttimeout * 1000);
 * 1) ifdef _LINUX

// Create ~/.smb/smb.conf char smb_conf[MAX_PATH]; sprintf(smb_conf, "%s/.smb", getenv("HOME")); mkdir(smb_conf, 0755); sprintf(smb_conf, "%s/.smb/smb.conf", getenv("HOME")); FILE* f = fopen(smb_conf, "w"); if (f != NULL) {     fprintf(f, "[global]\n"); // if a wins-server is set, we have to change name resolve order to     if ( g_guiSettings.GetString("smb.winsserver").length > 0 && !g_guiSettings.GetString("smb.winsserver").Equals("0.0.0.0") ) {       fprintf(f, "  wins server = %s\n", g_guiSettings.GetString("smb.winsserver").c_str); fprintf(f, " name resolve order = bcast wins host\n"); }     else fprintf(f, "name resolve order = bcast host\n");

if (g_advancedSettings.m_sambadoscodepage.length > 0) fprintf(f, "dos charset = %s\n", g_advancedSettings.m_sambadoscodepage.c_str); else fprintf(f, "dos charset = CP850\n");

fprintf(f, "lock directory = /tmp\n"); fprintf(f, "socket options = SO_KEEPALIVE TCP_NODELAY IPTOS_LOWDELAY\n");

fprintf(f, "workgroup = %s\n", g_guiSettings.GetString("smb.workgroup").c_str); fclose(f); }   // smbc_setWorkgroup(m_context,  strdup(g_guiSettings.GetString("smb.workgroup").c_str)); // initialize samba and do some hacking into the settings if (smbc_init_context(m_context)) {     /* setup old interface to use this context */ smbc_set_context(m_context);
 * 1) endif

// if a wins-server is set, we have to change name resolve order to     if ( g_guiSettings.GetString("smb.winsserver").length > 0 && !g_guiSettings.GetString("smb.winsserver").Equals("0.0.0.0") ) {       lp_do_parameter( -1, "wins server", g_guiSettings.GetString("smb.winsserver").c_str); lp_do_parameter( -1, "name resolve order", "bcast wins host"); }     else lp_do_parameter( -1, "name resolve order", "bcast host"); if (g_advancedSettings.m_sambadoscodepage.length > 0) lp_do_parameter( -1, "dos charset", g_advancedSettings.m_sambadoscodepage.c_str); else lp_do_parameter( -1, "dos charset", "CP850"); }   else {     smbc_free_context(m_context, 1); m_context = NULL; } } }
 * 1) ifndef _LINUX
 * 1) endif

void CSMB::Purge { CSingleLock lock(*this); smbc_purge; }
 * 1) ifndef _LINUX
 * 1) endif

/* * For each new connection samba creates a new session * But this is not what we want, we just want to have one session at the time * This means that we have to call smbc_purge if samba created a new session * Samba will create a new session when: * - connecting to another server * - connecting to another share on the same server (share, not a different folder!) * * We try to avoid lot's of purge commands because it slow samba down. */ void CSMB::PurgeEx(const CURL& url) { CSingleLock lock(*this); CStdString strShare = url.GetFileName.substr(0, url.GetFileName.Find('/'));

if (m_strLastShare.length > 0 && (m_strLastShare != strShare || m_strLastHost != url.GetHostName)) smbc_purge;
 * 1) ifndef _LINUX
 * 1) endif

m_strLastShare = strShare; m_strLastHost = url.GetHostName; }

CStdString CSMB::URLEncode(const CURL &url) { /* due to smb wanting encoded urls we have to build it manually */

CStdString flat = "smb://";

if(url.GetDomain.length > 0) {   flat += URLEncode(url.GetDomain); flat += ";"; }

/* samba messes up of password is set but no username is set. don't know why yet */ /* probably the url parser that goes crazy */ if(url.GetUserName.length > 0 /* || url.GetPassWord.length > 0 */) {   flat += URLEncode(url.GetUserName); flat += ":"; flat += URLEncode(url.GetPassWord); flat += "@"; } else if( !url.GetHostName.IsEmpty && !g_guiSettings.GetString("smb.username").IsEmpty ) {   /* okey this is abit uggly to do this here, as we don't really only url encode */ /* but it's the simplest place to do so */ flat += URLEncode(g_guiSettings.GetString("smb.username")); flat += ":"; flat += URLEncode(g_guiSettings.GetString("smb.password")); flat += "@"; }

flat += URLEncode(url.GetHostName);

/* okey sadly since a slash is an invalid name we have to tokenize */ std::vector parts; std::vector::iterator it; CUtil::Tokenize(url.GetFileName, parts, "/"); for( it = parts.begin; it != parts.end; it++ ) {   flat += "/"; flat += URLEncode((*it)); }

/* okey options should go here, thou current samba doesn't support any */

return flat; }

CStdString CSMB::URLEncode(const CStdString &value) {   int buffer_len = value.length*3+1; char* buffer = (char*)malloc(buffer_len); memset(buffer,0,buffer_len); char *strSrc = strdup(value.c_str); // urlencode requires char* and not const char *. just to be on the safe side // smbc_urlencode(buffer, strSrc, buffer_len); // SMBC_urlencode(buffer, strSrc, buffer_len); free(strSrc); CStdString encoded = buffer; free(buffer); return encoded; }
 * 1) ifdef _WIN32
 * 1) else
 * 1) endif

CStdString CSMB::URLDecode(const CStdString &value) {   int buffer_len = value.length+2; // decode is shorter char* buffer = (char*)malloc(buffer_len); memset(buffer,0,buffer_len); char *strSrc = strdup(value.c_str); // urlencode requires char* and not const char *. just to be on the safe side // smbc_urldecode(buffer, strSrc, buffer_len-1); // SMBC_urldecode(buffer, strSrc, buffer_len-1); free(strSrc); CStdString decoded = buffer; free(buffer); return decoded; }
 * 1) ifdef _WIN32
 * 1) else
 * 1) endif

DWORD CSMB::ConvertUnixToNT(int error) { DWORD nt_error; if (error == ENODEV || error == ENETUNREACH || error == WSAETIMEDOUT) nt_error = NT_STATUS_INVALID_COMPUTER_NAME; else if(error == WSAECONNREFUSED || error == WSAECONNABORTED) nt_error = NT_STATUS_CONNECTION_REFUSED; else nt_error = map_nt_error_from_unix(error);
 * 1) ifndef _LINUX

return nt_error; }
 * 1) endif

CSMB smb;

CFileSMB::CFileSMB { smb.Init; m_fd = -1; m_fileSize = 0; m_nBufferSize = 0; m_buffer = new BYTE[SMB_READ_CACHE_SIZE]; }

CFileSMB::~CFileSMB { Close; if (m_buffer) delete [] m_buffer; }

__int64 CFileSMB::GetPosition { if (m_fd == -1) return 0; CSingleLock lock(smb); smb.Init; __int64 pos = smbc_lseek(m_fd, 0, SEEK_CUR); if ( pos < 0 ) return 0; return pos; }

__int64 CFileSMB::GetLength { if (m_fd == -1) return 0; return m_fileSize; }

bool CFileSMB::Open(const CURL& url, bool bBinary) { m_bBinary = bBinary;

CStdString strPath; url.GetURL(strPath);

Close;

if (!g_application.IsPathAvailable(strPath, true)) return false; // smb share unavailable

// we can't open files like smb://file.f or smb://server/file.f // if a file matches the if below return false, it can't exist on a samba share. if (!IsValidFile(url.GetFileName)) {     CLog::Log(LOGNOTICE,"FileSmb->Open: Bad URL : '%s'",url.GetFileName.c_str); return false; } m_url = url;

CSingleLock lock(smb); smb.Init; CStdString strFileName; m_fd = OpenFile(url, strFileName); if (m_fd == -1) {   // write error to logfile int nt_error = smb.ConvertUnixToNT(errno); CLog::Log(LOGINFO, "FileSmb->Open: Unable to open file : '%s'\nunix_err:'%x' nt_err : '%x' error : '%s'", strFileName.c_str, errno, nt_error, get_friendly_nt_error_msg(nt_error)); CLog::Log(LOGINFO, "FileSmb->Open: Unable to open file : '%s'\nunix_err:'%x' error : '%s'", strFileName.c_str, errno, strerror(errno)); return false; } struct __stat64 tmpBuffer = {0}; struct stat tmpBuffer; if (smbc_fstat(m_fd, &tmpBuffer) < 0) {   smbc_close(m_fd); m_fd = -1; return false; } m_fileSize = tmpBuffer.st_size;
 * 1) ifndef _LINUX
 * 1) else
 * 1) endif
 * 1) ifndef _LINUX
 * 1) else
 * 1) endif

__int64 ret = smbc_lseek(m_fd, 0, SEEK_SET); if ( ret < 0 ) {   smbc_close(m_fd); m_fd = -1; return false; } // We've successfully opened the file! return true; }

int CFileSMB::OpenFile(const CURL &url, CStdString& strAuth) { if (!g_application.IsPathAvailable(url.GetShareName, true)) return -1;

int fd = -1; CSingleLock lock(smb); smb.Init;

/* original auth name */ strAuth = smb.URLEncode(url);

CStdString strPath = g_passwordManager.GetSMBAuthFilename(strAuth);

fd = smbc_open(strPath.c_str, O_RDONLY, 0); // file open failed, try to open the directory to force authentication if (fd < 0 && smb.ConvertUnixToNT(errno) == NT_STATUS_ACCESS_DENIED) if (fd < 0 && errno == EACCES) {   CURL urlshare(url); /* just replace the filename with the sharename */ urlshare.SetFileName(url.GetShareName);
 * 1) ifndef _LINUX
 * 1) else
 * 1) endif

CSMBDirectory smbDir; smbDir.SetAllowPrompting(false); fd = smbDir.Open(urlshare); // directory open worked, try opening the file again if (fd >= 0) {     // close current directory filehandle // dont need to purge since its the same server and share smbc_closedir(fd);

// set up new filehandle (as CFileSMB::Open does) strPath = g_passwordManager.GetSMBAuthFilename(strPath); fd = smbc_open(strPath.c_str, O_RDONLY, 0); } }

if (fd >= 0) strAuth = strPath;

return fd; }

bool CFileSMB::Exists(const CURL& url) { // if a file matches the if below return false, it can't exist on a samba share. if (url.GetFileName.Find('/') < 0 ||     url.GetFileName.at(0) == '.' ||      url.GetFileName.Find("/.") >= 0) return false;

CSingleLock lock(smb); smb.Init; CStdString strFileName = smb.URLEncode(url); strFileName = g_passwordManager.GetSMBAuthFilename(strFileName);

struct __stat64 info; struct stat info;
 * 1) ifndef _LINUX
 * 1) else
 * 1) endif

int iResult = smbc_stat(strFileName, &info);

if (iResult < 0) return false; return true; }

int CFileSMB::Stat(struct __stat64* buffer) { if (m_fd == -1) return -1;

CSingleLock lock(smb); smb.Init;

struct __stat64 tmpBuffer = {0}; struct stat tmpBuffer = {0};
 * 1) ifndef _LINUX
 * 1) else
 * 1) endif

int iResult = smbc_fstat(m_fd, &tmpBuffer);

buffer->st_dev = tmpBuffer.st_dev; buffer->st_ino = tmpBuffer.st_ino; buffer->st_mode = tmpBuffer.st_mode; buffer->st_nlink = tmpBuffer.st_nlink; buffer->st_uid = tmpBuffer.st_uid; buffer->st_gid = tmpBuffer.st_gid; buffer->st_rdev = tmpBuffer.st_rdev; buffer->st_size = tmpBuffer.st_size; buffer->st_atime = tmpBuffer.st_atime; buffer->st_mtime = tmpBuffer.st_mtime; buffer->st_ctime = tmpBuffer.st_ctime;

return iResult; }

int CFileSMB::Stat(const CURL& url, struct __stat64* buffer) { CSingleLock lock(smb); smb.Init; CStdString strFileName = smb.URLEncode(url); strFileName = g_passwordManager.GetSMBAuthFilename(strFileName);

struct __stat64 tmpBuffer = {0}; struct stat tmpBuffer = {0}; int iResult = smbc_stat(strFileName, &tmpBuffer);
 * 1) ifndef _LINUX
 * 1) else
 * 1) endif

buffer->st_dev = tmpBuffer.st_dev; buffer->st_ino = tmpBuffer.st_ino; buffer->st_mode = tmpBuffer.st_mode; buffer->st_nlink = tmpBuffer.st_nlink; buffer->st_uid = tmpBuffer.st_uid; buffer->st_gid = tmpBuffer.st_gid; buffer->st_rdev = tmpBuffer.st_rdev; buffer->st_size = tmpBuffer.st_size; buffer->st_atime = tmpBuffer.st_atime; buffer->st_mtime = tmpBuffer.st_mtime; buffer->st_ctime = tmpBuffer.st_ctime;

return iResult; }

bool CFileSMB::FillBuffer { if (m_fd == -1) return false;

CSingleLock lock(smb); smb.Init;

while (m_nBufferSize < SMB_READ_CACHE_SIZE) {   int nToRead = SMB_READ_CACHE_SIZE - m_nBufferSize; if (nToRead > 65000) // not 64K on purpose. 64k confuses some smb servers. nToRead = 65000; int bytesRead = smbc_read(m_fd, m_buffer + m_nBufferSize, nToRead); if (bytesRead < 0) {     CLog::Log(LOGERROR, "SMB: %s - Error( %s )", __FUNCTION__, get_friendly_nt_error_msg(smb.ConvertUnixToNT(errno))); CLog::Log(LOGERROR, "SMB: %s -fd = %d, Error( %s )", __FUNCTION__, m_fd, strerror(errno)); return false; }
 * 1) ifndef _LINUX
 * 1) else
 * 1) endif

if (bytesRead <= 0) break;

m_nBufferSize += bytesRead; }

return true; }

unsigned int CFileSMB::Read(void *lpBuf, __int64 uiBufSize) { if (m_fd == -1) return 0; if (uiBufSize > m_nBufferSize) {   if (!FillBuffer) {     Close; return 0; } }

if (m_nBufferSize == 0) return 0;

int nToCopy = (uiBufSize < m_nBufferSize)?uiBufSize:m_nBufferSize;

memmove(lpBuf, m_buffer, nToCopy); if (nToCopy < m_nBufferSize) memmove(m_buffer, m_buffer + nToCopy, m_nBufferSize - nToCopy); m_nBufferSize -= nToCopy;

return (unsigned int)nToCopy; }

__int64 CFileSMB::Seek(__int64 iFilePosition, int iWhence) { if (m_fd == -1) return -1; if(iWhence == SEEK_POSSIBLE) return 1;

CSingleLock lock(smb); smb.Init;

INT64 pos = smbc_lseek(m_fd, iFilePosition, iWhence); if ( pos < 0 ) {   CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, get_friendly_nt_error_msg(smb.ConvertUnixToNT(errno))); CLog::Log(LOGERROR, "SMB: %s - Error( %s )", __FUNCTION__, strerror(errno)); return -1; }
 * 1) ifndef _LINUX
 * 1) else
 * 1) endif

m_nBufferSize = 0; return (__int64)pos; }

void CFileSMB::Close { if (m_fd != -1) {   CSingleLock lock(smb); smbc_close(m_fd); } m_fd = -1; m_fileSize = 0; m_nBufferSize = 0; }

int CFileSMB::Write(const void* lpBuf, __int64 uiBufSize) { if (m_fd == -1) return -1; DWORD dwNumberOfBytesWritten = 0;

// lpBuf can be safely casted to void* since xmbc_write will only read from it. CSingleLock lock(smb); smb.Init; dwNumberOfBytesWritten = smbc_write(m_fd, (void*)lpBuf, (DWORD)uiBufSize);

return (int)dwNumberOfBytesWritten; }

bool CFileSMB::Delete(const CURL& url) { CStdString strFile = g_passwordManager.GetSMBAuthFilename(smb.URLEncode(url));

CSingleLock lock(smb); smb.Init;

int result = smbc_unlink(strFile.c_str);

if(result != 0) CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, get_friendly_nt_error_msg(smb.ConvertUnixToNT(errno))); CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, strerror(errno));
 * 1) ifndef _LINUX
 * 1) else
 * 1) endif

return (result == 0); }

bool CFileSMB::Rename(const CURL& url, const CURL& urlnew) { CStdString strFile = g_passwordManager.GetSMBAuthFilename(smb.URLEncode(url)); CStdString strFileNew = g_passwordManager.GetSMBAuthFilename(smb.URLEncode(urlnew));

CSingleLock lock(smb); smb.Init;

int result = smbc_rename(strFile.c_str, strFileNew.c_str);

if(result != 0) CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, get_friendly_nt_error_msg(smb.ConvertUnixToNT(errno))); CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, strerror(errno));
 * 1) ifndef _LINUX
 * 1) else
 * 1) endif

return (result == 0); }

bool CFileSMB::OpenForWrite(const CURL& url, bool bBinary, bool bOverWrite) { m_bBinary = bBinary; m_fileSize = 0;

CSingleLock lock(smb); smb.Init;

Close; // we can't open files like smb://file.f or smb://server/file.f // if a file matches the if below return false, it can't exist on a samba share. if (!IsValidFile(url.GetFileName)) return false;

CStdString strFileName = smb.URLEncode(url); strFileName = g_passwordManager.GetSMBAuthFilename(strFileName);

if (bOverWrite) {   CLog::Log(LOGWARNING, "FileSmb::OpenForWrite called with overwriting enabled! - %s", strFileName.c_str); m_fd = smbc_creat(strFileName.c_str, 0); } else {   m_fd = smbc_open(strFileName.c_str, O_RDWR, 0); }

if (m_fd == -1) {   // write error to logfile int nt_error = map_nt_error_from_unix(errno); CLog::Log(LOGERROR, "FileSmb->Open: Unable to open file : '%s'\nunix_err:'%x' nt_err : '%x' error : '%s'", strFileName.c_str, errno, nt_error, get_friendly_nt_error_msg(nt_error)); CLog::Log(LOGERROR, "FileSmb->Open: Unable to open file : '%s'\nunix_err:'%x' error : '%s'", strFileName.c_str, errno, strerror(errno)); return false; }
 * 1) ifndef _LINUX
 * 1) else
 * 1) endif

// We've successfully opened the file! return true; }

bool CFileSMB::IsValidFile(const CStdString& strFileName) { if (strFileName.Find('/') == -1 || /* doesn't have sharename */      strFileName.Right(2) == "/." || /* not current folder */      strFileName.Right(3) == "/..")  /* not parent folder */ return false; return true; }