1 /*
2 * Part of WCM Commander
3 * https://github.com/corporateshark/WCMCommander
4 * wcm@linderdaum.com
5 */
6
7 #ifdef _WIN32
8 # define _CRT_SECURE_NO_WARNINGS
9 #endif
10
11 #include "file-util.h"
12 #include "ncwin.h"
13 #include "string-util.h"
14 #include "vfs.h"
15 #include "vfs-uri.h"
16 #include "fileopers.h"
17
18 #include <inttypes.h> // for PRIX64 macro (and INT64_C macro from stdint.h)
19 #include <unordered_map>
20
21
22 /// Next Temp directory ID, incremented each time a new Temp directory is created
23 static int g_NextTempDirId = 1;
24
25 /// Temp directories ID to URI global list
26 static std::unordered_map<int, std::vector<unicode_t>> g_TempDirUriList;
27
28
29 bool DeleteDirRecursively( FS* fs, FSPath path );
30
DeleteListRecursively(FS * fs,FSPath path,FSList & list)31 bool DeleteListRecursively( FS* fs, FSPath path, FSList& list )
32 {
33 const int cnt = path.Count();
34 int ret_err;
35
36 for ( FSNode* node = list.First(); node; node = node->next )
37 {
38 if ( node->extType )
39 {
40 continue;
41 }
42
43 path.SetItemStr( cnt, node->Name() );
44
45 if ( node->IsDir() && !node->st.IsLnk() )
46 {
47 if ( !DeleteDirRecursively( fs, path ) )
48 {
49 return false;
50 }
51 }
52 else if ( fs->Delete( path, &ret_err, nullptr ) != 0 )
53 {
54 return false;
55 }
56 }
57
58 return true;
59 }
60
61 /// Deletes dir recursively
DeleteDirRecursively(FS * fs,FSPath path)62 bool DeleteDirRecursively( FS* fs, FSPath path )
63 {
64 FSList list;
65 int ret_err;
66 int ret = fs->ReadDir( &list, path, &ret_err, nullptr );
67 if ( ret == 0 )
68 {
69 DeleteListRecursively( fs, path, list );
70 return ( fs->RmDir( path, &ret_err, nullptr ) == 0 );
71 }
72
73 return false;
74 }
75
76
77 #ifdef _WIN32
78
GetHomeUriWin()79 std::vector<unicode_t> GetHomeUriWin()
80 {
81 wchar_t homeDrive[0x100];
82 wchar_t homePath[0x100];
83 int l1 = GetEnvironmentVariableW( L"HOMEDRIVE", homeDrive, 0x100 );
84 int l2 = GetEnvironmentVariableW( L"HOMEPATH", homePath, 0x100 );
85
86 if ( l1 > 0 && l1 < 0x100 && l2 > 0 && l2 < 0x100 )
87 {
88 return carray_cat<unicode_t>( Utf16ToUnicode( homeDrive ).data(), Utf16ToUnicode( homePath ).data() );
89 }
90
91 return std::vector<unicode_t>();
92 }
93
GetTempUriWin()94 std::vector<unicode_t> GetTempUriWin()
95 {
96 wchar_t TempPath[0x100];
97 int l1 = GetEnvironmentVariableW( L"TMP", TempPath, 0x100 );
98
99 if ( l1 > 0 && l1 < 0x100 )
100 {
101 return Utf16ToUnicode( TempPath );
102 }
103
104 return std::vector<unicode_t>();
105 }
106
107 #endif // _WIN32
108
109
GetSysTempDir(clPtr<FS> * fs,FSPath * path)110 bool GetSysTempDir( clPtr<FS>* fs, FSPath* path )
111 {
112 #ifdef _WIN32
113 std::vector<unicode_t> TempUri = GetTempUriWin();
114
115 if ( TempUri.data() )
116 {
117 *fs = ParzeURI( TempUri.data(), *path, std::vector<clPtr<FS>>() );
118
119 return !fs->IsNull();
120 }
121
122 return false;
123
124 #else
125 const sys_char_t* Temp = (sys_char_t*) getenv( "TMPDIR" );
126 if ( !Temp )
127 {
128 return false;
129 }
130
131 path->Set( sys_charset_id, Temp );
132 *fs = new FSSys();
133 return true;
134 #endif
135 }
136
CreateWcmTempDir(clPtr<FS> * fs,FSPath * path)137 int CreateWcmTempDir( clPtr<FS>* fs, FSPath* path )
138 {
139 if ( !GetSysTempDir( fs, path ) )
140 {
141 return 0;
142 }
143
144 const int Id = g_NextTempDirId++;
145
146 time_t TimeInSeconds = time( 0 ); // get time now
147
148 // convert current time from int64 to hex form
149 // got from here: http://stackoverflow.com/questions/7240391/decimal-to-hex-for-int64-in-c
150 char Buf[128];
151 sprintf( Buf, "WCM%" PRIx64 ".%d", (int64_t)TimeInSeconds, Id );
152
153 path->Push( CS_UTF8, Buf );
154
155 int ret_err;
156 if ( fs->Ptr()->MkDir( *path, 0777, &ret_err, nullptr ) == 0 )
157 {
158 // store created temp dir for later cleanup
159 g_TempDirUriList[Id] = new_unicode_str( (*fs)->Uri( *path ).GetUnicode() );
160 }
161
162 return Id;
163 }
164
RemoveWcmTempDir(const int Id)165 void RemoveWcmTempDir( const int Id )
166 {
167 auto iter = g_TempDirUriList.find( Id );
168
169 if ( iter != g_TempDirUriList.end() )
170 {
171 std::vector<unicode_t> TempUri = iter->second;
172
173 if ( TempUri.data() )
174 {
175 FSPath path;
176 clPtr<FS> fs = ParzeURI( TempUri.data(), path, std::vector<clPtr<FS>>() );
177
178 DeleteDirRecursively( fs.Ptr(), path );
179 }
180 }
181 }
182
RemoveAllWcmTempDirs()183 void RemoveAllWcmTempDirs()
184 {
185 for ( auto &iter : g_TempDirUriList )
186 {
187 std::vector<unicode_t> TempUri = iter.second;
188
189 if ( TempUri.data() )
190 {
191 FSPath path;
192 clPtr<FS> fs = ParzeURI( TempUri.data(), path, std::vector<clPtr<FS>>() );
193
194 DeleteDirRecursively( fs.Ptr(), path );
195 }
196 }
197 }
198
OpenHomeDir(PanelWin * p)199 void OpenHomeDir( PanelWin* p )
200 {
201 #ifdef _WIN32
202 std::vector<unicode_t> homeUri = GetHomeUriWin();
203
204 if ( homeUri.data() )
205 {
206 const std::vector<clPtr<FS>> checkFS =
207 {
208 p->GetFSPtr(),
209 g_MainWin->GetOtherPanel( p )->GetFSPtr()
210 };
211
212 FSPath path;
213 clPtr<FS> fs = ParzeURI( homeUri.data(), path, checkFS );
214
215 if ( fs.IsNull() )
216 {
217 char buf[4096];
218 FSString name = homeUri.data();
219 Lsnprintf( buf, sizeof( buf ), "bad home path: %s\n", name.GetUtf8() );
220 NCMessageBox( g_MainWin, "Home", buf, true );
221 }
222 else
223 {
224 p->LoadPath( fs, path, 0, 0, PanelWin::SET );
225 }
226 }
227
228 #else
229 const sys_char_t* home = (sys_char_t*) getenv( "HOME" );
230 if ( !home )
231 {
232 return;
233 }
234
235 FSPath path( sys_charset_id, home );
236 p->LoadPath( new FSSys(), path, 0, 0, PanelWin::SET );
237 #endif
238 }
239
240
241 //////////////////////////////// Load file data
242
243
244 class LoadFileDataThreadData : public OperData
245 {
246 public:
247 clPtr<FS>& m_SrcFs;
248 FSPath& m_SrcPath;
249 clPtr<FS>& m_DstFs;
250 FSPath& m_DstPath;
251 FSString m_ErrorString;
252
253 bool m_Success;
254
LoadFileDataThreadData(NCDialogParent * p,clPtr<FS> & srcFs,FSPath & srcPath,clPtr<FS> & dstFs,FSPath & dstPath)255 LoadFileDataThreadData( NCDialogParent* p, clPtr<FS>& srcFs, FSPath& srcPath, clPtr<FS>& dstFs, FSPath& dstPath )
256 : OperData( p )
257 , m_SrcFs( srcFs )
258 , m_SrcPath( srcPath )
259 , m_DstFs( dstFs )
260 , m_DstPath( dstPath )
261 , m_ErrorString( "" )
262 , m_Success( false )
263 {
264 }
265 };
266
267
268 class LoadFileDataThread : public OperFileThread
269 {
270 public:
LoadFileDataThread(const char * opName,NCDialogParent * par,OperThreadNode * n)271 LoadFileDataThread( const char* opName, NCDialogParent* par, OperThreadNode* n )
272 : OperFileThread( opName, par, n ) {}
273
274 virtual void Run() override;
275 };
276
Run()277 void LoadFileDataThread::Run()
278 {
279 MutexLock lock( Node().GetMutex() ); //!!!
280
281 if ( Node().NBStopped() )
282 {
283 return;
284 }
285
286 LoadFileDataThreadData* Data = ((LoadFileDataThreadData*) Node().Data());
287 clPtr<FS>& srcFs = Data->m_SrcFs;
288 FSPath& srcPath = Data->m_SrcPath;
289 clPtr<FS>& dstFs = Data->m_DstFs;
290 FSPath& dstPath = Data->m_DstPath;
291 lock.Unlock();
292
293 int ret_error;
294 int in = -1;
295 int out = -1;
296
297 try
298 {
299 in = srcFs->OpenRead( srcPath, FS::SHARE_READ, &ret_error, Info() );
300 if ( in < 0 )
301 {
302 throw_msg( "%s", srcFs->StrError( ret_error ).GetUtf8() );
303 }
304
305 out = dstFs->OpenCreate( dstPath, true, 0666, 0, &ret_error, Info() );
306 if ( out < 0 )
307 {
308 throw_msg( "%s", dstFs->StrError( ret_error ).GetUtf8() );
309 }
310
311 char buf[16 * 1024];
312
313 while ( true )
314 {
315 lock.Lock();
316 if ( Node().NBStopped() )
317 {
318 // stopped by user pressing Cancel button
319 lock.Unlock();
320 break;
321 }
322
323 lock.Unlock();
324
325 const int n = srcFs->Read( in, buf, sizeof( buf ), &ret_error, Info() );
326 if ( n < 0 )
327 {
328 throw_msg( "Can't load file:\n%s", srcFs->StrError( ret_error ).GetUtf8() );
329 }
330
331 if ( n == 0 )
332 {
333 // we reached end of file
334 lock.Lock();
335 Data->m_Success = true;
336 lock.Unlock();
337 break;
338 }
339
340 const int b = dstFs->Write( out, buf, n, &ret_error, Info() );
341 if ( b < 0 )
342 {
343 throw_msg( "Can't write file:\n%s", dstFs->StrError( ret_error ).GetUtf8() );
344 }
345
346 if ( b != n )
347 {
348 throw_msg( "Cannot save data, maybe disk is full" );
349 }
350 }
351
352 srcFs->Close( in, 0, Info() );
353 in = -1;
354
355 dstFs->Close( out, 0, Info() );
356 out = -1;
357 }
358 catch ( ... )
359 {
360 if ( in >= 0 )
361 {
362 srcFs->Close( in, 0, Info() );
363 }
364 if ( out >= 0 )
365 {
366 dstFs->Close( out, 0, Info() );
367 }
368
369 throw;
370 }
371 }
372
373
LoadFileDataThreadFunc(OperThreadNode * node)374 void LoadFileDataThreadFunc( OperThreadNode* node )
375 {
376 try
377 {
378 MutexLock lock( node->GetMutex() );
379 if ( !node->Data() )
380 {
381 return;
382 }
383
384 LoadFileDataThreadData* Data = ((LoadFileDataThreadData*) node->Data());
385 LoadFileDataThread thread( "Load file", Data->Parent(), node );
386 lock.Unlock();
387
388 try
389 {
390 thread.Run();
391 }
392 catch ( cexception* ex )
393 {
394 lock.Lock(); //!!!
395
396 if ( !node->NBStopped() ) //must always be checked, otherwise data maybe invalid
397 {
398 Data->m_ErrorString = ex->message();
399 }
400
401 lock.Unlock();
402 ex->destroy();
403 }
404 }
405 catch ( cexception* ex )
406 {
407 fprintf( stderr, "ERR!!! Exception in LoadFileDataThreadFunc - '%s'\n", ex->message() );
408 ex->destroy();
409 }
410 catch ( ... )
411 {
412 fprintf( stderr, "ERR!!! Unhandled exception in LoadFileDataThreadFunc\n" );
413 }
414 }
415
416
417 class LoadFileDataThreadDlg : public NCDialog
418 {
419 public:
420 LoadFileDataThreadData m_Data;
421
LoadFileDataThreadDlg(NCDialogParent * parent,clPtr<FS> & srcFs,FSPath & srcPath,clPtr<FS> & dstFs,FSPath & dstPath)422 LoadFileDataThreadDlg( NCDialogParent* parent, clPtr<FS>& srcFs, FSPath& srcPath, clPtr<FS>& dstFs, FSPath& dstPath )
423 : NCDialog( ::createDialogAsChild, 0, parent, utf8_to_unicode( "Loading file" ).data(), bListCancel )
424 , m_Data( parent, srcFs, srcPath, dstFs, dstPath )
425 {
426 }
427
428 virtual void OperThreadStopped() override;
429
OnCancel()430 virtual void OnCancel() override
431 {
432 SetStopFlag();
433 }
434 };
435
OperThreadStopped()436 void LoadFileDataThreadDlg::OperThreadStopped()
437 {
438 if ( !m_Data.m_ErrorString.IsEmpty() )
439 {
440 NCMessageBox( (NCDialogParent*) Parent(), "Failed to load file data", m_Data.m_ErrorString.GetUtf8(), true );
441 EndModal( 0 );
442 return;
443 }
444
445 EndModal( CMD_OK );
446 }
447
448
LoadToTempFile(NCDialogParent * parent,clPtr<FS> * fs,FSPath * path)449 int LoadToTempFile( NCDialogParent* parent, clPtr<FS>* fs, FSPath* path )
450 {
451 clPtr<FS> TempFs;
452 FSPath TempPath;
453 const int TempId = CreateWcmTempDir( &TempFs, &TempPath );
454 if ( !TempId )
455 {
456 return 0;
457 }
458
459 // append file name to the created temp dir
460 FSPath DstPath = TempPath;
461 DstPath.Push( CS_UTF8, path->GetItem( path->Count() - 1 )->GetUtf8() );
462
463 LoadFileDataThreadDlg dlg( parent, *fs, *path, TempFs, DstPath );
464 dlg.RunNewThread( "Load file", LoadFileDataThreadFunc, &dlg.m_Data );
465 dlg.DoModal();
466
467 if ( !dlg.m_Data.m_Success )
468 {
469 // cleanup created temp dir
470 RemoveWcmTempDir( TempId );
471 return 0;
472 }
473
474 *fs = TempFs;
475 *path = DstPath;
476 return TempId;
477 }
478