From 1bb432246d6867841cb2c6e573b3e02a35d4200a Mon Sep 17 00:00:00 2001 From: Chris Lance Date: Sun, 18 Sep 2011 16:38:40 -0400 Subject: [PATCH] Client interface is working for directory searches...and a bunch of other stuff --- include/libxafp.h | 49 +++++--- libxafp.xcodeproj/project.pbxproj | 28 ++++- src/AFPClient.cpp | 163 ++++++++++++++------------ src/AFPClient.h | 46 ++++++-- src/AFPProto.h | 2 +- src/Common.h | 29 +++++ src/DSIClient.cpp | 6 +- src/DSIClient.h | 7 +- src/DSIProto.h | 2 +- src/Logging.cpp | 2 +- src/Logging.h | 2 +- src/TCPClient.cpp | 13 +-- src/TCPClient.h | 2 +- src/Threads.cpp | 60 ++++++++++ src/Threads.h | 40 +++++++ src/Utils.cpp | 38 +----- src/Utils.h | 18 +-- src/libxafp.cpp | 184 +++++++++++++++++++++++++++++- src/libxafp_internal.h | 54 ++++++++- test/main.cpp | 56 ++++++++- 20 files changed, 620 insertions(+), 181 deletions(-) create mode 100644 src/Common.h create mode 100644 src/Threads.cpp create mode 100644 src/Threads.h diff --git a/include/libxafp.h b/include/libxafp.h index e09cf2f..0087315 100644 --- a/include/libxafp.h +++ b/include/libxafp.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2005-2011 Team XBMC + * Copyright (C) 2011 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -23,38 +23,51 @@ #include #include -struct xafp_client_context; -typedef struct xafp_client_context xafp_client_context; +typedef struct _client_context* xafp_client_handle; -struct xafp_node_iterator; -typedef struct xafp_node_iterator xafp_node_iterator; +typedef struct _fs_node_iterator* xafp_node_iterator; -struct xafp_node; -typedef struct xafp_node xafp_node; +typedef struct _fs_node* xafp_node_handle; + +typedef int xafp_file_handle; // TODO: define common error codes enum xafp_mount_flags { - xafp_mount_flag_read = 0x1 << 0, + xafp_mount_flag_none = 0x0, + xafp_mount_flag_read = 0x1 << 0, xafp_mount_flag_write = 0x1 << 1, }; +enum xafp_open_flags +{ + xafp_open_flag_read = 0x1 << 0, + xafp_open_flag_write = 0x1 << 1, +}; + +enum xafp_node_type +{ + xafp_node_file = 0x0, + xafp_node_directory = 0x1 +}; + // Client Interface Definition /////////////////////////////////////////////////////// -xafp_client_context* xafp_create_context(const char* pServer, int port=548, const char* pUser=NULL, const char* pPass=NULL); -xafp_client_context* xafp_create_context(const char* pServer, const char* pUser, const char* pPass); -void xafp_destroy_context(xafp_client_context* pCtx); +xafp_client_handle xafp_create_context(const char* pServer, unsigned int port=548, const char* pUser=NULL, const char* pPass=NULL); +xafp_client_handle xafp_create_context(const char* pServer, const char* pUser, const char* pPass); +void xafp_destroy_context(xafp_client_handle pCtx); -int xafp_mount(xafp_client_context* pCtx, const char* pVolumeName, xafp_mount_flags flags); -void xafp_unmount(xafp_client_context* pCtx); +int xafp_mount(xafp_client_handle hnd, const char* pVolumeName, xafp_mount_flags flags); +void xafp_unmount(xafp_client_handle hnd, const char* pVolumeName); -xafp_dir_iterator* xafp_list_dir(xafp_client_context* pCtx, const char* pPath); -xafp_node* xafp_next(xafp_node_iterator* pIter); +xafp_node_iterator xafp_get_dir_iter(xafp_client_handle hnd, const char* pPath); +xafp_node_handle xafp_next(xafp_node_iterator iter); +void xafp_free_iter(xafp_node_iterator iter); -// xafp_open_file -// xafp_read_file -// xafp_close_file +xafp_file_handle xafp_open_file(xafp_client_handle hnd, const char* pPath, xafp_open_flags flags); +int xafp_read_file(xafp_client_handle hnd, xafp_file_handle file, void* pBuf, unsigned int len); +void xafp_close_file(xafp_client_handle hnd, xafp_file_handle file); // xafp_create_file // xafp_create_dir diff --git a/libxafp.xcodeproj/project.pbxproj b/libxafp.xcodeproj/project.pbxproj index 9229061..cc6b826 100644 --- a/libxafp.xcodeproj/project.pbxproj +++ b/libxafp.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 83A86039142047E5000B8F90 /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83A86037142047E5000B8F90 /* Logging.cpp */; }; + 83A861B114265502000B8F90 /* Threads.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83A861B014265502000B8F90 /* Threads.cpp */; }; 83D4CEA3140AAE6000C157C2 /* AFPClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D4CE9C140AAE6000C157C2 /* AFPClient.cpp */; }; 83D4CEA4140AAE6000C157C2 /* DSIClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D4CE9E140AAE6000C157C2 /* DSIClient.cpp */; }; 83D4CEA5140AAE6000C157C2 /* TCPClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83D4CEA0140AAE6000C157C2 /* TCPClient.cpp */; }; @@ -41,6 +42,9 @@ 83A86037142047E5000B8F90 /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Logging.cpp; path = src/Logging.cpp; sourceTree = ""; }; 83A86038142047E5000B8F90 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = src/Logging.h; sourceTree = ""; }; 83A8612614219B72000B8F90 /* libxafp_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = libxafp_internal.h; path = src/libxafp_internal.h; sourceTree = ""; }; + 83A861AD1426549E000B8F90 /* Common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Common.h; path = src/Common.h; sourceTree = ""; }; + 83A861AF14265502000B8F90 /* Threads.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Threads.h; path = src/Threads.h; sourceTree = ""; }; + 83A861B014265502000B8F90 /* Threads.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Threads.cpp; path = src/Threads.cpp; sourceTree = ""; }; 83D4CE9C140AAE6000C157C2 /* AFPClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AFPClient.cpp; path = src/AFPClient.cpp; sourceTree = ""; }; 83D4CE9D140AAE6000C157C2 /* AFPClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFPClient.h; path = src/AFPClient.h; sourceTree = ""; }; 83D4CE9E140AAE6000C157C2 /* DSIClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DSIClient.cpp; path = src/DSIClient.cpp; sourceTree = ""; }; @@ -103,14 +107,33 @@ name = Products; sourceTree = ""; }; + 83A861A1142652FB000B8F90 /* Interface */ = { + isa = PBXGroup; + children = ( + 83A8612614219B72000B8F90 /* libxafp_internal.h */, + 83D4CEA6140AAEB800C157C2 /* libxafp.cpp */, + ); + name = Interface; + sourceTree = ""; + }; + 83A861AE142654E0000B8F90 /* Threading */ = { + isa = PBXGroup; + children = ( + 83A861AF14265502000B8F90 /* Threads.h */, + 83A861B014265502000B8F90 /* Threads.cpp */, + ); + name = Threading; + sourceTree = ""; + }; 83D4CE8D140AAD0B00C157C2 /* src */ = { isa = PBXGroup; children = ( + 83A861A1142652FB000B8F90 /* Interface */, 83D4D47A1415594300C157C2 /* Common */, 83D4CEC1140AAFFB00C157C2 /* AFP */, 83D4CEC0140AAFF500C157C2 /* DSI */, 83D4CEBF140AAFED00C157C2 /* TCP */, - 83D4CEA6140AAEB800C157C2 /* libxafp.cpp */, + 83A861AE142654E0000B8F90 /* Threading */, ); name = src; sourceTree = ""; @@ -164,11 +187,11 @@ 83D4D47A1415594300C157C2 /* Common */ = { isa = PBXGroup; children = ( + 83A861AD1426549E000B8F90 /* Common.h */, 83A86037142047E5000B8F90 /* Logging.cpp */, 83A86038142047E5000B8F90 /* Logging.h */, 83D4CEA2140AAE6000C157C2 /* Utils.h */, 83D4D47B1415596A00C157C2 /* Utils.cpp */, - 83A8612614219B72000B8F90 /* libxafp_internal.h */, ); name = Common; sourceTree = ""; @@ -277,6 +300,7 @@ 83D4CEA7140AAEB800C157C2 /* libxafp.cpp in Sources */, 83D4D47C1415596A00C157C2 /* Utils.cpp in Sources */, 83A86039142047E5000B8F90 /* Logging.cpp in Sources */, + 83A861B114265502000B8F90 /* Threads.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/AFPClient.cpp b/src/AFPClient.cpp index 9bf412c..31d84e7 100644 --- a/src/AFPClient.cpp +++ b/src/AFPClient.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Team XBMC + * Copyright (C) 2011 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -19,6 +19,8 @@ * */ +#include "Common.h" + #include #include #include @@ -26,15 +28,16 @@ #include "AFPClient.h" #include "AFPProto.h" -#include "Utils.h" - // AFP File and Directory Parameter Handling ///////////////////////////////////////////////////////////////////////////////// +// Node dates are represented by the number of seconds since '01/01/2000 00:00:00.0 UTC' +#define AFPTimeToTime_t(t) (t + ((2000 - 1970) * 365 * 86400) + (7 * 86400)) // Adjust for differences in epoch, including leap years + CNodeParams::CNodeParams() : m_pName(NULL) { - + // TODO: Initialize remaining members } CNodeParams::~CNodeParams() @@ -58,17 +61,17 @@ int CNodeParams::Parse(uint32_t bitmap, uint8_t* pData, uint32_t size) } if (bitmap & kFPCreateDateBit) { - m_CreateDate = ntohl(*(uint32_t*)p); + m_CreateDate = AFPTimeToTime_t(ntohl(*(uint32_t*)p)); p += sizeof(uint32_t); } if (bitmap & kFPModDateBit) { - m_ModDate = ntohl(*(uint32_t*)p); + m_ModDate = AFPTimeToTime_t(ntohl(*(uint32_t*)p)); p += sizeof(uint32_t); } if (bitmap & kFPBackupDateBit) { - m_BackupDate = ntohl(*(uint32_t*)p); + m_BackupDate = AFPTimeToTime_t(ntohl(*(uint32_t*)p)); p += sizeof(uint32_t); } if (bitmap & kFPFinderInfoBit) @@ -92,6 +95,7 @@ CDirParams::CDirParams(uint32_t bitmap, uint8_t* pData, uint32_t size) : CNodeParams() { Parse(bitmap, pData, size); + m_IsDirectory = true; } int CDirParams::Parse(uint32_t bitmap, uint8_t* pData, uint32_t size) @@ -163,6 +167,7 @@ CFileParams::CFileParams(uint32_t bitmap, uint8_t* pData, uint32_t size) : CNodeParams() { Parse(bitmap, pData, size); + m_IsDirectory = false; } int CFileParams::Parse(uint32_t bitmap, uint8_t* pData, uint32_t size) @@ -241,6 +246,67 @@ int CFileParams::Parse(uint32_t bitmap, uint8_t* pData, uint32_t size) return (p - pData); } + +// AFP Node List/Iterator +///////////////////////////////////////////////////////////////////////////////// +// TODO: Only one iterator is available at a time...should this change? +CAFPNodeList::CAFPNodeList(CDSIBuffer* pBuf, uint32_t dirBitmap, uint32_t fileBitmap,int count) : + m_pBuffer(pBuf), + m_DirBitmap(dirBitmap), + m_FileBitmap(fileBitmap), + m_Count(count), + m_Iter(pBuf, dirBitmap, fileBitmap) +{ + +} + +CAFPNodeList::~CAFPNodeList() +{ + delete m_pBuffer; +} + +CAFPNodeList::Iterator::Iterator(CDSIBuffer* pBuf, uint32_t dirBitmap, uint32_t fileBitmap) : + m_DirBitmap(dirBitmap), + m_FileBitmap(fileBitmap), + m_pCurrent(NULL) +{ + m_pData = ((uint8_t*)pBuf->GetData()) + 6; + m_pEnd = m_pData + pBuf->GetDataLen() - 6; + + //m_pCurrent = MoveNext(); // Parse the first node +}; + +CNodeParams* CAFPNodeList::Iterator::MoveNext() +{ + if (m_pData >= m_pEnd) + { + m_pCurrent = NULL; + return NULL; // We already reached the end + } + + uint16_t len = ntohs(*((uint16_t*)m_pData)); // Entry size + if ((m_pData + len) > m_pEnd) // Make sure we don't fall off the end of the world... + { + m_pData = m_pEnd; + m_pCurrent = NULL; + return NULL; + } + + if (*(m_pData + sizeof(uint16_t)) & 0x80) // Directory bit + pad(7bits) -- followed by another byte of padding + { + m_DirParams.Parse(m_DirBitmap, m_pData+4, len-4); + m_pCurrent = &m_DirParams; + } + else + { + m_FileParams.Parse(m_FileBitmap, m_pData+4, len-4); + m_pCurrent = &m_FileParams; + } + m_pData += len; + return m_pCurrent; +} + + // AFP Session Handling ///////////////////////////////////////////////////////////////////////////////// CAFPSession::CAFPSession() : @@ -294,7 +360,7 @@ void CAFPSession::Logout() int CAFPSession::OpenVolume(const char* pVolName) { if (!IsLoggedIn()) - return false; + return kFPUserNotAuth; CDSIBuffer reqBuf(4 + 1 + strlen(pVolName)); reqBuf.Write((uint8_t)FPOpenVol); // Command @@ -314,7 +380,7 @@ int CAFPSession::OpenVolume(const char* pVolName) return volID; } } - return 0; + return kNoError; } void CAFPSession::CloseVolume(int volId) @@ -352,7 +418,7 @@ int CAFPSession::GetDirectory(int volumeID, const char* pPath) int CAFPSession::OpenDir(int volumeID, int parentID, const char* pName) { if (!IsLoggedIn()) - return false; + return 0; CDSIBuffer reqBuf(13 + 4 + 1 + strlen(pName)); reqBuf.Write((uint8_t)FPGetFileDirParms); // Command @@ -386,10 +452,13 @@ int CAFPSession::OpenDir(int volumeID, int parentID, const char* pName) return 0; // TODO: Need error codes for callers... } -void CAFPSession::List(int volumeID, int dirID) +int CAFPSession::GetNodeList(CAFPNodeList** ppList, int volumeID, int dirID) { if (!IsLoggedIn()) - return; + return kFPUserNotAuth; + + if (!ppList) + return kParamError; // TODO: handle instances that require multiple calls (too many entries for one block) CDSIBuffer reqBuf(29); // TODO: This method of sizing the request block is WAAAY too error-prone... @@ -409,67 +478,21 @@ void CAFPSession::List(int volumeID, int dirID) reqBuf.Write((uint32_t)0x08000103); // Unknown reqBuf.Write((uint16_t)0); // Name Length - CDSIBuffer replyBuf; - uint32_t err = SendCommand(reqBuf, &replyBuf); + CDSIBuffer* pReplyBuf = new CDSIBuffer(); + uint32_t err = SendCommand(reqBuf, pReplyBuf); if (err == kNoError) { // TODO: Validate returned bitmap vs provided one - replyBuf.Read16(); // Skip File Bitmap (we already specified) - replyBuf.Read16(); // Skip Directory Bitmap (we already specified) - int count = replyBuf.Read16(); // Results Returned - - uint8_t* pData = ((uint8_t*)replyBuf.GetData()) + 6; - CFileParams fileParams; - CDirParams dirParams; - printf("total %d\n", count); - for (int i = 0; i < count; i++) - { - uint16_t len = ntohs(*((uint16_t*)pData)); // Entry size (adjusted for header size) - - bool isDir = *(pData + sizeof(uint16_t)) & 0x80; // Directory bit + pad(7bits) -- followed by another byte of padding - - CNodeParams* pParams = NULL; - if (!isDir) - { - fileParams.Parse(fileBitmap, pData + 4, len - 4); - pParams = &fileParams; - } - else - { - dirParams.Parse(dirBitmap, pData + 4, len - 4); - pParams = &dirParams; - } - - // drwxr-sr-x 4 chris Default 4096 Jul 5 19:35 SD - uint32_t attribs = pParams->GetAttributes(); - if (!(attribs & kFPInvisibleBit)) - { - uint32_t perms = pParams->GetPermissions(); - time_t modTime = pParams->GetModDate() + ((2000 - 1970) * 365 * 86400) + (7 * 86400); // Adjust for differences in epoch, including leap years - char timeString[32]; - strncpy(timeString, ctime(&modTime), 32); - timeString[strlen(timeString) - 1] = '\0'; - printf ("%s%s%s%s%s%s%s%s%s%s %s %d %d %d %s %s\n", - isDir ? "d" : "-", - (perms & kRPOwner) ? "r" : "-", - (perms & kWROwner) ? "w" : "-", - (perms & kSPGroup) ? "x" : "-", - (perms & kRPGroup) ? "r" : "-", - (perms & kWRGroup) ? "w" : "-", - (perms & kSPGroup) ? "x" : "-", - (perms & kRPOther) ? "r" : "-", - (perms & kWROther) ? "w" : "-", - (perms & kSPOther) ? "x" : "-", - "-", - pParams->GetUserId(), - pParams->GetGroupId(), - isDir ? 0 : 0, - timeString, - pParams->GetName() - ); - } - pData += len; - } + pReplyBuf->Read16(); // Skip File Bitmap (we already specified) + pReplyBuf->Read16(); // Skip Directory Bitmap (we already specified) + int count = pReplyBuf->Read16(); // Results Returned + *ppList = new CAFPNodeList(pReplyBuf, dirBitmap, fileBitmap, count); + return kNoError; + } + else + { + delete pReplyBuf; + return err; } } diff --git a/src/AFPClient.h b/src/AFPClient.h index 9ac662f..fae8549 100644 --- a/src/AFPClient.h +++ b/src/AFPClient.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2005-2011 Team XBMC + * Copyright (C) 2011 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -38,14 +38,15 @@ class CNodeParams inline const char* GetName() {return (m_pName == NULL) ? "" : m_pName;} inline const time_t GetModDate() {return (time_t)m_ModDate;} inline uint16_t GetAttributes() {return m_Attributes;} -protected: + inline bool IsDirectory() {return m_IsDirectory;} +protected: + bool m_IsDirectory; uint16_t m_Attributes; uint32_t m_ParentId; - // Dates are represented by the number of seconds since '01/01/2000 00:00:00.0 UTC' // TODO: Read server time at login and adjust all dates - uint32_t m_CreateDate; - uint32_t m_ModDate; - uint32_t m_BackupDate; + time_t m_CreateDate; + time_t m_ModDate; + time_t m_BackupDate; uint32_t m_NodeId; char* m_pName; struct @@ -85,6 +86,37 @@ class CFileParams : public CNodeParams uint64_t m_ResourceForkLen; }; +class CAFPNodeList +{ +public: + CAFPNodeList(CDSIBuffer* pBuf, uint32_t dirBitmap, uint32_t fileBitmap, int count); + virtual ~CAFPNodeList(); + int GetSize(){return m_Count;} + + class Iterator + { + public: + Iterator(CDSIBuffer* pBuf, uint32_t dirBitmap, uint32_t fileBitmap); + CNodeParams* MoveNext(); + CNodeParams* GetCurrent() {return m_pCurrent;} + protected: + uint8_t* m_pData; + uint8_t* m_pEnd; + CDirParams m_DirParams; + uint32_t m_DirBitmap; + CFileParams m_FileParams; + uint32_t m_FileBitmap; + CNodeParams* m_pCurrent; + }; + Iterator* GetIterator() {return &m_Iter;} +protected: + CDSIBuffer* m_pBuffer; + int m_Count; + uint32_t m_DirBitmap; + uint32_t m_FileBitmap; + Iterator m_Iter; +}; + // AFP Session Handling ///////////////////////////////////////////////////////////////////////////////// class CAFPSession : public CDSISession @@ -99,7 +131,7 @@ class CAFPSession : public CDSISession bool IsLoggedIn() {return m_LoggedIn;} int GetDirectory(int volumeID, const char* pPath); - void List(int volumeID, int dirID); + int GetNodeList(CAFPNodeList** ppList, int volumeID, int dirID); int OpenFile(int volumeId, int dirId, const char* pName); void CloseFile(int forkId); diff --git a/src/AFPProto.h b/src/AFPProto.h index 8c16f22..6a153fe 100644 --- a/src/AFPProto.h +++ b/src/AFPProto.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2005-2011 Team XBMC + * Copyright (C) 2011 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/src/Common.h b/src/Common.h new file mode 100644 index 0000000..2033483 --- /dev/null +++ b/src/Common.h @@ -0,0 +1,29 @@ +#pragma once +/* + * Copyright (C) 2011 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 + * + */ + +#include +#include +#include +#include + +#include "Logging.h" +#include "Utils.h" \ No newline at end of file diff --git a/src/DSIClient.cpp b/src/DSIClient.cpp index fde61d5..59f87dc 100755 --- a/src/DSIClient.cpp +++ b/src/DSIClient.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Team XBMC + * Copyright (C) 2011 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -19,6 +19,8 @@ * */ +#include "Common.h" + #include "DSIClient.h" #include "DSIProto.h" @@ -466,7 +468,7 @@ void CDSISession::OnReceive(CTCPPacketReader& reader) break; case DSICloseSession: // Notification from server that the session will be closed. // Signal all waiting callers and tell them something happened - XAFP_LOG_0(XAFP_LOG_FLAG_INFO, "DSI Protocol: Server Closed Session. Canceling all panding requests"); + XAFP_LOG_0(XAFP_LOG_FLAG_INFO, "DSI Protocol: Server Closed Session. Canceling all pending requests"); SignalAll(kDSISessionClosed); // TODO: Clean-up session (and possibly re-open?) break; diff --git a/src/DSIClient.h b/src/DSIClient.h index df3b27d..5ffaaa4 100644 --- a/src/DSIClient.h +++ b/src/DSIClient.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2005-2011 Team XBMC + * Copyright (C) 2011 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -20,11 +20,8 @@ * */ -#include -#include -#include #include "TCPClient.h" -#include "Utils.h" +#include "Threads.h" ///////////////////////////////////////////////////////////////////////////////// // DSI (Data Stream Interface) Protocol Layer diff --git a/src/DSIProto.h b/src/DSIProto.h index 3511a5e..e997470 100644 --- a/src/DSIProto.h +++ b/src/DSIProto.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2005-2011 Team XBMC + * Copyright (C) 2011 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/src/Logging.cpp b/src/Logging.cpp index 5f97b61..627d44b 100755 --- a/src/Logging.cpp +++ b/src/Logging.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Team XBMC + * Copyright (C) 2011 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/src/Logging.h b/src/Logging.h index 8761e2a..63e98ed 100755 --- a/src/Logging.h +++ b/src/Logging.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Team XBMC + * Copyright (C) 2011 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/src/TCPClient.cpp b/src/TCPClient.cpp index b4d69f2..2541d5d 100755 --- a/src/TCPClient.cpp +++ b/src/TCPClient.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Team XBMC + * Copyright (C) 2011 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -19,27 +19,20 @@ * */ -#include -#include -#include -#include -#include +#include "Common.h" + #include #include #include #include #include -#include #include #include #include -#include -#include #include #include #include "TCPClient.h" -#include "Utils.h" // TODO List // - Improve start/stop sync for receive thread diff --git a/src/TCPClient.h b/src/TCPClient.h index b8ed05c..54610a6 100644 --- a/src/TCPClient.h +++ b/src/TCPClient.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2005-2011 Team XBMC + * Copyright (C) 2011 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify diff --git a/src/Threads.cpp b/src/Threads.cpp new file mode 100644 index 0000000..db84576 --- /dev/null +++ b/src/Threads.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2011 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 + * + */ + +#include "Threads.h" + +// Simple Thread Synchronization Event Wrapper +////////////////////////////////////////////// +CThreadSyncEvent::CThreadSyncEvent() : +m_Signaled(false) +{ + pthread_mutex_init(&m_Mutex, NULL); + pthread_cond_init(&m_Cond, NULL); +} + +CThreadSyncEvent::~CThreadSyncEvent() +{ + pthread_mutex_destroy(&m_Mutex); + pthread_cond_destroy(&m_Cond); +} + +int CThreadSyncEvent::Wait(int timeout /*=-1*/) +{ + pthread_mutex_lock(&m_Mutex); + if (!m_Signaled) + pthread_cond_wait(&m_Cond, &m_Mutex); + pthread_mutex_unlock(&m_Mutex); + + return 0; +} + +void CThreadSyncEvent::Set() +{ + pthread_mutex_lock(&m_Mutex); + m_Signaled = true; + pthread_cond_signal(&m_Cond); + pthread_mutex_unlock(&m_Mutex); +} + +void CThreadSyncEvent::Reset() +{ + m_Signaled = false; +} \ No newline at end of file diff --git a/src/Threads.h b/src/Threads.h new file mode 100644 index 0000000..2ad45a3 --- /dev/null +++ b/src/Threads.h @@ -0,0 +1,40 @@ +#pragma once +/* + * Copyright (C) 2011 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 + * + */ + +#include "Common.h" +#include + +// Simple Thread Synchronization Event Wrapper +////////////////////////////////////////////// +class CThreadSyncEvent +{ +public: + CThreadSyncEvent(); + virtual ~CThreadSyncEvent(); + int Wait(int timeout = -1); + void Set(); + void Reset(); +protected: + pthread_mutex_t m_Mutex; + pthread_cond_t m_Cond; + bool m_Signaled; +}; diff --git a/src/Utils.cpp b/src/Utils.cpp index 0b3fdf9..fb3eb8b 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Team XBMC + * Copyright (C) 2011 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -122,39 +122,3 @@ const char* AFPProtoCommandToString(int id) return "???"; return AFPCommandNames[id]; } - -CThreadSyncEvent::CThreadSyncEvent() : - m_Signaled(false) -{ - pthread_mutex_init(&m_Mutex, NULL); - pthread_cond_init(&m_Cond, NULL); -} - -CThreadSyncEvent::~CThreadSyncEvent() -{ - pthread_mutex_destroy(&m_Mutex); - pthread_cond_destroy(&m_Cond); -} - -int CThreadSyncEvent::Wait(int timeout /*=-1*/) -{ - pthread_mutex_lock(&m_Mutex); - if (!m_Signaled) - pthread_cond_wait(&m_Cond, &m_Mutex); - pthread_mutex_unlock(&m_Mutex); - - return 0; -} - -void CThreadSyncEvent::Set() -{ - pthread_mutex_lock(&m_Mutex); - m_Signaled = true; - pthread_cond_signal(&m_Cond); - pthread_mutex_unlock(&m_Mutex); -} - -void CThreadSyncEvent::Reset() -{ - m_Signaled = false; -} diff --git a/src/Utils.h b/src/Utils.h index ea456d7..8188f8b 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2005-2011 Team XBMC + * Copyright (C) 2011 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -24,8 +24,6 @@ memcpy(_dst,((char*)_src + 1),*((uint8_t*)_src)); \ _dst[*((uint8_t*)_src)] = '\0'; -#include -#include "Logging.h" enum { @@ -35,17 +33,3 @@ enum const char* DSIProtoCommandToString(int id); const char* AFPProtoCommandToString(int id); - -class CThreadSyncEvent -{ -public: - CThreadSyncEvent(); - virtual ~CThreadSyncEvent(); - int Wait(int timeout = -1); - void Set(); - void Reset(); -protected: - pthread_mutex_t m_Mutex; - pthread_cond_t m_Cond; - bool m_Signaled; -}; \ No newline at end of file diff --git a/src/libxafp.cpp b/src/libxafp.cpp index 05355cb..16d43f2 100644 --- a/src/libxafp.cpp +++ b/src/libxafp.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Team XBMC + * Copyright (C) 2011 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -19,6 +19,186 @@ * */ -#include "../include/libxafp.h" +#include "Common.h" +#include "libxafp_internal.h" + +// Context Handling +////////////////////////////////////// +// Function: xafp_create_context +// Returns: Handle on success (> 0), null on failure (==0) +xafp_client_handle xafp_create_context(const char* pServer, unsigned int port/*=548*/, const char* pUser/*=NULL*/, const char* pPass/*=NULL*/) +{ + _client_context* pCtx = (_client_context*)malloc(sizeof(_client_context)); + + size_t len = strlen(pServer); + pCtx->server_dns_name = (char*)malloc(len + 1); + strncpy(pCtx->server_dns_name, pServer, len); + pCtx->server_dns_name[len] = '\0'; + + pCtx->port = port; + + if (pUser) + { + len = strlen(pUser); + pCtx->username = (char*)malloc(len + 1); + strncpy(pCtx->username, pUser, len); + pCtx->username[len] = '\0'; + } + else + pCtx->username = NULL; + + if (pPass) + { + len = strlen(pPass); + pCtx->password = (char*)malloc(len + 1); + strncpy(pCtx->password, pPass, len); + pCtx->password[len] = '\0'; + } + else + pCtx->password = NULL; + + pCtx->volumes = new volume_map(); + pCtx->session = new CAFPSession(); + + return pCtx; +} + +xafp_client_handle xafp_create_context(const char* pServer, const char* pUser, const char* pPass) +{ + return xafp_create_context(pServer, 548, pUser, pPass); +} + +void xafp_destroy_context(xafp_client_handle hnd) +{ + _client_context* pCtx = (_client_context*)hnd; + + delete pCtx->volumes; + delete pCtx->session; + + free(pCtx->server_dns_name); + free(pCtx->username); + free(pCtx->password); + free(pCtx); +} + +// TODO: Return useful error codes... + +// Volume Mount/Unmount +////////////////////////////////////// +int xafp_mount(xafp_client_handle hnd, const char* pVolumeName, xafp_mount_flags flags) +{ + _client_context* pCtx = (_client_context*)hnd; + + // This interface is late-binding, so the first mount request triggers connect/login + if (!pCtx->session->IsLoggedIn()) + { + if (!pCtx->session->IsConnected()) + { + if (!pCtx->session->Open(pCtx->server_dns_name, pCtx->port, 3000)) + return -1; + } + if (!pCtx->session->Login(pCtx->username,pCtx->password)) + return -2; + } + + int res = pCtx->session->OpenVolume(pVolumeName); + if (res < 0) + return -3; + + pCtx->volumes->insert(volume_map_entry(pVolumeName, res)); + + return 0; +} + +void xafp_unmount(xafp_client_handle hnd, const char* pVolumeName) +{ + int volId = xafp_find_volume_id(hnd, pVolumeName); + + if (volId) + { + _client_context* pCtx = (_client_context*)hnd; + pCtx->session->CloseVolume(volId); + pCtx->volumes->erase(pVolumeName); + } +} + +// Node Listing/Iteration +////////////////////////////////////// +xafp_node_iterator xafp_get_dir_iter(xafp_client_handle hnd, const char* pPath) +{ + if (!hnd) + return NULL; + + // TODO: Make this more reliable and complete + std::string path = (pPath[0] == '/') ? pPath + 1 : pPath; // Strip the leading '/' if there is one + + int pos = path.find('/'); + std::string volume = path.substr(0, pos); + + int volId = xafp_find_volume_id(hnd, volume.c_str()); + if (volId) + { + _client_context* pCtx = (_client_context*)hnd; + std::string dir = path.substr(pos); + int dirId = pCtx->session->GetDirectory(volId, dir.c_str()); + + if (dirId > 0) + { + CAFPNodeList* pList = NULL; + if (pCtx->session->GetNodeList(&pList, volId, dirId) == kNoError) + { + _fs_node_iterator* pIter = new _fs_node_iterator; + pIter->list = pList; + pIter->iter = pList->GetIterator(); + return (xafp_node_iterator)pIter; + } + } + } + return NULL; +} + +// TODO: Get rid of the extra layer here... +xafp_node_handle xafp_next(xafp_node_iterator iter) +{ + if (!iter) + return NULL; + + _fs_node_iterator* pIter = (_fs_node_iterator*)iter; + return (_fs_node*) pIter->iter->MoveNext(); +} + +void xafp_free_iter(xafp_node_iterator iter) +{ + if (!iter) + return; + + _fs_node_iterator* pIter = (_fs_node_iterator*)iter; + delete pIter->list; + delete pIter; +} + +// File I/O +////////////////////////////////////// + +// Function: xafp_open_file +// Returns: Handle on success (> 0), error code on failure (< 0) +xafp_file_handle xafp_open_file(xafp_client_handle hnd, const char* pPath, xafp_open_flags flags) +{ + return 0; +} + +// Function: xafp_read_file +// Returns: 0 on success, error code on failure (< 0) +int xafp_read_file(xafp_client_handle hnd, xafp_file_handle file, void* pBuf, unsigned int len) +{ + return 0; +} + +// Function: xafp_close_file +// Returns: None +void xafp_close_file(xafp_client_handle hnd, xafp_file_handle file) +{ + +} diff --git a/src/libxafp_internal.h b/src/libxafp_internal.h index 9065875..5d2e864 100644 --- a/src/libxafp_internal.h +++ b/src/libxafp_internal.h @@ -1,6 +1,6 @@ #pragma once /* - * Copyright (C) 2005-2011 Team XBMC + * Copyright (C) 2011 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify @@ -20,7 +20,55 @@ * */ -struct xafp_client_context +#include "../include/libxafp.h" + +#include "AFPProto.h" +#include "AFPClient.h" + +#define MAX_SERVER_NAME 32 +#define MAX_SERVER_TYPE 32 +#define MAX_USER_NAME 512 // Up to 255 Unicode chars +#define MAX_PASSWORD 16 + +typedef std::map volume_map; +typedef std::pair volume_map_entry; +typedef volume_map::iterator volume_map_iterator; + +struct _client_context +{ + char* server_dns_name; // Could also be IP-address string + unsigned int port; + char* username; + char* password; + + CAFPSession* session; + volume_map* volumes; +}; + +struct _fs_node { + CNodeParams* node; // Must be the first item in the struct + +// std::string name; +// std::string path; +// xafp_node_type type; +// bool hidden; +// time_t createTime; +}; -}; \ No newline at end of file +struct _fs_node_iterator +{ + CAFPNodeList* list; + CAFPNodeList::Iterator* iter; +}; + +inline int xafp_find_volume_id(xafp_client_handle hnd, const char* pVolumeName) +{ + _client_context* pCtx = (_client_context*)hnd; + + volume_map_iterator it = pCtx->volumes->find(pVolumeName); + if (it == pCtx->volumes->end()) + return 0; // Was not mounted + + return it->second; +} \ No newline at end of file diff --git a/test/main.cpp b/test/main.cpp index b3a115a..63b1794 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,9 +1,10 @@ #include +#include "../include/libxafp.h" #include "../src/AFPClient.h" -#include "../src/AFPClient.h" +#include "../src/AFPProto.h" -void ls(const char* path, const char* username, const char* pass) +void cp(const char* path, const char* dest, const char* username, const char* pass) { // Obtain an AFP Session // TODO: Should we implement a session manager to minimize setup/teardown? @@ -57,6 +58,55 @@ void ls(const char* path, const char* username, const char* pass) session.Close(); } +void ls(const char* path, const char* username, const char* pass) +{ + xafp_client_handle ctx = xafp_create_context("kennel", username, pass); + + // Authenticate and open the desired volume + // TODO: Strip out volume name + if (!xafp_mount(ctx, "Media", xafp_mount_flag_none)) + { + xafp_node_iterator iter = xafp_get_dir_iter(ctx, path); + xafp_node_handle node = xafp_next(iter); + while(node) + { + CNodeParams* pParams = (CNodeParams*)node; + if (!(pParams->GetAttributes() & kFPInvisibleBit)) + { + uint32_t perms = pParams->GetPermissions(); + time_t modTime = pParams->GetModDate(); + char timeString[32]; + strncpy(timeString, ctime(&modTime), 32); + timeString[strlen(timeString) - 1] = '\0'; + printf ("%s%s%s%s%s%s%s%s%s%s %s %d %d %d %s %s\n", + pParams->IsDirectory() ? "d" : "-", + (perms & kRPOwner) ? "r" : "-", + (perms & kWROwner) ? "w" : "-", + (perms & kSPGroup) ? "x" : "-", + (perms & kRPGroup) ? "r" : "-", + (perms & kWRGroup) ? "w" : "-", + (perms & kSPGroup) ? "x" : "-", + (perms & kRPOther) ? "r" : "-", + (perms & kWROther) ? "w" : "-", + (perms & kSPOther) ? "x" : "-", + "-", + pParams->GetUserId(), + pParams->GetGroupId(), + pParams->IsDirectory() ? 0 : 0, + timeString, + pParams->GetName() + ); + } + node = xafp_next(iter); + } + xafp_free_iter(iter); + xafp_unmount(ctx, "Media"); + } + + // Clean-up the session + xafp_destroy_context(ctx); +} + int main (int argc, char * const argv[]) { XafpSetLogLevel(XAFP_LOG_LEVEL_INFO); @@ -64,7 +114,7 @@ int main (int argc, char * const argv[]) FILE* fsecret = fopen(argv[1], "r"); fread(secret, 1, sizeof(secret), fsecret); - ls("video/Movies/BRRip","chris",secret); + ls("/Media/video/Movies/BRRip","chris",secret); return 0; }