Mercurial > gftp.yaz
diff lib/rfc959.c @ 1:8b1883341c6f
Initial revision
author | masneyb |
---|---|
date | Mon, 05 Aug 2002 19:46:57 +0000 |
parents | |
children | 5551ab2301fe |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/rfc959.c Mon Aug 05 19:46:57 2002 +0000 @@ -0,0 +1,1171 @@ +/*****************************************************************************/ +/* rfc959.c - General purpose routines for the FTP protocol (RFC 959) */ +/* Copyright (C) 1998-2002 Brian Masney <masneyb@gftp.org> */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA */ +/*****************************************************************************/ + +#include "gftp.h" + +static int rfc959_connect ( gftp_request * request ); +static void rfc959_disconnect ( gftp_request * request ); +static long rfc959_get_file ( gftp_request * request, + const char *filename, + FILE * fd, + off_t startsize ); +static int rfc959_put_file ( gftp_request * request, + const char *filename, + FILE * fd, + off_t startsize, + off_t totalsize ); +static long rfc959_transfer_file ( gftp_request *fromreq, + const char *fromfile, + off_t fromsize, + gftp_request *toreq, + const char *tofile, + off_t tosize ); +static int rfc959_end_transfer ( gftp_request * request ); +static int rfc959_list_files ( gftp_request * request ); +static int rfc959_set_data_type ( gftp_request * request, + int data_type ); +static off_t rfc959_get_file_size ( gftp_request * request, + const char *filename ); +static int rfc959_data_connection_new ( gftp_request * request ); +static int rfc959_accept_active_connection ( gftp_request * request ); +static int rfc959_send_command ( gftp_request * request, + const char *command ); +static int write_to_socket ( gftp_request *request, + const char *command ); +static int rfc959_read_response ( gftp_request * request ); +static int rfc959_chdir ( gftp_request * request, + const char *directory ); +static int rfc959_rmdir ( gftp_request * request, + const char *directory ); +static int rfc959_rmfile ( gftp_request * request, + const char *file ); +static int rfc959_mkdir ( gftp_request * request, + const char *directory ); +static int rfc959_rename ( gftp_request * request, + const char *oldname, + const char *newname ); +static int rfc959_chmod ( gftp_request * request, + const char *file, + int mode ); +static int rfc959_site ( gftp_request * request, + const char *command ); +static char *parse_ftp_proxy_string ( gftp_request * request ); + +void +rfc959_init (gftp_request * request) +{ + g_return_if_fail (request != NULL); + + request->protonum = GFTP_FTP_NUM; + request->init = rfc959_init; + request->destroy = NULL; + request->connect = rfc959_connect; + request->disconnect = rfc959_disconnect; + request->get_file = rfc959_get_file; + request->put_file = rfc959_put_file; + request->transfer_file = rfc959_transfer_file; + request->get_next_file_chunk = NULL; + request->put_next_file_chunk = NULL; + request->end_transfer = rfc959_end_transfer; + request->list_files = rfc959_list_files; + request->get_next_file = rfc959_get_next_file; + request->set_data_type = rfc959_set_data_type; + request->get_file_size = rfc959_get_file_size; + request->chdir = rfc959_chdir; + request->rmdir = rfc959_rmdir; + request->rmfile = rfc959_rmfile; + request->mkdir = rfc959_mkdir; + request->rename = rfc959_rename; + request->chmod = rfc959_chmod; + request->set_file_time = NULL; + request->site = rfc959_site; + request->parse_url = NULL; + request->url_prefix = "ftp"; + request->protocol_name = "FTP"; + request->need_hostport = 1; + request->need_userpass = 1; + request->use_cache = 1; + request->use_threads = 1; + request->always_connected = 0; + gftp_set_config_options (request); +} + + +static int +rfc959_connect (gftp_request * request) +{ + char tempchar, *startpos, *endpos, *tempstr, *dir; + int sock, ret, resp; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (request->hostname != NULL, -2); + g_return_val_if_fail (request->username != NULL, -2); + + if (request->sockfd != NULL) + return (0); + + if ((sock = gftp_connect_server (request, "ftp")) < 0) + return (-1); + + if ((request->sockfd = fdopen (sock, "rb")) == NULL) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Cannot fdopen() socket: %s\n"), + g_strerror (errno)); + close (sock); + return (-2); + } + + if ((request->sockfd_write = fdopen (dup (sock), "wb")) == NULL) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Cannot fdopen() socket: %s\n"), + g_strerror (errno)); + gftp_disconnect (request); + return (-2); + } + + /* Get the banner */ + if (rfc959_read_response (request) != '2') + { + gftp_disconnect (request); + return (-2); + } + + /* Login the proxy server if available */ + if (request->use_proxy) + { + resp = '3'; + startpos = endpos = tempstr = parse_ftp_proxy_string (request); + while ((resp == '3' || resp == '2') && *startpos != '\0') + { + if (*endpos == '\n' || *endpos == '\0') + { + tempchar = *(endpos + 1); + if (*endpos != '\0') + *(endpos + 1) = '\0'; + if ((resp = rfc959_send_command (request, startpos)) < 0) + return (-2); + if (*endpos != '\0') + *(endpos + 1) = tempchar; + else + break; + startpos = endpos + 1; + } + endpos++; + } + g_free (tempstr); + } + else + { + tempstr = g_strconcat ("USER ", request->username, "\r\n", NULL); + resp = rfc959_send_command (request, tempstr); + g_free (tempstr); + if (resp < 0) + return (-2); + if (resp == '3') + { + tempstr = g_strconcat ("PASS ", request->password, "\r\n", NULL); + resp = rfc959_send_command (request, tempstr); + g_free (tempstr); + if (resp < 0) + return (-2); + } + if (resp == '3' && request->account) + { + tempstr = g_strconcat ("ACCT ", request->account, "\r\n", NULL); + resp = rfc959_send_command (request, tempstr); + g_free (tempstr); + if (resp < 0) + return (-2); + } + } + + if (resp != '2') + { + gftp_disconnect (request); + return (-2); + } + + if (request->data_type == GFTP_TYPE_BINARY) + tempstr = "TYPE I\r\n"; + else + tempstr = "TYPE A\r\n"; + + if (rfc959_send_command (request, tempstr) < 0) + return (-2); + + ret = -1; + if (request->directory != NULL && *request->directory != '\0') + { + ret = rfc959_chdir (request, request->directory); + if (request->sockfd == NULL) + return (-2); + } + + if (ret != 0) + { + if (rfc959_send_command (request, "PWD\r\n") != '2' || + request->sockfd == NULL) + { + gftp_disconnect (request); + return (-2); + } + + if ((tempstr = strchr (request->last_ftp_response, '"')) == NULL) + { + gftp_disconnect (request); + return (-2); + } + dir = tempstr + 1; + + if ((tempstr = strchr (dir, '"')) == NULL) + { + gftp_disconnect (request); + return (0); + } + if (tempstr != NULL) + *tempstr = '\0'; + + request->directory = g_malloc (strlen (dir) + 1); + strcpy (request->directory, dir); + } + + if (request->sockfd == NULL) + return (-2); + + return (0); +} + + +static void +rfc959_disconnect (gftp_request * request) +{ + g_return_if_fail (request != NULL); + g_return_if_fail (request->protonum == GFTP_FTP_NUM); + + if (request->sockfd != NULL) + { + request->logging_function (gftp_logging_misc, request->user_data, + _("Disconnecting from site %s\n"), + request->hostname); + fclose (request->sockfd); + fclose (request->sockfd_write); + request->sockfd = request->sockfd_write = NULL; + if (request->datafd) + { + fclose (request->datafd); + request->datafd = NULL; + } + } +} + + +static long +rfc959_get_file (gftp_request * request, const char *filename, FILE * fd, + off_t startsize) +{ + char *command, *tempstr, resp; + int ret, flags; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (filename != NULL, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + + if (fd != NULL) + request->datafd = fd; + + if (request->datafd == NULL && + (ret = rfc959_data_connection_new (request)) < 0) + return (ret); + + flags = fcntl (fileno (request->datafd), F_GETFL, 0); + if (fcntl (fileno (request->datafd), F_SETFL, flags | O_NONBLOCK) < 0) + { + fclose (request->datafd); + request->datafd = NULL; + return (-1); + } + + if (startsize > 0) + { + command = g_strdup_printf ("REST %ld\r\n", startsize); + resp = rfc959_send_command (request, command); + g_free (command); + + if (resp != '3') + { + fclose (request->datafd); + request->datafd = NULL; + return (-2); + } + } + + tempstr = g_strconcat ("RETR ", filename, "\r\n", NULL); + ret = rfc959_send_command (request, tempstr); + g_free (tempstr); + + if (ret != '1') + { + fclose (request->datafd); + request->datafd = NULL; + return (-2); + } + + if (request->transfer_type == gftp_transfer_active && + (ret = rfc959_accept_active_connection (request)) < 0) + return (ret); + + if ((tempstr = strrchr (request->last_ftp_response, '(')) == NULL) + { + tempstr = request->last_ftp_response + 4; + while (!isdigit ((int) *tempstr) && *tempstr != '\0') + tempstr++; + } + else + tempstr++; + + return (strtol (tempstr, NULL, 10) + startsize); +} + + +static int +rfc959_put_file (gftp_request * request, const char *filename, FILE * fd, + off_t startsize, off_t totalsize) +{ + char *command, *tempstr, resp; + int ret, flags; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (filename != NULL, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + + if (fd != NULL) + fd = request->datafd; + + if (request->datafd == NULL && + (ret = rfc959_data_connection_new (request)) < 0) + return (ret); + + flags = fcntl (fileno (request->datafd), F_GETFL, 0); + if (fcntl (fileno (request->datafd), F_SETFL, flags | O_NONBLOCK) < 0) + { + fclose (request->datafd); + request->datafd = NULL; + return (-1); + } + + if (startsize > 0) + { + command = g_strdup_printf ("REST %ld\r\n", startsize); + resp = rfc959_send_command (request, command); + g_free (command); + if (resp != '3') + { + fclose (request->datafd); + request->datafd = NULL; + return (-2); + } + } + + tempstr = g_strconcat ("STOR ", filename, "\r\n", NULL); + ret = rfc959_send_command (request, tempstr); + g_free (tempstr); + if (ret != '1') + { + fclose (request->datafd); + request->datafd = NULL; + return (-2); + } + + if (request->transfer_type == gftp_transfer_active && + (ret = rfc959_accept_active_connection (request)) < 0) + return (ret); + + return (0); +} + +static long +rfc959_transfer_file (gftp_request *fromreq, const char *fromfile, + off_t fromsize, gftp_request *toreq, + const char *tofile, off_t tosize) +{ + char *tempstr, *pos, *endpos; + + g_return_val_if_fail (fromreq != NULL, -2); + g_return_val_if_fail (fromfile != NULL, -2); + g_return_val_if_fail (toreq != NULL, -2); + g_return_val_if_fail (tofile != NULL, -2); + g_return_val_if_fail (fromreq->sockfd != NULL, -2); + g_return_val_if_fail (toreq->sockfd != NULL, -2); + + fromreq->transfer_type = gftp_transfer_passive; + toreq->transfer_type = gftp_transfer_active; + + if (rfc959_send_command (fromreq, "PASV\r\n") != '2' || + fromreq->sockfd == NULL) + return (-2); + + pos = fromreq->last_ftp_response + 4; + while (!isdigit ((int) *pos) && *pos != '\0') + pos++; + if (*pos == '\0') + return (-2); + + endpos = pos; + while (*endpos != ')' && *endpos != '\0') + endpos++; + if (*endpos == ')') + *endpos = '\0'; + + tempstr = g_strconcat ("PORT ", pos, "\r\n", NULL); + g_free (tempstr); + if (rfc959_send_command (toreq, tempstr) != '2') + return (-2); + + tempstr = g_strconcat ("RETR ", fromfile, "\r\n", NULL); + g_free (tempstr); + if (write_to_socket (fromreq, tempstr) < 0) + return (-1); + + tempstr = g_strconcat ("STOR ", tofile, "\r\n", NULL); + g_free (tempstr); + if (write_to_socket (toreq, tempstr) < 0) + return (-1); + + if (rfc959_read_response (fromreq) < 0) + return (-2); + if (rfc959_read_response (toreq) < 0) + return (-2); + + return (0); +} + + +static int +rfc959_end_transfer (gftp_request * request) +{ + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + + if (request->datafd) + { + fclose (request->datafd); + request->datafd = NULL; + } + return (rfc959_read_response (request) == '2' ? 0 : -2); +} + + +static int +rfc959_list_files (gftp_request * request) +{ + char *tempstr, parms[3]; + int ret; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + + if ((ret = rfc959_data_connection_new (request)) < 0) + return (ret); + + *parms = '\0'; + strcat (parms, show_hidden_files ? "a" : ""); + strcat (parms, resolve_symlinks ? "L" : ""); + tempstr = g_strconcat ("LIST", *parms != '\0' ? " -" : "", parms, "\r\n", + NULL); + + ret = rfc959_send_command (request, tempstr); + g_free (tempstr); + + if (ret != '1') + return (-2); + + ret = 0; + if (request->transfer_type == gftp_transfer_active) + ret = rfc959_accept_active_connection (request); + + return (ret); +} + + +int +rfc959_get_next_file (gftp_request * request, gftp_file * fle, FILE * fd) +{ + char tempstr[255]; + size_t len; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (fle != NULL, -2); + g_return_val_if_fail (fd != NULL, -2); + + if (request->last_dir_entry) + { + g_free (request->last_dir_entry); + request->last_dir_entry = NULL; + } + do + { + /* I don't run select() here because select could return + successfully saying there is data, but the fgets call could block if + there is no carriage return */ + if (!fgets (tempstr, sizeof (tempstr), fd)) + { + gftp_file_destroy (fle); + if (ferror (fd)) + gftp_disconnect (request); + return (-2); + } + tempstr[sizeof (tempstr) - 1] = '\0'; + + if (gftp_parse_ls (tempstr, fle) != 0) + { + if (strncmp (tempstr, "total", strlen("total")) != 0 && + strncmp (tempstr, _("total"), strlen(_("total"))) != 0) + request->logging_function (gftp_logging_misc, request->user_data, + _("Warning: Cannot parse listing %s\n"), + tempstr); + gftp_file_destroy (fle); + continue; + } + else + break; + } + while (1); + + len = strlen (tempstr); + if (!request->cached) + { + request->last_dir_entry = g_malloc (len + 1); + strcpy (request->last_dir_entry, tempstr); + request->last_dir_entry_len = len; + } + return (len); +} + + +static int +rfc959_set_data_type (gftp_request * request, int data_type) +{ + char *tempstr; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + + if (request->sockfd != NULL && request->data_type != data_type) + { + if (data_type == GFTP_TYPE_BINARY) + tempstr = "TYPE I\r\n"; + else + tempstr = "TYPE A\r\n"; + + if (rfc959_send_command (request, tempstr) != '2') + return (-2); + } + request->data_type = data_type; + return (0); +} + + +static off_t +rfc959_get_file_size (gftp_request * request, const char *filename) +{ + char *tempstr; + int ret; + + g_return_val_if_fail (request != NULL, 0); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (filename != NULL, 0); + g_return_val_if_fail (request->sockfd != NULL, 0); + + tempstr = g_strconcat ("SIZE ", filename, "\r\n", NULL); + ret = rfc959_send_command (request, tempstr); + g_free (tempstr); + if (ret < 0) + return (-2); + + if (*request->last_ftp_response != '2') + return (0); + return (strtol (request->last_ftp_response + 4, NULL, 10)); +} + + +static int +rfc959_data_connection_new (gftp_request * request) +{ + char *pos, *pos1, resp, *command; + struct sockaddr_in data_addr; + size_t data_addr_len; + unsigned int temp[6]; + unsigned char ad[6]; + int i, sock; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + + if ((sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Failed to create a socket: %s\n"), + g_strerror (errno)); + gftp_disconnect (request); + return (-1); + } + + data_addr_len = sizeof (data_addr); + memset (&data_addr, 0, data_addr_len); + data_addr.sin_family = AF_INET; + + if (request->transfer_type == gftp_transfer_passive) + { + if ((resp = rfc959_send_command (request, "PASV\r\n")) != '2') + { + if (request->sockfd == NULL) + return (-2); + + request->transfer_type = gftp_transfer_active; + return (rfc959_data_connection_new (request)); + } + pos = request->last_ftp_response + 4; + while (!isdigit ((int) *pos) && *pos != '\0') + pos++; + if (*pos == '\0') + { + gftp_disconnect (request); + close (sock); + return (-2); + } + if (sscanf (pos, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2], + &temp[3], &temp[4], &temp[5]) != 6) + { + gftp_disconnect (request); + close (sock); + return (-2); + } + for (i = 0; i < 6; i++) + ad[i] = (unsigned char) (temp[i] & 0xff); + + memcpy (&data_addr.sin_addr, &ad[0], 4); + memcpy (&data_addr.sin_port, &ad[4], 2); + if (connect (sock, (struct sockaddr *) &data_addr, data_addr_len) == -1) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Cannot create a data connection: %s\n"), + g_strerror (errno)); + gftp_disconnect (request); + close (sock); + return (-1); + } + } + else + { + getsockname (fileno (request->sockfd), (struct sockaddr *) &data_addr, + &data_addr_len); + data_addr.sin_port = 0; + if (bind (sock, (struct sockaddr *) &data_addr, data_addr_len) == -1) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Cannot bind a port: %s\n"), + g_strerror (errno)); + gftp_disconnect (request); + close (sock); + return (-1); + } + + getsockname (sock, (struct sockaddr *) &data_addr, &data_addr_len); + if (listen (sock, 1) == -1) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Cannot listen on port %d: %s\n"), + ntohs (data_addr.sin_port), + g_strerror (errno)); + gftp_disconnect (request); + close (sock); + return (-1); + } + pos = (char *) &data_addr.sin_addr; + pos1 = (char *) &data_addr.sin_port; + command = g_strdup_printf ("PORT %u,%u,%u,%u,%u,%u\r\n", + pos[0] & 0xff, pos[1] & 0xff, pos[2] & 0xff, + pos[3] & 0xff, pos1[0] & 0xff, + pos1[1] & 0xff); + resp = rfc959_send_command (request, command); + g_free (command); + if (resp != '2') + { + gftp_disconnect (request); + close (sock); + return (-2); + } + } + + if ((request->datafd = fdopen (sock, "rb+")) == NULL) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Cannot fdopen() socket: %s\n"), + g_strerror (errno)); + gftp_disconnect (request); + return (-2); + } + + return (0); +} + + +static int +rfc959_accept_active_connection (gftp_request * request) +{ + struct sockaddr_in cli_addr; + size_t cli_addr_len; + int infd; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (request->datafd != NULL, -2); + g_return_val_if_fail (request->transfer_type == gftp_transfer_active, -2); + + cli_addr_len = sizeof (cli_addr); + if ((infd = accept (fileno (request->datafd), (struct sockaddr *) &cli_addr, + &cli_addr_len)) == -1) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Cannot accept connection from server: %s\n"), + g_strerror (errno)); + gftp_disconnect (request); + return (-1); + } + + fclose (request->datafd); + + if ((request->datafd = fdopen (infd, "rb+")) == NULL) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Cannot fdopen() socket: %s\n"), + g_strerror (errno)); + gftp_disconnect (request); + return (-2); + } + return (0); +} + + +static int +rfc959_send_command (gftp_request * request, const char *command) +{ + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (command != NULL, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + + if (strncmp (command, "PASS", 4) == 0) + { + request->logging_function (gftp_logging_send, request->user_data, + "PASS xxxx\n"); + } + else if (strncmp (command, "ACCT", 4) == 0) + { + request->logging_function (gftp_logging_send, request->user_data, + "ACCT xxxx\n"); + } + else + { + request->logging_function (gftp_logging_send, request->user_data, "%s", + command); + } + + if (write_to_socket (request, command) < 0) + { + gftp_disconnect (request); + return (-1); + } + + return (rfc959_read_response (request)); +} + +static int +write_to_socket (gftp_request *request, const char *command) +{ + struct timeval tv; + fd_set fset; + + FD_ZERO (&fset); + FD_SET (fileno (request->sockfd_write), &fset); + tv.tv_sec = request->network_timeout; + tv.tv_usec = 0; + if (!select (fileno (request->sockfd_write) + 1, NULL, &fset, NULL, &tv)) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Connection to %s timed out\n"), + request->hostname); + gftp_disconnect (request); + return (-1); + } + + fwrite (command, strlen (command), 1, request->sockfd_write); + if (ferror (request->sockfd_write)) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Error: Could not write to socket: %s\n"), + g_strerror (errno)); + gftp_disconnect (request); + return (-1); + } + + fflush (request->sockfd_write); + if (ferror (request->sockfd_write)) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Error: Could not write to socket: %s\n"), + g_strerror (errno)); + gftp_disconnect (request); + return (-1); + } + + return (0); +} + + + +static int +rfc959_read_response (gftp_request * request) +{ + char tempstr[255], code[4]; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + + *code = '\0'; + if (request->last_ftp_response) + { + g_free (request->last_ftp_response); + request->last_ftp_response = NULL; + } + + do + { + /* I don't run select() here because select could return + successfully saying there is data, but the fgets call could block if + there is no carriage return */ + if (!fgets (tempstr, sizeof (tempstr), request->sockfd)) + break; + tempstr[strlen (tempstr) - 1] = '\0'; + if (tempstr[strlen (tempstr) - 1] == '\r') + tempstr[strlen (tempstr) - 1] = '\0'; + if (isdigit ((int) *tempstr) && isdigit ((int) *(tempstr + 1)) + && isdigit ((int) *(tempstr + 2))) + { + strncpy (code, tempstr, 3); + code[3] = ' '; + } + request->logging_function (gftp_logging_recv, request->user_data, + "%s\n", tempstr); + } + while (strncmp (code, tempstr, 4) != 0); + + if (ferror (request->sockfd)) + { + request->logging_function (gftp_logging_send, request->user_data, + "Error reading from socket: %s\n", + g_strerror (errno)); + gftp_disconnect (request); + return (-1); + } + + request->last_ftp_response = g_malloc (strlen (tempstr) + 1); + strcpy (request->last_ftp_response, tempstr); + + if (*request->last_ftp_response == '4') + gftp_disconnect (request); + + return (*request->last_ftp_response); +} + + +static int +rfc959_chdir (gftp_request * request, const char *directory) +{ + char ret, *tempstr, *dir; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (directory != NULL, -2); + + if (strcmp (directory, "..") == 0) + ret = rfc959_send_command (request, "CDUP\r\n"); + else + { + tempstr = g_strconcat ("CWD ", directory, "\r\n", NULL); + ret = rfc959_send_command (request, tempstr); + g_free (tempstr); + } + + if (ret != '2') + return (-2); + + if (directory != request->directory) + { + if (request->directory) + { + g_free (request->directory); + request->directory = NULL; + } + + if (rfc959_send_command (request, "PWD\r\n") != '2') + return (-2); + + tempstr = strchr (request->last_ftp_response, '"'); + if (tempstr != NULL) + dir = tempstr + 1; + else + return (-2); + + tempstr = strchr (dir, '"'); + if (tempstr != NULL) + *tempstr = '\0'; + else + { + gftp_disconnect (request); + return (-2); + } + + request->directory = g_malloc (strlen (dir) + 1); + strcpy (request->directory, dir); + } + + return (0); +} + + +static int +rfc959_rmdir (gftp_request * request, const char *directory) +{ + char *tempstr, ret; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (directory != NULL, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + + tempstr = g_strconcat ("RMD ", directory, "\r\n", NULL); + ret = rfc959_send_command (request, tempstr); + g_free (tempstr); + return (ret == '2' ? 0 : -2); +} + + +static int +rfc959_rmfile (gftp_request * request, const char *file) +{ + char *tempstr, ret; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (file != NULL, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + + tempstr = g_strconcat ("DELE ", file, "\r\n", NULL); + ret = rfc959_send_command (request, tempstr); + g_free (tempstr); + return (ret == '2' ? 0 : -2); +} + + +static int +rfc959_mkdir (gftp_request * request, const char *directory) +{ + char *tempstr, ret; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (directory != NULL, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + + tempstr = g_strconcat ("MKD ", directory, "\r\n", NULL); + ret = rfc959_send_command (request, tempstr); + g_free (tempstr); + return (ret == '2' ? 0 : -2); +} + + +static int +rfc959_rename (gftp_request * request, const char *oldname, + const char *newname) +{ + char *tempstr, ret; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (oldname != NULL, -2); + g_return_val_if_fail (newname != NULL, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + + tempstr = g_strconcat ("RNFR ", oldname, "\r\n", NULL); + ret = rfc959_send_command (request, tempstr); + g_free (tempstr); + if (ret != '3') + return (-2); + + tempstr = g_strconcat ("RNTO ", newname, "\r\n", NULL); + ret = rfc959_send_command (request, tempstr); + g_free (tempstr); + return (ret == '2' ? 0 : -2); +} + + +static int +rfc959_chmod (gftp_request * request, const char *file, int mode) +{ + char *tempstr, ret; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (file != NULL, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + + tempstr = g_malloc (strlen (file) + (mode / 10) + 16); + sprintf (tempstr, "SITE CHMOD %d %s\r\n", mode, file); + ret = rfc959_send_command (request, tempstr); + g_free (tempstr); + return (ret == '2' ? 0 : -2); +} + + +static int +rfc959_site (gftp_request * request, const char *command) +{ + char *tempstr, ret; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2); + g_return_val_if_fail (command != NULL, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + + tempstr = g_strconcat ("SITE ", command, "\r\n", NULL); + ret = rfc959_send_command (request, tempstr); + g_free (tempstr); + return (request->sockfd != NULL ? ret : -2); +} + + +static char * +parse_ftp_proxy_string (gftp_request * request) +{ + char *startpos, *endpos, *oldstr, *newstr, *newval, *tempport; + + g_return_val_if_fail (request != NULL, NULL); + g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, NULL); + + newstr = g_malloc (1); + *newstr = '\0'; + startpos = endpos = request->proxy_config; + while (*endpos != '\0') + { + tempport = NULL; + if (*endpos == '%' && tolower ((int) *(endpos + 1)) == 'p') + { + switch (tolower ((int) *(endpos + 2))) + { + case 'u': + newval = request->proxy_username; + break; + case 'p': + newval = request->proxy_password; + break; + case 'h': + newval = request->proxy_hostname; + break; + case 'o': + tempport = g_strdup_printf ("%d", request->proxy_port); + newval = tempport; + break; + case 'a': + newval = request->proxy_account; + break; + default: + endpos++; + continue; + } + } + else if (*endpos == '%' && tolower ((int) *(endpos + 1)) == 'h') + { + switch (tolower ((int) *(endpos + 2))) + { + case 'u': + newval = request->username; + break; + case 'p': + newval = request->password; + break; + case 'h': + newval = request->hostname; + break; + case 'o': + tempport = g_strdup_printf ("%d", request->port); + newval = tempport; + break; + case 'a': + newval = request->account; + break; + default: + endpos++; + continue; + } + } + else if (*endpos == '%' && tolower ((int) *(endpos + 1)) == 'n') + { + *endpos = '\0'; + oldstr = newstr; + newstr = g_strconcat (oldstr, startpos, "\r\n", NULL); + g_free (oldstr); + endpos += 2; + startpos = endpos; + continue; + } + else + { + endpos++; + continue; + } + + *endpos = '\0'; + oldstr = newstr; + if (!newval) + newstr = g_strconcat (oldstr, startpos, NULL); + else + newstr = g_strconcat (oldstr, startpos, newval, NULL); + if (tempport) + { + g_free (tempport); + tempport = NULL; + } + g_free (oldstr); + endpos += 3; + startpos = endpos; + } + return (newstr); +} +