/ncftp-3.2.5/libncftp/io_getmem.c
C | 268 lines | 209 code | 28 blank | 31 comment | 98 complexity | f2aefca7baa65e4454161d6da6f2f25a MD5 | raw file
Possible License(s): AGPL-3.0
1/* io_getmem.c
2 *
3 * Copyright (c) 1996-2005 Mike Gleason, NcFTP Software.
4 * All rights reserved.
5 *
6 */
7
8#include "syshdrs.h"
9#ifdef PRAGMA_HDRSTOP
10# pragma hdrstop
11#endif
12
13#ifndef NO_SIGNALS
14# define NO_SIGNALS 1
15#endif
16
17int
18FTPGetFileToMemory(
19 const FTPCIPtr cip,
20 const char *const file,
21 char *memBuf,
22 const size_t maxNumberOfBytesToWriteToMemBuf,
23 size_t *const numberOfBytesWrittenToMemBuf,
24 const longest_int startPoint,
25 const int deleteflag
26 )
27{
28 int tmpResult;
29 volatile int result;
30 int atEOF;
31 longest_int expectedSize;
32 size_t ntoread;
33 read_return_t nread;
34 size_t numberOfBytesLeftInMemBuf;
35#if !defined(NO_SIGNALS)
36 volatile FTPSigProc osigpipe;
37 volatile FTPCIPtr vcip;
38 int sj;
39#endif /* NO_SIGNALS */
40
41 result = kNoErr;
42 atEOF = 1;
43 cip->usingTAR = 0;
44 numberOfBytesLeftInMemBuf = maxNumberOfBytesToWriteToMemBuf;
45 if (numberOfBytesWrittenToMemBuf != NULL)
46 *numberOfBytesWrittenToMemBuf = 0;
47
48 if ((file == NULL) || (file[0] == '\0') || (memBuf == NULL) || (maxNumberOfBytesToWriteToMemBuf == 0)) {
49 return (kErrBadParameter);
50 }
51
52 FTPCheckForRestartModeAvailability(cip);
53 if ((startPoint != 0) && (cip->hasREST == kCommandNotAvailable)) {
54 cip->errNo = kErrRESTNotAvailable;
55 return (cip->errNo);
56 }
57
58 (void) FTPFileSize(cip, file, &expectedSize, kTypeBinary);
59 if ((expectedSize != (longest_int) 0) && (startPoint > expectedSize)) {
60 /* Don't go to all the trouble of downloading nothing. */
61 if (deleteflag == kDeleteYes)
62 (void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
63 return (kNoErr);
64 }
65
66 if ((cip->numDownloads == 0) && (cip->dataSocketRBufSize != 0)) {
67 /* If dataSocketSBufSize is non-zero, it means you
68 * want to explicitly try to set the size of the
69 * socket's I/O buffer.
70 *
71 * If it is zero, it means you want to just use the
72 * TCP stack's default value, which is typically
73 * between 8 and 64 kB.
74 *
75 * If you try to set the buffer larger than 64 kB,
76 * the TCP stack should try to use RFC 1323 to
77 * negotiate "TCP Large Windows" which may yield
78 * significant performance gains.
79 */
80 if (cip->hasSITE_RETRBUFSIZE == kCommandAvailable)
81 (void) FTPCmd(cip, "SITE RETRBUFSIZE %lu", (unsigned long) cip->dataSocketRBufSize);
82 else if (cip->hasSITE_RBUFSIZ == kCommandAvailable)
83 (void) FTPCmd(cip, "SITE RBUFSIZ %lu", (unsigned long) cip->dataSocketRBufSize);
84 else if (cip->hasSITE_RBUFSZ == kCommandAvailable)
85 (void) FTPCmd(cip, "SITE RBUFSZ %lu", (unsigned long) cip->dataSocketRBufSize);
86 else if (cip->hasSITE_BUFSIZE == kCommandAvailable)
87 (void) FTPCmd(cip, "SITE BUFSIZE %lu", (unsigned long) cip->dataSocketSBufSize);
88 }
89
90#ifdef NO_SIGNALS
91#else /* NO_SIGNALS */
92 vcip = cip;
93 osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
94
95 gGotBrokenData = 0;
96 gCanBrokenDataJmp = 0;
97
98#ifdef HAVE_SIGSETJMP
99 sj = sigsetjmp(gBrokenDataJmp, 1);
100#else
101 sj = setjmp(gBrokenDataJmp);
102#endif /* HAVE_SIGSETJMP */
103
104 if (sj != 0) {
105 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
106 FTPShutdownHost(vcip);
107 vcip->errNo = kErrRemoteHostClosedConnection;
108 return(vcip->errNo);
109 }
110 gCanBrokenDataJmp = 1;
111#endif /* NO_SIGNALS */
112
113 tmpResult = FTPStartDataCmd(cip, kNetReading, kTypeBinary, startPoint, "RETR %s", file);
114
115 if (tmpResult < 0) {
116 result = tmpResult;
117 if (result == kErrGeneric)
118 result = kErrRETRFailed;
119 cip->errNo = result;
120#if !defined(NO_SIGNALS)
121 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
122#endif /* NO_SIGNALS */
123 return (result);
124 }
125
126 if ((startPoint != 0) && (cip->startPoint == 0)) {
127 /* Remote could not or would not set the start offset
128 * to what we wanted.
129 */
130 cip->errNo = kErrSetStartPoint;
131#if !defined(NO_SIGNALS)
132 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
133#endif /* NO_SIGNALS */
134 return (cip->errNo);
135 }
136
137 FTPInitIOTimer(cip);
138 cip->expectedSize = expectedSize;
139 cip->lname = NULL; /* could be NULL */
140 cip->rname = file;
141 FTPStartIOTimer(cip);
142
143 /* Binary */
144 for (;;) {
145 if (! WaitForRemoteInput(cip)) { /* could set cancelXfer */
146 cip->errNo = result = kErrDataTimedOut;
147 FTPLogError(cip, kDontPerror, "Remote read timed out.\n");
148 break;
149 }
150 if (cip->cancelXfer > 0) {
151 FTPAbortDataTransfer(cip);
152 result = cip->errNo = kErrDataTransferAborted;
153 break;
154 }
155
156 ntoread = numberOfBytesLeftInMemBuf;
157 if (ntoread > cip->bufSize)
158 ntoread = cip->bufSize; /* Break it up into blocks. */
159
160#ifdef NO_SIGNALS
161 nread = (read_return_t) SRead(cip->dataSocket, memBuf, ntoread, (int) cip->xferTimeout, kFullBufferNotRequired|kNoFirstSelect);
162 if (nread == kTimeoutErr) {
163 cip->errNo = result = kErrDataTimedOut;
164 FTPLogError(cip, kDontPerror, "Remote read timed out.\n");
165 break;
166 } else if (nread < 0) {
167 if (errno == EPIPE) {
168 result = cip->errNo = kErrSocketReadFailed;
169 errno = EPIPE;
170 FTPLogError(cip, kDoPerror, "Lost data connection to remote host.\n");
171 } else if (errno == EINTR) {
172 continue;
173 } else {
174 FTPLogError(cip, kDoPerror, "Remote read failed.\n");
175 result = kErrSocketReadFailed;
176 cip->errNo = kErrSocketReadFailed;
177 }
178 break;
179 } else if (nread == 0) {
180 break;
181 }
182#else
183 gCanBrokenDataJmp = 1;
184 if (cip->xferTimeout > 0)
185 (void) alarm(cip->xferTimeout);
186 nread = read(cip->dataSocket, memBuf, (read_size_t) ntoread);
187 if (nread < 0) {
188 if ((gGotBrokenData != 0) || (errno == EPIPE)) {
189 result = cip->errNo = kErrSocketReadFailed;
190 errno = EPIPE;
191 FTPLogError(cip, kDoPerror, "Lost data connection to remote host.\n");
192 } else if (errno == EINTR) {
193 continue;
194 } else {
195 result = cip->errNo = kErrSocketReadFailed;
196 FTPLogError(cip, kDoPerror, "Remote read failed.\n");
197 }
198 (void) shutdown(cip->dataSocket, 2);
199 break;
200 } else if (nread == 0) {
201 /* At EOF. */
202 break;
203 }
204 gCanBrokenDataJmp = 0;
205#endif /* NO_SIGNALS */
206
207 memBuf += nread;
208 if (numberOfBytesWrittenToMemBuf != NULL)
209 *numberOfBytesWrittenToMemBuf += (size_t) nread;
210 cip->bytesTransferred += (longest_int) nread;
211 FTPUpdateIOTimer(cip);
212
213 if ((size_t) nread > numberOfBytesLeftInMemBuf) {
214 /* assertion failure */
215 result = cip->errNo = kErrBugInLibrary;
216 break;
217 }
218
219 numberOfBytesLeftInMemBuf -= nread;
220 if (numberOfBytesLeftInMemBuf == 0) {
221 /* Done (but maybe not at EOF of remote file). */
222 atEOF = 0;
223 if ((cip->bytesTransferred + startPoint) == expectedSize)
224 atEOF = 1;
225 break;
226 }
227 }
228
229#if !defined(NO_SIGNALS)
230 if (cip->xferTimeout > 0)
231 (void) alarm(0);
232 gCanBrokenDataJmp = 0;
233#endif /* NO_SIGNALS */
234
235 /* If there hasn't been an error, and you limited
236 * the number of bytes, we need to abort the
237 * remaining data.
238 */
239 if ((result == kNoErr) && (atEOF == 0)) {
240 FTPAbortDataTransfer(cip);
241 tmpResult = FTPEndDataCmd(cip, 1);
242 if ((tmpResult < 0) && (result == 0) && (tmpResult != kErrDataTransferFailed)) {
243 result = kErrRETRFailed;
244 cip->errNo = kErrRETRFailed;
245 }
246 } else {
247 tmpResult = FTPEndDataCmd(cip, 1);
248 if ((tmpResult < 0) && (result == 0)) {
249 result = kErrRETRFailed;
250 cip->errNo = kErrRETRFailed;
251 }
252 }
253
254 FTPStopIOTimer(cip);
255#if !defined(NO_SIGNALS)
256 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
257#endif /* NO_SIGNALS */
258
259 if (result == kNoErr) {
260 cip->numDownloads++;
261
262 if (deleteflag == kDeleteYes) {
263 result = FTPDelete(cip, file, kRecursiveNo, kGlobNo);
264 }
265 }
266
267 return (result);
268} /* FTPGetOneF */