Dev-С++ отправляет файл через ftp со случайным именем файла

Я хочу создать скрипт для отправки файла с именем office-data.txt на ftp-сервер, но я бы хотел, чтобы этот скрипт при отправке файла на ftp-сервер должен был менять свое имя случайным образом, только удаленно. Например, office-data-12478.txt или office-data-22478.txt и случайно сгенерированное имя никогда не должны совпадать при каждом запуске скрипта. Как я могу изменить имя файла при его отправке на FTP-сервер?

#include <string>
#include <winsock.h>
#include <windows.h>
#include <sstream>
#include <iostream>
#include <stdio.h>
#include <cstdlib>

using namespace std;

void stringtoint(const string &s, int &i){
   istringstream myStream(s);
   myStream>>i;
}

void sendLogIn(SOCKET _LSoc){
 char userbuffer[] = "username";                
 char passbuffer[] = "password";                
 char username[] = "USER ";
 char password[] = "PASS ";
 char servermessage[1000];

 strcat(username, userbuffer);
 strcat(username, "\r\n");

 send(_LSoc, username, strlen(username), 0);
 Sleep(1000);
    recv(_LSoc, servermessage, 1000, 0);

 strcat(password, passbuffer);
 strcat(password, "\r\n");

 send(_LSoc, password, strlen(password), 0);
 Sleep(1000);
    recv(_LSoc, servermessage, 1000, 0);
}

int sendConnInfo(SOCKET _CSoc){
 char servermessage[10000];
 char ftpmessage[50];
 string message;
 string portbuffer;
    string port1;
 string port2;
 size_t position;
 size_t position2;
 int port;
 int portbuf;
 int _portbuf;

 send(_CSoc, "TYPE I\r\n", 8, 0);
 Sleep(1000);
    recv(_CSoc, servermessage, 10000, 0);
 Sleep(1000);
 Sleep(1000);
    send(_CSoc, "PASV\r\n", 6, 0);
 Sleep(1000);
    recv(_CSoc, ftpmessage, 50, 0);

 message = ftpmessage;
 position = message.find("Mode");
 portbuffer = message.substr(position+21);

    position = portbuffer.find(",");
    position2 = portbuffer.find(">");

    port1 = portbuffer.substr(0, position);
    port2 = portbuffer.substr(position+1, position2-1);

    stringtoint(port1, portbuf);
    stringtoint(port2, _portbuf);

    port = portbuf*256;
    port = port + _portbuf;
    return port;
}

void sendFileRequest(SOCKET _FSoc){
     send(_FSoc, "STOR test.txt\r\n", strlen("STOR test.txt\r\n"), 0);
     Sleep(1000);
}

BOOL ftpSocket(int port){
     SOCKET sock;
     SOCKADDR_IN pasvserver;
     char servermessage[MAX_PATH];
     HANDLE HFile;
     DWORD read;
     char *buffer;
     char filename[] = "C:\\test.txt";
     int connectionerror2;
     int trycount2 = 2;     

     sock = socket(2, SOCK_STREAM, IPPROTO_TCP);
     if(sock == INVALID_SOCKET){
   WSACleanup();
   return 0;
  }

 pasvserver.sin_family = 2;
 pasvserver.sin_port = htons(port);                                
 pasvserver.sin_addr.s_addr = inet_addr("66.220.9.50");           //Once again the drivehq ftp server

 connectionerror2 = connect(sock, (LPSOCKADDR)&pasvserver, sizeof(struct sockaddr));
     while(connectionerror2 == SOCKET_ERROR){
            connectionerror2 = connect(sock, (LPSOCKADDR)&pasvserver, sizeof(struct sockaddr));
            trycount2++;
            if(trycount2 = 10){
                         closesocket(sock);
                         WSACleanup();
                         return 0;
            }
    }


    HFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    buffer = (char *)malloc(4096);
    SetFilePointer(HFile, 0, NULL, FILE_BEGIN);

    while(ReadFile(HFile, buffer, 4096, &read, NULL) && read > 0){
         send(sock, buffer, read, 0);
    }

    return true;   
}


int sendFile(){
 FreeConsole();
 WSAData WData;
 SOCKET FSoc;
 SOCKADDR_IN server;
 int connectionerror;
 int trycount = 2;
 char servermessage[MAX_PATH];
 int port;


 WSAStartup(MAKEWORD(2,2), &WData);
 FSoc = socket(2, SOCK_STREAM, IPPROTO_TCP);
        if(FSoc == INVALID_SOCKET){
   WSACleanup();
   return 0;
  }

 server.sin_family = 2;
 server.sin_port = htons(21);
 server.sin_addr.s_addr = inet_addr("66.220.9.50"); //this is the drivehq ftp server address.


 connectionerror = connect(FSoc, (LPSOCKADDR)&server, sizeof(struct sockaddr));
     while(connectionerror == SOCKET_ERROR){
            connectionerror = connect(FSoc, (LPSOCKADDR)&server, sizeof(struct sockaddr));
            trycount++;
            if(trycount = 10){
                         closesocket(FSoc);
                         WSACleanup();
                         return 0;
            }
    }

 recv(FSoc, servermessage, sizeof(servermessage),0);

 sendLogIn(FSoc);
 Sleep(1000);  //give the server and the client sometime to deal with the influx of new messages
               //so that data for the ip doesnt get mixed up.
    port = sendConnInfo(FSoc);
    sendFileRequest(FSoc);
    ftpSocket(port);
    WSACleanup();
    return 0;
}
int main(){
   sendFile();
   return 1;
}

person M Smith    schedule 26.11.2016    source источник
comment
Пожалуйста, будьте внимательнее при вставке кода. Однако следует отметить, что Stack Overflow не является службой написания кода.   -  person Brian Tompsett - 汤莱恩    schedule 29.11.2016


Ответы (1)


Прежде всего, я предлагаю вам прекратить использовать Dev-C++. Он действительно устарел, и последняя бета была опубликована более 10 лет назад:

21 февраля 2005 г.: выпуск Dev-C++ 5 Beta 9.2 (4.9.9.2)!

Вам следует попробовать пакет NetBeans C/C++ и компилятор/компоновщик (gcc/g++/make) из Cygwin. Дополнительные сведения о настройке такой среды разработки см. по этой ссылке здесь. Также вы можете попробовать Eclipse CDT или даже Visual Studio Express или новейшая Сообщество Visual Studio.

Теперь ваша программа и код.

Хотя ваша программа работает, я обнаружил несколько мелких ошибок, из-за которых она не работала должным образом, когда скомпилировала и запустила ее.

Вы хотите загрузить файл на FTP-сервер и сохранить его с уникальным именем файла по удаленному пути.

Вы можете просто использовать STOU команду ftp вместо STOR и ftp-сервер сохранит файл с уникальным именем в текущем рабочем каталоге. Итак, вам придется изменить процедуру sendFileRequest, чтобы использовать CWD для перехода в каталог, в который мы хотим сохранить файл, а затем просто использовать STOU без каких-либо параметров, а сервер обработает все остальное и сохранит с уникальным именем файла. Например, сервер Pure-FTP создает такое имя файла, как pureftpd.583c3777.dd.0000.

Процедура sendFileRequest станет такой:

void sendFileRequest(SOCKET _FSoc){
    char cwdCmd[MAX_PATH] = "CWD /web/share/tmp/\r\n";
    send(_FSoc,  cwdCmd, strlen(cwdCmd), 0);
    Sleep(1000);

    //char stor[] = "STOR /web/share/tmp/test.txt\r\n";
    char stor[] = "STOU\r\n";
    send(_FSoc, stor, strlen(stor), 0);
    Sleep(1000);
}

Осторожно, некоторые ftp-серверы не поддерживают команду STOU.

Теперь, если вы хотите контролировать имена файлов и генерировать имена на основе некоторых критериев, вам нужно будет прочитать список файлов в каталоге, в который вы хотите загрузить файл, а затем создать уникальное имя файла, которого нет в списке. файлов удаленного каталога.

Мы хотим выполнить команду ftp, чтобы вернуть имена файлов текущего каталога. Команда NLST — это то, что нам нужно, поскольку она отправляет буфер, содержащий текстовые данные. с разделителями перевода строки для каждого файла; список всех имен файлов/папок (только имена файлов, без данных) в текущем рабочем каталоге. Нам также необходимо открыть соединение для передачи данных в пассивном режиме для получения этих данных. Также я иногда замечал, что хотя файл был создан с уникальным именем пути к удаленному серверу, данные вообще не передавались. Это происходит из-за того, что я выполнял программу очень быстро, а предыдущие сеансы ftp не закрывались должным образом с помощью QUIT команда. Когда я добавил команду QUIT в конце процесса, все проблемы с передачей файлов с нулевыми данными исчезли.

Проблемы и замечания на уровне кода

  • Я изменил TYPE I image/binary на TYPE A text, потому что большинство ftp-серверов исправляют перевод строки CR LF при передаче текстовых файлов из формата unix в формат windows и наоборот.

  • Вы должны всегда закрывать подключения к данным с помощью shutdown и/или closesocket к соединению сокета данных, иначе сервер может прервать передачу ваших данных.

  • У вас есть функция для преобразования std::string в целое число, но вы также можете использовать std::stoi, если вы укажете компилятору использовать стандарты C++11 с помощью параметра -std=c++11.

  • Когда вы используете recv для чтения ответа сервера, вы должны всегда обнулять строку буфера ответа, используя возвращаемое значение recv, или обрабатывать только то количество байтов, которое возвращает recv.

  • Программа не имеет поведения чтения состояния, что может привести к множеству ошибок и полному сбою. Правильное поведение ftp-приложения всегда должно зависеть от ответов, которые сервер отправляет на командное соединение. Например, если ftp-сервер достиг максимального количества клиентов, ваша программа не будет знать об этом и будет продолжать выдавать команды до тех пор, пока не выйдет из строя или не завершится. Программа не должна и не может продолжать работу при возникновении таких ошибок.

Параметры компилятора

-std=c++11 за std::stoi

Параметры компоновщика

-lws2_32 для включения cygwin libws2_32.a, необходимого для winsock2.h

Псевдокод

  • Инициализировать подключение команды ftp-сервера
  • Отправить данные для входа на фтп
  • Generate unique filename
    • Read the remote directory filenames
    • Генерировать случайные имена, пока у нас не будет имени в списке файлов
  • Отправить файл с уникальным именем файла на сервер

Для уникального имени файла я просто использовал массив символов, заполненный символами [A-Za-z0-0]. Вы также можете выбрать, будете ли вы использовать расширение локального файла для проверки и добавить его в конец уникального имени файла:

string generateUniqueFilename(SOCKET _FSoc,
                              int port,
                              string remotePath,
                              bool useLocalFileExtension,
                              string localFile)

Например, установка useLocalFileExtension на true при наличии локального файла .txt приведет к уникальному имени файла с расширением .txt O3xh8p939YN4hV.txt.

Моя последняя программа

#include <winsock2.h>
#include <windows.h>
#include <sstream>
#include <iostream>
#include <stdio.h>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <vector>


#define REMOTE_FTP_HOST_IP "x.x.x.x"
#define REMOTE_FTP_USERNAME "username"              
#define REMOTE_FTP_PASSWORD "password"

#define ECHO_FTP_RESPONSE 1 // Set to 0 to disable ftp responses output

using namespace std;


// Declarations ---------------------------------------------------

void echoResponse(string);
void echoResponse(LPSTR);
void recvResponseAndEcho(SOCKET, LPSTR, int);
void recvResponseAndEcho(SOCKET);
string generateRandomFilename(int);
int readSocketData(int, LPSTR, int);
string generateUniqueFilename(SOCKET, int, string, bool, string);
void sendLogIn(SOCKET socket);
void sendFileRequest(SOCKET, string, string);
void sendTypeICmd(SOCKET);
void sendTypeACmd(SOCKET);
int sendPasvCmd(SOCKET);
void sendQuitCmd(SOCKET);
BOOL ftpSocket(string, int);
int sendFile(string, string);


// Program entry point --------------------------------------------

int main(){
    sendFile("C:\\test.txt", "/remote/ftp/path/");
    return EXIT_SUCCESS;
}

void echoResponse(string response) {
    if(ECHO_FTP_RESPONSE) {
        string respclean = response.erase(response.find_last_not_of(" \t\n\r") + 1);
        cout << respclean << endl;
    }
}

void echoResponse(LPSTR response) {
    if(ECHO_FTP_RESPONSE) {
        string respstr = response;
        echoResponse(respstr);
    }
}

void recvResponseAndEcho(SOCKET socket, LPSTR buffer, int bufsize) {
    int nread = recv(socket, buffer, bufsize, 0);
    *(buffer + nread) = 0;
    echoResponse(buffer);
}

void recvResponseAndEcho(SOCKET socket) {
    char srvResponse[4096];
    recvResponseAndEcho(socket, (LPSTR)&srvResponse, sizeof(srvResponse));
}

// Generate o random filename using an array of charactres [A-Za-z0-9]
string generateRandomFilename(int length) {
    string rndFilename;
    char fileChars[] = 
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
             "abcdefghijklmnopqrstuvwxyz"
             "0123456789";
    int fileCharsLength = strlen(fileChars);

    srand(time(NULL));

    for(int i = 0; i < length; i++)
        rndFilename += fileChars[rand() % fileCharsLength];

    return rndFilename;
}

int readSocketData(int port, LPSTR buffer, int buffer_size) {
    SOCKET sock;
    SOCKADDR_IN pasvserver;
    int connectionerror2;
    int trycount2 = 2;

    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sock == INVALID_SOCKET){
        WSACleanup();
        return 0;
    }

    pasvserver.sin_family = AF_INET;
    pasvserver.sin_port = htons(port);                                
    pasvserver.sin_addr.s_addr = inet_addr(REMOTE_FTP_HOST_IP);

    connectionerror2 = connect(sock, (LPSOCKADDR)&pasvserver, sizeof(struct sockaddr));
    while(connectionerror2 == SOCKET_ERROR){
        connectionerror2 = connect(sock, (LPSOCKADDR)&pasvserver, sizeof(struct sockaddr));
        trycount2++;
        if(trycount2 = 10) {
            closesocket(sock);
            WSACleanup();
            return 0;
        }
    }

    int result = recv(sock, buffer, buffer_size, 0);
    shutdown(sock, SD_BOTH);
    closesocket(sock);
    return result;
}

string generateUniqueFilename(SOCKET _FSoc, int port, string remotePath, bool useLocalFileExtension, string localFile) {
    string newFilename;
    char cwd[MAX_PATH];
    char cmd[7] = "NLST\r\n";
    char listFilesBuffer[4096];
    char servermessage[2048];
    int listFilesBufferReadLength;

    strcpy(cwd, "CWD ");
    strcat(cwd, remotePath.c_str());
    strcat(cwd, "\r\n");
    send(_FSoc,  cwd, strlen(cwd), 0);
    Sleep(1000);

    recvResponseAndEcho(_FSoc, (LPSTR)&servermessage, sizeof(servermessage));
    Sleep(1000);

    send(_FSoc, cmd, strlen(cmd), 0);
    listFilesBufferReadLength = readSocketData(port, listFilesBuffer, 4096);

    if(listFilesBufferReadLength > 0) {
        std::istringstream fileLines(listFilesBuffer);
        string fileLine, localFileExtension;
        std::vector<std::string> filesList;

        while (std::getline(fileLines, fileLine)) {
            // Trim whitespaces from each line
            fileLine = fileLine.erase(fileLine.find_last_not_of(" \t\n\r") + 1);
            if(fileLine != "." && fileLine != "..")
                // Save to a vector or strings if it's not current and level up directory
                filesList.push_back(fileLine);
        }

        if(useLocalFileExtension) {
            int dotPos = localFile.find_last_of(".");
            localFileExtension = dotPos > 0 ? localFile.substr(dotPos) : "";
        }

        vector<string>::iterator fileFind;

        do {
            // Generate random filenames of 14 characters length
            // untill the filename does not exist on the remote FTP server path
            newFilename = generateRandomFilename(14);
            if(useLocalFileExtension)
                newFilename += localFileExtension;
        } while((fileFind = std::find(filesList.begin(), filesList.end(), newFilename)) != filesList.end());
    }

    return newFilename;
}

void sendLogIn(SOCKET socket) {               
    char userCmd[128] = "USER ";
    char passCmd[128] = "PASS ";

    strcat(userCmd, REMOTE_FTP_USERNAME);
    strcat(userCmd, "\r\n");
    send(socket, userCmd, strlen(userCmd), 0);
    recvResponseAndEcho(socket);

    strcat(passCmd, REMOTE_FTP_PASSWORD);
    strcat(passCmd, "\r\n");
    send(socket, passCmd, strlen(passCmd), 0);
    recvResponseAndEcho(socket);
}

void sendFileRequest(SOCKET socket, string remoteDirectory, string storFilename) {
    char cwdCmd[MAX_PATH] = "CWD ";
    char storCmd[MAX_PATH] = "STOR ";
    //char stouCmd[] = "STOU\r\n";

    // Change current working directory
    strcat(cwdCmd, remoteDirectory.c_str());
    strcat(cwdCmd, "\r\n");
    send(socket,  cwdCmd, strlen(cwdCmd), 0);
    Sleep(1000);
    recvResponseAndEcho(socket);

    // Send stor ftp command
    strcat(storCmd, storFilename.c_str());
    strcat(storCmd, "\r\n");
    send(socket, storCmd, strlen(storCmd), 0);

    // Send stou ftp command insted of stor
    //send(socket, stouCmd, strlen(stouCmd), 0);
}


// Image/binary type
void sendTypeICmd(SOCKET socket) {
    char typeCmd[] = "TYPE I\r\n";
    send(socket, typeCmd, strlen(typeCmd), 0);
    Sleep(1000);
    recvResponseAndEcho(socket);
}

// Text type
void sendTypeACmd(SOCKET socket) {
    char typeCmd[] = "TYPE A\r\n";
    send(socket, typeCmd, strlen(typeCmd), 0);
    Sleep(1000);
    recvResponseAndEcho(socket);
}

int sendPasvCmd(SOCKET socket) {
    int result = -1;
    char pasvCmd[] = "PASV\r\n";
    char srvResponse[4096];

    send(socket, pasvCmd, strlen(pasvCmd), 0);
    Sleep(1000);
    recvResponseAndEcho(socket, (LPSTR)&srvResponse, sizeof(srvResponse));

    // Read the pasv response, something like:
    // 227 Entering Passive Mode (x,x,x,x,y,y)
    // where x are the ip bytes and y are the port bytes
    string pasvMessage = srvResponse;
    int modeStart = pasvMessage.find("Mode");

    if(modeStart > 0) {
        int parenthesisStart = pasvMessage.find("(", modeStart);
        if(parenthesisStart > 0) {
            string currentPasvByte;
            char currentChar;
            int currentCharPos = parenthesisStart,
                currentPasvByteIndex = 0,
                highByte = -1,
                lowByte = -1;

            do {
                currentChar = pasvMessage[++currentCharPos];
                if(currentChar == ',' || currentChar == ')') {
                    if(currentPasvByteIndex == 4)
                        highByte = stoi(currentPasvByte);
                    else if(currentPasvByteIndex == 5)
                        lowByte = stoi(currentPasvByte);
                    currentPasvByteIndex++;
                    currentPasvByte = "";
                } else
                    currentPasvByte += currentChar;
            } while(currentChar != ')');

            // Assemble the port number by joing hi and lo port number bytes
            if(highByte != -1 && lowByte != -1)
                result = (highByte << 8) | lowByte;
        }
    }
    return result;
}

void sendQuitCmd(SOCKET socket) {
    char quitCmd[] = "QUIT\r\n";
    send(socket, quitCmd, strlen(quitCmd), 0);
    recvResponseAndEcho(socket);
}

BOOL ftpSocket(string localFile, int port) {
    BOOL result = false;
    SOCKET sock;
    SOCKADDR_IN pasvserver;
    int connectionerror2;
    int trycount2 = 2;

    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sock == INVALID_SOCKET){
        WSACleanup();
        return 0;
    }

    pasvserver.sin_family = AF_INET;
    pasvserver.sin_port = htons(port);                                
    pasvserver.sin_addr.s_addr = inet_addr(REMOTE_FTP_HOST_IP); //Once again the drivehq ftp server

    connectionerror2 = connect(sock, (LPSOCKADDR)&pasvserver, sizeof(struct sockaddr));
    while(connectionerror2 == SOCKET_ERROR){
        connectionerror2 = connect(sock, (LPSOCKADDR)&pasvserver, sizeof(struct sockaddr));
        trycount2++;
        if(trycount2 = 10) {
            closesocket(sock);
            WSACleanup();
            return false;
        }
    }

    HANDLE hFile = CreateFile(localFile.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);;

    if(hFile != INVALID_HANDLE_VALUE) {
        DWORD read;
        char buffer[4096];
        SetFilePointer(hFile, 0, NULL, FILE_BEGIN);

        while(ReadFile(hFile, buffer, 4096, &read, NULL) && read > 0) {
            send(sock, buffer, read, 0);
        }

        CloseHandle(hFile);
        result = true;   
    }

    shutdown(sock, SD_BOTH);
    closesocket(sock);
    return result;
}

int sendFile(string localFile, string remotePath) {
    FreeConsole();
    WSAData WData;
    SOCKET FSoc;
    SOCKADDR_IN server;
    int connectionerror;
    int trycount = 2;
    int port;

    WSAStartup(MAKEWORD(2,2), &WData);
    FSoc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(FSoc == INVALID_SOCKET){
        WSACleanup();
        return 0;
    }

    server.sin_family = AF_INET;
    server.sin_port = htons(21);
    server.sin_addr.s_addr = inet_addr(REMOTE_FTP_HOST_IP); //this is the drivehq ftp server address.                                                    

    connectionerror = connect(FSoc, (LPSOCKADDR)&server, sizeof(struct sockaddr));
    while(connectionerror == SOCKET_ERROR){
        connectionerror = connect(FSoc, (LPSOCKADDR)&server, sizeof(struct sockaddr));
        trycount++;
        if(trycount = 10){
            closesocket(FSoc);
            WSACleanup();
            return 0;
        }
    }

    recvResponseAndEcho(FSoc);

    sendLogIn(FSoc);
    Sleep(1000);  //give the server and the client sometime to deal with the influx of new messages
    //so that data for the ip doesnt get mixed up.

    // Send typea command to transfer text data.
    // It works better for windows to unix text files transfer, bacause it fixes
    // the line endnings CR, LF on most ftp servers
    // Use typei command for binary data.
    //sendTypeACmd(FSoc);
    sendTypeACmd(FSoc);

    // Open passing mode for the files list data
    port = sendPasvCmd(FSoc);

    // Generate a unique filename that does not exists on the FTP remote directory
    string filename = generateUniqueFilename(FSoc, port, remotePath, true, localFile);

    // Open passing mode for the file transfer data
    port = sendPasvCmd(FSoc);

    sendFileRequest(FSoc, remotePath, filename);
    ftpSocket(localFile, port);
    recvResponseAndEcho(FSoc);

    sendQuitCmd(FSoc);
    shutdown(FSoc, SD_BOTH);
    closesocket(FSoc);
    WSACleanup();
    return 0;
}
person Christos Lytras    schedule 28.11.2016