void molten_stats(connection_t *con) { size_t length; length = stats_get_count(); for(size_t i = 0; i < length; i++) { if (con_print(con, "STAT %s %lu\r\n", stats_get_name(i), stats_get_value_by_num(i)) < 0) { enqueue(&molten_quit, con); return; } } length = stats_derived_count(); for(size_t i = 0; i < length; i++) { if (con_print(con, "STAT %s %lu\r\n", stats_derived_name(i), stats_derived_value(i)) < 0) { enqueue(&molten_quit, con); return; } } con_write_bl(con, "END\r\n", 5) < 0 ? enqueue(&molten_quit, con) : enqueue(&molten_parse, con); return; }
/** * @brief Get the UIDL for a message or collection of messages, in response to a POP3 UIDL command. * @param con the POP3 client connection issuing the command. * @return This function returns no value. */ void pop_uidl(connection_t *con) { uint64_t number; inx_cursor_t *cursor; meta_message_t *active; bool_t result; if (con->pop.session_state != 1) { pop_invalid(con); return; } // Get the argument, if any. result = pop_num_parse(con, &number, false); meta_user_rlock(con->pop.user); // Output all of the messages that aren't deleted or appended. if (!result) { number = 1; con_print(con, "+OK %llu messages total.\r\n", pop_total_messages(con->pop.user->messages)); if (con->pop.user->messages && (cursor = inx_cursor_alloc(con->pop.user->messages))) { while ((active = inx_cursor_value_next(cursor))) { if ((active->status & (MAIL_STATUS_APPENDED | MAIL_STATUS_HIDDEN)) == 0) { con_print(con, "%llu %llu\r\n", number++, active->messagenum); } else if ((active->status & MAIL_STATUS_APPENDED) == 0) { number++; } } inx_cursor_free(cursor); } con_write_bl(con, ".\r\n", 3); } // Output a specific message. else { if (!(active = pop_get_message(con->pop.user->messages, number))) { con_write_bl(con, "-ERR Message not found.\r\n", 25); } else if ((active->status & MAIL_STATUS_HIDDEN) == MAIL_STATUS_HIDDEN) { con_write_bl(con, "-ERR Message marked for deletion.\r\n", 35); } else { con_print(con, "+OK %llu %llu\r\n", number, active->messagenum); } } meta_user_unlock(con->pop.user); return; }
/** * @brief Display the POP3 server capabilities, in response to a POP3 CAPA command. * @param con the POP3 client connection issuing the command. * @return This function returns no value. */ void pop_capa(connection_t *con) { // If the user is in authorization mode via an insecure channel and there is an SSL context, output the STLS capability. if (con->pop.session_state == 0 && con_secure(con) == 0) { con_print(con, "+OK Capabilities follow.\r\nTOP\r\nUSER\r\nUIDL\r\nSTLS\r\nPIPELINING\r\nEXPIRE NEVER\r\nLOGIN-DELAY 0\r\n" "RESP-CODES\r\nAUTH-RESP-CODE\r\nIMPLEMENTATION magmad %s\r\n.\r\n", build_version()); } // Otherwise don't advertise STLS. else { con_print(con, "+OK Capabilities follow.\r\nTOP\r\nUSER\r\nUIDL\r\nPIPELINING\r\nEXPIRE NEVER\r\nLOGIN-DELAY 0\r\n" "RESP-CODES\r\nAUTH-RESP-CODE\r\nIMPLEMENTATION magmad %s\r\n.\r\n", build_version()); } return; }
/** * @brief Get the sequence number of the last read message, in response to a POP3 LAST command. * @see pop_get_last() * @param con the POP3 client connection issuing the command. * @return This function returns no value. */ void pop_last(connection_t *con) { uint64_t number = 0; if (con->pop.session_state != 1) { pop_invalid(con); return; } meta_user_rlock(con->pop.user); number = pop_get_last(con->pop.user->messages); meta_user_unlock(con->pop.user); // Print the information. con_print(con, "+OK %llu\r\n", number); return; }
/** * @brief Display a user's message statistics, in response to a POP3 STAT command. * @see pop_total_messages(), pop_total_size() * @param con the POP3 client connection issuing the command. * @return This function returns no value. */ void pop_stat(connection_t *con) { uint64_t count, size; if (con->pop.session_state != 1) { pop_invalid(con); return; } meta_user_rlock(con->pop.user); count = pop_total_messages(con->pop.user->messages); size = pop_total_size(con->pop.user->messages); meta_user_unlock(con->pop.user); // Print the information. con_print(con, "+OK %llu %llu\r\n", count, size); return; }
void con_print_pos(int x, int y, const char *s) { con_goto(x,y); con_print(s); }
/** * @brief Retrieve a user's message, in response to a POP3 RETR command. * @note This function will fail if a deleted message was specified by the user. * @param con the POP3 client connection issuing the command. * @return This function returns no value. */ void pop_retr(connection_t *con) { uint64_t number; meta_message_t *meta; mail_message_t *message; if (con->pop.session_state != 1) { pop_invalid(con); return; } // Which message are we getting. if (!pop_num_parse(con, &number, true)) { con_write_bl(con, "-ERR The retrieve command requires a numeric argument.\r\n", 56); return; } meta_user_rlock(con->pop.user); // Get the message. if (!(meta = pop_get_message(con->pop.user->messages, number))) { meta_user_unlock(con->pop.user); con_write_bl(con, "-ERR Message not found.\r\n", 25); return; } // Check for deletion. if ((meta->status & MAIL_STATUS_HIDDEN) == MAIL_STATUS_HIDDEN) { meta_user_unlock(con->pop.user); con_write_bl(con, "-ERR This message has been marked for deletion.\r\n", 49); return; } // Load the message and spit back the right number of lines. if (!(message = mail_load_message(meta, con->pop.user, con->server, 1))) { meta_user_unlock(con->pop.user); con_write_bl(con, "-ERR The message you requested could not be loaded into memory. It has either been " "deleted by another connection or is corrupted.\r\n", 131); return; } meta_user_unlock(con->pop.user); // Dot stuff the message. st_replace(&(message->text), PLACER("\n.", 2), PLACER("\n..", 3)); // Tell the client to prepare for a message. The size is strictly informational. con_print(con, "+OK %u characters follow.\r\n", st_length_get(message->text)); // We use raw socket IO because it is much faster when writing large amounts of data. con_write_st(con, message->text); // If the message didn't end with a line break, spit two. if (*(st_char_get(message->text) + st_length_get(message->text) - 1) == '\n') { con_write_bl(con, ".\r\n", 3); } else { con_write_bl(con, "\r\n.\r\n", 5); } mail_destroy(message); return; }
/** * @brief Perform client command processing on an established imap session. * @note This function will read the next line of user input, parse the command, and then attempt to execute it with the appropriate handler. * @param con a pointer to the connection object underlying the imap session. * @return This function returns no value. */ void imap_process(connection_t *con) { int_t state; command_t *command, client = { .function = NULL }; // If the connection indicates an error occurred, or the socket was closed by the client we send the connection to the logout function. if (((state = con_read_line(con, false)) < 0) || (state == -2)) { con->command = NULL; enqueue(&imap_logout, con); return; } else if (pl_empty(con->network.line) && ((con->protocol.spins++) + con->protocol.violations) > con->server->violations.cutoff) { con->command = NULL; enqueue(&imap_logout, con); return; } else if (pl_empty(con->network.line)) { con->command = NULL; enqueue(&imap_process, con); return; } // Parse the line into its tag and command elements. if ((state = imap_command_parser(con)) < 0) { // Try to be helpful about the parsing error. if (state == -1) { con_write_bl(con, "* BAD Unable to parse the command tag.\r\n", 40); } else if (state == -2) { con_print(con, "%.*s BAD Unable to parse the command.\r\n", st_length_int(con->imap.tag), st_char_get(con->imap.tag)); } else { con_print(con, "%.*s BAD The command arguments were submitted using an invalid syntax.\r\n", st_length_int(con->imap.tag), st_char_get(con->imap.tag)); } // If the client keeps breaking rules drop them. if (((con->protocol.spins++) + con->protocol.violations) > con->server->violations.cutoff) { con->command = NULL; enqueue(&imap_logout, con); return; } // Requeue and hope the next line of data is useful. con->command = NULL; enqueue(&imap_process, con); return; } client.string = st_char_get(con->imap.command); client.length = st_length_get(con->imap.command); if ((command = bsearch(&client, imap_commands, sizeof(imap_commands) / sizeof(imap_commands[0]), sizeof(command_t), imap_compare))) { con->command = command; con->protocol.spins = 0; if (command->function == &imap_logout) { enqueue(command->function, con); } else { requeue(command->function, &imap_requeue, con); } } else { con->command = NULL; requeue(&imap_invalid, &imap_requeue, con); } return; }