diff options
Diffstat (limited to 'linden/indra/llmessage/llxfer_file.cpp')
-rw-r--r-- | linden/indra/llmessage/llxfer_file.cpp | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/linden/indra/llmessage/llxfer_file.cpp b/linden/indra/llmessage/llxfer_file.cpp new file mode 100644 index 0000000..8d38644 --- /dev/null +++ b/linden/indra/llmessage/llxfer_file.cpp | |||
@@ -0,0 +1,435 @@ | |||
1 | /** | ||
2 | * @file llxfer_file.cpp | ||
3 | * @brief implementation of LLXfer_File class for a single xfer (file) | ||
4 | * | ||
5 | * Copyright (c) 2001-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | #include "linden_common.h" | ||
29 | |||
30 | #if !LL_WINDOWS | ||
31 | #include <errno.h> | ||
32 | #include <unistd.h> | ||
33 | #endif | ||
34 | |||
35 | #include "llxfer_file.h" | ||
36 | #include "lluuid.h" | ||
37 | #include "llerror.h" | ||
38 | #include "llmath.h" | ||
39 | #include "llstring.h" | ||
40 | #include "lldir.h" | ||
41 | |||
42 | // size of chunks read from/written to disk | ||
43 | const U32 LL_MAX_XFER_FILE_BUFFER = 65536; | ||
44 | |||
45 | // local function to copy a file | ||
46 | S32 copy_file(const char* from, const char* to); | ||
47 | |||
48 | /////////////////////////////////////////////////////////// | ||
49 | |||
50 | LLXfer_File::LLXfer_File (S32 chunk_size) | ||
51 | : LLXfer(chunk_size) | ||
52 | { | ||
53 | init(LLString::null, FALSE, chunk_size); | ||
54 | } | ||
55 | |||
56 | LLXfer_File::LLXfer_File (const LLString& local_filename, BOOL delete_local_on_completion, S32 chunk_size) | ||
57 | : LLXfer(chunk_size) | ||
58 | { | ||
59 | init(local_filename, delete_local_on_completion, chunk_size); | ||
60 | } | ||
61 | |||
62 | /////////////////////////////////////////////////////////// | ||
63 | |||
64 | LLXfer_File::~LLXfer_File () | ||
65 | { | ||
66 | free(); | ||
67 | } | ||
68 | |||
69 | /////////////////////////////////////////////////////////// | ||
70 | |||
71 | void LLXfer_File::init (const LLString& local_filename, BOOL delete_local_on_completion, S32 chunk_size) | ||
72 | { | ||
73 | |||
74 | mFp = NULL; | ||
75 | mLocalFilename[0] = 0; | ||
76 | mRemoteFilename[0] = 0; | ||
77 | mRemotePath = LL_PATH_NONE; | ||
78 | mTempFilename[0] = 0; | ||
79 | mDeleteLocalOnCompletion = FALSE; | ||
80 | mDeleteRemoteOnCompletion = FALSE; | ||
81 | |||
82 | if (!local_filename.empty()) | ||
83 | { | ||
84 | strncpy(mLocalFilename, local_filename.c_str(), LL_MAX_PATH); /* Flawfinder : ignore */ | ||
85 | |||
86 | // You can only automatically delete .tmp file as a safeguard against nasty messages. | ||
87 | mDeleteLocalOnCompletion = (delete_local_on_completion && (strstr(mLocalFilename,".tmp") == &mLocalFilename[strlen(mLocalFilename)-4])); /* Flawfinder : ignore */ | ||
88 | } | ||
89 | } | ||
90 | |||
91 | /////////////////////////////////////////////////////////// | ||
92 | |||
93 | void LLXfer_File::free () | ||
94 | { | ||
95 | if (mFp) | ||
96 | { | ||
97 | fclose(mFp); | ||
98 | mFp = NULL; | ||
99 | } | ||
100 | |||
101 | LLFile::remove(mTempFilename); | ||
102 | |||
103 | if (mDeleteLocalOnCompletion) | ||
104 | { | ||
105 | lldebugs << "Removing file: " << mLocalFilename << llendl; | ||
106 | LLFile::remove(mLocalFilename); | ||
107 | } | ||
108 | else | ||
109 | { | ||
110 | lldebugs << "Keeping local file: " << mLocalFilename << llendl; | ||
111 | } | ||
112 | |||
113 | LLXfer::free(); | ||
114 | } | ||
115 | |||
116 | /////////////////////////////////////////////////////////// | ||
117 | |||
118 | S32 LLXfer_File::initializeRequest(U64 xfer_id, | ||
119 | const LLString& local_filename, | ||
120 | const LLString& remote_filename, | ||
121 | ELLPath remote_path, | ||
122 | const LLHost& remote_host, | ||
123 | BOOL delete_remote_on_completion, | ||
124 | void (*callback)(void**,S32), | ||
125 | void** user_data) | ||
126 | { | ||
127 | S32 retval = 0; // presume success | ||
128 | |||
129 | mID = xfer_id; | ||
130 | strncpy(mLocalFilename, local_filename.c_str(), LL_MAX_PATH); /* Flawfinder : ignore */ | ||
131 | strncpy(mRemoteFilename,remote_filename.c_str(), LL_MAX_PATH); /* Flawfinder : ignore */ | ||
132 | mRemotePath = remote_path; | ||
133 | mRemoteHost = remote_host; | ||
134 | mDeleteRemoteOnCompletion = delete_remote_on_completion; | ||
135 | |||
136 | snprintf(mTempFilename, sizeof(mTempFilename), "%s",gDirUtilp->getTempFilename().c_str()); /* Flawfinder : ignore */ | ||
137 | |||
138 | mCallback = callback; | ||
139 | mCallbackDataHandle = user_data; | ||
140 | mCallbackResult = LL_ERR_NOERR; | ||
141 | |||
142 | llinfos << "Requesting xfer from " << remote_host << " for file: " << mLocalFilename << llendl; | ||
143 | |||
144 | if (mBuffer) | ||
145 | { | ||
146 | delete(mBuffer); | ||
147 | mBuffer = NULL; | ||
148 | } | ||
149 | |||
150 | mBuffer = new char[LL_MAX_XFER_FILE_BUFFER]; | ||
151 | mBufferLength = 0; | ||
152 | |||
153 | mPacketNum = 0; | ||
154 | |||
155 | mStatus = e_LL_XFER_PENDING; | ||
156 | return retval; | ||
157 | } | ||
158 | |||
159 | /////////////////////////////////////////////////////////// | ||
160 | |||
161 | S32 LLXfer_File::startDownload() | ||
162 | { | ||
163 | S32 retval = 0; // presume success | ||
164 | mFp = LLFile::fopen(mTempFilename,"w+b"); /* Flawfinder : ignore */ | ||
165 | if (mFp) | ||
166 | { | ||
167 | fclose(mFp); | ||
168 | mFp = NULL; | ||
169 | |||
170 | gMessageSystem->newMessageFast(_PREHASH_RequestXfer); | ||
171 | gMessageSystem->nextBlockFast(_PREHASH_XferID); | ||
172 | gMessageSystem->addU64Fast(_PREHASH_ID, mID); | ||
173 | gMessageSystem->addStringFast(_PREHASH_Filename, mRemoteFilename); | ||
174 | gMessageSystem->addU8("FilePath", (U8) mRemotePath); | ||
175 | gMessageSystem->addBOOL("DeleteOnCompletion", mDeleteRemoteOnCompletion); | ||
176 | gMessageSystem->addBOOL("UseBigPackets", BOOL(mChunkSize == LL_XFER_LARGE_PAYLOAD)); | ||
177 | gMessageSystem->addUUIDFast(_PREHASH_VFileID, LLUUID::null); | ||
178 | gMessageSystem->addS16Fast(_PREHASH_VFileType, -1); | ||
179 | |||
180 | gMessageSystem->sendReliable(mRemoteHost); | ||
181 | mStatus = e_LL_XFER_IN_PROGRESS; | ||
182 | } | ||
183 | else | ||
184 | { | ||
185 | llwarns << "Couldn't create file to be received!" << llendl; | ||
186 | retval = -1; | ||
187 | } | ||
188 | |||
189 | return (retval); | ||
190 | } | ||
191 | |||
192 | /////////////////////////////////////////////////////////// | ||
193 | |||
194 | S32 LLXfer_File::startSend (U64 xfer_id, const LLHost &remote_host) | ||
195 | { | ||
196 | S32 retval = LL_ERR_NOERR; // presume success | ||
197 | |||
198 | mRemoteHost = remote_host; | ||
199 | mID = xfer_id; | ||
200 | mPacketNum = -1; | ||
201 | |||
202 | // cout << "Sending file: " << mLocalFilename << endl; | ||
203 | |||
204 | delete [] mBuffer; | ||
205 | mBuffer = new char[LL_MAX_XFER_FILE_BUFFER]; | ||
206 | |||
207 | mBufferLength = 0; | ||
208 | mBufferStartOffset = 0; | ||
209 | |||
210 | mFp = LLFile::fopen(mLocalFilename,"rb"); /* Flawfinder : ignore */ | ||
211 | if (mFp) | ||
212 | { | ||
213 | fseek(mFp,0,SEEK_END); | ||
214 | |||
215 | S32 file_size = ftell(mFp); | ||
216 | if (file_size <= 0) | ||
217 | { | ||
218 | return LL_ERR_FILE_EMPTY; | ||
219 | } | ||
220 | setXferSize(file_size); | ||
221 | |||
222 | fseek(mFp,0,SEEK_SET); | ||
223 | } | ||
224 | else | ||
225 | { | ||
226 | llinfos << "Warning: " << mLocalFilename << " not found." << llendl; | ||
227 | return (LL_ERR_FILE_NOT_FOUND); | ||
228 | } | ||
229 | |||
230 | mStatus = e_LL_XFER_PENDING; | ||
231 | |||
232 | return (retval); | ||
233 | } | ||
234 | |||
235 | /////////////////////////////////////////////////////////// | ||
236 | |||
237 | S32 LLXfer_File::getMaxBufferSize () | ||
238 | { | ||
239 | return(LL_MAX_XFER_FILE_BUFFER); | ||
240 | } | ||
241 | |||
242 | /////////////////////////////////////////////////////////// | ||
243 | |||
244 | S32 LLXfer_File::suck(S32 start_position) | ||
245 | { | ||
246 | S32 retval = 0; | ||
247 | |||
248 | if (mFp) | ||
249 | { | ||
250 | // grab a buffer from the right place in the file | ||
251 | fseek (mFp,start_position,SEEK_SET); | ||
252 | |||
253 | mBufferLength = (U32)fread(mBuffer,1,LL_MAX_XFER_FILE_BUFFER,mFp); | ||
254 | mBufferStartOffset = start_position; | ||
255 | |||
256 | if (feof(mFp)) | ||
257 | { | ||
258 | mBufferContainsEOF = TRUE; | ||
259 | } | ||
260 | else | ||
261 | { | ||
262 | mBufferContainsEOF = FALSE; | ||
263 | } | ||
264 | } | ||
265 | else | ||
266 | { | ||
267 | retval = -1; | ||
268 | } | ||
269 | |||
270 | return (retval); | ||
271 | } | ||
272 | |||
273 | /////////////////////////////////////////////////////////// | ||
274 | |||
275 | S32 LLXfer_File::flush() | ||
276 | { | ||
277 | S32 retval = 0; | ||
278 | if (mBufferLength) | ||
279 | { | ||
280 | if (mFp) | ||
281 | { | ||
282 | llerrs << "Overwriting open file pointer!" << llendl; | ||
283 | } | ||
284 | mFp = LLFile::fopen(mTempFilename,"a+b"); /* Flawfinder : ignore */ | ||
285 | |||
286 | if (mFp) | ||
287 | { | ||
288 | fwrite(mBuffer,1,mBufferLength,mFp); | ||
289 | // llinfos << "******* wrote " << mBufferLength << " bytes of file xfer" << llendl; | ||
290 | fclose(mFp); | ||
291 | mFp = NULL; | ||
292 | |||
293 | mBufferLength = 0; | ||
294 | } | ||
295 | else | ||
296 | { | ||
297 | llwarns << "LLXfer_File::flush() unable to open " << mTempFilename << " for writing!" << llendl; | ||
298 | retval = LL_ERR_CANNOT_OPEN_FILE; | ||
299 | } | ||
300 | } | ||
301 | return (retval); | ||
302 | } | ||
303 | |||
304 | /////////////////////////////////////////////////////////// | ||
305 | |||
306 | S32 LLXfer_File::processEOF() | ||
307 | { | ||
308 | S32 retval = 0; | ||
309 | mStatus = e_LL_XFER_COMPLETE; | ||
310 | |||
311 | S32 flushval = flush(); | ||
312 | |||
313 | // If we have no other errors, our error becomes the error generated by | ||
314 | // flush. | ||
315 | if (!mCallbackResult) | ||
316 | { | ||
317 | mCallbackResult = flushval; | ||
318 | } | ||
319 | |||
320 | LLFile::remove(mLocalFilename); | ||
321 | |||
322 | if (!mCallbackResult) | ||
323 | { | ||
324 | if (LLFile::rename(mTempFilename,mLocalFilename)) | ||
325 | { | ||
326 | #if !LL_WINDOWS | ||
327 | S32 error_number = errno; | ||
328 | llinfos << "Rename failure (" << error_number << ") - " | ||
329 | << mTempFilename << " to " << mLocalFilename << llendl; | ||
330 | if(EXDEV == error_number) | ||
331 | { | ||
332 | if(copy_file(mTempFilename, mLocalFilename) == 0) | ||
333 | { | ||
334 | llinfos << "Rename across mounts; copying+unlinking the file instead." << llendl; | ||
335 | unlink(mTempFilename); | ||
336 | } | ||
337 | else | ||
338 | { | ||
339 | llwarns << "Copy failure - " << mTempFilename << " to " | ||
340 | << mLocalFilename << llendl; | ||
341 | } | ||
342 | } | ||
343 | else | ||
344 | { | ||
345 | //FILE* fp = LLFile::fopen(mTempFilename, "r"); | ||
346 | //llwarns << "File " << mTempFilename << " does " | ||
347 | // << (!fp ? "not" : "" ) << " exit." << llendl; | ||
348 | //if(fp) fclose(fp); | ||
349 | //fp = LLFile::fopen(mLocalFilename, "r"); | ||
350 | //llwarns << "File " << mLocalFilename << " does " | ||
351 | // << (!fp ? "not" : "" ) << " exit." << llendl; | ||
352 | //if(fp) fclose(fp); | ||
353 | llwarns << "Rename fatally failed, can only handle EXDEV (" | ||
354 | << EXDEV << ")" << llendl; | ||
355 | } | ||
356 | #else | ||
357 | llwarns << "Rename failure - " << mTempFilename << " to " | ||
358 | << mLocalFilename << llendl; | ||
359 | #endif | ||
360 | } | ||
361 | } | ||
362 | |||
363 | if (mFp) | ||
364 | { | ||
365 | fclose(mFp); | ||
366 | mFp = NULL; | ||
367 | } | ||
368 | |||
369 | retval = LLXfer::processEOF(); | ||
370 | |||
371 | return(retval); | ||
372 | } | ||
373 | |||
374 | /////////////////////////////////////////////////////////// | ||
375 | |||
376 | BOOL LLXfer_File::matchesLocalFilename(const LLString& filename) | ||
377 | { | ||
378 | return (filename == mLocalFilename); | ||
379 | } | ||
380 | |||
381 | /////////////////////////////////////////////////////////// | ||
382 | |||
383 | BOOL LLXfer_File::matchesRemoteFilename(const LLString& filename, ELLPath remote_path) | ||
384 | { | ||
385 | return ((filename == mRemoteFilename) && (remote_path == mRemotePath)); | ||
386 | } | ||
387 | |||
388 | |||
389 | /////////////////////////////////////////////////////////// | ||
390 | |||
391 | const char * LLXfer_File::getName() | ||
392 | { | ||
393 | return (mLocalFilename); | ||
394 | } | ||
395 | |||
396 | /////////////////////////////////////////////////////////// | ||
397 | |||
398 | // hacky - doesn't matter what this is | ||
399 | // as long as it's different from the other classes | ||
400 | U32 LLXfer_File::getXferTypeTag() | ||
401 | { | ||
402 | return LLXfer::XFER_FILE; | ||
403 | } | ||
404 | |||
405 | /////////////////////////////////////////////////////////// | ||
406 | |||
407 | #if !LL_WINDOWS | ||
408 | |||
409 | // This is really close to, but not quite a general purpose copy | ||
410 | // function. It does not really spam enough information, but is useful | ||
411 | // for this cpp file, because this should never be called in a | ||
412 | // production environment. | ||
413 | S32 copy_file(const char* from, const char* to) | ||
414 | { | ||
415 | S32 rv = 0; | ||
416 | FILE* in = LLFile::fopen(from, "rb"); | ||
417 | FILE* out = LLFile::fopen(to, "wb"); | ||
418 | if(in && out) | ||
419 | { | ||
420 | S32 read = 0; | ||
421 | const S32 COPY_BUFFER_SIZE = 16384; | ||
422 | U8 buffer[COPY_BUFFER_SIZE]; | ||
423 | while(((read = fread(buffer, 1, sizeof(buffer), in)) > 0) | ||
424 | && (fwrite(buffer, 1, read, out) == (U32)read)); /* Flawfinder : ignore */ | ||
425 | if(ferror(in) || ferror(out)) rv = -2; | ||
426 | } | ||
427 | else | ||
428 | { | ||
429 | rv = -1; | ||
430 | } | ||
431 | if(in) fclose(in); | ||
432 | if(out) fclose(out); | ||
433 | return rv; | ||
434 | } | ||
435 | #endif | ||