Newer
Older
Daniel Stenberg
committed
if(!sshc->homedir) {
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
conn->data->state.most_recent_ftp_entrypath = sshc->homedir;
Daniel Stenberg
committed
}
else {
/* Return the error type */
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;
DEBUGF(infof(data, "error = %d makes libcurl = %d\n",
err, (int)result));
Daniel Stenberg
committed
state(conn, SSH_STOP);
break;
}
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
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
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
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
/*
* 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
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
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
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
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
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
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
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
}
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)
Daniel Stenberg
committed
/* 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
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
}
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
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
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
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;
}