mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-02-16 00:24:29 +01:00
Land #494, add date filtering to fs_search
This commit is contained in:
commit
cb078144cd
@ -124,6 +124,7 @@ typedef struct _PacketApi
|
||||
QWORD(*get_tlv_value_qword)(Packet* packet, TlvType type);
|
||||
TlvMetaType(*get_tlv_meta)(Packet* packet, Tlv* tlv);
|
||||
UINT(*get_tlv_value_uint)(Packet* packet, TlvType type);
|
||||
BOOL(*get_tlv_uint)(Packet* packet, TlvType type, UINT* output);
|
||||
VOID(*destroy)(Packet* packet);
|
||||
wchar_t*(*get_tlv_value_wstring)(Packet* packet, TlvType type);
|
||||
LPCSTR(*get_tlv_value_reflective_loader)(Packet* packet);
|
||||
|
@ -22,10 +22,38 @@ const GUID MET_DBGUID_DEFAULT = {0xc8b521fb,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0
|
||||
#define MET_DBGUID_DEFAULT DBGUID_DEFAULT
|
||||
#endif
|
||||
|
||||
#define TICKS_PER_SECOND 10000000LL
|
||||
#define MILI_SEC_TO_UNIX_EPOCH 116444736000000000LL
|
||||
|
||||
BOOL uintToSYSTEMTIME(UINT epoch, SYSTEMTIME* lpst) {
|
||||
UINT64 t = (TICKS_PER_SECOND * epoch) + MILI_SEC_TO_UNIX_EPOCH;
|
||||
ULARGE_INTEGER li;
|
||||
li.QuadPart = t;
|
||||
FILETIME ft = {0};
|
||||
ft.dwHighDateTime = li.HighPart;
|
||||
ft.dwLowDateTime = li.LowPart;
|
||||
if (!FileTimeToSystemTime(&ft, lpst)) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//Epoch to FILETIME based on https://stackoverflow.com/questions/41016000/how-to-convert-filetime-to-unix-epoch-with-milliseconds-resolution
|
||||
UINT FILETIMETouint(FILETIME ft) {
|
||||
ULARGE_INTEGER li;
|
||||
li.HighPart = ft.dwHighDateTime;
|
||||
li.LowPart = ft.dwLowDateTime;
|
||||
UINT64 t = li.QuadPart;
|
||||
UINT64 y = (t - MILI_SEC_TO_UNIX_EPOCH) / TICKS_PER_SECOND;
|
||||
ULARGE_INTEGER li2;
|
||||
li2.QuadPart = y;
|
||||
return li2.LowPart;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to add a search result to the response packet.
|
||||
*/
|
||||
VOID search_add_result(Packet * pResponse, wchar_t *directory, wchar_t *fileName, DWORD dwFileSize)
|
||||
VOID search_add_result(Packet * pResponse, wchar_t *directory, wchar_t *fileName, DWORD dwFileSize, FILETIME ftFileTime)
|
||||
{
|
||||
char *dir = met_api->string.wchar_to_utf8(directory);
|
||||
char *file = met_api->string.wchar_to_utf8(fileName);
|
||||
@ -38,7 +66,7 @@ VOID search_add_result(Packet * pResponse, wchar_t *directory, wchar_t *fileName
|
||||
met_api->packet.add_tlv_string(group, TLV_TYPE_FILE_PATH, dir);
|
||||
met_api->packet.add_tlv_string(group, TLV_TYPE_FILE_NAME, file);
|
||||
met_api->packet.add_tlv_uint(group, TLV_TYPE_FILE_SIZE, dwFileSize);
|
||||
|
||||
met_api->packet.add_tlv_uint(group, TLV_TYPE_SEARCH_MTIME, FILETIMETouint(ftFileTime));
|
||||
met_api->packet.add_group(pResponse, TLV_TYPE_SEARCH_RESULTS, group);
|
||||
}
|
||||
|
||||
@ -216,7 +244,7 @@ HRESULT wds_execute(ICommand * pCommand, Packet * pResponse)
|
||||
DBCOUNTITEM dbCount = 0;
|
||||
DWORD dwResult = 0;
|
||||
HRESULT hr = 0;
|
||||
DBBINDING dbBindings[2] = {0};
|
||||
DBBINDING dbBindings[3] = {0};
|
||||
SEARCH_ROW rowSearchResults = {0};
|
||||
HROW hRow[1] = {0};
|
||||
HROW * pRows = &hRow[0];
|
||||
@ -233,7 +261,7 @@ HRESULT wds_execute(ICommand * pCommand, Packet * pResponse)
|
||||
BREAK_WITH_ERROR("[SEARCH] wds_execute: IRowset_QueryInterface _IID_IAccessor Failed", hr);
|
||||
}
|
||||
|
||||
memset(&dbBindings, 0, sizeof(DBBINDING)*2);
|
||||
memset(&dbBindings, 0, sizeof(DBBINDING)*3);
|
||||
|
||||
dbBindings[0].iOrdinal = 1;
|
||||
dbBindings[0].dwPart = DBPART_STATUS | DBPART_LENGTH | DBPART_VALUE;
|
||||
@ -257,7 +285,19 @@ HRESULT wds_execute(ICommand * pCommand, Packet * pResponse)
|
||||
dbBindings[1].obLength = offsetof(SEARCH_ROW, dwPathLength);
|
||||
dbBindings[1].obValue = offsetof(SEARCH_ROW, wPathValue);
|
||||
|
||||
hr = IAccessor_CreateAccessor(pAccessor, DBACCESSOR_ROWDATA, 2, (DBBINDING *)&dbBindings, 0, &hAccessor, NULL);
|
||||
|
||||
dbBindings[2].iOrdinal = 3;
|
||||
dbBindings[2].dwPart = DBPART_STATUS | DBPART_LENGTH | DBPART_VALUE;
|
||||
dbBindings[2].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
|
||||
dbBindings[2].cbMaxLen = sizeof(DWORD);
|
||||
dbBindings[2].dwFlags = 0;
|
||||
dbBindings[2].eParamIO = DBPARAMIO_NOTPARAM;
|
||||
dbBindings[2].wType = DBTYPE_FILETIME;
|
||||
dbBindings[2].obStatus = offsetof(SEARCH_ROW, dbDateStatus);
|
||||
dbBindings[2].obLength = offsetof(SEARCH_ROW, dwDateLength);
|
||||
dbBindings[2].obValue = offsetof(SEARCH_ROW, wDateValue);
|
||||
|
||||
hr = IAccessor_CreateAccessor(pAccessor, DBACCESSOR_ROWDATA, 3, (DBBINDING *)&dbBindings, 0, &hAccessor, NULL);
|
||||
if (FAILED(hr)) {
|
||||
BREAK_WITH_ERROR("[SEARCH] wds_execute: IAccessor_CreateAccessor Failed", hr);
|
||||
}
|
||||
@ -285,7 +325,7 @@ HRESULT wds_execute(ICommand * pCommand, Packet * pResponse)
|
||||
// "iehistory://{*}/"
|
||||
wchar_t * history = wcsstr(rowSearchResults.wPathValue, L"}");
|
||||
if (history) {
|
||||
search_add_result(pResponse, L"", history + 2, 0);
|
||||
search_add_result(pResponse, L"", history + 2, 0, rowSearchResults.wDateValue);
|
||||
}
|
||||
}
|
||||
else if (_memicmp(L"mapi:", rowSearchResults.wPathValue, sizeof(L"mapi:")) == 0)
|
||||
@ -293,7 +333,7 @@ HRESULT wds_execute(ICommand * pCommand, Packet * pResponse)
|
||||
// "mapi://{*}/"
|
||||
wchar_t * history = wcsstr(rowSearchResults.wPathValue, L"}");
|
||||
if (history) {
|
||||
search_add_result(pResponse, L"", history + 2, 0);
|
||||
search_add_result(pResponse, L"", history + 2, 0, rowSearchResults.wDateValue);
|
||||
}
|
||||
}
|
||||
else if (rowSearchResults.dwSizeValue > 0) {
|
||||
@ -325,7 +365,8 @@ HRESULT wds_execute(ICommand * pCommand, Packet * pResponse)
|
||||
directory = L"";
|
||||
fileName = directory;
|
||||
}
|
||||
search_add_result(pResponse, directory, fileName, rowSearchResults.dwSizeValue);
|
||||
|
||||
search_add_result(pResponse, directory, fileName, rowSearchResults.dwSizeValue, rowSearchResults.wDateValue);
|
||||
}
|
||||
|
||||
hr = IRowset_ReleaseRows(pRowset, dbCount, pRows, NULL, NULL, NULL);
|
||||
@ -512,29 +553,55 @@ DWORD wds3_search(WDS_INTERFACE * pWDSInterface, wchar_t * wpProtocol, wchar_t *
|
||||
BREAK_WITH_ERROR("[SEARCH] wds3_search: ISearchCatalogManager_GetQueryHelper Failed", hr);
|
||||
}
|
||||
|
||||
hr = ISearchQueryHelper_put_QuerySelectColumns(pQueryHelper, L"size,path");
|
||||
hr = ISearchQueryHelper_put_QuerySelectColumns(pQueryHelper, L"size,path,write");
|
||||
if (FAILED(hr)) {
|
||||
BREAK_WITH_ERROR("[SEARCH] wds3_search: ISearchQueryHelper_put_QuerySelectColumns Failed", hr);
|
||||
}
|
||||
|
||||
wchar_t* where;
|
||||
size_t where_len = 0;
|
||||
size_t where_max_len = 160;
|
||||
if (directory)
|
||||
{
|
||||
size_t len = wcslen(directory) + 128;
|
||||
wchar_t *where = calloc(len, sizeof(wchar_t));
|
||||
where_max_len = wcslen(directory) + where_max_len;
|
||||
where = calloc(where_max_len, sizeof(wchar_t));
|
||||
if (where) {
|
||||
if (pOptions->bResursive) {
|
||||
swprintf_s(where, len, L"AND SCOPE='%s:%s'", wpProtocol, directory);
|
||||
where_len = swprintf_s(where, where_max_len, L"AND SCOPE='%s:%s'", wpProtocol, directory);
|
||||
}
|
||||
else {
|
||||
swprintf_s(where, len, L"AND DIRECTORY='%s:%s'", wpProtocol, directory);
|
||||
where_len = swprintf_s(where, where_max_len, L"AND DIRECTORY='%s:%s'", wpProtocol, directory);
|
||||
}
|
||||
ISearchQueryHelper_put_QueryWhereRestrictions(pQueryHelper, where);
|
||||
free(where);
|
||||
} else {
|
||||
dprintf("[SEARCH] wds3_search: !where", ERROR_OUTOFMEMORY);
|
||||
BREAK_WITH_ERROR("[SEARCH] wds3_search: !where", ERROR_OUTOFMEMORY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (pOptions->uiStartDate != FS_SEARCH_NO_DATE) {
|
||||
SYSTEMTIME LPST = { 0 };
|
||||
if (!uintToSYSTEMTIME(pOptions->uiStartDate, &LPST)) {
|
||||
BREAK_WITH_ERROR("[SEARCH] unable to convert start date", GetLastError());
|
||||
}
|
||||
where_len += swprintf_s(where + where_len, where_max_len - where_len,
|
||||
L" AND System.DateModified>='%04d-%02d-%02dT%02d:%02d:%02d'",
|
||||
LPST.wYear, LPST.wMonth, LPST.wDay, LPST.wHour, LPST.wMinute, LPST.wSecond);
|
||||
}
|
||||
if (pOptions->uiEndDate != FS_SEARCH_NO_DATE) {
|
||||
SYSTEMTIME LPST = { 0 };
|
||||
if (!uintToSYSTEMTIME(pOptions->uiEndDate, &LPST)) {
|
||||
BREAK_WITH_ERROR("[SEARCH] unable to convert end date", GetLastError());
|
||||
}
|
||||
where_len += swprintf_s(where + where_len, where_max_len - where_len,
|
||||
L" AND System.DateModified<='%04d-%02d-%02dT%02d:%02d:%02d'",
|
||||
LPST.wYear, LPST.wMonth, LPST.wDay, LPST.wHour, LPST.wMinute, LPST.wSecond);
|
||||
}
|
||||
|
||||
if (where) {
|
||||
ISearchQueryHelper_put_QueryWhereRestrictions(pQueryHelper, where);
|
||||
free(where);
|
||||
}
|
||||
|
||||
hr = ISearchQueryHelper_GenerateSQLFromUserQuery(pQueryHelper, pOptions->glob, &wpSQL);
|
||||
if (FAILED(hr)) {
|
||||
BREAK_WITH_ERROR("[SEARCH] wds3_search: ISearchQueryHelper_GenerateSQLFromUserQuery Failed", hr);
|
||||
@ -647,16 +714,24 @@ DWORD search_files(wchar_t * directory, SEARCH_OPTIONS * pOptions, Packet * pRes
|
||||
{
|
||||
wchar_t firstFile[FS_MAX_PATH];
|
||||
swprintf_s(firstFile, FS_MAX_PATH, L"%s\\%s", directory, pOptions->glob);
|
||||
|
||||
WIN32_FIND_DATAW data;
|
||||
HANDLE hFile = FindFirstFileW(firstFile, &data);
|
||||
|
||||
|
||||
if (hFile != INVALID_HANDLE_VALUE) {
|
||||
do
|
||||
{
|
||||
if (wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..") &&
|
||||
!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
search_add_result(pResponse, directory, data.cFileName, data.nFileSizeLow);
|
||||
UINT epoch = FILETIMETouint(data.ftLastWriteTime);
|
||||
if ((pOptions->uiStartDate != FS_SEARCH_NO_DATE) && (pOptions->uiStartDate > epoch)) {
|
||||
continue;
|
||||
}
|
||||
if ((pOptions->uiEndDate != FS_SEARCH_NO_DATE) && (pOptions->uiEndDate < epoch)) {
|
||||
continue;
|
||||
}
|
||||
search_add_result(pResponse, directory, data.cFileName, data.nFileSizeLow, data.ftLastWriteTime);
|
||||
}
|
||||
} while (FindNextFileW(hFile, &data) != 0);
|
||||
|
||||
@ -818,6 +893,14 @@ DWORD request_fs_search(Remote * pRemote, Packet * pPacket)
|
||||
options.glob = met_api->string.utf8_to_wchar(
|
||||
met_api->packet.get_tlv_value_string(pPacket, TLV_TYPE_SEARCH_GLOB));
|
||||
|
||||
if (met_api->packet.get_tlv_uint(pPacket, TLV_TYPE_SEARCH_M_START_DATE, &options.uiStartDate) == FALSE) {
|
||||
options.uiStartDate = FS_SEARCH_NO_DATE;
|
||||
}
|
||||
|
||||
if (met_api->packet.get_tlv_uint(pPacket, TLV_TYPE_SEARCH_M_END_DATE, &options.uiEndDate) == FALSE) {
|
||||
options.uiEndDate = FS_SEARCH_NO_DATE;
|
||||
}
|
||||
|
||||
if (options.rootDirectory && wcslen(options.rootDirectory) == 0) {
|
||||
free(options.rootDirectory);
|
||||
options.rootDirectory = NULL;
|
||||
@ -835,6 +918,7 @@ DWORD request_fs_search(Remote * pRemote, Packet * pPacket)
|
||||
}
|
||||
|
||||
dprintf("[SEARCH] root: '%S' glob: '%S'", options.rootDirectory, options.glob);
|
||||
dprintf("[SEARCH] dates: from %u to %u", options.uiStartDate, options.uiEndDate);
|
||||
|
||||
options.bResursive = met_api->packet.get_tlv_value_bool(pPacket, TLV_TYPE_SEARCH_RECURSE);
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define _METERPRETER_SOURCE_EXTENSION_STDAPI_STDAPI_SERVER_FS_SEARCH_H
|
||||
|
||||
#include <shlwapi.h>
|
||||
#include <windows.h>
|
||||
#include <searchapi.h>
|
||||
#include <msdasc.h>
|
||||
#include <ntquery.h>
|
||||
@ -33,11 +34,16 @@ typedef struct _WDS_INTERFACE
|
||||
|
||||
} WDS_INTERFACE;
|
||||
|
||||
|
||||
#define FS_SEARCH_NO_DATE UINT_MAX
|
||||
|
||||
typedef struct _SEARCH_OPTIONS
|
||||
{
|
||||
wchar_t *glob;
|
||||
wchar_t *rootDirectory;
|
||||
BOOL bResursive;
|
||||
UINT uiStartDate;
|
||||
UINT uiEndDate;
|
||||
} SEARCH_OPTIONS;
|
||||
|
||||
// sf: The padding DWORD's ensure we dont get an IAccessor_CreateAccessor error due to alignment on x64.
|
||||
@ -54,6 +60,12 @@ typedef struct _SEARCH_ROW
|
||||
DWORD dwPathLength;
|
||||
DWORD dwPadding5;
|
||||
wchar_t wPathValue[MAX_PATH];
|
||||
DBSTATUS dbDateStatus;
|
||||
DWORD dwPadding6;
|
||||
DWORD dwDateLength;
|
||||
DWORD dwPadding7;
|
||||
FILETIME wDateValue;
|
||||
DWORD dwPadding8;
|
||||
} SEARCH_ROW;
|
||||
|
||||
// we manually define these ourselves...
|
||||
|
@ -41,6 +41,9 @@
|
||||
#define TLV_TYPE_SEARCH_GLOB MAKE_CUSTOM_TLV( TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_STDAPI, 1231 )
|
||||
#define TLV_TYPE_SEARCH_ROOT MAKE_CUSTOM_TLV( TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_STDAPI, 1232 )
|
||||
#define TLV_TYPE_SEARCH_RESULTS MAKE_CUSTOM_TLV( TLV_META_TYPE_GROUP, TLV_TYPE_EXTENSION_STDAPI, 1233 )
|
||||
#define TLV_TYPE_SEARCH_MTIME MAKE_CUSTOM_TLV( TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_STDAPI, 1235 )
|
||||
#define TLV_TYPE_SEARCH_M_START_DATE MAKE_CUSTOM_TLV( TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_STDAPI, 1236 )
|
||||
#define TLV_TYPE_SEARCH_M_END_DATE MAKE_CUSTOM_TLV( TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_STDAPI, 1237 )
|
||||
|
||||
// Process
|
||||
|
||||
|
@ -15,23 +15,6 @@ DWORD get_migrate_context(LPDWORD contextSize, LPCOMMONMIGRATECONTEXT* contextBu
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
BOOL packet_get_tlv_uint(Packet *packet, TlvType type, UINT* output)
|
||||
{
|
||||
Tlv uintTlv;
|
||||
if (packet_get_tlv(packet, type, &uintTlv) != ERROR_SUCCESS)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((uintTlv.header.length < sizeof(DWORD)))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*output = ntohl(*(LPDWORD)uintTlv.buffer);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void set_transport_session_expiry(Remote* remote, Packet* packet)
|
||||
{
|
||||
int sessionExpiry = 0;
|
||||
|
@ -843,20 +843,41 @@ wchar_t* packet_get_tlv_value_wstring(Packet* packet, TlvType type)
|
||||
* @brief Get the unsigned int value of a TLV.
|
||||
* @param packet Pointer to the packet to get the TLV from.
|
||||
* @param type Type of TLV to get (optional).
|
||||
* @return The value found in the TLV.
|
||||
* @todo On failure, 0 is returned. We need to make sure this is the right
|
||||
* thing to do because 0 might also be a valid value.
|
||||
* @param output Pointer to the uint output
|
||||
* @return True if the value could be optioned
|
||||
*/
|
||||
BOOL packet_get_tlv_uint(Packet *packet, TlvType type, UINT* output)
|
||||
{
|
||||
Tlv uintTlv;
|
||||
if (packet_get_tlv(packet, type, &uintTlv) != ERROR_SUCCESS)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((uintTlv.header.length < sizeof(DWORD)))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*output = ntohl(*(LPDWORD)uintTlv.buffer);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get the unsigned int value of a TLV.
|
||||
* @param packet Pointer to the packet to get the TLV from.
|
||||
* @param type Type of TLV to get (optional).
|
||||
* @return The value found in the TLV, or 0
|
||||
*/
|
||||
UINT packet_get_tlv_value_uint(Packet *packet, TlvType type)
|
||||
{
|
||||
Tlv uintTlv;
|
||||
|
||||
if ((packet_get_tlv(packet, type, &uintTlv) != ERROR_SUCCESS) || (uintTlv.header.length < sizeof(DWORD)))
|
||||
UINT output = 0;
|
||||
if (packet_get_tlv_uint(packet, type, &output) == FALSE)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ntohl(*(LPDWORD)uintTlv.buffer);
|
||||
return output;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -38,6 +38,8 @@ DWORD packet_get_tlv_string(Packet *packet, TlvType type, Tlv *tlv);
|
||||
DWORD packet_get_tlv_group_entry(Packet *packet, Tlv *group, TlvType type,Tlv *entry);
|
||||
DWORD packet_enum_tlv(Packet *packet, DWORD index, TlvType type, Tlv *tlv);
|
||||
|
||||
BOOL packet_get_tlv_uint(Packet *packet, TlvType type, UINT* output);
|
||||
|
||||
LPCSTR packet_get_tlv_value_reflective_loader(Packet* packet);
|
||||
PCHAR packet_get_tlv_value_string(Packet *packet, TlvType type);
|
||||
wchar_t* packet_get_tlv_value_wstring(Packet* packet, TlvType type);
|
||||
|
@ -39,6 +39,7 @@ MetApi api_instance = {
|
||||
packet_get_tlv_value_qword,
|
||||
packet_get_tlv_meta,
|
||||
packet_get_tlv_value_uint,
|
||||
packet_get_tlv_uint,
|
||||
packet_destroy,
|
||||
packet_get_tlv_value_wstring,
|
||||
packet_get_tlv_value_reflective_loader,
|
||||
|
@ -18,7 +18,7 @@ public class stdapi_fs_ls implements Command {
|
||||
if (pathString.contains("*")) {
|
||||
String root = path.getParent();
|
||||
String match = path.getName();
|
||||
List entries = stdapi_fs_search.findFiles(root, match, false);
|
||||
List entries = stdapi_fs_search.findFiles(root, match, false, null, null);
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
String entry = entries.get(i).toString();
|
||||
if (entry.equals(".") || entry.equals("..")) {
|
||||
|
@ -20,6 +20,11 @@ public class stdapi_fs_search implements Command {
|
||||
private static final int TLV_TYPE_SEARCH_ROOT = TLVPacket.TLV_META_TYPE_STRING | 1232;
|
||||
private static final int TLV_TYPE_SEARCH_RESULTS = TLVPacket.TLV_META_TYPE_GROUP | 1233;
|
||||
|
||||
private static final int TLV_TYPE_SEARCH_MTIME = TLVPacket.TLV_META_TYPE_UINT | 1235;
|
||||
private static final int TLV_TYPE_SEARCH_M_START_DATE = TLVPacket.TLV_META_TYPE_UINT | 1236;
|
||||
private static final int TLV_TYPE_SEARCH_M_END_DATE = TLVPacket.TLV_META_TYPE_UINT | 1237;
|
||||
|
||||
|
||||
/**
|
||||
* Simple glob implementation.
|
||||
*/
|
||||
@ -56,7 +61,8 @@ public class stdapi_fs_search implements Command {
|
||||
}
|
||||
}
|
||||
|
||||
public static List findFiles(String path, String mask, boolean recurse) {
|
||||
|
||||
public static List findFiles(String path, String mask, boolean recurse, Integer startDate, Integer endDate) {
|
||||
try {
|
||||
File pathfile = Loader.expand(path);
|
||||
if (!pathfile.exists() || !pathfile.isDirectory()) {
|
||||
@ -76,10 +82,16 @@ public class stdapi_fs_search implements Command {
|
||||
if (recurse && file.isDirectory()
|
||||
// don't follow links to avoid infinite recursion
|
||||
&& file.getCanonicalPath().equals(file.getAbsolutePath())) {
|
||||
glob.addAll(findFiles(file.getAbsolutePath(), mask, true));
|
||||
glob.addAll(findFiles(file.getAbsolutePath(), mask, true, startDate, endDate));
|
||||
}
|
||||
// Match file mask
|
||||
if (matches(file.getName(), mask)) {
|
||||
if (startDate != null && (startDate.longValue() > (file.lastModified()/1000))) {
|
||||
continue;
|
||||
}
|
||||
if (endDate != null && (endDate.longValue() < (file.lastModified()/1000))) {
|
||||
continue;
|
||||
}
|
||||
glob.add(path + "/" + file.getName());
|
||||
}
|
||||
}
|
||||
@ -94,13 +106,18 @@ public class stdapi_fs_search implements Command {
|
||||
String root = request.getStringValue(TLV_TYPE_SEARCH_ROOT, ".");
|
||||
String glob = request.getStringValue(TLV_TYPE_SEARCH_GLOB);
|
||||
boolean recurse = request.getBooleanValue(TLV_TYPE_SEARCH_RECURSE);
|
||||
List files = findFiles(root, glob, recurse);
|
||||
Integer startDate = (Integer) request.getValue(TLV_TYPE_SEARCH_M_START_DATE, null);
|
||||
Integer endDate = (Integer) request.getValue(TLV_TYPE_SEARCH_M_END_DATE, null);
|
||||
|
||||
List files = findFiles(root, glob, recurse, startDate, endDate);
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
File f = new File((String) files.get(i));
|
||||
long mtime = f.lastModified()/1000;
|
||||
TLVPacket file_tlvs = new TLVPacket();
|
||||
file_tlvs.add(TLVType.TLV_TYPE_FILE_PATH, f.getParentFile().getPath());
|
||||
file_tlvs.add(TLVType.TLV_TYPE_FILE_NAME, f.getName());
|
||||
file_tlvs.add(TLV_TYPE_FILE_SIZE, (int) f.length());
|
||||
file_tlvs.add(TLV_TYPE_SEARCH_MTIME, (int) mtime);
|
||||
response.addOverflow(TLV_TYPE_SEARCH_RESULTS, file_tlvs);
|
||||
}
|
||||
return ERROR_SUCCESS;
|
||||
|
@ -27,7 +27,9 @@ define("TLV_TYPE_SEARCH_RECURSE", TLV_META_TYPE_BOOL | 1230);
|
||||
define("TLV_TYPE_SEARCH_GLOB", TLV_META_TYPE_STRING | 1231);
|
||||
define("TLV_TYPE_SEARCH_ROOT", TLV_META_TYPE_STRING | 1232);
|
||||
define("TLV_TYPE_SEARCH_RESULTS", TLV_META_TYPE_GROUP | 1233);
|
||||
|
||||
define("TLV_TYPE_SEARCH_MTIME", TLV_META_TYPE_UINT | 1235);
|
||||
define("TLV_TYPE_SEARCH_M_START_DATE", TLV_META_TYPE_UINT | 1236);
|
||||
define("TLV_TYPE_SEARCH_M_END_DATE", TLV_META_TYPE_UINT | 1237);
|
||||
define("TLV_TYPE_FILE_MODE_T", TLV_META_TYPE_UINT | 1234);
|
||||
|
||||
##
|
||||
@ -340,7 +342,7 @@ define('GLOB_RECURSE',2048);
|
||||
* GLOB_NODOTS, GLOB_RECURSE
|
||||
*/
|
||||
if (!function_exists('safe_glob')) {
|
||||
function safe_glob($pattern, $flags=0) {
|
||||
function safe_glob($pattern, $flags=0, $start_date=null, $end_date=null) {
|
||||
$split=explode('/',str_replace('\\','/',$pattern));
|
||||
$mask=array_pop($split);
|
||||
$path=implode('/',$split);
|
||||
@ -356,14 +358,21 @@ function safe_glob($pattern, $flags=0) {
|
||||
&& (!is_link($path."/".$file))
|
||||
)
|
||||
) {
|
||||
$glob = array_merge($glob, array_prepend(safe_glob($path.'/'.$file.'/'.$mask, $flags),
|
||||
($flags&GLOB_PATH?'':$file.'/')));
|
||||
$newglob = safe_glob($path.'/'.$file.'/'.$mask, $flags, $start_date, $end_date);
|
||||
if ($newglob !== false) {
|
||||
$glob = array_merge($glob, array_prepend($newglob,
|
||||
($flags&GLOB_PATH?'':$file.'/')));
|
||||
}
|
||||
}
|
||||
// Match file mask
|
||||
if (fnmatch($mask,$file)) {
|
||||
$tmp_f_stat = stat($path.'/'.$file);
|
||||
$mtime = $tmp_f_stat['mtime'];
|
||||
if ( ( (!($flags&GLOB_ONLYDIR)) || is_dir("$path/$file") )
|
||||
&& ( (!($flags&GLOB_NODIR)) || (!is_dir($path.'/'.$file)) )
|
||||
&& ( (!($flags&GLOB_NODOTS)) || (!in_array($file,array('.','..'))) ) )
|
||||
&& ( (!($flags&GLOB_NODOTS)) || (!in_array($file,array('.','..'))) )
|
||||
&& ( ($start_date === null) || ($start_date <= $mtime))
|
||||
&& ( ($end_date === null) || ($end_date >= $mtime)) )
|
||||
$glob[] = ($flags&GLOB_PATH?$path.'/':'') . $file . ($flags&GLOB_MARK?'/':'');
|
||||
}
|
||||
}
|
||||
@ -682,27 +691,38 @@ function stdapi_fs_search($req, &$pkt) {
|
||||
$glob = canonicalize_path($glob_tlv['value']);
|
||||
$recurse_tlv = packet_get_tlv($req, TLV_TYPE_SEARCH_RECURSE);
|
||||
$recurse = $recurse_tlv['value'];
|
||||
$start_date_tlv = packet_get_tlv($req, TLV_TYPE_SEARCH_M_START_DATE);
|
||||
$start_date = null;
|
||||
if ($start_date_tlv) {
|
||||
$start_date = $start_date_tlv['value'];
|
||||
}
|
||||
$end_date_tlv = packet_get_tlv($req, TLV_TYPE_SEARCH_M_END_DATE);
|
||||
$end_date = null;
|
||||
if ($end_date_tlv) {
|
||||
$end_date = $end_date_tlv['value'];
|
||||
}
|
||||
|
||||
if (!$root) {
|
||||
$root = '.';
|
||||
}
|
||||
|
||||
my_print("glob: $glob, root: $root, recurse: $recurse");
|
||||
$flags = GLOB_PATH;
|
||||
$flags = GLOB_PATH | GLOB_NODOTS;
|
||||
if ($recurse) {
|
||||
$flags |= GLOB_RECURSE;
|
||||
}
|
||||
$files = safe_glob($root ."/". $glob, $flags);
|
||||
$files = safe_glob($root ."/". $glob, $flags, $start_date, $end_date);
|
||||
if ($files and is_array($files)) {
|
||||
dump_array($files);
|
||||
foreach ($files as $file) {
|
||||
$file_tlvs = "";
|
||||
$s = stat($file);
|
||||
$p = dirname($file);
|
||||
$f = basename($file);
|
||||
$p = canonicalize_path(dirname($file));
|
||||
$f = canonicalize_path(basename($file));
|
||||
$file_tlvs .= tlv_pack(create_tlv(TLV_TYPE_FILE_PATH, $p));
|
||||
$file_tlvs .= tlv_pack(create_tlv(TLV_TYPE_FILE_NAME, $f));
|
||||
$file_tlvs .= tlv_pack(create_tlv(TLV_TYPE_FILE_SIZE, $s['size']));
|
||||
$file_tlvs .= tlv_pack(create_tlv(TLV_TYPE_SEARCH_MTIME, $s['mtime']));
|
||||
packet_add_tlv($pkt, create_tlv(TLV_TYPE_SEARCH_RESULTS, $file_tlvs));
|
||||
}
|
||||
}
|
||||
|
@ -474,6 +474,9 @@ TLV_TYPE_SEARCH_ROOT = TLV_META_TYPE_STRING | 1232
|
||||
TLV_TYPE_SEARCH_RESULTS = TLV_META_TYPE_GROUP | 1233
|
||||
|
||||
TLV_TYPE_FILE_MODE_T = TLV_META_TYPE_UINT | 1234
|
||||
TLV_TYPE_SEARCH_MTIME = TLV_META_TYPE_UINT | 1235
|
||||
TLV_TYPE_SEARCH_M_START_DATE = TLV_META_TYPE_UINT | 1236
|
||||
TLV_TYPE_SEARCH_M_END_DATE = TLV_META_TYPE_UINT | 1237
|
||||
|
||||
##
|
||||
# Net
|
||||
@ -1518,20 +1521,36 @@ def stdapi_fs_search(request, response):
|
||||
search_root = unicode(search_root)
|
||||
glob = packet_get_tlv(request, TLV_TYPE_SEARCH_GLOB)['value']
|
||||
recurse = packet_get_tlv(request, TLV_TYPE_SEARCH_RECURSE)['value']
|
||||
start_date = packet_get_tlv(request,TLV_TYPE_SEARCH_M_START_DATE)
|
||||
end_date = packet_get_tlv(request,TLV_TYPE_SEARCH_M_END_DATE)
|
||||
if recurse:
|
||||
for root, dirs, files in os.walk(search_root):
|
||||
for f in filter(lambda f: fnmatch.fnmatch(f, glob), files):
|
||||
file_stat = os.stat(os.path.join(root, f))
|
||||
mtime = int(file_stat.st_mtime)
|
||||
if start_date and start_date['value'] > mtime:
|
||||
continue
|
||||
if end_date and end_date['value'] < mtime:
|
||||
continue
|
||||
file_tlv = bytes()
|
||||
file_tlv += tlv_pack(TLV_TYPE_FILE_PATH, root)
|
||||
file_tlv += tlv_pack(TLV_TYPE_FILE_NAME, f)
|
||||
file_tlv += tlv_pack(TLV_TYPE_FILE_SIZE, os.stat(os.path.join(root, f)).st_size)
|
||||
file_tlv += tlv_pack(TLV_TYPE_FILE_SIZE, file_stat.st_size)
|
||||
file_tlv += tlv_pack(TLV_TYPE_SEARCH_MTIME, mtime)
|
||||
response += tlv_pack(TLV_TYPE_SEARCH_RESULTS, file_tlv)
|
||||
else:
|
||||
for f in filter(lambda f: fnmatch.fnmatch(f, glob), os.listdir(search_root)):
|
||||
file_stat = os.stat(os.path.join(search_root, f))
|
||||
mtime = int(file_stat.st_mtime)
|
||||
if start_date and start_date['value'] > mtime:
|
||||
continue
|
||||
if end_date and end_date['value'] < mtime:
|
||||
continue
|
||||
file_tlv = bytes()
|
||||
file_tlv += tlv_pack(TLV_TYPE_FILE_PATH, search_root)
|
||||
file_tlv += tlv_pack(TLV_TYPE_FILE_NAME, f)
|
||||
file_tlv += tlv_pack(TLV_TYPE_FILE_SIZE, os.stat(os.path.join(search_root, f)).st_size)
|
||||
file_tlv += tlv_pack(TLV_TYPE_FILE_SIZE, file_stat.st_size)
|
||||
file_tlv += tlv_pack(TLV_TYPE_SEARCH_MTIME, mtime)
|
||||
response += tlv_pack(TLV_TYPE_SEARCH_RESULTS, file_tlv)
|
||||
return ERROR_SUCCESS, response
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user