[core] leave file open when calling hash with file argument
[scilab.git] / scilab / modules / core / sci_gateway / cpp / sci_hash.cpp
1 /*
2  *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  *  Copyright (C) 2021 - St├ęphane MOTTELET
4  *
5  * This file is hereby licensed under the terms of the GNU GPL v3.0,
6  * For more information, see the COPYING file which you should have received
7  * along with this program.
8  *
9  */
10
11 #include <cwctype>
12 #include "core_gw.hxx"
13 #include "types.hxx"
14 #include "function.hxx"
15 #include "string.hxx"
16 #include "double.hxx"
17 #include "file.hxx"
18 #include "filemanager.hxx"
19 #include "crc32.hxx"
20 #include "md5.hxx"
21 #include "sha1.hxx"
22 #include "sha256.hxx"
23 #include "sha3.hxx"
24
25 extern "C"
26 {
27 #include "Scierror.h"
28 #include "sci_malloc.h"
29 }
30
31 types::Function::ReturnValue sci_hash(types::typed_list &in, int _iRetCount, types::typed_list &out)
32 {
33     bool bStringMode = false;
34     char* pstInput = NULL;
35     int iSize = 0;
36     types::String* pIn = NULL;
37     types::String* pOutput = NULL;
38     types::Double* pDbl = NULL;
39     Hash* digest = NULL;
40
41     if (_iRetCount > 1)
42     {
43         Scierror(78, _("%s: Wrong number of output argument(s): %d expected.\n"), "hash", 1);
44         return types::Function::Error;
45     }
46
47     if (in.size() != 2)
48     {
49         Scierror(77, _("%s: Wrong number of input argument(s): %d expected.\n"), "hash", 2);
50         return types::Function::Error;
51     }
52
53     if (in[0]->isString() == false && in[0]->isDouble() == false)
54     {
55         Scierror(999, _("%s: Wrong type of input argument #%d: String or File descriptor expected.\n"), "hash", 1);
56         return types::Function::Error;
57     }
58
59     if (in[1]->isString() == false || in[1]->getAs<types::String>()->isScalar() == false)
60     {
61         Scierror(999, _("%s: Wrong type of input argument #%d: a single string expected.\n"), "hash", 2);
62         return types::Function::Error;
63     }
64
65     std::wstring wstrAlgo(in[1]->getAs<types::String>()->get()[0]);
66     std::transform(wstrAlgo.begin(), wstrAlgo.end(), wstrAlgo.begin(),std::towlower);
67
68     if (wstrAlgo == L"crc32")
69     {
70         digest = new CRC32();
71     }
72     else if (wstrAlgo == L"md5")
73     {
74         digest = new MD5();
75     }
76     else if (wstrAlgo == L"sha1")
77     {
78         digest = new SHA1();
79     }
80     else if (wstrAlgo == L"sha2" || wstrAlgo == L"sha256")
81     {
82         digest = new SHA256();
83     }
84     else if (wstrAlgo == L"sha3-224")
85     {
86         digest = new SHA3(SHA3::Bits224);
87     }
88     else if (wstrAlgo == L"sha3-256")
89     {
90         digest = new SHA3(SHA3::Bits256);
91     }
92     else if (wstrAlgo == L"sha3-384")
93     {
94         digest = new SHA3(SHA3::Bits384);
95     }
96     else if (wstrAlgo == L"sha3-512")
97     {
98         digest = new SHA3(SHA3::Bits512);
99     }
100     else
101     {
102         Scierror(999, _("%s: algorithm %ls is unknown.\n"), "hash", wstrAlgo.c_str());
103         return types::Function::Error;
104     }
105
106     if (in[0]->isString())
107     {
108         pIn = in[0]->getAs<types::String>();
109         pOutput = new types::String(pIn->getRows(), pIn->getCols());
110         bStringMode = true;
111         iSize = pIn->getSize();
112     }
113     else
114     {
115         pDbl = in[0]->getAs<types::Double>();
116         pOutput = new types::String(pDbl->getRows(), pDbl->getCols());
117         iSize = pDbl->getSize();
118     }
119
120     for (int i = 0; i < iSize; ++i)
121     {
122         wchar_t *pwstHash = NULL;
123
124         if (bStringMode)
125         {
126             wchar_t *pwstInput = pIn->get(i);
127             pstInput = wide_string_to_UTF8(pwstInput);
128             digest->add(pstInput, strlen(pstInput));
129             FREE(pstInput);
130         }
131         else
132         {
133             FILE *fp = NULL;
134             char pStData[64];
135             size_t iLen = 0;
136             int iFile = (int) pDbl->get(i);
137             types::File* pF = NULL;
138
139             switch (iFile)
140             {
141                 case 0: // stderr
142                 case 5: // stdin
143                 case 6: // stdout
144                     break;
145                 default :
146                     pF =  FileManager::getFile(iFile);
147             }
148
149             if (pF == NULL)
150             {
151                 Scierror(999, _("%s: Wrong file descriptor: %d.\n"), "hash", iFile);
152                 return types::Function::Error;
153             }
154             else if (pF->getFileMode()[0] != L'r')
155             {
156                 Scierror(999, _("%s: File must be opened for reading.\n"), "hash", iFile);
157                 fclose(pF->getFiledesc());
158                 FileManager::deleteFile(iFile);
159                 return types::Function::Error;
160             }
161
162             fp = pF->getFiledesc();
163             if (fp) // argument is a valid path to an existing file
164             {
165                 while (!feof(fp))
166                 {
167                     iLen = fread(pStData, 1, sizeof(pStData), fp);
168                     digest->add(pStData, iLen);
169                 }
170                 //fclose(fp);
171                 // after closing, don't forget to remove file from the list of opened files
172                 //FileManager::deleteFile(iFile);
173             }
174         }
175
176         std::string strHash = digest->getHash();
177         if (strHash.length() > 0)
178         {
179             pwstHash = to_wide_string(strHash.c_str());
180             pOutput->set(i, pwstHash);
181             FREE(pwstHash);
182         }
183         else
184         {
185             pOutput->set(i, L"");
186         }
187
188         digest->reset();
189     }
190
191     delete digest;
192     out.push_back(pOutput);
193     return types::Function::OK;
194 }
195 /*--------------------------------------------------------------------------*/