Removing warnings reported by cppcheck tool.
[framework/web/wrt-installer.git] / src / jobs / widget_install / task_encrypt_resource.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_ecnrypt_resource.cpp
18  * @author  Soyoung Kim (sy037.kim@samsung.com)
19  * @version 1.0
20  * @brief   Implementation file for installer task encrypt resource
21  */
22 #include "task_encrypt_resource.h"
23
24 #undef __USE_FILE_OFFSET64
25
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <fts.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <cstdio>
32
33 #include <memory>
34
35 #include <dpl/log/log.h>
36 #include <dpl/errno_string.h>
37 #include <dpl/foreach.h>
38 #include <dpl/scoped_fclose.h>
39 #include <dpl/wrt-dao-ro/global_config.h>
40 #include <dpl/string.h>
41
42 #include <widget_install/job_widget_install.h>
43 #include <widget_install/widget_install_context.h>
44 #include <widget_install/widget_install_errors.h>
45
46 using namespace WrtDB;
47 using namespace WRTEncryptor;
48
49 namespace {
50 const std::size_t ENCRYPTION_CHUNK_MAX_SIZE = 1024; // bytes
51
52 std::set<std::string>& getSupportedForEncryption()
53 {
54     static std::set<std::string> encryptSet;
55     if (encryptSet.empty()) {
56         encryptSet.insert(".html");
57         encryptSet.insert(".css");
58         encryptSet.insert(".js");
59     }
60     return encryptSet;
61 }
62
63 bool isSupportedForEncryption(const std::string &file)
64 {
65     size_t foundKey = file.rfind(".");
66     if (std::string::npos != foundKey) {
67         std::string mimeType = file.substr(foundKey);
68         return getSupportedForEncryption().count(mimeType) > 0;
69     }
70     return false;
71 }
72
73 /**
74  * Opens a file.
75  *
76  * @param path Path to a file.
77  * @param mode Mode.
78  * @return Stream handle.
79  * @throw ExtractFileFailed If error (other than EINTR) occurs.
80  */
81 FILE* openFile(const std::string& path, const std::string& mode)
82 {
83     FILE* result = NULL;
84
85     do
86     {
87         result = fopen(path.c_str(), mode.c_str());
88     } while ((NULL == result) && (EINTR == errno));
89
90     if (NULL == result)
91     {
92         ThrowMsg(Jobs::WidgetInstall::Exceptions::InternalError,
93                  "Could not open file " << path);
94     }
95
96     return result;
97 }
98
99 /**
100  * Reads bytes from a stream.
101  *
102  * @param buffer Buffer to read the bytes into.
103  * @param count Number of bytes to read.
104  * @param stream Stream to read from.
105  * @return Number of bytes read
106  * @throw ExtractFileFailed If error (other than EINTR) occurs.
107  */
108 std::size_t readBytes(unsigned char* buffer, std::size_t count, FILE* stream)
109 {
110     std::size_t result = std::fread(buffer,
111                                     sizeof(unsigned char),
112                                     count,
113                                     stream);
114
115     if (result != count)
116     {
117         int error = errno;
118         if (0 != std::ferror(stream))
119         {
120             if (EINTR != error)
121             {
122                 ThrowMsg(Jobs::WidgetInstall::Exceptions::InternalError,
123                          "Error while reading data" <<
124                          " [" << DPL::GetErrnoString(error) << "]");
125             }
126         }
127     }
128
129     return result;
130 }
131
132 /**
133  * Writes bytes to a stream.
134  *
135  * @param buffer Data to write.
136  * @param count Number of bytes.
137  * @param stream Stream to write to.
138  * @throw ExtractFileFailed If error (other than EINTR) occurs.
139  */
140 void writeBytes(unsigned char* buffer, std::size_t count, FILE* stream)
141 {
142     std::size_t bytesWritten = 0;
143     std::size_t bytesToWrite = 0;
144     do
145     {
146         bytesToWrite = count - bytesWritten;
147         bytesWritten = std::fwrite(buffer + bytesWritten,
148                                    sizeof(unsigned char),
149                                    count - bytesWritten,
150                                    stream);
151         if ((bytesWritten != bytesToWrite) && (EINTR != errno))
152         {
153             int error = errno;
154             ThrowMsg(Jobs::WidgetInstall::Exceptions::InternalError,
155                      "Error while writing data" <<
156                      " [" << DPL::GetErrnoString(error) << "]");
157         }
158     } while ((bytesWritten != bytesToWrite) && (EINTR == errno));
159 }
160 }
161
162 namespace Jobs {
163 namespace WidgetInstall {
164 TaskEncryptResource::TaskEncryptResource(InstallerContext& context) :
165     DPL::TaskDecl<TaskEncryptResource>(this),
166     m_context(context),
167     m_resEnc(NULL)
168 {
169     AddStep(&TaskEncryptResource::StepEncryptResource);
170 }
171
172 void TaskEncryptResource::StepEncryptResource()
173 {
174     LogDebug("Step Encrypt resource");
175     m_resEnc = new ResourceEncryptor;
176     m_resEnc->CreateEncryptionKey(DPL::ToUTF8String(m_context.
177                                                         widgetConfig.tzAppid));
178
179     EncryptDirectory(m_context.locations->getTemporaryRootDir());
180     m_context.job->UpdateProgress(
181             InstallerContext::INSTALL_ECRYPTION_FILES,
182             "Ecrypt resource files");
183 }
184
185 void TaskEncryptResource::EncryptDirectory(std::string path)
186 {
187     FTS *fts;
188     FTSENT *ftsent;
189     char * const paths[] = { const_cast<char * const>(path.c_str()), NULL };
190
191     if ((fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) {
192         //ERROR
193         int error = errno;
194         LogWarning(__PRETTY_FUNCTION__ << ": fts_open failed with error: "
195                                        << strerror(error));
196         ThrowMsg(Exceptions::InternalError, "Error reading directory: "
197                  << path);
198     }
199
200     while ((ftsent = fts_read(fts)) != NULL) {
201         switch (ftsent->fts_info) {
202         case FTS_DP:
203         case FTS_DC:
204         case FTS_D:
205         case FTS_DEFAULT:
206         case FTS_SLNONE:
207             //directories, non-regular files, dangling symbolic links
208             break;
209         case FTS_F:
210         case FTS_NSOK:
211         case FTS_SL:
212             //regular files and other objects that can be counted
213             if (isSupportedForEncryption(ftsent->fts_path)) {
214                 EncryptFile(ftsent->fts_path);
215             }
216             break;
217         case FTS_NS:
218         case FTS_DOT:
219         case FTS_DNR:
220         case FTS_ERR:
221         default:
222             LogWarning(__PRETTY_FUNCTION__
223                        << ": traversal failed on file: "
224                        << ftsent->fts_path
225                        << " with error: "
226                        << strerror(ftsent->fts_errno));
227             ThrowMsg(Exceptions::InternalError, "Error reading file");
228             break;
229         }
230     }
231
232     if (fts_close(fts) == -1) {
233         int error = errno;
234         LogWarning(__PRETTY_FUNCTION__ << ": fts_close failed with error: "
235                                        << strerror(error));
236     }
237 }
238
239 void TaskEncryptResource::EncryptFile(const std::string &fileName)
240 {
241     try
242     {
243         LogDebug("Encrypt file: " << fileName);
244         std::string encFile = fileName + ".enc";
245
246         struct stat info;
247         memset(&info, 0, sizeof(info));
248         if (stat(fileName.c_str(), &info) != 0)
249         {
250             int error = errno;
251             ThrowMsg(Exceptions::InternalError,
252                      "Could not access file " << fileName <<
253                      "[" << DPL::GetErrnoString(error) << "]");
254         }
255         const std::size_t fileSize = info.st_size;
256
257         DPL::ScopedFClose inFile(openFile(fileName, "r"));
258         DPL::ScopedFClose outFile(openFile(encFile, "w"));
259
260         const std::size_t chunkSize = (fileSize > ENCRYPTION_CHUNK_MAX_SIZE
261                 ? ENCRYPTION_CHUNK_MAX_SIZE : fileSize);
262         const int maxBlockSize = m_resEnc->GetBlockSize(chunkSize);
263
264         std::unique_ptr<unsigned char[]> inChunk(new unsigned char[chunkSize]);
265         std::unique_ptr<unsigned char[]> outChunk;
266
267         std::size_t bytesRead = 0;
268         int curBlockSize = 0;
269         do
270         {
271             bytesRead = readBytes(inChunk.get(), chunkSize, inFile.Get());
272             if (chunkSize != bytesRead)
273             {
274                 curBlockSize = m_resEnc->GetBlockSize(bytesRead);
275                 outChunk.reset(new unsigned char[curBlockSize]);
276             }
277             else
278             {
279                 if (maxBlockSize != curBlockSize)
280                 {
281                     curBlockSize = maxBlockSize;
282                     outChunk.reset(new unsigned char[curBlockSize]);
283                 }
284             }
285
286             m_resEnc->EncryptChunk(inChunk.get(), outChunk.get(), bytesRead);
287
288             writeBytes(outChunk.get(), curBlockSize, outFile.Get());
289
290         } while (0 == std::feof(inFile.Get()));
291
292         LogDebug("File encrypted successfully");
293
294         outFile.Reset();
295         inFile.Reset();
296
297         LogDebug("Remove plain-text file: " << fileName);
298         if (0 != unlink(fileName.c_str()))
299         {
300             Throw(Exceptions::InternalError);
301         }
302
303         LogDebug("Rename encrypted file");
304         if (0 != std::rename(encFile.c_str(), fileName.c_str()))
305         {
306             Throw(Exceptions::InternalError);
307         }
308
309         std::string realPath = fileName;
310         realPath.replace(0,
311                          m_context.locations->getTemporaryRootDir().length(),
312                          m_context.locations->getSourceDir());
313
314         WrtDB::EncryptedFileInfo fileInfo;
315         fileInfo.fileName = DPL::FromUTF8String(realPath);
316         fileInfo.fileSize = fileSize;
317
318         m_context.widgetConfig.encryptedFiles.insert(fileInfo);
319     }
320     Catch (Exceptions::InternalError)
321     {
322         ReThrowMsg(Exceptions::ExtractFileFailed, fileName);
323     }
324     Catch (ResourceEncryptor::Exception::Base)
325     {
326         ReThrowMsg(Exceptions::ExtractFileFailed, fileName);
327     }
328 }
329
330 } //namespace WidgetInstall
331 } //namespace Jobs