std::ofstream file(SERVER_FOLDER + filename, std::ios::binary); // open the file in binary mode
if (file.is_open()) { // if the file is open
// Receive file size
std::streampos fileSize; // streampos is a type that represents the current position of the file pointer
int bytesRead = recv(clientSocket, reinterpret_cast<char*>(&fileSize), sizeof(fileSize), 0); // receive the number of bytes in the file from the client (reinterpret_cast is used to convert fileSize to char*)
if (bytesRead != sizeof(fileSize)) {
std::cerr << "Error receiving file size for: " << filename << std::endl; // if the file size is not received correctly
file.close();
return; // return from the function
}
char buffer[BUFFER_SIZE]; // create a buffer of size 1KB
while (fileSize > 0) { // while the file size is greater than 0
bytesRead = recv(clientSocket, buffer, std::min(sizeof(buffer), static_cast<size_t>(fileSize)), 0); // receive the file content, min() is used to avoid buffer overflow and static_cast is used to convert fileSize to size_t
if (bytesRead <= 0) { // if the file content is not received correctly
std::string filename(buffer); // convert the buffer to a string to get the file name
files.push_back(filename); // add the file name to the vector of files to compare later on the server side (to delete files that are no longer existing on client side)
send(clientSocket, "OK", 2, 0); // send the acknowledgment to the client that the file name was received successfully
time_t lastModifiedTime; // create a variable to store the last modified time
std::memset(buffer, 0, sizeof(buffer)); // reset the buffer
bytesRead = recv(clientSocket, buffer, sizeof(buffer), 0); // receive the last modified time from the client
if (bytesRead != sizeof(lastModifiedTime)) { // if the last modified time is not received correctly
std::cerr << "Error receiving last modified time for: " << filename << std::endl;
continue; // continue to the next file
}
std::memcpy(&lastModifiedTime, buffer, sizeof(lastModifiedTime)); // copy the last modified time from the buffer to the variable
std::ofstream changelog(SERVER_FOLDER"changelog.txt", std::ios::app); // open the changelog file in append mode (to append new logs to the end of the file)
bool existing = fileExists(SERVER_FOLDER + filename); // check if the file already exists before receiving it, to know if we need to log an update or a receive
send(clientSocket, "SEND", 4, 0); // send the message to the client to send the file
receiveFile(clientSocket, filename); // receive the file from the client
time_t now = time(0); // get the current time to log it
if (existing){ // if the file already existed before receiving, we log an update
changelog <<"["<< now << "] -" << " [-UPDATED-] - "<< filename << " from " << getFileLastModifiedTime(SERVER_FOLDER + filename) << " to " << lastModifiedTime << std::endl;
} else {
changelog <<"["<< now << "] -" << "[x] RECEIVED - "<< filename << std::endl; // if the file didn't exist before receiving, we log a receive
}
changelog.close(); // close the changelog file
} else {
send(clientSocket, "SKIP", 4, 0); // send the message to the client to skip the file if the file already exists
}
}
// compare client names to files on remote directory, to delete files that are no longer existing on client side
DIR* dir; // pointer to the directory
structdirent* ent; // pointer to the directory entry (file)
// check all files in server directory to see if they are in the vector, if not, we delete
if ((dir = opendir(SERVER_FOLDER)) != nullptr) { // open the server folder and check if it is not null (nullptr)
while ((ent = readdir(dir)) != nullptr) { // read the directory entries one by one and check if it is not null (nullptr)
if ((ent->d_type == DT_REG) && (strcmp(ent->d_name, "server") != 0) && (strcmp(ent->d_name, "changelog.txt") != 0)) { // regular file and not "server" or "changelog.txt"
std::string filename(ent->d_name); // get the filename from the directory entry
if (std::find(files.begin(), files.end(), filename) == files.end()) { // if the file is not found in the vector
std::ofstream changelog(SERVER_FOLDER"changelog.txt", std::ios::app); // open the changelog file in append mode (to append new logs to the end of the file)
time_t now = time(0);
changelog <<"["<< now << "] -" << " [=DELETED=] X " << filename << std::endl;
std::cout <<"["<< now << "] -" << " [=DELETED=] X " << filename << std::endl;
remove((SERVER_FOLDER + filename).c_str()); // delete the file
serverAddr.sin_family = AF_INET; // set the address family to IPv4
serverAddr.sin_addr.s_addr = INADDR_ANY; // set the IP address to the localhost, INADDR_ANY allows to bind to any address
serverAddr.sin_port = htons(PORT); // set the port number
// binding the socket
if (bind(serverSocket, (structsockaddr*)&serverAddr, sizeof(serverAddr)) == -1) {
std::cerr << "Error binding socket" << std::endl;
close(serverSocket);
exit(EXIT_FAILURE);
}
// listen for incoming connections
if (listen(serverSocket, 5) == -1) { // 5 is the maximum size of the queue, if listen() returns -1, it means that the queue is full or the socket is not listening
std::cerr << "Error listening for connections" << std::endl;
close(serverSocket);
exit(EXIT_FAILURE);
}
std::cout << "Server listening on port " << PORT << std::endl;
clientSocket = accept(serverSocket, (structsockaddr*)&clientAddr, &addrLen); // accept the connection from the client
if (clientSocket == -1) { // check if connection is not accepted
char clientIP[INET_ADDRSTRLEN]; // create a buffer to store the client IP address, inet_addrstrlen is the maximum length of the IP address
inet_ntop(AF_INET, &(clientAddr.sin_addr), clientIP, INET_ADDRSTRLEN); // convert the client IP address to a string, sin_addr is the IP address in the client address structure
std::cout << "Connection accepted from " << clientIP << ":" << ntohs(clientAddr.sin_port) << std::endl; // ntohs() converts the port number to host byte order
while (true) {
synchronizeFiles(clientSocket); // synchronize the files
// Sleep for SYNC_INTERVAL seconds before the next synchronization
std::cout << "Waiting for the next synchronization in " << SYNC_INTERVAL << " seconds..." << std::endl;