Newer
Older
Daniel Stenberg
committed
/* This is the last step in the SFTP connect phase. Do note that while
we get the homedir here, we get the "workingpath" in the DO action
since the homedir will remain the same between request but the
working path will not. */
DEBUGF(infof(data, "SSH CONNECT phase done\n"));
state(conn, SSH_STOP);
break;
Daniel Stenberg
committed
case SSH_SFTP_QUOTE_INIT:
result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path);
Daniel Stenberg
committed
state(conn, SSH_STOP);
James Housley
committed
break;
}
Daniel Stenberg
committed
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
if(data->set.quote) {
infof(data, "Sending quote commands\n");
sshc->quote_item = data->set.quote;
state(conn, SSH_SFTP_QUOTE);
}
else {
state(conn, SSH_SFTP_TRANS_INIT);
}
break;
case SSH_SFTP_POSTQUOTE_INIT:
if(data->set.postquote) {
infof(data, "Sending quote commands\n");
sshc->quote_item = data->set.postquote;
state(conn, SSH_SFTP_QUOTE);
}
else {
state(conn, SSH_STOP);
}
break;
case SSH_SFTP_QUOTE:
/* Send any quote commands */
{
const char *cp;
/*
Daniel Stenberg
committed
* Support some of the "FTP" commands
*/
Daniel Stenberg
committed
if(curl_strequal("pwd", sshc->quote_item->data)) {
/* output debug output if that is requested */
char *tmp = aprintf("257 \"%s\" is current directory.\n",
sftp_scp->path);
if(!tmp) {
result = CURLE_OUT_OF_MEMORY;
state(conn, SSH_SFTP_CLOSE);
if(data->set.verbose) {
Daniel Stenberg
committed
Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4, conn);
Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp), conn);
}
/* this sends an FTP-like "header" to the header callback so that the
current directory can be read very similar to how it is read when
using ordinary FTP. */
result = Curl_client_write(conn, CLIENTWRITE_HEADER, tmp, strlen(tmp));
Daniel Stenberg
committed
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
}
else if(sshc->quote_item->data) {
/*
* the arguments following the command must be separated from the
* command with a space so we can check for it unconditionally
*/
cp = strchr(sshc->quote_item->data, ' ');
if(cp == NULL) {
failf(data, "Syntax error in SFTP command. Supply parameter(s)!");
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
/*
* also, every command takes at least one argument so we get that
* first argument right now
*/
result = get_pathname(&cp, &sshc->quote_path1);
failf(data, "Out of memory");
else
Daniel Stenberg
committed
failf(data, "Syntax error: Bad first parameter");
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
James Housley
committed
break;
}
Daniel Stenberg
committed
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
/*
* SFTP is a binary protocol, so we don't send text commands to
* the server. Instead, we scan for commands for commands used by
* OpenSSH's sftp program and call the appropriate libssh2
* functions.
*/
if(curl_strnequal(sshc->quote_item->data, "chgrp ", 6) ||
curl_strnequal(sshc->quote_item->data, "chmod ", 6) ||
curl_strnequal(sshc->quote_item->data, "chown ", 6) ) {
/* attribute change */
/* sshc->quote_path1 contains the mode to set */
/* get the destination */
result = get_pathname(&cp, &sshc->quote_path2);
if(result) {
if(result == CURLE_OUT_OF_MEMORY)
failf(data, "Out of memory");
else
failf(data, "Syntax error in chgrp/chmod/chown: "
"Bad second parameter");
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
sshc->actualcode = result;
break;
}
memset(&sshc->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
state(conn, SSH_SFTP_QUOTE_STAT);
break;
}
else if(curl_strnequal(sshc->quote_item->data, "ln ", 3) ||
curl_strnequal(sshc->quote_item->data, "symlink ", 8)) {
/* symbolic linking */
/* sshc->quote_path1 is the source */
/* get the destination */
result = get_pathname(&cp, &sshc->quote_path2);
if(result) {
if(result == CURLE_OUT_OF_MEMORY)
failf(data, "Out of memory");
else
failf(data,
"Syntax error in ln/symlink: Bad second parameter");
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
sshc->actualcode = result;
break;
}
state(conn, SSH_SFTP_QUOTE_SYMLINK);
break;
}
else if(curl_strnequal(sshc->quote_item->data, "mkdir ", 6)) {
/* create dir */
state(conn, SSH_SFTP_QUOTE_MKDIR);
break;
}
else if(curl_strnequal(sshc->quote_item->data, "rename ", 7)) {
/* rename file */
/* first param is the source path */
/* second param is the dest. path */
result = get_pathname(&cp, &sshc->quote_path2);
if(result) {
if(result == CURLE_OUT_OF_MEMORY)
failf(data, "Out of memory");
else
failf(data, "Syntax error in rename: Bad second parameter");
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
sshc->actualcode = result;
break;
}
state(conn, SSH_SFTP_QUOTE_RENAME);
break;
}
else if(curl_strnequal(sshc->quote_item->data, "rmdir ", 6)) {
/* delete dir */
state(conn, SSH_SFTP_QUOTE_RMDIR);
break;
}
else if(curl_strnequal(sshc->quote_item->data, "rm ", 3)) {
state(conn, SSH_SFTP_QUOTE_UNLINK);
break;
}
failf(data, "Unknown SFTP command");
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
James Housley
committed
}
Daniel Stenberg
committed
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
}
if(!sshc->quote_item) {
state(conn, SSH_SFTP_TRANS_INIT);
}
break;
case SSH_SFTP_NEXT_QUOTE:
if(sshc->quote_path1) {
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
}
if(sshc->quote_path2) {
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
}
sshc->quote_item = sshc->quote_item->next;
if(sshc->quote_item) {
state(conn, SSH_SFTP_QUOTE);
}
else {
if(sshc->nextstate != SSH_NO_STATE) {
state(conn, sshc->nextstate);
sshc->nextstate = SSH_NO_STATE;
}
else {
state(conn, SSH_SFTP_TRANS_INIT);
}
}
break;
case SSH_SFTP_QUOTE_STAT:
if(!curl_strnequal(sshc->quote_item->data, "chmod", 5)) {
/* Since chown and chgrp only set owner OR group but libssh2 wants to
* set them both at once, we need to obtain the current ownership
* first. This takes an extra protocol round trip.
Daniel Stenberg
committed
*/
rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
(unsigned int)strlen(sshc->quote_path2),
LIBSSH2_SFTP_STAT,
&sshc->quote_attrs);
Daniel Stenberg
committed
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc != 0) { /* get those attributes */
err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
James Housley
committed
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Daniel Stenberg
committed
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
failf(data, "Attempt to get SFTP stats failed: %s",
sftp_libssh2_strerror(err));
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
sshc->actualcode = CURLE_QUOTE_ERROR;
James Housley
committed
break;
}
}
Daniel Stenberg
committed
/* Now set the new attributes... */
if(curl_strnequal(sshc->quote_item->data, "chgrp", 5)) {
Daniel Stenberg
committed
sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
if(sshc->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0])) {
James Housley
committed
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Daniel Stenberg
committed
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
failf(data, "Syntax error: chgrp gid not a number");
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
sshc->actualcode = CURLE_QUOTE_ERROR;
James Housley
committed
break;
}
}
Daniel Stenberg
committed
else if(curl_strnequal(sshc->quote_item->data, "chmod", 5)) {
Daniel Stenberg
committed
sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
/* permissions are octal */
if(sshc->quote_attrs.permissions == 0 &&
!ISDIGIT(sshc->quote_path1[0])) {
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
failf(data, "Syntax error: chmod permissions not a number");
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
Daniel Stenberg
committed
else if(curl_strnequal(sshc->quote_item->data, "chown", 5)) {
Daniel Stenberg
committed
sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
if(sshc->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0])) {
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
failf(data, "Syntax error: chown uid not a number");
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
James Housley
committed
}
Daniel Stenberg
committed
/* Now send the completed structure... */
state(conn, SSH_SFTP_QUOTE_SETSTAT);
Daniel Stenberg
committed
case SSH_SFTP_QUOTE_SETSTAT:
rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
(unsigned int)strlen(sshc->quote_path2),
LIBSSH2_SFTP_SETSTAT,
Daniel Stenberg
committed
&sshc->quote_attrs);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
Daniel Stenberg
committed
else if(rc != 0) {
err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
Daniel Stenberg
committed
failf(data, "Attempt to set SFTP stats failed: %s",
sftp_libssh2_strerror(err));
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
Daniel Stenberg
committed
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SFTP_QUOTE_SYMLINK:
rc = libssh2_sftp_symlink_ex(sshc->sftp_session, sshc->quote_path1,
(unsigned int)strlen(sshc->quote_path1),
sshc->quote_path2,
(unsigned int)strlen(sshc->quote_path2),
LIBSSH2_SFTP_SYMLINK);
Daniel Stenberg
committed
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc != 0) {
err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
James Housley
committed
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
Daniel Stenberg
committed
failf(data, "symlink command failed: %s",
sftp_libssh2_strerror(err));
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
James Housley
committed
break;
}
Daniel Stenberg
committed
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_MKDIR:
rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshc->quote_path1,
(unsigned int)strlen(sshc->quote_path1),
0755);
Daniel Stenberg
committed
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc != 0) {
err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
James Housley
committed
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Daniel Stenberg
committed
failf(data, "mkdir command failed: %s", sftp_libssh2_strerror(err));
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
James Housley
committed
break;
}
Daniel Stenberg
committed
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_RENAME:
rc = libssh2_sftp_rename_ex(sshc->sftp_session, sshc->quote_path1,
(unsigned int)strlen(sshc->quote_path1),
sshc->quote_path2,
(unsigned int)strlen(sshc->quote_path2),
LIBSSH2_SFTP_RENAME_OVERWRITE |
LIBSSH2_SFTP_RENAME_ATOMIC |
LIBSSH2_SFTP_RENAME_NATIVE);
Daniel Stenberg
committed
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc != 0) {
err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
James Housley
committed
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
Curl_safefree(sshc->quote_path2);
sshc->quote_path2 = NULL;
Daniel Stenberg
committed
failf(data, "rename command failed: %s", sftp_libssh2_strerror(err));
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_QUOTE_ERROR;
James Housley
committed
break;
}
Daniel Stenberg
committed
state(conn, SSH_SFTP_NEXT_QUOTE);
James Housley
committed
break;
Daniel Stenberg
committed
case SSH_SFTP_QUOTE_RMDIR:
rc = libssh2_sftp_rmdir_ex(sshc->sftp_session, sshc->quote_path1,
(unsigned int)strlen(sshc->quote_path1));
Daniel Stenberg
committed
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc != 0) {
err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
Daniel Stenberg
committed
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
failf(data, "rmdir command failed: %s", sftp_libssh2_strerror(err));
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SFTP_QUOTE_UNLINK:
rc = libssh2_sftp_unlink_ex(sshc->sftp_session, sshc->quote_path1,
(unsigned int)strlen(sshc->quote_path1));
Daniel Stenberg
committed
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc != 0) {
err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
Daniel Stenberg
committed
Curl_safefree(sshc->quote_path1);
sshc->quote_path1 = NULL;
failf(data, "rm command failed: %s", sftp_libssh2_strerror(err));
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
state(conn, SSH_SFTP_NEXT_QUOTE);
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SFTP_TRANS_INIT:
if(data->set.upload)
state(conn, SSH_SFTP_UPLOAD_INIT);
else {
if(data->set.opt_no_body)
state(conn, SSH_STOP);
else if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
state(conn, SSH_SFTP_READDIR_INIT);
else
state(conn, SSH_SFTP_DOWNLOAD_INIT);
}
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SFTP_UPLOAD_INIT:
{
unsigned long flags;
/*
* NOTE!!! libssh2 requires that the destination path is a full path
* that includes the destination file and name OR ends in a "/"
* If this is not done the destination file will be named the
* same name as the last directory in the path.
*/
Daniel Stenberg
committed
if(data->state.resume_from != 0) {
LIBSSH2_SFTP_ATTRIBUTES attrs;
if(data->state.resume_from < 0) {
rc = libssh2_sftp_stat_ex(sshc->sftp_session, sftp_scp->path,
(unsigned int)strlen(sftp_scp->path),
LIBSSH2_SFTP_STAT, &attrs);
Daniel Stenberg
committed
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc) {
data->state.resume_from = 0;
}
else {
curl_off_t size = attrs.filesize;
if(size < 0) {
failf(data, "Bad file size (%" FORMAT_OFF_T ")", size);
return CURLE_BAD_DOWNLOAD_RESUME;
}
Daniel Stenberg
committed
data->state.resume_from = attrs.filesize;
}
}
}
Daniel Stenberg
committed
if(data->set.ftp_append)
/* Try to open for append, but create if nonexisting */
flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND;
else if (data->state.resume_from > 0)
/* If we have restart position then open for append */
flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND;
else
/* Clear file before writing (normal behaviour) */
flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC;
Daniel Stenberg
committed
sshc->sftp_handle =
libssh2_sftp_open_ex(sshc->sftp_session, sftp_scp->path,
(unsigned int)strlen(sftp_scp->path),
flags, data->set.new_file_perms,
LIBSSH2_SFTP_OPENFILE);
Daniel Stenberg
committed
if(!sshc->sftp_handle) {
rc = libssh2_session_last_errno(sshc->ssh_session);
Daniel Stenberg
committed
Daniel Stenberg
committed
if(LIBSSH2_ERROR_EAGAIN == rc)
break;
else {
if(LIBSSH2_ERROR_SFTP_PROTOCOL == rc)
/* only when there was an SFTP protocol error can we extract
the sftp error! */
err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
Daniel Stenberg
committed
else
err = -1; /* not an sftp error at all */
Daniel Stenberg
committed
Daniel Stenberg
committed
if(sshc->secondCreateDirs) {
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = err>= LIBSSH2_FX_OK?
sftp_libssh2_error_to_CURLE(err):CURLE_SSH;
failf(data, "Creating the dir/file failed: %s",
sftp_libssh2_strerror(err));
break;
}
else if(((err == LIBSSH2_FX_NO_SUCH_FILE) ||
(err == LIBSSH2_FX_FAILURE) ||
(err == LIBSSH2_FX_NO_SUCH_PATH)) &&
(data->set.ftp_create_missing_dirs &&
(strlen(sftp_scp->path) > 1))) {
/* try to create the path remotely */
sshc->secondCreateDirs = 1;
state(conn, SSH_SFTP_CREATE_DIRS_INIT);
break;
}
James Housley
committed
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
sshc->actualcode = err>= LIBSSH2_FX_OK?
sftp_libssh2_error_to_CURLE(err):CURLE_SSH;
Daniel Stenberg
committed
if(!sshc->actualcode) {
/* Sometimes, for some reason libssh2_sftp_last_error() returns
zero even though libssh2_sftp_open() failed previously! We need
to work around that! */
sshc->actualcode = CURLE_SSH;
err=-1;
}
failf(data, "Upload failed: %s (%d/%d)",
err>= LIBSSH2_FX_OK?sftp_libssh2_strerror(err):"ssh error",
err, rc);
break;
}
James Housley
committed
}
Daniel Stenberg
committed
/* If we have restart point then we need to seek to the correct
position. */
if(data->state.resume_from > 0) {
/* Let's read off the proper amount of bytes from the input. */
if(conn->seek_func) {
seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
SEEK_SET);
}
Daniel Stenberg
committed
Daniel Stenberg
committed
if(seekerr != CURL_SEEKFUNC_OK){
Daniel Stenberg
committed
if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
failf(data, "Could not seek stream");
return CURLE_FTP_COULDNT_USE_REST;
}
/* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
else {
curl_off_t passed=0;
do {
size_t readthisamountnow =
(data->state.resume_from - passed > CURL_OFF_T_C(BUFSIZE)) ?
BUFSIZE : curlx_sotouz(data->state.resume_from - passed);
Daniel Stenberg
committed
size_t actuallyread =
conn->fread_func(data->state.buffer, 1, readthisamountnow,
conn->fread_in);
Daniel Stenberg
committed
passed += actuallyread;
if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
Daniel Stenberg
committed
/* this checks for greater-than only to make sure that the
CURL_READFUNC_ABORT return code still aborts */
failf(data, "Failed to read data");
return CURLE_FTP_COULDNT_USE_REST;
}
} while(passed < data->state.resume_from);
}
}
Daniel Stenberg
committed
/* now, decrease the size of the read */
Daniel Stenberg
committed
data->set.infilesize -= data->state.resume_from;
data->req.size = data->set.infilesize;
Curl_pgrsSetUploadSize(data, data->set.infilesize);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
}
data->req.size = data->set.infilesize;
Curl_pgrsSetUploadSize(data, data->set.infilesize);
}
Daniel Stenberg
committed
/* upload data */
Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
Daniel Stenberg
committed
/* not set by Curl_setup_transfer to preserve keepon bits */
conn->sockfd = conn->writesockfd;
Daniel Stenberg
committed
if(result) {
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = result;
}
else {
/* store this original bitmask setup to use later on if we can't
figure out a "real" bitmask */
sshc->orig_waitfor = data->req.keepon;
/* we want to use the _sending_ function even when the socket turns
out readable as the underlying libssh2 sftp send function will deal
with both accordingly */
conn->cselect_bits = CURL_CSELECT_OUT;
/* since we don't really wait for anything at this point, we want the
state machine to move on as soon as possible so we set a very short
timeout here */
Curl_expire(data, 1);
Daniel Stenberg
committed
state(conn, SSH_STOP);
}
James Housley
committed
break;
}
James Housley
committed
Daniel Stenberg
committed
case SSH_SFTP_CREATE_DIRS_INIT:
if(strlen(sftp_scp->path) > 1) {
sshc->slash_pos = sftp_scp->path + 1; /* ignore the leading '/' */
state(conn, SSH_SFTP_CREATE_DIRS);
}
Daniel Stenberg
committed
else {
state(conn, SSH_SFTP_UPLOAD_INIT);
}
break;
Daniel Stenberg
committed
case SSH_SFTP_CREATE_DIRS:
if((sshc->slash_pos = strchr(sshc->slash_pos, '/')) != NULL) {
*sshc->slash_pos = 0;
infof(data, "Creating directory '%s'\n", sftp_scp->path);
state(conn, SSH_SFTP_CREATE_DIRS_MKDIR);
James Housley
committed
break;
}
else {
Daniel Stenberg
committed
state(conn, SSH_SFTP_UPLOAD_INIT);
James Housley
committed
}
break;
Daniel Stenberg
committed
case SSH_SFTP_CREATE_DIRS_MKDIR:
/* 'mode' - parameter is preliminary - default to 0644 */
rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sftp_scp->path,
(unsigned int)strlen(sftp_scp->path),
data->set.new_directory_perms);
Daniel Stenberg
committed
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
*sshc->slash_pos = '/';
++sshc->slash_pos;
if(rc == -1) {
unsigned int sftp_err = 0;
/*
* Abort if failure wasn't that the dir already exists or the
* permission was denied (creation might succeed further down the
* path) - retry on unspecific FAILURE also
*/
sftp_err = (unsigned int)(libssh2_sftp_last_error(sshc->sftp_session));
Daniel Stenberg
committed
if((sftp_err != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
(sftp_err != LIBSSH2_FX_FAILURE) &&
(sftp_err != LIBSSH2_FX_PERMISSION_DENIED)) {
result = sftp_libssh2_error_to_CURLE(sftp_err);
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
sshc->actualcode = result?result:CURLE_SSH;
break;
}
Daniel Stenberg
committed
}
state(conn, SSH_SFTP_CREATE_DIRS);
break;
Daniel Stenberg
committed
case SSH_SFTP_READDIR_INIT:
/*
* This is a directory that we are trying to get, so produce a directory
* listing
*/
sshc->sftp_handle = libssh2_sftp_open_ex(sshc->sftp_session,
sftp_scp->path,
(unsigned int)
strlen(sftp_scp->path),
0, 0, LIBSSH2_SFTP_OPENDIR);
Daniel Stenberg
committed
if(!sshc->sftp_handle) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
rc = LIBSSH2_ERROR_EAGAIN;
break;
}
Daniel Stenberg
committed
else {
err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
Daniel Stenberg
committed
failf(data, "Could not open directory for reading: %s",
sftp_libssh2_strerror(err));
state(conn, SSH_SFTP_CLOSE);
Daniel Stenberg
committed
result = sftp_libssh2_error_to_CURLE(err);
sshc->actualcode = result?result:CURLE_SSH;
break;
}
Daniel Stenberg
committed
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
}
if((sshc->readdir_filename = malloc(PATH_MAX+1)) == NULL) {
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
if((sshc->readdir_longentry = malloc(PATH_MAX+1)) == NULL) {
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
state(conn, SSH_SFTP_READDIR);
break;
case SSH_SFTP_READDIR:
sshc->readdir_len = libssh2_sftp_readdir_ex(sshc->sftp_handle,
sshc->readdir_filename,
PATH_MAX,
sshc->readdir_longentry,
PATH_MAX,
&sshc->readdir_attrs);
if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
rc = LIBSSH2_ERROR_EAGAIN;
break;
}
if(sshc->readdir_len > 0) {
sshc->readdir_filename[sshc->readdir_len] = '\0';
if(data->set.ftp_list_only) {
char *tmpLine;
tmpLine = aprintf("%s\n", sshc->readdir_filename);
if(tmpLine == NULL) {
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
result = Curl_client_write(conn, CLIENTWRITE_BODY,
tmpLine, sshc->readdir_len+1);
Curl_safefree(tmpLine);
James Housley
committed
Daniel Stenberg
committed
if(result) {
state(conn, SSH_STOP);
break;
}
/* since this counts what we send to the client, we include the
newline in this counter */
data->req.bytecount += sshc->readdir_len+1;
/* output debug output if that is requested */
if(data->set.verbose) {
Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_filename,
sshc->readdir_len, conn);
}
}
else {
sshc->readdir_currLen = (int)strlen(sshc->readdir_longentry);
Daniel Stenberg
committed
sshc->readdir_totalLen = 80 + sshc->readdir_currLen;
sshc->readdir_line = calloc(sshc->readdir_totalLen, 1);
if(!sshc->readdir_line) {
James Housley
committed
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
James Housley
committed
break;
}
Daniel Stenberg
committed
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
memcpy(sshc->readdir_line, sshc->readdir_longentry,
sshc->readdir_currLen);
if((sshc->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
((sshc->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFLNK)) {
sshc->readdir_linkPath = malloc(PATH_MAX + 1);
if(sshc->readdir_linkPath == NULL) {
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
snprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", sftp_scp->path,
sshc->readdir_filename);
state(conn, SSH_SFTP_READDIR_LINK);
break;
}
state(conn, SSH_SFTP_READDIR_BOTTOM);
James Housley
committed
break;
}
Daniel Stenberg
committed
}
else if(sshc->readdir_len == 0) {
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
state(conn, SSH_SFTP_READDIR_DONE);
break;
}
else if(sshc->readdir_len <= 0) {
err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
Daniel Stenberg
committed
result = sftp_libssh2_error_to_CURLE(err);
sshc->actualcode = result?result:CURLE_SSH;
failf(data, "Could not open remote file for reading: %s :: %d",
sftp_libssh2_strerror(err),
libssh2_session_last_errno(sshc->ssh_session));
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
state(conn, SSH_SFTP_CLOSE);
James Housley
committed
break;
}
break;
Daniel Stenberg
committed
case SSH_SFTP_READDIR_LINK:
sshc->readdir_len =
libssh2_sftp_symlink_ex(sshc->sftp_session,
sshc->readdir_linkPath,
(unsigned int) strlen(sshc->readdir_linkPath),
sshc->readdir_filename,
PATH_MAX, LIBSSH2_SFTP_READLINK);
Daniel Stenberg
committed
if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
rc = LIBSSH2_ERROR_EAGAIN;
break;
}
Curl_safefree(sshc->readdir_linkPath);
sshc->readdir_linkPath = NULL;
sshc->readdir_line = realloc(sshc->readdir_line,
sshc->readdir_totalLen + 4 +
sshc->readdir_len);
if(!sshc->readdir_line) {
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
James Housley
committed
Daniel Stenberg
committed
sshc->readdir_currLen += snprintf(sshc->readdir_line +
sshc->readdir_currLen,
sshc->readdir_totalLen -
sshc->readdir_currLen,
" -> %s",
sshc->readdir_filename);
Daniel Stenberg
committed
state(conn, SSH_SFTP_READDIR_BOTTOM);
break;
Daniel Stenberg
committed
case SSH_SFTP_READDIR_BOTTOM:
sshc->readdir_currLen += snprintf(sshc->readdir_line +
sshc->readdir_currLen,
sshc->readdir_totalLen -
sshc->readdir_currLen, "\n");
result = Curl_client_write(conn, CLIENTWRITE_BODY,
sshc->readdir_line,
sshc->readdir_currLen);
Daniel Stenberg
committed
if(result == CURLE_OK) {
Daniel Stenberg
committed
/* output debug output if that is requested */
if(data->set.verbose) {
Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line,
sshc->readdir_currLen, conn);
}
data->req.bytecount += sshc->readdir_currLen;
}
Daniel Stenberg
committed
Curl_safefree(sshc->readdir_line);
sshc->readdir_line = NULL;
if(result) {
state(conn, SSH_STOP);
}
else
state(conn, SSH_SFTP_READDIR);
break;
Daniel Stenberg
committed
case SSH_SFTP_READDIR_DONE:
if(libssh2_sftp_closedir(sshc->sftp_handle) ==
LIBSSH2_ERROR_EAGAIN) {
rc = LIBSSH2_ERROR_EAGAIN;
James Housley
committed
break;
}
Daniel Stenberg
committed
sshc->sftp_handle = NULL;
Curl_safefree(sshc->readdir_filename);
sshc->readdir_filename = NULL;
Curl_safefree(sshc->readdir_longentry);
sshc->readdir_longentry = NULL;
James Housley
committed
Daniel Stenberg
committed
/* no data to transfer */
Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
Daniel Stenberg
committed
state(conn, SSH_STOP);
James Housley
committed
break;
Daniel Stenberg
committed
case SSH_SFTP_DOWNLOAD_INIT:
/*
Daniel Stenberg
committed
* Work on getting the specified file
*/
Daniel Stenberg
committed
sshc->sftp_handle =
libssh2_sftp_open_ex(sshc->sftp_session, sftp_scp->path,
(unsigned int)strlen(sftp_scp->path),
LIBSSH2_FXF_READ, data->set.new_file_perms,
LIBSSH2_SFTP_OPENFILE);
Daniel Stenberg
committed
if(!sshc->sftp_handle) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
rc = LIBSSH2_ERROR_EAGAIN;
break;
Daniel Stenberg
committed
}
else {
err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
Daniel Stenberg
committed
failf(data, "Could not open remote file for reading: %s",
sftp_libssh2_strerror(err));
state(conn, SSH_SFTP_CLOSE);
result = sftp_libssh2_error_to_CURLE(err);
sshc->actualcode = result?result:CURLE_SSH;
break;
Daniel Stenberg
committed
}
}
Daniel Stenberg
committed
state(conn, SSH_SFTP_DOWNLOAD_STAT);
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SFTP_DOWNLOAD_STAT:
{
LIBSSH2_SFTP_ATTRIBUTES attrs;
rc = libssh2_sftp_stat_ex(sshc->sftp_session, sftp_scp->path,
(unsigned int)strlen(sftp_scp->path),
LIBSSH2_SFTP_STAT, &attrs);
Daniel Stenberg
committed
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc) {
/*
* libssh2_sftp_open() didn't return an error, so maybe the server
* just doesn't support stat()
*/
data->req.size = -1;
data->req.maxdownload = -1;
}
else {
curl_off_t size = attrs.filesize;
Daniel Stenberg
committed
if(size < 0) {
failf(data, "Bad file size (%" FORMAT_OFF_T ")", size);
return CURLE_BAD_DOWNLOAD_RESUME;
}
Daniel Stenberg
committed
if(conn->data->state.use_range) {
curl_off_t from, to;
char *ptr;
char *ptr2;
from=curlx_strtoofft(conn->data->state.range, &ptr, 0);
Daniel Stenberg
committed
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
ptr++;
to=curlx_strtoofft(ptr, &ptr2, 0);
if((ptr == ptr2) /* no "to" value given */
|| (to >= size)) {
to = size - 1;
}
if(from < 0) {
/* from is relative to end of file */
from += size;
}
if(from >= size) {
failf(data, "Offset (%"
FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")",
from, attrs.filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
if(from > to) {
from = to;
size = 0;
}
else {
size = to - from + 1;
}
SFTP_SEEK(conn->proto.sshc.sftp_handle, from);
}
data->req.size = size;
data->req.maxdownload = size;