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