[cstdstring] demise Format, replacing with StringUtils::Format
[vuplus_xbmc] / xbmc / utils / FileOperationJob.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "FileOperationJob.h"
22 #include "filesystem/File.h"
23 #include "filesystem/Directory.h"
24 #include "filesystem/ZipManager.h"
25 #include "filesystem/FileDirectoryFactory.h"
26 #include "filesystem/MultiPathDirectory.h"
27 #include "filesystem/SpecialProtocol.h"
28 #include "log.h"
29 #include "Util.h"
30 #include "URIUtils.h"
31 #include "utils/StringUtils.h"
32 #include "URL.h"
33 #include "guilib/LocalizeStrings.h"
34 #include "guilib/GUIWindowManager.h"
35 #include "dialogs/GUIDialogExtendedProgressBar.h"
36
37 #ifdef HAS_FILESYSTEM_RAR
38 #include "filesystem/RarManager.h"
39 #endif
40
41 using namespace std;
42 using namespace XFILE;
43
44 CFileOperationJob::CFileOperationJob()
45 {
46   m_handle = NULL;
47   m_displayProgress = false;
48 }
49
50 CFileOperationJob::CFileOperationJob(FileAction action, CFileItemList & items,
51                                     const CStdString& strDestFile,
52                                     bool displayProgress,
53                                     int heading, int line)
54 {
55   m_handle = NULL;
56   m_displayProgress = displayProgress;
57   m_heading = heading;
58   m_line = line;
59   SetFileOperation(action, items, strDestFile);
60 }
61
62 void CFileOperationJob::SetFileOperation(FileAction action, CFileItemList &items, const CStdString &strDestFile)
63 {
64   m_action = action;
65   m_strDestFile = strDestFile;
66
67   m_items.Clear();
68   for (int i = 0; i < items.Size(); i++)
69     m_items.Add(CFileItemPtr(new CFileItem(*items[i])));
70 }
71
72 bool CFileOperationJob::DoWork()
73 {
74   FileOperationList ops;
75   double totalTime = 0.0;
76
77   if (m_displayProgress)
78   {
79     CGUIDialogExtendedProgressBar* dialog =
80       (CGUIDialogExtendedProgressBar*)g_windowManager.GetWindow(WINDOW_DIALOG_EXT_PROGRESS);
81     m_handle = dialog->GetHandle(GetActionString(m_action));
82   }
83
84   bool success = DoProcess(m_action, m_items, m_strDestFile, ops, totalTime);
85
86   unsigned int size = ops.size();
87
88   double opWeight = 100.0 / totalTime;
89   double current = 0.0;
90
91   for (unsigned int i = 0; i < size && success; i++)
92     success &= ops[i].ExecuteOperation(this, current, opWeight);
93
94   if (m_handle)
95     m_handle->MarkFinished();
96
97   return success;
98 }
99
100 bool CFileOperationJob::DoProcessFile(FileAction action, const CStdString& strFileA, const CStdString& strFileB, FileOperationList &fileOperations, double &totalTime)
101 {
102   int64_t time = 1;
103
104   if (action == ActionCopy || action == ActionReplace || (action == ActionMove && !CanBeRenamed(strFileA, strFileB)))
105   {
106     struct __stat64 data;
107     if(CFile::Stat(strFileA, &data) == 0)
108       time += data.st_size;
109   }
110
111   fileOperations.push_back(CFileOperation(action, strFileA, strFileB, time));
112
113   totalTime += time;
114
115   return true;
116 }
117
118 bool CFileOperationJob::DoProcessFolder(FileAction action, const CStdString& strPath, const CStdString& strDestFile, FileOperationList &fileOperations, double &totalTime)
119 {
120   // check whether this folder is a filedirectory - if so, we don't process it's contents
121   CFileItem item(strPath, false);
122   IFileDirectory *file = CFileDirectoryFactory::Create(strPath, &item);
123   if (file)
124   {
125     delete file;
126     return true;
127   }
128   CLog::Log(LOGDEBUG,"FileManager, processing folder: %s",strPath.c_str());
129   CFileItemList items;
130   //m_rootDir.GetDirectory(strPath, items);
131   CDirectory::GetDirectory(strPath, items, "", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_GET_HIDDEN);
132   for (int i = 0; i < items.Size(); i++)
133   {
134     CFileItemPtr pItem = items[i];
135     pItem->Select(true);
136     CLog::Log(LOGDEBUG,"  -- %s",pItem->GetPath().c_str());
137   }
138
139   if (!DoProcess(action, items, strDestFile, fileOperations, totalTime)) return false;
140
141   if (action == ActionMove)
142   {
143     fileOperations.push_back(CFileOperation(ActionDeleteFolder, strPath, "", 1));
144     totalTime += 1.0;
145   }
146
147   return true;
148 }
149
150 bool CFileOperationJob::DoProcess(FileAction action, CFileItemList & items, const CStdString& strDestFile, FileOperationList &fileOperations, double &totalTime)
151 {
152   for (int iItem = 0; iItem < items.Size(); ++iItem)
153   {
154     CFileItemPtr pItem = items[iItem];
155     if (pItem->IsSelected())
156     {
157       CStdString strNoSlash = pItem->GetPath();
158       URIUtils::RemoveSlashAtEnd(strNoSlash);
159       CStdString strFileName = URIUtils::GetFileName(strNoSlash);
160
161       // URL Decode for cases where source uses URL encoding and target does not 
162       if ( URIUtils::ProtocolHasEncodedFilename(CURL(pItem->GetPath()).GetProtocol() )
163        && !URIUtils::ProtocolHasEncodedFilename(CURL(strDestFile).GetProtocol() ) )
164         CURL::Decode(strFileName);
165
166       // special case for upnp
167       if (URIUtils::IsUPnP(items.GetPath()) || URIUtils::IsUPnP(pItem->GetPath()))
168       {
169         // get filename from label instead of path
170         strFileName = pItem->GetLabel();
171
172         if(!pItem->m_bIsFolder && !URIUtils::HasExtension(strFileName))
173         {
174           // FIXME: for now we only work well if the url has the extension
175           // we should map the content type to the extension otherwise
176           strFileName += URIUtils::GetExtension(pItem->GetPath());
177         }
178
179         strFileName = CUtil::MakeLegalFileName(strFileName);
180       }
181
182       CStdString strnewDestFile;
183       if(!strDestFile.IsEmpty()) // only do this if we have a destination
184         strnewDestFile = URIUtils::AddFileToFolder(strDestFile, strFileName);
185
186       if (pItem->m_bIsFolder)
187       {
188         // in ActionReplace mode all subdirectories will be removed by the below
189         // DoProcessFolder(ActionDelete) call as well, so ActionCopy is enough when
190         // processing those
191         FileAction subdirAction = (action == ActionReplace) ? ActionCopy : action;
192         // create folder on dest. drive
193         if (action != ActionDelete && action != ActionDeleteFolder)
194           DoProcessFile(ActionCreateFolder, strnewDestFile, "", fileOperations, totalTime);
195         if (action == ActionReplace && CDirectory::Exists(strnewDestFile))
196           DoProcessFolder(ActionDelete, strnewDestFile, "", fileOperations, totalTime);
197         if (!DoProcessFolder(subdirAction, pItem->GetPath(), strnewDestFile, fileOperations, totalTime))
198           return false;
199         if (action == ActionDelete || action == ActionDeleteFolder)
200           DoProcessFile(ActionDeleteFolder, pItem->GetPath(), "", fileOperations, totalTime);
201       }
202       else
203         DoProcessFile(action, pItem->GetPath(), strnewDestFile, fileOperations, totalTime);
204     }
205   }
206   return true;
207 }
208
209 CFileOperationJob::CFileOperation::CFileOperation(FileAction action, const CStdString &strFileA, const CStdString &strFileB, int64_t time) : m_action(action), m_strFileA(strFileA), m_strFileB(strFileB), m_time(time)
210 {
211 }
212
213 struct DataHolder
214 {
215   CFileOperationJob *base;
216   double current;
217   double opWeight;
218 };
219
220 CStdString CFileOperationJob::GetActionString(FileAction action)
221 {
222   CStdString result;
223   switch (action)
224   {
225     case ActionCopy:
226     case ActionReplace:
227       result = g_localizeStrings.Get(115);
228       break;
229     case ActionMove:
230       result = g_localizeStrings.Get(116);
231       break;
232     case ActionDelete:
233     case ActionDeleteFolder:
234       result = g_localizeStrings.Get(117);
235       break;
236     case ActionCreateFolder:
237       result = g_localizeStrings.Get(119);
238       break;
239     default:
240       break;
241   }
242
243   return result;
244 }
245
246 bool CFileOperationJob::CFileOperation::ExecuteOperation(CFileOperationJob *base, double &current, double opWeight)
247 {
248   bool bResult = true;
249
250   base->m_currentFile = CURL(m_strFileA).GetFileNameWithoutPath();
251   base->m_currentOperation = GetActionString(m_action);
252
253   if (base->ShouldCancel((unsigned)current, 100))
254     return false;
255
256   if (base->m_handle)
257   {
258     base->m_handle->SetText(base->GetCurrentFile());
259     base->m_handle->SetPercentage((float)current);
260   }
261
262   DataHolder data = {base, current, opWeight};
263
264   switch (m_action)
265   {
266     case ActionCopy:
267     case ActionReplace:
268     {
269       CLog::Log(LOGDEBUG,"FileManager: copy %s -> %s\n", m_strFileA.c_str(), m_strFileB.c_str());
270
271       bResult = CFile::Cache(m_strFileA, m_strFileB, this, &data);
272     }
273     break;
274     case ActionMove:
275     {
276       CLog::Log(LOGDEBUG,"FileManager: move %s -> %s\n", m_strFileA.c_str(), m_strFileB.c_str());
277
278       if (CanBeRenamed(m_strFileA, m_strFileB))
279         bResult = CFile::Rename(m_strFileA, m_strFileB);
280       else if (CFile::Cache(m_strFileA, m_strFileB, this, &data))
281         bResult = CFile::Delete(m_strFileA);
282       else
283         bResult = false;
284     }
285     break;
286     case ActionDelete:
287     {
288       CLog::Log(LOGDEBUG,"FileManager: delete %s\n", m_strFileA.c_str());
289
290       bResult = CFile::Delete(m_strFileA);
291     }
292     break;
293     case ActionDeleteFolder:
294     {
295       CLog::Log(LOGDEBUG,"FileManager: delete folder %s\n", m_strFileA.c_str());
296
297       bResult = CDirectory::Remove(m_strFileA);
298     }
299     break;
300     case ActionCreateFolder:
301     {
302       CLog::Log(LOGDEBUG,"FileManager: create folder %s\n", m_strFileA.c_str());
303
304       bResult = CDirectory::Create(m_strFileA);
305     }
306     break;
307   }
308
309   current += (double)m_time * opWeight;
310
311   return bResult;
312 }
313
314 inline bool CFileOperationJob::CanBeRenamed(const CStdString &strFileA, const CStdString &strFileB)
315 {
316 #ifndef TARGET_POSIX
317   if (strFileA[1] == ':' && strFileA[0] == strFileB[0])
318     return true;
319 #else
320   if (URIUtils::IsHD(strFileA) && URIUtils::IsHD(strFileB))
321     return true;
322 #endif
323   return false;
324 }
325
326 void CFileOperationJob::CFileOperation::Debug()
327 {
328   printf("%i | %s > %s\n", m_action, m_strFileA.c_str(), m_strFileB.c_str());
329 }
330
331 bool CFileOperationJob::CFileOperation::OnFileCallback(void* pContext, int ipercent, float avgSpeed)
332 {
333   DataHolder *data = (DataHolder *)pContext;
334   double current = data->current + ((double)ipercent * data->opWeight * (double)m_time)/ 100.0;
335
336   if (avgSpeed > 1000000.0f)
337     data->base->m_avgSpeed = StringUtils::Format("%.1f MB/s", avgSpeed / 1000000.0f);
338   else
339     data->base->m_avgSpeed = StringUtils::Format("%.1f KB/s", avgSpeed / 1000.0f);
340
341   if (data->base->m_handle)
342   {
343     CStdString line;
344     line = StringUtils::Format("%s (%s)",
345                                data->base->GetCurrentFile().c_str(),
346                                data->base->GetAverageSpeed().c_str());
347     data->base->m_handle->SetText(line);
348     data->base->m_handle->SetPercentage((float)current);
349   }
350
351   return !data->base->ShouldCancel((unsigned)current, 100);
352 }
353
354 bool CFileOperationJob::operator==(const CJob* job) const
355 {
356   if (strcmp(job->GetType(),GetType()) == 0)
357   {
358     const CFileOperationJob* rjob = dynamic_cast<const CFileOperationJob*>(job);
359     if (rjob)
360     {
361       if (GetAction() == rjob->GetAction() &&
362           m_strDestFile == rjob->m_strDestFile &&
363           m_items.Size() == rjob->m_items.Size())
364       {
365         for (int i=0;i<m_items.Size();++i)
366         {
367           if (m_items[i]->GetPath() != rjob->m_items[i]->GetPath() ||
368               m_items[i]->IsSelected() != rjob->m_items[i]->IsSelected())
369             return false;
370         }
371         return true;
372       }
373     }
374   }
375   return false;
376 }