tizen beta release
[framework/web/wrt-installer.git] / src / jobs / widget_install / task_unzip.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 /*
17  * @file    task_unzip.cpp
18  * @author  Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com)
19  * @version 1.0
20  * @brief   Implementation file for installer task unzip
21  */
22 #include <widget_install/task_unzip.h>
23 #include <widget_install/widget_install_errors.h>
24 #include <widget_install/widget_install_context.h>
25 #include <dpl/wrt-dao-ro/global_config.h>
26 #include <dpl/log/log.h>
27 #include <dpl/copy.h>
28 #include <dpl/file_output.h>
29 #include <dpl/abstract_waitable_input_adapter.h>
30 #include <dpl/errno_string.h>
31 #include <unistd.h>
32 #include <sys/stat.h>
33 #include <sys/time.h>
34 #include <errno.h>
35 #include <ftw.h>
36 #include <dpl/utils/file_utils.h>
37
38 using namespace WrtDB;
39
40 namespace // anonymous
41 {
42 const char * const TEMPORARY_PATH_POSTFIX = "temp";
43 const mode_t TEMPORARY_PATH_MODE = 0775;
44
45 struct PathAndFilePair
46 {
47     std::string path;
48     std::string file;
49
50     PathAndFilePair(const std::string &p,
51             const std::string &f) :
52         path(p),
53         file(f)
54     {
55     }
56 };
57
58 PathAndFilePair SplitFileAndPath(const std::string &filePath)
59 {
60     std::string::size_type position = filePath.rfind('/');
61
62     // Is this only a file without a path ?
63     if (position == std::string::npos) {
64         return PathAndFilePair(std::string(), filePath);
65     }
66
67     // This is full file-path pair
68     return PathAndFilePair(filePath.substr(0,
69                                            position),
70                            filePath.substr(position + 1));
71 }
72
73 static int lambdaDeleteFile(const char *fpath,
74         const struct stat *sb,
75         int tflag,
76         struct FTW *ftwbuf)
77 {
78     (void)sb;
79     (void)ftwbuf;
80
81     switch (tflag) {
82     case FTW_D:
83     case FTW_DNR:
84     case FTW_DP:
85         LogInfo("Removing old temporary directory" << fpath);
86         return rmdir(fpath);
87         break;
88     default:
89         LogInfo("Unlinking old temporary file" << fpath);
90         return unlink(fpath);
91         break;
92     }
93
94     return 0;
95 }
96 } // namespace anonymous
97
98 namespace Jobs {
99 namespace WidgetInstall {
100 TaskUnzip::TaskUnzip(InstallerContext &installerContext) :
101     DPL::TaskDecl<TaskUnzip>(this),
102     m_installerContext(installerContext)
103 {
104     // Install steps
105     AddStep(&TaskUnzip::StepCreateTempPath);
106     AddStep(&TaskUnzip::StepUnzipPrepare);
107     AddStep(&TaskUnzip::StepUnzipProgress);
108     AddStep(&TaskUnzip::StepUnzipFinished);
109
110     AddAbortStep(&TaskUnzip::StepAbort);
111 }
112
113 void TaskUnzip::ExtractFile(DPL::ZipInput::File *input,
114         const std::string &destFileName)
115 {
116     Try
117     {
118         DPL::AbstractWaitableInputAdapter inputAdapter(input);
119         DPL::FileOutput output(destFileName);
120
121         DPL::Copy(&inputAdapter, &output);
122     }
123     Catch(DPL::FileOutput::Exception::OpenFailed)
124     {
125         ReThrowMsg(Exceptions::ExtractFileFailed, destFileName);
126     }
127     Catch(DPL::CopyFailed)
128     {
129         ReThrowMsg(Exceptions::ExtractFileFailed, destFileName);
130     }
131 }
132
133 void TaskUnzip::StepCreateTempPath()
134 {
135     LogInfo("Step: Creating temporary path");
136
137     // Temporary path
138     std::ostringstream tempPathBuilder;
139
140     tempPathBuilder << GlobalConfig::GetUserInstalledWidgetPath();
141     tempPathBuilder << "/";
142     tempPathBuilder << "widget";
143     tempPathBuilder << "/";
144     tempPathBuilder << TEMPORARY_PATH_POSTFIX;
145     tempPathBuilder << "_";
146
147     timeval tv;
148     gettimeofday(&tv, NULL);
149     tempPathBuilder <<
150     (static_cast<unsigned long long>(tv.tv_sec) * 1000000ULL +
151      static_cast<unsigned long long>(tv.tv_usec));
152
153     std::string tempPath = tempPathBuilder.str();
154
155     // Remove old path if any
156     struct stat fileInfo;
157
158     // FIXME: what if there are more then maxDepth recursive directories
159     static const int maxDepth = 1024;
160     if (stat(tempPath.c_str(), &fileInfo) == 0) {
161         int error = nftw(
162                 tempPath.c_str(), lambdaDeleteFile, maxDepth, FTW_DEPTH);
163
164         if (error == -1) {
165             ThrowMsg(DPL::CommonException::InternalError,
166                      DPL::GetErrnoString());
167         }
168     }
169     // Create new path
170     FileUtils::MakePath(tempPath, TEMPORARY_PATH_MODE);
171
172     // Step succedded, save temporary widget path
173     m_installerContext.tempWidgetPath = tempPath;
174     m_installerContext.unzipStarted = true;
175 }
176
177 void TaskUnzip::StepUnzipPrepare()
178 {
179     LogInfo("Prepare to unzip...");
180
181     Try
182     {
183         m_zip.Reset(new DPL::ZipInput(m_installerContext.widgetFilePath));
184         LogInfo("Widget package comment: " << m_zip->GetGlobalComment());
185
186         // Widget package must not be empty
187         if (m_zip->empty()) {
188             ThrowMsg(Exceptions::ZipEmpty, m_installerContext.widgetFilePath);
189         }
190
191         // Set iterator to first file
192         m_zipIterator = m_zip->begin();
193     }
194     Catch(DPL::ZipInput::Exception::OpenFailed)
195     {
196         ReThrowMsg(Exceptions::OpenZipFailed, m_installerContext.widgetFilePath);
197     }
198 }
199
200 void TaskUnzip::StepUnzipProgress()
201 {
202     // Show file info
203     LogInfo("Unzipping: '" << m_zipIterator->name <<
204             "', Comment: '" << m_zipIterator->comment <<
205             "', Compressed size: " << m_zipIterator->compressedSize <<
206             ", Uncompressed size: " << m_zipIterator->uncompressedSize);
207
208     // Normalize file paths
209     // FIXME: Implement checking for invalid characters
210
211     // Extract file or path
212     std::string fileName = m_zipIterator->name;
213
214     if (fileName[fileName.size() - 1] == '/') {
215         // This is path
216         std::string newPath = m_installerContext.tempWidgetPath + "/" +
217             fileName.substr(0, fileName.size() - 1);
218         LogPedantic("Path to extract: " << newPath);
219
220         // Create path in case of it is empty
221         FileUtils::MakePath(newPath, TEMPORARY_PATH_MODE);
222     } else {
223         // This is regular file
224         std::string fileExtractPath =
225             m_installerContext.tempWidgetPath + "/" + fileName;
226
227         LogPedantic("File to extract: " << fileExtractPath);
228
229         // Split into pat & file pair
230         PathAndFilePair pathAndFile = SplitFileAndPath(fileExtractPath);
231
232         LogPedantic("Path and file: " <<
233                     pathAndFile.path <<
234                     " : " << pathAndFile.file);
235
236         // First, ensure that path exists
237         FileUtils::MakePath(pathAndFile.path, TEMPORARY_PATH_MODE);
238
239         Try
240         {
241             // Open file
242             DPL::ScopedPtr<DPL::ZipInput::File> file(
243                 m_zip->OpenFile(fileName));
244
245             // Extract single file
246             ExtractFile(file.Get(), fileExtractPath);
247         }
248         Catch(DPL::ZipInput::Exception::OpenFileFailed)
249         {
250             ThrowMsg(Exceptions::ExtractFileFailed, fileName);
251         }
252     }
253
254     // Check whether there are more files to extract
255     if (++m_zipIterator == m_zip->end()) {
256         LogInfo("Unzip progress finished successfuly");
257     } else {
258         SwitchToStep(&TaskUnzip::StepUnzipProgress);
259     }
260 }
261
262 void TaskUnzip::StepUnzipFinished()
263 {
264     // Unzip finished, close internal structures
265     m_zip.Reset();
266
267     m_installerContext.unzipFinished = true;
268
269     // Done
270     LogInfo("Unzip finished");
271 }
272
273 void TaskUnzip::StepAbort()
274 {
275     LogError("[Unzip Task] Aborting... (removing temporary dir: " <<
276              m_installerContext.tempWidgetPath << " )");
277
278     static const int maxDepth = 1024;
279     struct stat fileInfo;
280     if (stat(m_installerContext.tempWidgetPath.c_str(), &fileInfo) == 0) {
281         nftw(m_installerContext.tempWidgetPath.c_str(),
282              lambdaDeleteFile, maxDepth, FTW_DEPTH);
283     }
284 }
285 } //namespace WidgetInstall
286 } //namespace Jobs