diff --git a/source/glest_game/menu/menu_state_connected_game.cpp b/source/glest_game/menu/menu_state_connected_game.cpp index 8b67b129a..2d6314541 100644 --- a/source/glest_game/menu/menu_state_connected_game.cpp +++ b/source/glest_game/menu/menu_state_connected_game.cpp @@ -54,9 +54,13 @@ MenuStateConnectedGame::MenuStateConnectedGame(Program *program, MainMenu *mainM currentTechName_factionPreview=""; currentFactionName_factionPreview=""; - ftpClientThread = NULL; - getMissingMapFromFTPServer = ""; - getMissingMapFromFTPServerInProgress = false; + + ftpClientThread = NULL; + ftpMissingDataType = ftpmsg_MissingNone; + getMissingMapFromFTPServer = ""; + getMissingMapFromFTPServerInProgress = false; + getMissingTilesetFromFTPServer = ""; + getMissingTilesetFromFTPServerInProgress = false; currentFactionLogo = ""; factionTexture=NULL; @@ -389,8 +393,16 @@ MenuStateConnectedGame::MenuStateConnectedGame(Program *program, MainMenu *mainM if(mapPathList.size() > 1) { mapsPath.second = mapPathList[1]; } + std::pair tilesetsPath; + vector tilesetsList = Config::getInstance().getPathListForType(ptTilesets); + if(tilesetsList.size() > 0) { + tilesetsPath.first = tilesetsList[0]; + if(tilesetsList.size() > 1) { + tilesetsPath.second = tilesetsList[1]; + } + } - ftpClientThread = new FTPClientThread(portNumber,serverUrl,mapsPath, this); + ftpClientThread = new FTPClientThread(portNumber,serverUrl,mapsPath,tilesetsPath,this); ftpClientThread->start(); } @@ -432,15 +444,28 @@ void MenuStateConnectedGame::mouseClick(int x, int y, MouseButton mouseButton){ soundRenderer.playFx(coreData.getClickSoundA()); ftpMessageBox.setEnabled(false); if(button == 1) { - getMissingMapFromFTPServerInProgress = true; + if(ftpMissingDataType == ftpmsg_MissingMap) { + getMissingMapFromFTPServerInProgress = true; - char szMsg[1024]=""; - sprintf(szMsg,"Player: %s is attempting to download the map: %s",getHumanPlayerName().c_str(),getMissingMapFromFTPServer.c_str()); - clientInterface->sendTextMessage(szMsg,-1, true); + char szMsg[1024]=""; + sprintf(szMsg,"Player: %s is attempting to download the map: %s",getHumanPlayerName().c_str(),getMissingMapFromFTPServer.c_str()); + clientInterface->sendTextMessage(szMsg,-1, true); - if(ftpClientThread != NULL) { - ftpClientThread->addMapToRequests(getMissingMapFromFTPServer); - } + if(ftpClientThread != NULL) { + ftpClientThread->addMapToRequests(getMissingMapFromFTPServer); + } + } + else if(ftpMissingDataType == ftpmsg_MissingTileset) { + getMissingTilesetFromFTPServerInProgress = true; + + char szMsg[1024]=""; + sprintf(szMsg,"Player: %s is attempting to download the tileset: %s",getHumanPlayerName().c_str(),getMissingTilesetFromFTPServer.c_str()); + clientInterface->sendTextMessage(szMsg,-1, true); + + if(ftpClientThread != NULL) { + ftpClientThread->addTilesetToRequests(getMissingTilesetFromFTPServer); + } + } } } } @@ -951,30 +976,47 @@ void MenuStateConnectedGame::update() { if(gameSettings == NULL) { throw runtime_error("gameSettings == NULL"); } - // tileset - if(std::find(this->tileSets.begin(),this->tileSets.end(),gameSettings->getTileset()) != this->tileSets.end()) { - lastMissingTileSet = ""; - tilesets.push_back(formatString(gameSettings->getTileset())); + if(getMissingTilesetFromFTPServerInProgress == false) { + // tileset + if(std::find(this->tileSets.begin(),this->tileSets.end(),gameSettings->getTileset()) != this->tileSets.end()) { + lastMissingTileSet = ""; + + tilesets.push_back(formatString(gameSettings->getTileset())); + } + else { + // try to get the map via ftp + if(ftpClientThread != NULL && getMissingTilesetFromFTPServer != gameSettings->getTileset()) { + if(ftpMessageBox.getEnabled() == false) { + getMissingTilesetFromFTPServer = gameSettings->getTileset(); + Lang &lang= Lang::getInstance(); + + char szBuf[1024]=""; + sprintf(szBuf,"%s %s ?",lang.get("DownloadMissingTilesetQuestion").c_str(),gameSettings->getTileset().c_str()); + + ftpMissingDataType = ftpmsg_MissingTileset; + showFTPMessageBox(szBuf, lang.get("Question"), false); + } + } + + tilesets.push_back("***missing***"); + + NetworkManager &networkManager= NetworkManager::getInstance(); + ClientInterface* clientInterface= networkManager.getClientInterface(); + const GameSettings *gameSettings = clientInterface->getGameSettings(); + + if(lastMissingTileSet != gameSettings->getTileset()) { + lastMissingTileSet = gameSettings->getTileset(); + + SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + + char szMsg[1024]=""; + sprintf(szMsg,"Player: %s is missing the tileset: %s",getHumanPlayerName().c_str(),gameSettings->getTileset().c_str()); + clientInterface->sendTextMessage(szMsg,-1, true); + } + } + listBoxTileset.setItems(tilesets); } - else { - tilesets.push_back("***missing***"); - - NetworkManager &networkManager= NetworkManager::getInstance(); - ClientInterface* clientInterface= networkManager.getClientInterface(); - const GameSettings *gameSettings = clientInterface->getGameSettings(); - - if(lastMissingTileSet != gameSettings->getTileset()) { - lastMissingTileSet = gameSettings->getTileset(); - - SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); - - char szMsg[1024]=""; - sprintf(szMsg,"Player: %s is missing the tileset: %s",getHumanPlayerName().c_str(),gameSettings->getTileset().c_str()); - clientInterface->sendTextMessage(szMsg,-1, true); - } - } - listBoxTileset.setItems(tilesets); // techtree techtree.push_back(formatString(gameSettings->getTech())); @@ -1015,6 +1057,7 @@ void MenuStateConnectedGame::update() { char szBuf[1024]=""; sprintf(szBuf,"%s %s ?",lang.get("DownloadMissingMapQuestion").c_str(),currentMap.c_str()); + ftpMissingDataType = ftpmsg_MissingMap; showFTPMessageBox(szBuf, lang.get("Question"), false); } } @@ -1631,25 +1674,46 @@ void MenuStateConnectedGame::showFTPMessageBox(const string &text, const string } } -void MenuStateConnectedGame::FTPClient_CallbackEvent(string mapFilename, FTP_Client_ResultType result) { +void MenuStateConnectedGame::FTPClient_CallbackEvent(string itemName, FTP_Client_ResultType result) { SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line %d]\n",__FILE__,__FUNCTION__,__LINE__); - getMissingMapFromFTPServerInProgress = false; - printf("Got FTP Callback for [%s] result = %d\n",mapFilename.c_str(),result); + if(ftpMissingDataType == ftpmsg_MissingMap) { + getMissingMapFromFTPServerInProgress = false; + printf("Got FTP Callback for [%s] result = %d\n",itemName.c_str(),result); - NetworkManager &networkManager= NetworkManager::getInstance(); - ClientInterface* clientInterface= networkManager.getClientInterface(); - const GameSettings *gameSettings = clientInterface->getGameSettings(); + NetworkManager &networkManager= NetworkManager::getInstance(); + ClientInterface* clientInterface= networkManager.getClientInterface(); + const GameSettings *gameSettings = clientInterface->getGameSettings(); - if(result == ftp_crt_SUCCESS) { - char szMsg[1024]=""; - sprintf(szMsg,"Player: %s SUCCESSFULLY downloaded the map: %s",getHumanPlayerName().c_str(),gameSettings->getMap().c_str()); - clientInterface->sendTextMessage(szMsg,-1, true); + if(result == ftp_crt_SUCCESS) { + char szMsg[1024]=""; + sprintf(szMsg,"Player: %s SUCCESSFULLY downloaded the map: %s",getHumanPlayerName().c_str(),gameSettings->getMap().c_str()); + clientInterface->sendTextMessage(szMsg,-1, true); + } + else { + char szMsg[1024]=""; + sprintf(szMsg,"Player: %s FAILED to download the map: %s",getHumanPlayerName().c_str(),gameSettings->getMap().c_str()); + clientInterface->sendTextMessage(szMsg,-1, true); + } } - else { - char szMsg[1024]=""; - sprintf(szMsg,"Player: %s FAILED to download the map: %s",getHumanPlayerName().c_str(),gameSettings->getMap().c_str()); - clientInterface->sendTextMessage(szMsg,-1, true); + else if(ftpMissingDataType == ftpmsg_MissingTileset) { + getMissingTilesetFromFTPServerInProgress = false; + printf("Got FTP Callback for [%s] result = %d\n",itemName.c_str(),result); + + NetworkManager &networkManager= NetworkManager::getInstance(); + ClientInterface* clientInterface= networkManager.getClientInterface(); + const GameSettings *gameSettings = clientInterface->getGameSettings(); + + if(result == ftp_crt_SUCCESS) { + char szMsg[1024]=""; + sprintf(szMsg,"Player: %s SUCCESSFULLY downloaded the tileset: %s",getHumanPlayerName().c_str(),gameSettings->getTileset().c_str()); + clientInterface->sendTextMessage(szMsg,-1, true); + } + else { + char szMsg[1024]=""; + sprintf(szMsg,"Player: %s FAILED to download the tileset: %s",getHumanPlayerName().c_str(),gameSettings->getTileset().c_str()); + clientInterface->sendTextMessage(szMsg,-1, true); + } } } diff --git a/source/glest_game/menu/menu_state_connected_game.h b/source/glest_game/menu/menu_state_connected_game.h index cb4a9d45b..0f62e3346 100644 --- a/source/glest_game/menu/menu_state_connected_game.h +++ b/source/glest_game/menu/menu_state_connected_game.h @@ -27,6 +27,11 @@ enum JoinMenu { jmCount }; +enum FTPMessageType { + ftpmsg_MissingNone, + ftpmsg_MissingMap, + ftpmsg_MissingTileset +}; // =============================== // class MenuStateConnectedGame @@ -138,9 +143,14 @@ private: GraphicMessageBox ftpMessageBox; FTPClientThread *ftpClientThread; + FTPMessageType ftpMissingDataType; + string getMissingMapFromFTPServer; bool getMissingMapFromFTPServerInProgress; + string getMissingTilesetFromFTPServer; + bool getMissingTilesetFromFTPServerInProgress; + public: MenuStateConnectedGame(Program *program, MainMenu *mainMenu, JoinMenu joinMenuInfo=jmSimple, bool openNetworkSlots= false); @@ -171,7 +181,7 @@ private: void showMessageBox(const string &text, const string &header, bool toggle); void showFTPMessageBox(const string &text, const string &header, bool toggle); - virtual void FTPClient_CallbackEvent(string mapFilename, FTP_Client_ResultType result); + virtual void FTPClient_CallbackEvent(string itemName, FTP_Client_ResultType result); }; }}//end namespace diff --git a/source/glest_game/network/server_interface.cpp b/source/glest_game/network/server_interface.cpp index 0be06cca8..4f66eba97 100644 --- a/source/glest_game/network/server_interface.cpp +++ b/source/glest_game/network/server_interface.cpp @@ -96,9 +96,18 @@ ServerInterface::ServerInterface() { } } + std::pair tilesetsPath; + vector tilesetsList = Config::getInstance().getPathListForType(ptTilesets); + if(tilesetsList.size() > 0) { + tilesetsPath.first = tilesetsList[0]; + if(tilesetsList.size() > 1) { + tilesetsPath.second = tilesetsList[1]; + } + } + int portNumber = Config::getInstance().getInt("FTPServerPort",intToStr(ServerSocket::getFTPServerPort()).c_str()); ServerSocket::setFTPServerPort(portNumber); - ftpServer = new FTPServerThread(mapsPath,portNumber); + ftpServer = new FTPServerThread(mapsPath,tilesetsPath,portNumber); ftpServer->start(); } } diff --git a/source/shared_lib/include/platform/posix/miniftpclient.h b/source/shared_lib/include/platform/posix/miniftpclient.h index 0864e7c5e..a201f14dc 100644 --- a/source/shared_lib/include/platform/posix/miniftpclient.h +++ b/source/shared_lib/include/platform/posix/miniftpclient.h @@ -44,20 +44,29 @@ protected: string serverUrl; FTPClientCallbackInterface *pCBObject; std::pair mapsPath; + std::pair tilesetsPath; Mutex mutexMapFileList; vector mapFileList; + + Mutex mutexTilesetList; + vector tilesetList; + void getMapFromServer(string mapFilename); FTP_Client_ResultType getMapFromServer(string mapFileName, string ftpUser, string ftpUserPassword); + void getTilesetFromServer(string tileSetName); + FTP_Client_ResultType getTilesetFromServer(string tileSetName, string tileSetNameSubfolder, string ftpUser, string ftpUserPassword); + public: - FTPClientThread(int portNumber,string serverUrl, std::pair mapsPath, FTPClientCallbackInterface *pCBObject); + FTPClientThread(int portNumber,string serverUrl, std::pair mapsPath, std::pair tilesetsPath, FTPClientCallbackInterface *pCBObject); virtual void execute(); virtual void signalQuit(); virtual bool shutdownAndWait(); void addMapToRequests(string mapFilename); + void addTilesetToRequests(string tileSetName); }; }}//end namespace diff --git a/source/shared_lib/include/platform/posix/miniftpserver.h b/source/shared_lib/include/platform/posix/miniftpserver.h index b650721b8..0df52d221 100644 --- a/source/shared_lib/include/platform/posix/miniftpserver.h +++ b/source/shared_lib/include/platform/posix/miniftpserver.h @@ -31,12 +31,12 @@ class FTPServerThread : public BaseThread { protected: std::pair mapsPath; + std::pair tilesetsPath; int portNumber; - //uint32 externalIp; public: - FTPServerThread(std::pair mapsPath, int portNumber); + FTPServerThread(std::pair mapsPath, std::pair tilesetsPath, int portNumber); ~FTPServerThread(); virtual void execute(); virtual void signalQuit(); diff --git a/source/shared_lib/sources/platform/posix/miniftpclient.cpp b/source/shared_lib/sources/platform/posix/miniftpclient.cpp index 139ad7aca..b5f93572b 100644 --- a/source/shared_lib/sources/platform/posix/miniftpclient.cpp +++ b/source/shared_lib/sources/platform/posix/miniftpclient.cpp @@ -56,7 +56,7 @@ static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream) { if(SystemFlags::VERBOSE_MODE_ENABLED) printf ("===> FTP Client thread opening file for writing [%s]\n",out->filename); /* open file for writing */ - out->stream=fopen(out->filename, "wb"); + out->stream = fopen(out->filename, "wb"); if(out->stream == NULL) { if(SystemFlags::VERBOSE_MODE_ENABLED) printf ("===> FTP Client thread FAILED to open file for writing [%s]\n",out->filename); @@ -66,10 +66,57 @@ static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream) { return fwrite(buffer, size, nmemb, out->stream); } -FTPClientThread::FTPClientThread(int portNumber, string serverUrl, std::pair mapsPath, FTPClientCallbackInterface *pCBObject) : BaseThread() { + +static long file_is_comming(struct curl_fileinfo *finfo,void *data,int remains) +{ + printf("%3d %40s %10luB ", remains, finfo->filename,(unsigned long)finfo->size); + + switch(finfo->filetype) { + case CURLFILETYPE_DIRECTORY: + printf(" DIR\n"); + break; + case CURLFILETYPE_FILE: + printf("FILE "); + break; + default: + printf("OTHER\n"); + break; + } + + if(finfo->filetype == CURLFILETYPE_FILE) { + // do not transfer files >= 50B + //if(finfo->size > 50) { + // printf("SKIPPED\n"); + // return CURL_CHUNK_BGN_FUNC_SKIP; + //} + + struct FtpFile *out=(struct FtpFile *)data; + out->stream = fopen(finfo->filename, "w"); + if(!out->stream) { + return CURL_CHUNK_BGN_FUNC_FAIL; + } + } + + return CURL_CHUNK_BGN_FUNC_OK; +} + +static long file_is_downloaded(void *data) +{ + struct FtpFile *out=(struct FtpFile *)data; + if(out->stream) { + printf("DOWNLOADED\n"); + + fclose(out->stream); + out->stream = 0x0; + } + return CURL_CHUNK_END_FUNC_OK; +} + +FTPClientThread::FTPClientThread(int portNumber, string serverUrl, std::pair mapsPath, std::pair tilesetsPath, FTPClientCallbackInterface *pCBObject) : BaseThread() { this->portNumber = portNumber; this->serverUrl = serverUrl; this->mapsPath = mapsPath; + this->tilesetsPath = tilesetsPath; this->pCBObject = pCBObject; } @@ -175,6 +222,127 @@ void FTPClientThread::addMapToRequests(string mapFilename) { } } +void FTPClientThread::addTilesetToRequests(string tileSetName) { + MutexSafeWrapper safeMutex(&mutexTilesetList); + if(std::find(tilesetList.begin(),tilesetList.end(),tileSetName) == tilesetList.end()) { + tilesetList.push_back(tileSetName); + } +} + +void FTPClientThread::getTilesetFromServer(string tileSetName) { + FTP_Client_ResultType result = getTilesetFromServer(tileSetName, "", "tilsets_custom", "mg_ftp_server"); + if(result != ftp_crt_SUCCESS && this->getQuitStatus() == false) { + result = getTilesetFromServer(tileSetName, "tilesets", "", "mg_ftp_server"); + } + + if(this->pCBObject != NULL) { + this->pCBObject->FTPClient_CallbackEvent(tileSetName,result); + } +} + +FTP_Client_ResultType FTPClientThread::getTilesetFromServer(string tileSetName, string tileSetNameSubfolder, string ftpUser, string ftpUserPassword) { + CURLcode res; + + FTP_Client_ResultType result = ftp_crt_FAIL; + + string destFile = this->mapsPath.second; + + if(EndsWith(destFile,"/") == false && EndsWith(destFile,"\\") == false) { + destFile += "/"; + } + destFile += tileSetName; + + if(tileSetNameSubfolder != "") { + destFile += tileSetNameSubfolder; + + if(EndsWith(destFile,"/") == false && EndsWith(destFile,"\\") == false) { + destFile += "/"; + } + } + + if(SystemFlags::VERBOSE_MODE_ENABLED) printf ("===> FTP Client thread about to try to RETR into [%s]\n",destFile.c_str()); + + struct FtpFile ftpfile = { + destFile.c_str(), /* name to store the file as if succesful */ + NULL, + this + }; + + //curl_global_init(CURL_GLOBAL_DEFAULT); + + CURL *curl = curl_easy_init(); + if(curl) { + ftpfile.stream = NULL; + + char szBuf[1024]=""; + sprintf(szBuf,"ftp://%s:%s@%s:%d/%s*",ftpUser.c_str(),ftpUserPassword.c_str(),serverUrl.c_str(),portNumber,tileSetName.c_str()); + + curl_easy_setopt(curl, CURLOPT_URL,szBuf); + + // turn on wildcard matching + curl_easy_setopt(curl, CURLOPT_WILDCARDMATCH, 1L); + + // callback is called before download of concrete file started + curl_easy_setopt(curl, CURLOPT_CHUNK_BGN_FUNCTION, file_is_comming); + + // callback is called after data from the file have been transferred + curl_easy_setopt(curl, CURLOPT_CHUNK_END_FUNCTION, file_is_downloaded); + + // put transfer data into callbacks + // helper data + //struct callback_data data = { 0 }; + curl_easy_setopt(curl, CURLOPT_CHUNK_DATA, &ftpfile); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile); + + // Define our callback to get called when there's data to be written + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite); + // Set a pointer to our struct to pass to the callback + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile); + + // Switch on full protocol/debug output + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + + res = curl_easy_perform(curl); + + if(CURLE_OK != res) { + // we failed + fprintf(stderr, "curl told us %d\n", res); + } + else { + result = ftp_crt_SUCCESS; + + bool requireMoreFolders = false; + if(tileSetNameSubfolder == "") { + tileSetNameSubfolder = "models"; + requireMoreFolders = true; + } + else if(tileSetNameSubfolder == "models") { + tileSetNameSubfolder = "sounds"; + requireMoreFolders = true; + } + else if(tileSetNameSubfolder == "sounds") { + tileSetNameSubfolder = "textures"; + requireMoreFolders = true; + } + else if(tileSetNameSubfolder == "textures") { + tileSetNameSubfolder = "textures"; + requireMoreFolders = true; + } + + if(requireMoreFolders == true) { + FTP_Client_ResultType result2 = getTilesetFromServer(tileSetName, tileSetNameSubfolder, ftpUser, ftpUserPassword); + } + } + + curl_easy_cleanup(curl); + } + + if(ftpfile.stream) { + fclose(ftpfile.stream); + ftpfile.stream = NULL; + } + return result; +} void FTPClientThread::execute() { { diff --git a/source/shared_lib/sources/platform/posix/miniftpserver.cpp b/source/shared_lib/sources/platform/posix/miniftpserver.cpp index 125fd052a..9c5f9dfb8 100644 --- a/source/shared_lib/sources/platform/posix/miniftpserver.cpp +++ b/source/shared_lib/sources/platform/posix/miniftpserver.cpp @@ -36,8 +36,9 @@ ip_t FindExternalFTPServerIp(ip_t clientIp) { return result; } -FTPServerThread::FTPServerThread(std::pair mapsPath,int portNumber) : BaseThread() { +FTPServerThread::FTPServerThread(std::pair mapsPath,std::pair tilesetsPath, int portNumber) : BaseThread() { this->mapsPath = mapsPath; + this->tilesetsPath = tilesetsPath; this->portNumber = portNumber; ftpInit(&FindExternalFTPServerIp,&UPNP_Tools::AddUPNPPortForward,&UPNP_Tools::RemoveUPNPPortForward); @@ -82,6 +83,7 @@ void FTPServerThread::execute() { try { //ftpCreateAccount("anonymous", "", mapsPath.first.c_str(), FTP_ACC_RD | FTP_ACC_LS | FTP_ACC_DIR); + // Setup FTP Users and permissions for maps if(mapsPath.first != "") { SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] mapsPath #1 [%s]\n",__FILE__,__FUNCTION__,__LINE__,mapsPath.first.c_str()); ftpCreateAccount("maps", "mg_ftp_server", mapsPath.first.c_str(), FTP_ACC_RD | FTP_ACC_LS | FTP_ACC_DIR); @@ -91,6 +93,16 @@ void FTPServerThread::execute() { ftpCreateAccount("maps_custom", "mg_ftp_server", mapsPath.second.c_str(), FTP_ACC_RD | FTP_ACC_LS | FTP_ACC_DIR); } + // Setup FTP Users and permissions for tilesets + if(tilesetsPath.first != "") { + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] tilesetsPath #1 [%s]\n",__FILE__,__FUNCTION__,__LINE__,tilesetsPath.first.c_str()); + ftpCreateAccount("tilsets", "mg_ftp_server", tilesetsPath.first.c_str(), FTP_ACC_RD | FTP_ACC_LS | FTP_ACC_DIR); + } + if(tilesetsPath.second != "") { + SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] tilesetsPath #2 [%s]\n",__FILE__,__FUNCTION__,__LINE__,tilesetsPath.second.c_str()); + ftpCreateAccount("tilsets_custom", "mg_ftp_server", tilesetsPath.second.c_str(), FTP_ACC_RD | FTP_ACC_LS | FTP_ACC_DIR); + } + /* ftpCreateAccount("anonymous", "", "./", FTP_ACC_RD | FTP_ACC_LS | FTP_ACC_DIR); ftpCreateAccount("nothing", "", "./", 0);