status_t handle_cdup_command(user_session_t *session, string_t *args, size_t len) { status_t error; if (!session->logged_in) { error = send_530(session); goto exit0; } string_t s; string_initialize(&s); string_assign_from_char_array(&s, session->directory); string_concatenate_char_array_with_size(&s, "/..", 3); char *resolved_dir = realpath(string_c_str(&s), NULL); if (resolved_dir == NULL || !is_directory(resolved_dir)) { error = send_550(session); goto exit1; } free(session->directory); session->directory = resolved_dir; error = send_200(session); if (error) { goto exit1; } exit1: string_uninitialize(&s); exit0: return error; }
/** * Turns the backtrace into a string that can be displayed * * @param bt * @return */ char *arch_debug_bt2string(struct bt *bt) { #if defined(__SASC) && defined(DEBUG_RESTRACK) string str; char buf[120]; int i; int last_line = -1; char *last_file = NULL; if (!bt) return NULL; if (!(string_initialize(&str,200))) return NULL; /* Load debug infos if not done yet */ arch_debug_load(); string_append(&str,"Backtrace\n"); for (i=0;i<bt->addr_used;i++) { ULONG offset = (ULONG)bt->addr[i] - (ULONG)__code_base; sm_snprintf(buf,sizeof(buf),"\t\t%p (offset %p",bt->addr[i],offset); string_append(&str,buf); if (debug_info_count > 0) { struct debug_info *di; int offset = (ULONG)bt->addr[i] - (ULONG)__code_base; BIN_SEARCH(debug_info_array, 0, debug_info_count-1, (debug_info_array[m].offset <= offset) ? (offset < debug_info_array[m+1].offset?0:(1)):(-1), di); if (di && (di->filename != last_file || di->line != last_line)) { string_append(&str,", "); sm_snprintf(buf,sizeof(buf),"%s/%d",di->filename,di->line); string_append(&str,buf); last_file = di->filename; last_line = di->line; } } string_append(&str,")\n"); } return str.str; #else return NULL; #endif }
status_t handle_cwd_command(user_session_t *session, string_t *args, size_t len) { status_t error; if (!session->logged_in) { error = send_530(session); goto exit0; } if (len < 2) { error = send_501(session); goto exit0; } string_t new_dir; string_initialize(&new_dir); char *first_c_str = string_c_str(args + 1); if (first_c_str[0] != '/' && first_c_str[0] != '~') { string_assign_from_char_array(&new_dir, session->directory); char_vector_push_back(&new_dir, '/'); } size_t i; for (i = 1; i < len; i++) { string_concatenate(&new_dir, args + i); } char *resolved_dir = realpath(string_c_str(&new_dir), NULL); if (resolved_dir == NULL || !is_directory(resolved_dir)) { error = send_550(session); goto exit1; } free(session->directory); session->directory = resolved_dir; error = send_250(session); if (error) { goto exit1; } exit1: string_uninitialize(&new_dir); exit0: return error; }
status_t send_257(user_session_t *session) { string_t wd; string_initialize(&wd); string_assign_from_char_array(&wd, "\""); string_concatenate_char_array(&wd, session->directory); string_concatenate_char_array(&wd, "\""); status_t error = send_response(session->command_sock, PATH_CREATED, string_c_str(&wd), session->server->log, 0); string_uninitialize(&wd); return error; }
status_t handle_pasv_command(user_session_t *session, string_t *args, size_t len) { status_t error = SUCCESS; if (!session->server->pasv_enabled) { error = send_502(session); goto exit0; } if (!session->logged_in) { error = send_530(session); goto exit0; } int listen_sock; uint16_t listen_port; error = set_up_listen_socket(&listen_sock, &listen_port, AF_INET, session->server->ip4); if (error) { goto exit0; } string_t message; string_initialize(&message); string_assign_from_char_array(&message, "Entering passive mode ("); create_comma_delimited_address(&message, session->server->ip4, listen_port); char_vector_push_back(&message, ')'); error = send_response(session->command_sock, ENTERING_PASSIVE_MODE, string_c_str(&message), session->server->log, 0); if (error) { goto exit2; } struct sockaddr_in cad; socklen_t clilen = sizeof cad; session->data_sock = accept(listen_sock, (struct sockaddr *) &cad, &clilen); if (session->data_sock < 0) { error = ACCEPT_ERROR; goto exit2; } exit2: close(listen_sock); exit1: string_uninitialize(&message); exit0: return error; }
status_t open_log_file_in_dir(log_t *log, char *dirname, int files_to_keep, int next_log_num, uint8_t threaded) { status_t error = SUCCESS; string_t generic_filename; string_initialize(&generic_filename); string_assign_from_char_array(&generic_filename, dirname); string_concatenate_char_array(&generic_filename, LOG_FILE_NAME); //plus 1 for the '\0' char next_string[LOG_FILE_EXT_LEN + 1]; sprintf(next_string, "%03d", next_log_num); string_t opening_name; string_initialize(&opening_name); char_vector_copy(&opening_name, &generic_filename); string_concatenate_char_array(&opening_name, next_string); error = open_log_file_clobber_opt(log, string_c_str(&opening_name), threaded, 1); if (error) { goto exit0; } if (files_to_keep > 0) { sprintf(next_string, "%03d", (next_log_num - files_to_keep + MAX_LOG_FILES) % MAX_LOG_FILES); string_concatenate_char_array(&generic_filename, next_string); unlink(string_c_str(&generic_filename)); } exit0: string_uninitialize(&opening_name); string_uninitialize(&generic_filename); return error; }
status_t handle_port_command(user_session_t *session, string_t *args, size_t len) { status_t error; if (!session->server->port_enabled) { //This command can't actually send back a 'not implemented' response, so //approximate it with an "unrecognized command" response error = send_500(session); goto exit0; } if (!session->logged_in) { error = send_530(session); goto exit0; } size_t ip_len; string_t *split = string_split(args + 1, ',', &ip_len); string_t host; string_initialize(&host); uint16_t port; error = parse_ip_and_port(split, ip_len, &host, &port); if (error) { error = send_501(session); goto exit1; } error = make_connection(&session->data_sock, string_c_str(&host), port); if (error) { send_response(session->command_sock, SERVICE_NOT_AVAILABLE, "Could not connect to port", session->server->log, 0); goto exit1; } error = send_200(session); if (error) { goto exit1; } exit1: string_uninitialize(&host); exit0: return error; }
status_t handle_pass_command(user_session_t *session, string_t *args, size_t len) { status_t error; //Make sure that a USER command has already been executed by checking this //pointer value if (session->account == NULL) { error = send_503(session); goto exit0; } if (len < 2) { error = send_501(session); goto exit0; } string_t password; string_initialize(&password); size_t i; for (i = 1; i < len; i++) { string_concatenate(&password, args + i); } if (!bool_strcmp(string_c_str(&password), session->account->password)) { error = send_530(session); } error = send_330(session); if (error) { goto exit1; } session->logged_in = 1; exit1: string_uninitialize(&password); exit0: return error; }
status_t handle_user_command(user_session_t *session, string_t *args, size_t len) { status_t error; if (session->logged_in) { error = send_330(session); goto exit0; } if (len < 2) { error = send_501(session); goto exit0; } string_t username; string_initialize(&username); size_t i; for (i = 1; i < len; i++) { string_concatenate(&username, args + i); } get_account_by_username(session->server->accounts, string_c_str(&username), &session->account); if (session->account == NULL) { error = send_530(session); goto exit1; } error = send_331(session); if (error) { goto exit1; } exit1: string_uninitialize(&username); exit0: return error; }
status_t prepend_and_write_to_log(log_t *log, string_t *message, char *prepend, size_t size) { status_t error = SUCCESS; string_t final_message; string_initialize(&final_message); string_assign_from_char_array_with_size(&final_message, prepend, size); string_concatenate(&final_message, message); error = write_log(log, string_c_str(&final_message), string_length(&final_message)); if (error) { goto exit0; } exit0: string_uninitialize(&final_message); return error; }
status_t send_response(int sock, char *code, char *message, log_t *log, uint8_t multiline) { status_t error; char sep; if (multiline) { sep = '-'; } else { sep = ' '; } string_t response; string_initialize(&response); string_assign_from_char_array_with_size(&response, code, 3); char_vector_push_back(&response, sep); string_concatenate_char_array(&response, message); string_concatenate_char_array(&response, "\r\n"); if (multiline) { string_concatenate_char_array_with_size(&response, code, 3); string_concatenate_char_array_with_size(&response, " \r\n", 3); } error = send_string(sock, &response, log); if (error) { goto exit0; } exit0: string_uninitialize(&response); return error; }
char *text2html(unsigned char *buffer, int buffer_len, int flags, const char *fonttag) { unsigned char *saved_buffer = buffer; string str; if (string_initialize(&str,1024)) { char buf[512]; int last_color = 0; /* the color of the current line */ int eval_color = 2; /* recheck the color */ int initial_color = 1; int level = 0; int line = 0; /* the type of the line */ if (flags & TEXT2HTML_BODY_TAG) { sm_snprintf(buf,sizeof(buf),"<BODY BGCOLOR=\"#%06x\" TEXT=\"#%06x\" LINK=\"#%06x\">",user.config.read_background,user.config.read_text,user.config.read_link); string_append(&str,buf); } string_append(&str,fonttag); /* accepts NULL pointer */ /* check for >0, otherwise we'll be in an endless loop if buffer_len becomes <0 */ while (buffer_len > 0) { if (eval_color) { int new_level = 0; int buffer2_len = buffer_len; unsigned char *buffer2 = buffer; int new_color = 0; /* Determine the citation level. Afterwards, buffer2 will point to the end of the citation symbols. */ while (buffer2_len) { unsigned char c = *buffer2; if (c == '>') { new_level++; if (new_color == 1) new_color = 2; else new_color = 1; } else { if (c != ' ') break; if (c == ' ' && !new_level) break; } buffer2_len--; buffer2++; } if (user.config.read_graphical_quote_bar) { /* When graphical quote bar is enabled we skip all quotation symbols */ buffer = buffer2; buffer_len = buffer2_len; if (level != new_level) { const char *begin_quote_string = "<TABLE BGCOLOR=\"#%06x\" WIDTH=\"100%%\" STYLE=\"border-left: 3px solid #%06x; border-right: 3px solid #%06x;\"><TD><FONT COLOR=\"#%06x\">"; const char *end_quote_string = "</FONT></TD></TABLE>"; /* If new level is larger */ for (;level < new_level; level++) { unsigned int color = level%2?user.config.read_quoted:user.config.read_old_quoted; sm_snprintf(buf,sizeof(buf),begin_quote_string,user.config.read_quoted_background,color,color,color); string_append(&str,buf); } /* If new level is lower */ for (;level > new_level; level--) string_append(&str,end_quote_string); } } else { if (last_color != new_color) { const char *begin_quote_string = "<FONT COLOR=\"#%x\">"; const char *end_quote_string = "</FONT>"; if (!initial_color) string_append(&str,end_quote_string); if (new_color == 1) { sm_snprintf(buf,sizeof(buf),begin_quote_string,user.config.read_quoted); string_append(&str,buf); } else if (new_color == 2) { sm_snprintf(buf,sizeof(buf),begin_quote_string,user.config.read_old_quoted); string_append(&str,buf); } last_color = new_color; if (new_color) initial_color = 0; else initial_color = 1; } } eval_color = 0; } if (!mystrnicmp("http:",(char*)buffer,5)) write_uri(&buffer, &buffer_len, &str); else if (!mystrnicmp("mailto:",(char*)buffer,7)) write_uri(&buffer, &buffer_len, &str); else if (!mystrnicmp("ftp:",(char*)buffer,4)) write_uri(&buffer, &buffer_len, &str); else if (!mystrnicmp("https:",(char*)buffer,6)) write_uri(&buffer, &buffer_len, &str); else { unsigned char c; c = *buffer; if ((c == '@') && (buffer > saved_buffer)) { /* A @ has been encountered, check if this belongs to an email adresse by traversing back * within the string */ unsigned char *buffer2 = buffer - 1; unsigned char *buffer3; unsigned char c2; int buffer2_len = buffer_len + 1; char *address; while ((c2 = *buffer2) && buffer2 > saved_buffer) { static const char noaliaschars[] = { " ()<>@,;:\\\"[]\n\r"}; if (strchr(noaliaschars,c2)) { buffer2++; break; } buffer2_len++; buffer2--; } if ((buffer3 = (unsigned char*)parse_addr_spec((char*)buffer2, &address))) { int email_len; /* crop the string to the beginning of the email address */ string_crop(&str,0,str.len - (buffer - buffer2)); buffer_len += buffer - buffer2; buffer -= buffer - buffer2; email_len = buffer3 - buffer; buffer_len -= email_len; buffer = buffer3; sm_snprintf(buf,sizeof(buf),"<A HREF=\"mailto:%s\"%s>",address, user.config.read_link_underlined?"":" STYLE=\"TEXT-DECORATION: none\""); string_append(&str,buf); write_unicode(address,&str); string_append(&str,"</A>"); free(address); continue; } } if (user.config.read_smilies) { unsigned int i; int smily_used = 0; /* No look into the smily table, this is slow and needs to be improved */ for (i=0;i<sizeof(smily)/sizeof(struct smily);i++) { if (!strncmp(smily[i].ascii,(char*)buffer,strlen(smily[i].ascii))) { buffer += strlen(smily[i].ascii); buffer_len -= strlen(smily[i].ascii); sm_snprintf(buf,sizeof(buf),"<IMG SRC=\"PROGDIR:Images/%s\" VALIGN=\"middle\" ALT=\"%s\">",smily[i].gfx,smily[i].ascii); string_append(&str,buf); smily_used = 1; } } if (smily_used) continue; } if (!strncmp("\n<sb>",(char*)buffer,5)) { if (line) string_append(&str,"<BR></TD><TD WIDTH=\"50%\"><HR></TD></TR></TABLE>"); line = 1; buffer += 5; buffer_len -= 5; string_append(&str,"<TABLE WIDTH=\"100%\" BORDER=\"0\"><TR><TD VALIGN=\"middle\" WIDTH=\"50%\"><HR></TD><TD>"); continue; } if (!strncmp("\n<tsb>",(char*)buffer,6)) { if (line) string_append(&str,"<BR></TD><TD WIDTH=\"50%\"><HR></TD></TR></TABLE>"); line = 2; buffer += 6; buffer_len -= 6; string_append(&str,"<TABLE WIDTH=\"100%\" BORDER=\"0\"><TR><TD VALIGN=\"middle\" WIDTH=\"50%\"><HR></TD><TD>"); continue; } if (c < 128) { buffer++; buffer_len--; if (c== '<') string_append(&str,"<"); else if (c== '>') string_append(&str,">"); else if (c== '&') string_append(&str,"&"); else if (c == 10) { eval_color = 1; string_append(&str,"<BR>\n"); if (line) { string_append(&str,"</TD><TD WIDTH=\"50%\"><HR></TD></TR></TABLE>"); line = 0; } } else { if (c == 32) { if (*buffer == 32 || (flags & TEXT2HTML_NOWRAP)) string_append(&str," "); else string_append(&str," "); } else { if (c) { string_append_part(&str,(char*)&c,1); } } } } else { unsigned int unicode; int len = 0; /* check if it really could be a utf8 char */ if (utf8islegal((utf8*)buffer, (utf8*)(buffer+buffer_len))) { len = utf8tochar((utf8*)buffer, &unicode, user.config.default_codeset); } if ((len == 0) || (len > buffer_len)) { /* something wrong with that utf8 sequence */ unicode = '?'; len = 1; } buffer_len -= len; buffer += len; if (unicode == 0) unicode = '_'; sm_snprintf(buf,sizeof(buf),"&#%d;",unicode); string_append(&str,buf); } } } if (fonttag) string_append(&str,"</FONT>"); if (flags & TEXT2HTML_ENDBODY_TAG) string_append(&str,"</BODY>"); SM_DEBUGF(20,("%s\n",str.str)); return str.str; } return NULL; }
status_t handle_list_command(user_session_t *session, string_t *args, size_t len) { /* Possible codes: 125: Data connection already open; transfer starting 150: File status okay; about to open data connection 226: Closing data connection; success 250: File action okay, completed 425: Can't open data connection 426: Connection closed 451: Aborted; local error 450: Requested file action not taken 500: Syntax error; command unrecognized 501: Syntax error in params 502: Not implemented 421: Service connection closing 530: Not logged in */ status_t error; if (!session->logged_in) { error = send_530(session); goto exit0; } if (session->data_sock < 0) { error = send_425(session); goto exit0; } string_t listing; string_initialize(&listing); string_t tmp; string_initialize(&tmp); DIR *directory; if (len < 2) { directory = opendir(session->directory); if (directory == NULL) { error = send_451(session); goto exit1; } struct dirent *entry; while ((entry = readdir(directory))) { string_concatenate_char_array(&listing, entry->d_name); char_vector_push_back(&listing, '\n'); } closedir(directory); } else { string_assign_from_char_array(&tmp, session->directory); char_vector_push_back(&tmp, '/'); string_concatenate(&tmp, args + 1); if (access(string_c_str(&tmp), F_OK) < 0) { error = send_501(session); goto exit1; } directory = opendir(string_c_str(&tmp)); if (directory == NULL) { if (errno != ENOTDIR) { error = send_451(session); goto exit1; } //Already know that the file exists because of the call to access, //so if the file is not a directory, assuming that it's a regular //file, so just list it. This could also be checked using the stat //function. string_concatenate(&listing, args + 1); char_vector_push_back(&listing, '\n'); } else { struct dirent *entry; while ((entry = readdir(directory))) { string_concatenate_char_array(&listing, entry->d_name); char_vector_push_back(&listing, '\n'); } closedir(directory); } } error = send_125(session); if (error) { send_451(session); goto exit1; } error = send_data_string(session->data_sock, &listing, session->server->log); if (error) { send_451(session); } else { error = send_226(session); } exit1: string_uninitialize(&tmp); string_uninitialize(&listing); close(session->data_sock); session->data_sock = -1; exit0: return error; }
status_t handle_retr_command(user_session_t *session, string_t *args, size_t len) { /** * Possible codes: * 125: transfer_starting * 150: about to open data connection * 226: Completed successfully; closing * 250: Requested file action completed * 425: Can't open data connection * 426: Connection closed * 451: Action aborted; local error * 450: File unavailable (busy) * 550: File unavailable (doesn't exit) * 500, 501, 421 */ status_t error; if (!session->logged_in) { error = send_530(session); goto exit0; } if (session->data_sock < 0) { error = send_425(session); goto exit0; } if (len < 2) { error = send_501(session); goto exit1; } string_t path; string_initialize(&path); string_assign_from_char_array(&path, session->directory); char_vector_push_back(&path, '/'); string_concatenate(&path, args + 1); int fd = open(string_c_str(&path), O_RDONLY, 0); if (fd < 0) { error = send_550(session); goto exit2; } string_t file_string; string_initialize(&file_string); ssize_t chars_read; char buff[512]; while ((chars_read = read(fd, buff, sizeof buff)) > 0) { string_concatenate_char_array_with_size(&file_string, buff, sizeof buff); } if (chars_read < 0) { error = send_451(session); goto exit3; } error = send_125(session); if (error) { //let the first error supercede any that might occur here, //so don't save to error send_451(session); goto exit3; } error = send_data_string(session->data_sock, &file_string, session->server->log); if (error) { send_451(session); } else { error = send_226(session); } exit3: string_uninitialize(&file_string); exit2: string_uninitialize(&path); exit1: close(session->data_sock); session->data_sock = -1; exit0: return error; }
void *client_handler(void *void_args) { user_session_t *session = (user_session_t *) void_args; status_t error; //Finish session initialization session->directory = realpath(".", NULL); if (session->directory == NULL) { error = REALPATH_ERROR; goto exit0; } session->data_sock = -1; //End session initialization error = send_response(session->command_sock, SERVICE_READY, "Ready. Please send USER.", session->server->log, 0); if (error) { goto exit1; } string_t command; string_initialize(&command); uint8_t done = 0; do { char_vector_clear(&command); error = read_single_line(session->command_sock, &command); if (!error) { error = write_received_message_to_log(session->server->log, &command); if (!error) { //Remove the CRLF from the command char_vector_pop_back(&command); char_vector_pop_back(&command); //Split the string up by spaces size_t len; string_t *split = string_split_skip_consecutive(&command, ' ', &len, 1); char *c_str = string_c_str(split + 0); //Determine which command has been sent if (bool_strcmp(c_str, "USER")) { error = handle_user_command(session, split, len); } else if (bool_strcmp(c_str, "PASS")) { error = handle_pass_command(session, split, len); } else if (bool_strcmp(c_str, "CWD")) { error = handle_cwd_command(session, split, len); } else if (bool_strcmp(c_str, "CDUP")) { error = handle_cdup_command(session, split, len); } else if (bool_strcmp(c_str, "QUIT")) { done = 1; error = handle_quit_command(session, split, len); } else if (bool_strcmp(c_str, "PASV")) { error = handle_pasv_command(session, split, len); } else if (bool_strcmp(c_str, "EPSV")) { error = handle_epsv_command(session, split, len); } else if (bool_strcmp(c_str, "PORT")) { error = handle_port_command(session, split, len); } else if (bool_strcmp(c_str, "EPRT")) { error = handle_eprt_command(session, split, len); } else if (bool_strcmp(c_str, "RETR")) { error = handle_retr_command(session, split, len); } else if (bool_strcmp(c_str, "PWD")) { error = handle_pwd_command(session, split, len); } else if (bool_strcmp(c_str, "LIST")) { error = handle_list_command(session, split, len); } else if (bool_strcmp(c_str, "HELP")) { error = handle_help_command(session, split, len); } else { error = handle_unrecognized_command(session, split, len); } size_t i; for (i = 0; i < len; i++) { string_uninitialize(split + i); } free(split); } } if (error) { char message[] = "Error encountered while processing: "; string_t error_message; string_initialize(&error_message); char *error_string = get_error_message(error); string_assign_from_char_array(&error_message, error_string); prepend_and_write_to_log(session->server->log, &error_message, message, sizeof message); string_uninitialize(&error_message); } } while (!error && !done); char quitting_message[] = "Client quitting.\n"; exit2: string_uninitialize(&command); exit1: free(session->directory); exit0: write_log(session->server->log, quitting_message, sizeof quitting_message); printf("%s", quitting_message); free(session); pthread_exit(NULL); }