Recursive FTP directory exploration using Wininet
Hello, I have a problem trying to count the files of a directory and its subdirectories using a recursive function; I have done a separated test from my main program to see why I did'nt work and try to find solutions, but I'm not able to find them. That's what I have:
Code:
//headers and some globals
#include <wininet.h>
#include <windows.h>
static int padding=-1;
Code:
void printf_error()
{
unsigned long dw,dq;
char bff[512];
LPVOID lpMsgBuf;
if(GetLastError()==ERROR_INTERNET_EXTENDED_ERROR)
{
dq=512;
InternetGetLastResponseInfo(&dw,bff,&dq);
printf("(IGLRI)%s\n",bff);
}
else
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR)&lpMsgBuf,0,NULL);
printf("(GLE)(%ld)%s\n",GetLastError(),lpMsgBuf);
LocalFree(lpMsgBuf);
}
}
void backslash(char *bff)
{
if(bff[strlen(bff)-1]!='/')
{
strcat(bff,"/");
}
}
void printf_pad()
{
int q;
for(q=0;q<padding;q++)
{
printf(" ");
}
}
int conta_arxius(HINTERNET hCon,char *dir_treball)
{
//conta_arxius=cout files
//dir_treball=working directory, is the current selected dir
int inc,ctd=0;
HINTERNET hf=NULL;
WIN32_FIND_DATA fd;
char bff[2048];
padding++;
if(!(hf=FtpFindFirstFile(hCon,NULL,&fd,0,0)))
{
ctd=-1;
printf("Error FindFirstFile: ");
printf_error();
goto finalitza;
}
do
{
if(fd.dwFileAttributes==FILE_ATTRIBUTE_DIRECTORY)
{
sprintf(bff,"%s",dir_treball);
backslash(bff);
sprintf(bff,fd.cFileName);
printf_pad();
printf("Select %s\n",bff);
if(!FtpSetCurrentDirectory(hCon,bff))
{
ctd=-1;
printf("Error SetCurrentDirectori: ");
printf_error();
goto finalitza;
}
printf("Count %s\n",bff);
if((inc=conta_arxius(hCon,dir_treball))<0)
{
ctd=-1;
printf_error();
goto finalitza;
}
printf("Return to %s\n",dir_treball);
if(!FtpSetCurrentDirectory(hCon,dir_treball))
{
ctd=-1;
printf("Error SetCurrentDirectori: ");
printf_error();
goto finalitza;
}
ctd+=inc;
}
else
{
printf_pad();
printf("%s\n",fd.cFileName);
ctd++;
}
} while(InternetFindNextFile(hf,&fd));
InternetCloseHandle(hf);
hf=NULL;
finalitza:
if(hf) {InternetCloseHandle(hf);}
padding--;
return ctd;
}
The start call to the function is done by
Code:
ctd=conta_arxius(hCon,folder_to_explore);
where 'folder_to_explore' is the current directory on the 'hCon' FTP instance that I use to work.
If I omit the recursive count (that is, if I comment the line to the recursive call to the function), the function works (obviously only counting the files for the first directory). But when I make the first recursive call, the function 'FtpFindFirstFile' fails and I get an error through 'GetLastError()' that's 317 (and I don't get any kind of error through 'InternetGetLastResponseInfo'). But when I use this error code to display a text information it only shows 'null'. That error code is ERROR_MR_MID_NOT_FOUND that means that the system isn't able to find any error code for the error.
Does someone had the same problem? Or maybe I'm doing something wrong on the woking-idea of my code? I'm totally lost.
Thank's in advance
Niara
This might make you happy... :)
This is a little patcher program that I made. Have fun with it. It will connect to the ftp server and recursively go through each directory and download files if the ones on the server are newer than the ones located on the clients hard drive.
Feel free to modify any part and use it as you wish.. Good Luck
Code:
#include "stdafx.h"
#include <windows.h>
#include <wininet.h>
#include <iostream>
#include <list>
#include <string>
#include <stdlib.h>
#include <fstream>
#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "user32.lib")
using namespace std;
void StartClient();
void GetFiles(HINTERNET &hConnnect);
void ListDirectory(HINTERNET &hConnnect);
bool FindFile(HINTERNET &hConnect, HINTERNET &hFind, LPCWSTR temp, WIN32_FIND_DATA &SdirInfo);
wchar_t relative[MAX_PATH];
size_t relativesize;
int _tmain(int argc, _TCHAR* argv[])
{
/*
if(RunDirectx){
STARTUPINFO sj;
PROCESS_INFORMATION pj;
memset(&sj, 0, sizeof(sj));
sj.cb = sizeof(sj);
ZeroMemory( &pj, sizeof(pj) );
// Start the child process p1.exe. Make sure p1.exe is in the
// same folder as current application. Otherwise write the full path in first argument.
if(!CreateProcess(L".\\directx\\DXSETUP.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &sj, &pj)){
printf( "CreateProcess failed (%d)\n", GetLastError() );
}
// Wait until child processes exit.
WaitForSingleObject( pj.hProcess, INFINITE );
// Close process and thread handles.
CloseHandle( pj.hProcess );
CloseHandle( pj.hThread );
}
*/
HINTERNET hInternet = InternetOpen(L"Patcher", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
DWORD err;
if (hInternet == NULL){
err=GetLastError();
cout<<"Internet Connection Not Detected. If you are connected, please notify [email protected] with this error number:"<<err<<endl;
InternetCloseHandle(hInternet);
return 0;
}
cout<<"Internet Connectioned Detected..."<<endl;
HINTERNET hConnect = InternetConnect(hInternet, L"nolimitsdesigns.com", INTERNET_DEFAULT_FTP_PORT, L"[email protected]", NULL, INTERNET_SERVICE_FTP, 0, 0);
if (hConnect == NULL){
err=GetLastError();
cout<<"Cannot Connect to the ftp server. please notify [email protected] with this error number:"<<err<<endl;
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
return 0;
}
cout<<"Successfully connected to the ftp server.."<<endl;
relativesize =GetCurrentDirectory(MAX_PATH, relative);
if(relativesize==0){
cout<<"error calling GetCurrentDirectory"<<endl;
} else {
relativesize*=sizeof(wchar_t);// i do not want the null char at the end
wcout<<"Current relative Client Directory set to: "<<relative<<endl<<" Lenght of Directory is: "<<relativesize<<endl;
}
cout<<"Beginning search for files... Please wait"<<endl;
GetFiles(hConnect);
InternetCloseHandle(hInternet);
InternetCloseHandle(hConnect);
cout<<"Continue to load client (Y/N)?:";
char k;
cin>>k;
if(k=='y' || k=='Y'){
StartClient();
}
return 0;
}
void StartClient(){
system("Client.exe");
cout<<"Starting Client..";
}
void GetFiles(HINTERNET &hConnect){
WIN32_FIND_DATA SdirInfo;
HINTERNET hFind;
DWORD err;
size_t CurDirSize(0);// the number of bytes in array.. not the string lenght
wchar_t CurDir[MAX_PATH], ServDir[MAX_PATH];// servDir is the servers directory, curdir is the client
DWORD pt(MAX_PATH);
if(FtpGetCurrentDirectory(hConnect, CurDir, &pt)){
CurDirSize=wcsnlen(CurDir, MAX_PATH)*sizeof(wchar_t);// i do not want the null char at the end
memcpy(ServDir, CurDir, CurDirSize*sizeof(wchar_t));// make a copy before we modify
for(size_t i(0); i!=CurDirSize; i++){
if(CurDir[i]==47){// using ascii numbers because I am cool like that!
CurDir[i]=92;
}
}
wcout<<"Current Server Directory is: "<<ServDir<<endl<<"Lenght of Directory is:"<<CurDirSize<<endl;
if(!SetCurrentDirectory(relative)){
wcout<<"Failed to SetCurrentDirectory on the client to"<<relative<<endl;
} else {
wcout<<"SetCurrentDirectory on the client to"<<relative<<endl;
}
if(CurDirSize>2){// if we should change directories.. then do it
wchar_t newdir[MAX_PATH];
memcpy(newdir, relative, relativesize);
size_t beg(relativesize/sizeof(wchar_t));
size_t end(beg + (CurDirSize/sizeof(wchar_t)));
for( size_t i(0); beg!=end; beg++){
newdir[beg]=CurDir[i++];
}
newdir[end]=0;// add the null char
if(!CreateDirectory(newdir, NULL)){
err = GetLastError( );
if(err==ERROR_ALREADY_EXISTS){
wcout<<"Directory: "<<newdir<<" Exists, continuing search."<<endl;
} else{
wcout<<"Bad error calling CreateDirectory on: "<<newdir<<endl;
return;
}
} else {
wcout<<"Created the directory: "<<newdir<<endl;
}
if(!SetCurrentDirectory(newdir)){
wcout<<"Failed to SetCurrentDirectory on the client to"<<newdir<<endl;
}
}// else skip this.. its the first directory /
} else {
err = GetLastError( );
cout<<"Error calling get directory... "<<err<<endl;
return;
}
ListDirectory(hConnect);
hFind = FtpFindFirstFile( hConnect, NULL, &SdirInfo, INTERNET_FLAG_DONT_CACHE, 0 );
if ( hFind == NULL ){
err = GetLastError( );
if( err == ERROR_NO_MORE_FILES ){
cout<<"End of Directory listing"<<endl;
} else {
cout<<"FtpFindFirstFile Failed "<<err<<endl;
}
return;// done listing the directory get out
}
if(SdirInfo.cFileName[0]==46){// we are working on periods!! ewwww, I need a tampon
InternetFindNextFile( hFind, (LPVOID) &SdirInfo );
}
for( ; InternetFindNextFile( hFind, (LPVOID) &SdirInfo ); ){
if( SdirInfo.dwFileAttributes==FILE_ATTRIBUTE_DIRECTORY){
if(wcscmp(SdirInfo.cFileName , L"incoming")==0){// skip the file
continue;
}
wcout<<"Attempting to change directories on the ftp server to: "<<SdirInfo.cFileName<<" ....";
if(FtpSetCurrentDirectory(hConnect, SdirInfo.cFileName)){
cout<<"Succesful"<<endl;
InternetCloseHandle(hFind);
GetFiles(hConnect);// recursive function to search through all directories
wcout<<"Resuming search .. Attempting to change directories on the ftp server to: "<<ServDir<<" ....";;
if(FtpSetCurrentDirectory(hConnect, ServDir)){ // resuming the search, change directories back
cout<<"Success"<<endl;
wchar_t newdir[MAX_PATH];
memcpy(newdir, relative, relativesize);
size_t beg(relativesize/sizeof(wchar_t));
size_t end(beg + (CurDirSize/sizeof(wchar_t)));
for( size_t i(0); beg!=end; beg++){
newdir[beg]=CurDir[i++];
}
newdir[end]=0;// add the null char
if(!SetCurrentDirectory(newdir)){
wcout<<"Failed to SetCurrentDirectory on the client to"<<newdir<<endl;
} else {
wcout<<"SetCurrentDirectory on the client to"<<newdir<<endl;
}
ListDirectory(hConnect);
wchar_t temp[MAX_PATH];
size_t q(0);
do{
temp[q]=SdirInfo.cFileName[q];
}while(SdirInfo.cFileName[q++]!=0);
temp[q]=0;// add null char
if(!FindFile(hConnect, hFind, temp, SdirInfo)){
return;// cannot find the file, something is really wrong. Get out now!
} else {
continue;
}
} else {
err = GetLastError( );
wchar_t see[MAX_PATH];
DWORD wer(0), msd(MAX_PATH);
if(InternetGetLastResponseInfo(&wer, see, &msd)){
wcout<<"Error "<<see<<endl;
}
cout<<"Failed "<<err;
}
} else {
err = GetLastError( );
wchar_t see[MAX_PATH];
DWORD wer(0), msd(MAX_PATH);
if(InternetGetLastResponseInfo(&wer, see, &msd)){
wcout<<"Error "<<see<<endl;
}
cout<<"Failed"<<err;
return;
}
} else {// we are deadling with files, not directories
if(wcscmp(SdirInfo.cFileName , L".ftpquota")==0){// skip the file
continue;
}
WIN32_FIND_DATA tfile;
HANDLE t = FindFirstFile(SdirInfo.cFileName, &tfile);// this is the file on the local drive
SYSTEMTIME ltime, stime;
FileTimeToSystemTime(&tfile.ftLastWriteTime, <ime);
FileTimeToSystemTime(&SdirInfo.ftLastWriteTime, &stime);
if(t!=INVALID_HANDLE_VALUE){// we have the file
wcout<<"File "<<tfile.cFileName<<" found on hard drive. Checking to see if this matches the file on the server...";
if(tfile.nFileSizeLow == SdirInfo.nFileSizeLow && ltime.wYear >= stime.wYear){// we might have the same file... do better check
if(ltime.wMonth > stime.wMonth){// update-to-date file..... continue
cout<<" Same file found. Continuing to next."<<endl;
continue;
} else if(ltime.wMonth == stime.wMonth){// check date
if(ltime.wDay >= stime.wDay){
cout<<" Same file found. Continuing to next."<<endl;
continue;
}// else we need to update... download
}// else the file has a month less than the servers.... download file
}
}
wcout<<"File: "<<SdirInfo.cFileName<<" Does not exist. Downloading file from the server .."<<endl;
FtpGetFile(hConnect, SdirInfo.cFileName, SdirInfo.cFileName, false, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_BINARY, 0);
}
}
cout<<"End of Directory listing"<<endl;
InternetCloseHandle(hFind);
}
void ListDirectory(HINTERNET &hConnect){
WIN32_FIND_DATA SdirInfo;
HINTERNET hFind;
DWORD err;
cout<<"Listing Server Directory..."<<endl;
hFind = FtpFindFirstFile( hConnect, NULL, &SdirInfo, INTERNET_FLAG_DONT_CACHE, 0 );
if ( hFind == NULL ){
err = GetLastError( );
if( err == ERROR_NO_MORE_FILES ){
cout<<"End of Directory listing"<<endl;
} else {
cout<<"FtpFindFirstFile Failed "<<err<<endl;
}
return;// done listing the directory get out
}
wcout<<SdirInfo.cFileName<<endl;
for( ; InternetFindNextFile( hFind, (LPVOID) &SdirInfo ); ){
wcout<<SdirInfo.cFileName<<endl;
}
InternetCloseHandle(hFind);
}
bool FindFile(HINTERNET &hConnect, HINTERNET &hFind, LPCWSTR temp, WIN32_FIND_DATA &SdirInfo){
DWORD err;
wcout<<"Finding the file "<<temp<<endl;
hFind = FtpFindFirstFile( hConnect, NULL, &SdirInfo, INTERNET_FLAG_DONT_CACHE, 0 );
if ( hFind == NULL ){
err = GetLastError( );
if( err == ERROR_NO_MORE_FILES ){
cout<<"End of Directory listing"<<endl;
} else {
cout<<"FtpFindFirstFile Failed "<<err<<endl;
}
return false;// done listing the directory get out
}
do{
if(wcscmp(temp, SdirInfo.cFileName)==0){
wcout<<"Found "<<SdirInfo.cFileName<<endl;
return true;
}
}while(InternetFindNextFile( hFind, (LPVOID) &SdirInfo ));
wcout<<"Could Not Find the File "<<temp<<endl;
return false;
}