예제 #1
0
파일: main.cpp 프로젝트: martok/miranda-ng
static INT_PTR nAddChangeRemoveShare(WPARAM wParam, LPARAM lParam) {
	if (!lParam)
		return 1001;

	STFileShareInfo * pclNew = (STFileShareInfo*)lParam;

	// make the server path lowercase
	char* pszPos = pclNew->pszSrvPath;
	while (*pszPos) {
		*pszPos = (char)tolower(*pszPos);
		pszPos++;
	}

	if (pclNew->lStructSize != sizeof(STFileShareInfo))
		return 1002;

	CLFileShareListAccess clCritSection;
	bool bIsDirectory = (pclNew->pszSrvPath[strlen(pclNew->pszSrvPath)-1] == '/');

	CLFileShareNode **pclPrev = &pclFirstNode;
	CLFileShareNode * pclCur = pclFirstNode;

	// insert files after directories
	if (!bIsDirectory) {
		while (pclCur && pclCur->bIsDirectory()) {
			pclPrev = &pclCur->pclNext;
			pclCur = pclCur->pclNext;
		}
	}

	while (pclCur) {
		if (_stricmp(pclCur->st.pszSrvPath, pclNew->pszSrvPath) == 0) {
			if (pclCur->bAnyUsers()) {
				// Some downloads are in progress we will try an terminate them !!
				// we try for 5 sec.
				pclCur->CloseAllTransfers();
				int nTryCount = 0;
				do {
					nTryCount++;
					if (nTryCount >= 100)
						return 1004;
					clCritSection.Unlock();
					Sleep(50);
					clCritSection.Lock();
				} while (pclCur->bAnyUsers());
			}

			if (!pclNew->pszRealPath || pclNew->pszRealPath[0] == 0) {
				// remove this one
				*pclPrev = pclCur->pclNext;
				delete pclCur;
			} else {
				// update info !!
				if (! pclCur->bSetInfo(pclNew))
					return 1003;
			}
			return !bWriteConfigurationFile();
		}
		pclPrev = &pclCur->pclNext;
		pclCur = pclCur->pclNext;
	}

	// Node was not found we will add a new one.
	CLFileShareNode* pclNewNode = new CLFileShareNode(pclNew);
	pclNewNode->pclNext = *pclPrev;
	*pclPrev = pclNewNode;

	/* Add by Sérgio Vieira Rolanski */
	if (pclNew->dwOptions & OPT_SEND_LINK)
		SendLinkToUser(wParam, pclNew->pszSrvPath);

	return !bWriteConfigurationFile();
}
예제 #2
0
bool CLHttpUser::bProcessGetRequest(char * pszRequest, bool bIsGetCommand) {
	//LogEvent("Request", pszRequest);

	int nUriLength = nUnescapedURI(pszRequest);
	if (nUriLength <= 0)
		return false;

	CLFileShareListAccess clCritSection;

	if (bShutdownInProgress)
		return false;

	static char szTempfile[MAX_PATH+1];
	szTempfile[0] = '\0';

	if (!bReadGetParameters(pszRequest)) {
		SendError(400, "Bad Request");
		return false;
	}

	DWORD dwRemoteIP = ntohl(stAddr.S_un.S_addr);
	for (CLFileShareNode * pclCur = pclFirstNode; pclCur ; pclCur = pclCur->pclNext) {
		if ((pclCur->st.dwAllowedIP ^ dwRemoteIP) & pclCur->st.dwAllowedMask)
			continue; // Not an allowed IP address

		if (!pclCur->bIsDirectory() && pclCur->nGetSrvPathLen() != nUriLength)
			continue; // not the right length, quickly move on to the next.

		if (pclCur->bIsDirectory() ?
		    (strncmp(pclCur->st.pszSrvPath, pszRequest, pclCur->nGetSrvPathLen() - 1) == 0) :
		    (strncmp(pclCur->st.pszSrvPath, pszRequest, pclCur->nGetSrvPathLen()) == 0)) {
			/*OutputDebugString( "Request for file OK : ");
			OutputDebugString( pclCur->st.pszSrvPath );
			OutputDebugString( "\n" );*/

			static char szSrvPath[MAX_PATH+1];
			static char szRealPath[MAX_PATH+1];
			char* pszSrvPath  = pclCur->st.pszSrvPath;
			char* pszRealPath = pclCur->st.pszRealPath;

			if (pclCur->bIsDirectory()) {
				strcpy(szRealPath, pclCur->st.pszRealPath);
				strcpy(szSrvPath, pclCur->st.pszSrvPath);
				pszRealPath = szRealPath;
				pszSrvPath = szSrvPath;

				if (nUriLength > MAX_PATH)
					nUriLength = MAX_PATH;

				pszRequest[nUriLength] = '\0';

				if (pclCur->nGetSrvPathLen() - nUriLength == 1) {
					SendRedir(302, "Found", "The document has moved", pszRequest);
					return false;
				} else {
					strmcat(pszRealPath, &pszRequest[pclCur->nGetSrvPathLen()], MAX_PATH);
					strmcat(pszSrvPath,  &pszRequest[pclCur->nGetSrvPathLen()], MAX_PATH);
				}
				pszRequest[nUriLength] = ' ';

				// hacker protection - should be removed by the browser
				if (strstr(pszRealPath, "..")) {
					SendError(404, "Not Found", "The requested URL was not found on this server.");
					return false;
				}

				char* pszTmp = pszRealPath;
				while (pszTmp = strchr(pszTmp, '/'))
					* pszTmp = '\\';

				hFile = CreateFile(pszRealPath, GENERIC_READ ,
				    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

				if (hFile == INVALID_HANDLE_VALUE) {
					if (pszSrvPath[strlen(pszSrvPath)-1] != '/') {
						strmcat(pszRealPath, "\\", MAX_PATH);
						strmcat(pszSrvPath,  "/", MAX_PATH);
					}

					// a directory with index.htm
					strmcat(szRealPath, "index.htm", MAX_PATH);

					hFile = CreateFile(pszRealPath, GENERIC_READ ,
					    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_HIDDEN, NULL);

					if (hFile == INVALID_HANDLE_VALUE) {
						// a directory with index.html
						strmcat(szRealPath, "l", MAX_PATH);

						hFile = CreateFile(pszRealPath, GENERIC_READ ,
						    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_HIDDEN, NULL);

						if (hFile == INVALID_HANDLE_VALUE) {
							// generate directory index in temporary file
							if (*szTempfile == '\0') {
								GetTempPath(MAX_PATH, szTempfile);
								strmcat(szTempfile, "\\HttpServerTemp", MAX_PATH);
								strmcat(szTempfile, pszSrvPath, MAX_PATH);
								char* pszTmp = szTempfile;
								while (pszTmp = strchr(pszTmp, '/'))
									* pszTmp = '~';
							}
							pszRealPath[strlen(pszRealPath) - 10] = '\0';

							// detecting browser function removed
							// every browser should support it by now
							bool BrowserSupportsXML = true; 
							  //(apszParam[eUserAgent] != NULL) &&
							  //  (strstr(apszParam[eUserAgent], "Firefox") ||
							  //   (strstr(apszParam[eUserAgent], "MSIE") && !strstr(apszParam[eUserAgent], "Opera")));

							if ((indexCreationMode == INDEX_CREATION_XML ||
							     (indexCreationMode == INDEX_CREATION_DETECT && BrowserSupportsXML)) &&
							    bCreateIndexXML(pszRealPath, szTempfile, pszSrvPath, dwRemoteIP)) {
								hFile = CreateFile(szTempfile, GENERIC_READ ,
								    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

								strcpy(szRealPath, "a.xml"); // restore .xml for mime type
							} else if ((indexCreationMode == INDEX_CREATION_HTML ||
							    indexCreationMode == INDEX_CREATION_DETECT) &&
							    bCreateIndexHTML(pszRealPath, szTempfile, pszSrvPath, dwRemoteIP)) {
								hFile = CreateFile(szTempfile, GENERIC_READ,
								    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

								strcpy(szRealPath, "a.html"); // restore .html for mime type
							} else {
								continue;
							}
						} else {
							strmcat(pszSrvPath, "index.html", MAX_PATH);
							szTempfile[0] = '\0';
						}
					} else {
						strmcat(pszSrvPath, "index.htm", MAX_PATH);
						szTempfile[0] = '\0';
					}
				} else {
					szTempfile[0] = '\0';
				}
			} else {
				hFile = CreateFile(pszRealPath, GENERIC_READ ,
				    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

				if (hFile == INVALID_HANDLE_VALUE) {
					SendError(404, "Not Found", "HTTP server failed to open local file");
					return false;
				}
			}

			strcpy(this->szCurrentDLSrvPath, pszSrvPath);

			DWORD nDataSize = GetFileSize(hFile, NULL);
			dwTotalSize = nDataSize;

			FILETIME stFileTime;
			GetFileTime(hFile, NULL, NULL, &stFileTime);

			char szCurTime[ 100 ];
			time_t ltime;
			time(&ltime);
			strftime(szCurTime, sizeof(szCurTime), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&ltime));

			char szFileTime[ 100 ];
			FileTimeToUnixTime(&stFileTime, &ltime);
			strftime(szFileTime, sizeof(szFileTime), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&ltime));
			
			if (apszParam[eIfModifiedSince] && strcmp(apszParam[eIfModifiedSince], szFileTime) == 0) {
				SendError(304, "Not Modified" );
				return true;
			}
	
			// we found match send file !!
			if (bIsGetCommand) {
				if (! pclCur->bAddUser(this)) {
					SendError(403, "Forbidden", "Access has been denied because there are too many connections");
					return false;
				}
			}

			if (*(ULONG*)(&stAddr) != 0x0100007F && // do not show popup of 127.0.0.1
			    strstr(pszRealPath, "\\@") == NULL) { // and of shares which start with an @
				ShowPopupWindow(inet_ntoa(stAddr), pszSrvPath);
			}

			clCritSection.Unlock();

			DWORD dwFileStart = 0;
			DWORD dwDataToSend = nDataSize;

			char szETag[ 50 ];
			{
				int nETagLen = mir_snprintf(szETag, SIZEOF(szETag), "\"%x-%x-%x\"",
				    nDataSize, stFileTime.dwHighDateTime, stFileTime.dwLowDateTime);

				if (!apszParam[eIfRange] || (strncmp(szETag, apszParam[eIfRange], nETagLen) == 0)) {
					char * pszRange = apszParam[eRange];
					if (pszRange) {
						if (strncmp(pszRange, "bytes=", 6) == 0) {
							pszRange += 6;
							// Do resume !!!
							char *pszEnd;
							if (pszRange[0] == '-') {
								// its a suffix-byte-range-spec
								DWORD dwLen = strtol(pszRange + 1, &pszEnd, 10);
								if (dwLen < nDataSize)
									dwFileStart = nDataSize - dwLen;
							} else {
								DWORD dwLen = strtol(pszRange, &pszEnd, 10);
								if (*pszEnd == '-' && dwLen < nDataSize) {
									dwFileStart = dwLen;
									pszRange = pszEnd + 1;
									if (*pszRange != 0) {
										dwLen = strtol(pszEnd + 1, &pszEnd, 10);
										if (dwLen > dwFileStart)
											dwDataToSend = (dwLen + 1) - dwFileStart;
										else
											dwFileStart = 0;
									}
								} else {
									SendError(400, "Bad Request");
									return false;
								}
							}
						}
					}
				}
			}

			if (dwFileStart >= nDataSize)
				dwFileStart = 0;

			if (dwFileStart + dwDataToSend >= nDataSize)
				dwDataToSend = nDataSize - dwFileStart;


			DWORD dwBytesToWrite = 0;
			// To optimize send speed it it ideal to always send larges size packages
			// But this size depended on network media but on Ethernet it is 1518 bytes.
			// Ethernet, IP and TCP headers use some of this space and leaves 1460 bytes
			// for data transfer.
			// We will use a multiply of this to always send optimal sized packages.
			char szBuf[1460 * 4];

			if (dwFileStart > 0 || dwDataToSend != nDataSize) {
				if (SetFilePointer(hFile, dwFileStart, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
					SendError(416, "Requested Range Not Satisfiable");
					return true;
				}

				const char szHttpPartial[] = "HTTP/1.1 206 Partial Content\r\n"
				    "Connection: Keep-Alive\r\n"
				    "Date: %s\r\n"
				    "Server: MirandaWeb/%s\r\n"
				    "Accept-Ranges: bytes\r\n"
				    "ETag: %s\r\n"
				    "Content-Length: %d\r\n"
				    "Content-Type: %s\r\n"
				    "Content-Range: bytes %d-%d/%d\r\n"
				    "Last-Modified: %s\r\n"
				    "\r\n";

				dwBytesToWrite = mir_snprintf(szBuf, SIZEOF(szBuf), szHttpPartial,
				    szCurTime,
				    PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
				    szETag,
				    dwDataToSend,
				    pszGetMimeType(pszRealPath),
				    dwFileStart,
				    (dwFileStart + dwDataToSend - 1),
				    nDataSize,
				    szFileTime);
			} else {
				const char szHttpOk[] = "HTTP/1.1 200 OK\r\n"
				    "Connection: Keep-Alive\r\n"
				    "Date: %s\r\n"
				    "Server: MirandaWeb/%s\r\n"
				    "Accept-Ranges: bytes\r\n"
				    "ETag: %s\r\n"
				    "Content-Length: %d\r\n"
				    "Content-Type: %s\r\n"
				    "Last-Modified: %s\r\n"
				    "\r\n";

				dwBytesToWrite = mir_snprintf(szBuf, SIZEOF(szBuf), szHttpOk,
				    szCurTime,
				    PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
				    szETag,
				    nDataSize,
				    pszGetMimeType(pszRealPath),
				    szFileTime);
			}

			Netlib_Send(hConnection, szBuf, dwBytesToWrite, 0);

			if (bIsGetCommand) {
				static int nThreadCount = 0;
				nThreadCount++;

				DWORD dwLastUpdate = GetTickCount();
				DWORD dwLastCurrentDL = 0;
				/*

				dwLastDLSTickCount = dwCurTick;
				dwCurrentDL;*/

				//DWORD dwMaxSpeed = 8192;// Byte pr Sek
				//DWORD dwMaxSpeed = 20000;// Byte pr Sek
				//DWORD dwMaxSpeed = 163840;// Byte pr Sek
				//DWORD dwMaxSpeed = 4096;// Byte pr Sek

				DWORD dwLastResetTime = GetTickCount();
				int nMaxBytesToSend = nMaxUploadSpeed / nThreadCount;

				for (;;) {
					{
						DWORD dwCurTick = GetTickCount();
						if (dwCurTick - dwLastUpdate >= 1000) {
							dwSpeed = ((dwCurrentDL - dwLastCurrentDL) * 1000) / (dwCurTick - dwLastUpdate);
							dwLastUpdate = dwCurTick;
							dwLastCurrentDL = dwCurrentDL;
						}
					}

					if (nMaxUploadSpeed == 0) {
						Sleep(1000);
						continue;
					}

					bool bSpeedLimit = (nMaxUploadSpeed >= 0) && (bIsOnline || !bLimitOnlyWhenOnline);

					DWORD dwCurOpr = sizeof(szBuf);
					if (bSpeedLimit)
						dwCurOpr = min(nMaxBytesToSend, sizeof(szBuf));

					if (!ReadFile(hFile, szBuf, dwCurOpr, &dwBytesToWrite, NULL))
						break;

					if (dwBytesToWrite <= 0)
						break;

					if (dwCurrentDL + dwBytesToWrite > dwDataToSend)
						dwBytesToWrite = dwDataToSend - dwCurrentDL;

					if (bSpeedLimit)
						nMaxBytesToSend -= dwBytesToWrite;

					DWORD dwSend = Netlib_Send(hConnection, szBuf, dwBytesToWrite, MSG_NODUMP);
					if (dwSend == SOCKET_ERROR)
						break;
					dwCurrentDL += dwSend;
					if (dwSend != dwBytesToWrite)
						break;

					if (dwCurrentDL >= dwDataToSend)
						break;

					if (bSpeedLimit && nMaxBytesToSend <= 0) {
						// we have reached the limmit
						DWORD dwTimeUsed = GetTickCount() - dwLastResetTime;
						if (dwTimeUsed < 1000)
							Sleep(1000 - dwTimeUsed);
						dwLastResetTime = GetTickCount();
						nMaxBytesToSend = nMaxUploadSpeed / nThreadCount;
					}
				}

				// file is always closed in destructor 
				if (szTempfile[0] != '\0') {
					// and here - since it is a temporary index which as to be deleted
					CloseHandle(hFile);
					hFile = INVALID_HANDLE_VALUE;

					DeleteFile(szTempfile);
				}
				clCritSection.Lock();
				nThreadCount--;

				bool bNeedToWriteConfig = false;

				if (dwCurrentDL == nDataSize) {
					if (pclCur->st.nMaxDownloads > 0) {
						pclCur->st.nMaxDownloads--;
						bNeedToWriteConfig = true;
					}
				}

				pclCur->bRemoveUser(this);

				// nMaxDownloads can have been decreesed by another thread.
				// Therefore we test it even if we did'en decreese it
				if (pclCur->st.nMaxDownloads == 0 && !pclCur->bAnyUsers()) {
					CLFileShareNode **pclPrev = &pclFirstNode;
					for (CLFileShareNode * pcl = pclFirstNode ; pcl ; pcl = pcl->pclNext) {
						if (pcl == pclCur) {
							*pclPrev = pclCur->pclNext;
							ShowPopupWindow(Translate("Share removed"), pclCur->st.pszSrvPath, RGB(255, 189, 189));
							delete pclCur;
							bNeedToWriteConfig = true;
							break;
						}
						pclPrev = &pcl->pclNext;
					}
				}

				if (bNeedToWriteConfig) {
					bWriteConfigurationFile();
				}
			}

			return true;
		}
	}


#ifdef _DEBUG
	OutputDebugString("###########   Request Failed   ###########\n");
	OutputDebugString(pszRequest);
#endif

	pszRequest[nUriLength] = 0;

	if ((nUriLength != 12 || strncmp(pszRequest, "/favicon.ico", nUriLength)) &&  // do not show popup of favicon.ico
	    *(ULONG*)(&stAddr) != 0x0100007F) { // do not show popup of 127.0.0.1
		ShowPopupWindow(inet_ntoa(stAddr), pszRequest, RGB(255, 189, 189));
	}

	SendError(404, "Not Found", "The requested URL was not found on this server.");

	return false;
}
예제 #3
0
bool bCreateIndexXML(const char * pszRealPath, const char * pszIndexPath, const char * pszSrvPath, DWORD dwRemoteIP)
{
	char szMask[MAX_PATH+1];
	mir_snprintf(szMask, _countof(szMask), "%s*", pszRealPath);

	WIN32_FIND_DATAA fdFindFileData;
	HANDLE hFind = FindFirstFile(szMask, &fdFindFileData);
	if (hFind == INVALID_HANDLE_VALUE)
		return FALSE;

	HANDLE hFile = CreateFile(pszIndexPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, 
		OPEN_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);

	if (hFile == INVALID_HANDLE_VALUE) {
		FindClose(hFind);
		return FALSE;
	}

	const int BUFFER_SIZE = 1000;
	char  szBuffer[BUFFER_SIZE+1];
	char* pszBuffer = szBuffer;
	char  szFileName[MAX_PATH+1] = "";
	char* pszExt;
	DWORD dwBytesWritten = 0;

	// Generate Dirname
	strncpy(szBuffer, pszSrvPath, BUFFER_SIZE);
	char* pszTemp = strrchr(szBuffer, '/');
	if (pszTemp)
		*pszTemp = '\0';

	pszTemp = strrchr(szBuffer, '/');
	if (pszTemp)
		strncpy(szFileName, pszTemp + 1, MAX_PATH);

	// Write Header
	WriteFile(hFile, szXmlHeader1, sizeof(szXmlHeader1) - 1, &dwBytesWritten, NULL);

	// check if a index.xsl exists in the same directory otherwise use the global
	mir_snprintf(szMask, _countof(szMask), "%s%s", pszRealPath, "index.xsl");

	HANDLE hFileExists = CreateFile(szMask, GENERIC_READ, 
		FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 
		FILE_ATTRIBUTE_NORMAL, NULL);

	if (hFileExists == INVALID_HANDLE_VALUE) {
		strncpy(szBuffer, "/index.xsl", BUFFER_SIZE);
	}
	else {
		CloseHandle(hFileExists);
		strncpy(szBuffer, "index.xsl", BUFFER_SIZE);
	}

	WriteFile(hFile, szBuffer, (DWORD)mir_strlen(szBuffer), &dwBytesWritten, NULL);

	WriteFile(hFile, szXmlHeader2, sizeof(szXmlHeader2) - 1, &dwBytesWritten, NULL);

	// Write dirname
	ReplaceSign(szFileName, MAX_PATH, '&', "&amp;");
	pszBuffer += mir_snprintf(pszBuffer, BUFFER_SIZE - (pszBuffer - szBuffer), 
		"  <dirname>%s</dirname>\r\n", szFileName);
	WriteFile(hFile, szBuffer, pszBuffer - szBuffer, &dwBytesWritten, NULL);

	// Find files and directories
	do {
		if (mir_strcmp(fdFindFileData.cFileName, ".") &&
		    strncmp(fdFindFileData.cFileName, "@", 1) &&
		    (mir_strcmp(fdFindFileData.cFileName, "..") || mir_strcmp(pszSrvPath, "/"))) { // hide .. in root
			pszBuffer = szBuffer;

			mir_strcpy(szFileName, fdFindFileData.cFileName);
			ReplaceSign(szFileName, MAX_PATH, '&', "&amp;");

			if (fdFindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
				pszBuffer += mir_snprintf(pszBuffer, BUFFER_SIZE - (pszBuffer - szBuffer), 
					"  <item name=\"%s\" isdir=\"true\"/>\r\n", szFileName);
			} else {
				pszExt = strrchr(szFileName, '.');

				if (pszExt != NULL) {
					*pszExt = '\0';
					pszExt++;
				}

				pszBuffer += mir_snprintf(pszBuffer, BUFFER_SIZE - (pszBuffer - szBuffer), 
					"  <item name=\"%s\" ext=\"%s\" size=\"%i\" ",
				  szFileName, (pszExt == NULL) ? "" : pszExt, fdFindFileData.nFileSizeLow);

				SYSTEMTIME systemTime;
				FileTimeToSystemTime(&fdFindFileData.ftCreationTime, &systemTime);
				pszBuffer += mir_snprintf(pszBuffer, BUFFER_SIZE - (pszBuffer - szBuffer), 
					"created=\"%i/%02i/%02i %i:%02i:%02i\" ", 
					systemTime.wYear, systemTime.wMonth, systemTime.wDay, systemTime.wHour,
					systemTime.wMinute, systemTime.wSecond);

				FileTimeToSystemTime(&fdFindFileData.ftLastWriteTime, &systemTime);
				pszBuffer += mir_snprintf(pszBuffer, BUFFER_SIZE - (pszBuffer - szBuffer), 
					"modified=\"%i/%02i/%02i %i:%02i:%02i\" ", 
					systemTime.wYear, systemTime.wMonth, systemTime.wDay, systemTime.wHour,
					systemTime.wMinute, systemTime.wSecond);

				pszBuffer += mir_snprintf(pszBuffer, BUFFER_SIZE - (pszBuffer - szBuffer), 
					"/>\r\n");
			}

			if (!WriteFile(hFile, szBuffer, pszBuffer - szBuffer, &dwBytesWritten, NULL))
				break;
		}

	} while (FindNextFile(hFind, &fdFindFileData));

	if (hFind != 0)
		FindClose(hFind);

	// Add other shared files & directories
	for (CLFileShareNode * pclCur = pclFirstNode; pclCur ; pclCur = pclCur->pclNext) {
		if (!((pclCur->st.dwAllowedIP ^ dwRemoteIP) & pclCur->st.dwAllowedMask) &&  // hide inaccessible shares
		    (size_t)(pclCur->nGetSrvPathLen()) > mir_strlen(pszSrvPath) &&
		    !strstr(pclCur->st.pszRealPath, "\\@") &&
		    !strncmp(pclCur->st.pszSrvPath, pszSrvPath, mir_strlen(pszSrvPath))) {
			pszBuffer = szBuffer;

			mir_strcpy(szFileName, &pclCur->st.pszSrvPath[mir_strlen(pszSrvPath)]);
			ReplaceSign(szFileName, MAX_PATH, '&', "&amp;");

			if (pclCur->bIsDirectory()) {
				szFileName[mir_strlen(szFileName)-1] = '\0';
				if (!strchr(szFileName, '/')) { // only one level deeper
					pszBuffer += mir_snprintf(pszBuffer, BUFFER_SIZE - (pszBuffer - szBuffer), 
						"  <item name=\"%s\" isdir=\"true\"/>\r\n", szFileName);

					if (!WriteFile(hFile, szBuffer, pszBuffer - szBuffer, &dwBytesWritten, NULL))
						break;
				}
			} else {
				if (!strchr(szFileName, '/') &&   // only one level deeper
				    strncmp(pszRealPath, pclCur->st.pszRealPath, mir_strlen(pszRealPath))) { // no duplicates
					pszExt = strrchr(szFileName, '.');

					if (pszExt != NULL) {
						*pszExt = '\0';
						pszExt++;
					}

					DWORD dwFileSize = 0;
					FILETIME ftFileCreateTime;
					FILETIME ftFileAccessTime;
					FILETIME ftFileModifyTime;
					HANDLE hFileS = CreateFile(pclCur->st.pszRealPath, GENERIC_READ, 
						FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
					if (hFileS != INVALID_HANDLE_VALUE) {
						dwFileSize = GetFileSize(hFileS, NULL);
						GetFileTime(hFileS, &ftFileCreateTime, &ftFileAccessTime, &ftFileModifyTime);
						CloseHandle(hFileS);
					}

					pszBuffer += mir_snprintf(pszBuffer, BUFFER_SIZE - (pszBuffer - szBuffer), 
						"  <item name=\"%s\" ext=\"%s\" size=\"%i\" ",
						szFileName, (pszExt == NULL) ? "" : pszExt, dwFileSize);

					SYSTEMTIME systemTime;
					FileTimeToSystemTime(&ftFileCreateTime, &systemTime);
					pszBuffer += mir_snprintf(pszBuffer, BUFFER_SIZE - (pszBuffer - szBuffer), 
						"created=\"%i/%02i/%02i %i:%02i:%02i\" ", 
						systemTime.wYear, systemTime.wMonth, systemTime.wDay, systemTime.wHour,
						systemTime.wMinute, systemTime.wSecond);

					FileTimeToSystemTime(&ftFileModifyTime, &systemTime);
					pszBuffer += mir_snprintf(pszBuffer, BUFFER_SIZE - (pszBuffer - szBuffer), 
						"modified=\"%i/%02i/%02i %i:%02i:%02i\" ", 
						systemTime.wYear, systemTime.wMonth, systemTime.wDay, systemTime.wHour,
						systemTime.wMinute, systemTime.wSecond);

					pszBuffer += mir_snprintf(pszBuffer, BUFFER_SIZE - (pszBuffer - szBuffer), 
						"/>\r\n");

					if (!WriteFile(hFile, szBuffer, pszBuffer - szBuffer, &dwBytesWritten, NULL))
						break;
				}
			}
		}
	}

	WriteFile(hFile, szXmlTail, sizeof(szXmlTail) - 1, &dwBytesWritten, NULL);

	SetEndOfFile(hFile);
	CloseHandle(hFile);

	return TRUE;
}