#include #include #include #include #include #include #include #include #include #define PORT 31337 #define BUFFER_SIZE (32 * 1024) #define MAX_CLIENTS 10 #define UID 1000 #define SLIDES_PATH "femtotcp.gitslides" /* Telnet negotiation commands */ #define IAC 255 #define DO 253 #define WILL 251 #define SUPPRESS_GO_AHEAD 3 #define ECHO 1 #define LINEMODE 34 #define CMD_SIZE 256 #define PATH_SIZE 256 struct Client { int fd; char repo_path[PATH_SIZE]; char slides_path[PATH_SIZE]; char current_slide[BUFFER_SIZE]; }; /* Telnet negotiation to enable non-canonical mode */ void send_telnet_negotiation(int client_fd) { unsigned char suppress_go_ahead[] = {IAC, WILL, SUPPRESS_GO_AHEAD}; unsigned char disable_echo[] = {IAC, DO, ECHO}; unsigned char enable_linemode[] = {IAC, DO, LINEMODE}; write(client_fd, suppress_go_ahead, sizeof(suppress_go_ahead)); write(client_fd, disable_echo, sizeof(disable_echo)); write(client_fd, enable_linemode, sizeof(enable_linemode)); } /* Clear the screen */ void clear_screen(int client_fd) { const char *ESC_CLEAR = "\x1b[2J\x1b[H"; write(client_fd, ESC_CLEAR, strlen(ESC_CLEAR)); } /* Execute a git-slides command */ void execute_git_slides(const char *repo_path, const char *slide_command) { char command[CMD_SIZE]; snprintf(command, sizeof(command), "cd %s && git-slides %s", repo_path, slide_command); system(command); } /* Load slide content from file */ void load_slide_content(const char *slides_path, char *output) { int fd = open(slides_path, O_RDONLY); if (fd < 0) { snprintf(output, BUFFER_SIZE, "Error: Could not open slides file: %s\n", strerror(errno)); return; } ssize_t n = read(fd, output, BUFFER_SIZE - 1); if (n < 0) { snprintf(output, BUFFER_SIZE, "Error: Could not read slides file: %s\n", strerror(errno)); } else { output[n] = '\0'; /* Null-terminate the buffer */ } close(fd); } /* Handle client input and update slides */ void handle_client_input(struct Client *client, char command) { if (command == ' ') { execute_git_slides(client->repo_path, "next"); } else if (command == '\x08' || command == '\x7f') { /* Backspace or DEL */ execute_git_slides(client->repo_path, "prev"); } else if (command == 'q') { clear_screen(client->fd); close(client->fd); client->fd = -1; /* Mark as disconnected */ return; } else { snprintf(client->current_slide, BUFFER_SIZE, "Unknown command '%c'. Use SPACE, BACKSPACE, or Q.\n", command); } load_slide_content(client->slides_path, client->current_slide); } /* Send welcome message to the client */ void send_welcome_message(struct Client *client) { clear_screen(client->fd); const char *welcome_msg = "Welcome to the git-slides presentation!\n" "Use SPACE (forward), BACKSPACE (back), or Q (quit).\n\n"; write(client->fd, welcome_msg, strlen(welcome_msg)); write(client->fd, client->current_slide, strlen(client->current_slide)); } /* Clean up client resources */ void cleanup_client(struct Client *client) { if (client->fd != -1) { close(client->fd); client->fd = -1; } if (strlen(client->repo_path) > 0) { char rm_command[CMD_SIZE]; snprintf(rm_command, sizeof(rm_command), "rm -rf %s", client->repo_path); system(rm_command); } } int main() { int server_fd; struct sockaddr_in server_addr; setuid(UID); /* Drop privileges */ printf("User ID: %d\n", getuid()); server_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_fd < 0) { perror("socket"); exit(EXIT_FAILURE); } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); server_addr.sin_addr.s_addr = INADDR_ANY; if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("bind"); close(server_fd); exit(EXIT_FAILURE); } if (listen(server_fd, MAX_CLIENTS) < 0) { perror("listen"); close(server_fd); exit(EXIT_FAILURE); } struct pollfd fds[MAX_CLIENTS + 1]; struct Client clients[MAX_CLIENTS] = {0}; int i; for (i = 0; i < MAX_CLIENTS; i++) { clients[i].fd = -1; } fds[0].fd = server_fd; fds[0].events = POLLIN; printf("Server running on port %d\n", PORT); while (1) { int num_fds = 1; /* Prepare the pollfd array */ for (i = 0; i < MAX_CLIENTS; i++) { if (clients[i].fd != -1) { fds[num_fds].fd = clients[i].fd; fds[num_fds].events = POLLIN; num_fds++; } } int ret = poll(fds, num_fds, -1); if (ret < 0) { perror("poll"); break; } /* Handle new connections */ if (fds[0].revents & POLLIN) { int client_fd = accept(server_fd, NULL, NULL); if (client_fd < 0) { perror("accept"); continue; } /* Find an empty slot for the client */ int slot = -1; for (i = 0; i < MAX_CLIENTS; i++) { if (clients[i].fd == -1) { slot = i; break; } } if (slot == -1) { const char *msg = "Server full. Try again later.\n"; write(client_fd, msg, strlen(msg)); close(client_fd); } else { struct Client *client = &clients[slot]; char cp_command[CMD_SIZE]; client->fd = client_fd; snprintf(client->repo_path, PATH_SIZE, "/tmp/git_repo_%d", client_fd); snprintf(client->slides_path, PATH_SIZE, "%s/%s", client->repo_path, SLIDES_PATH); /* Create a temporary copy of the repo */ snprintf(cp_command, sizeof(cp_command), "cp -a . %s", client->repo_path); system(cp_command); execute_git_slides(client->repo_path, "prev"); load_slide_content(client->slides_path, client->current_slide); send_telnet_negotiation(client_fd); send_welcome_message(client); printf("Client connected: %d\n", client_fd); } } /* Handle client events */ for (i = 1; i < num_fds; i++) { if (fds[i].revents & POLLIN) { char command; int client_idx = i - 1; struct Client *client = &clients[client_idx]; ssize_t n = read(client->fd, &command, 1); if (n <= 0) { cleanup_client(client); continue; } handle_client_input(client, command); clear_screen(client->fd); write(client->fd, client->current_slide, strlen(client->current_slide)); } } } /* Cleanup */ for (i = 0; i < MAX_CLIENTS; i++) { cleanup_client(&clients[i]); } close(server_fd); return 0; }