Newer
Older
Daniel Stenberg
committed
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
1017
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
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
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
if(curl_strequal("pwd", sshc->quote_item->data)) {
/* output debug output if that is requested */
if(data->set.verbose) {
char tmp[PATH_MAX+1];
Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4, conn);
snprintf(tmp, PATH_MAX, "257 \"%s\" is current directory.\n",
sftp_scp->path);
Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp), conn);
}
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);
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
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
/*
* 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);
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);
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);
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);
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
James Housley
committed
}
Daniel Stenberg
committed
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
}
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);
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);
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);
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);
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;
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
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
}
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
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
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
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
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;
Curl_pgrsSetDownloadSize(data, size);
}
/* We can resume if we can seek to the resume position */
if(data->state.resume_from) {
if(data->state.resume_from < 0) {
Daniel Stenberg
committed
/* We're supposed to download the last abs(from) bytes */
if((curl_off_t)attrs.filesize < -data->state.resume_from) {
failf(data, "Offset (%"
FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")",
data->state.resume_from, attrs.filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
/* download from where? */
data->state.resume_from += attrs.filesize;
Daniel Stenberg
committed
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
}
else {
if((curl_off_t)attrs.filesize < data->state.resume_from) {
failf(data, "Offset (%" FORMAT_OFF_T
") was beyond file size (%" FORMAT_OFF_T ")",
data->state.resume_from, attrs.filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
}
/* Does a completed file need to be seeked and started or closed ? */
/* Now store the number of bytes we are expected to download */
data->req.size = attrs.filesize - data->state.resume_from;
data->req.maxdownload = attrs.filesize - data->state.resume_from;
Curl_pgrsSetDownloadSize(data,
attrs.filesize - data->state.resume_from);
SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
}
}
/* Setup the actual download */
if(data->req.size == 0) {
/* no data to transfer */
Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);