Exemple #1
0
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;
}
Exemple #2
0
/**
 * 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
}
Exemple #3
0
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;
}
Exemple #4
0
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;
}
Exemple #5
0
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;
}
Exemple #6
0
Fichier : log.c Projet : awh44/FTP
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;
}
Exemple #7
0
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;
}
Exemple #8
0
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;
}
Exemple #9
0
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;
}
Exemple #10
0
Fichier : log.c Projet : awh44/FTP
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;

}
Exemple #11
0
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;
}
Exemple #12
0
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,"&lt;");
					else if (c== '>') string_append(&str,"&gt;");
					else if (c== '&') string_append(&str,"&amp;");
					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,"&nbsp;");
							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;
}
Exemple #13
0
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;
}
Exemple #14
0
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;
}
Exemple #15
0
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);
}