a89b1617e10b5a9ced57db8e4e6dfd2d69b916cf
[scilab.git] / scilab / modules / webtools / sci_gateway / cpp / sci_http_upload.cpp
1 /*
2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2017 - ESI-Group - Cedric DELAMARRE
4 *
5 *
6 * This file is hereby licensed under the terms of the GNU GPL v2.0,
7 * pursuant to article 5.3.4 of the CeCILL v.2.1.
8 * This file was originally licensed under the terms of the CeCILL v2.1,
9 * and continues to be available under such terms.
10 * For more information, see the COPYING file which you should have received
11 * along with this program.
12 *
13 */
14 /*--------------------------------------------------------------------------*/
15
16 #include <curl/curl.h>
17 #include "webtools_gw.hxx"
18 #include "function.hxx"
19 #include "string.hxx"
20 #include "double.hxx"
21 #include "struct.hxx"
22 #include "sciCurl.hxx"
23 #include "json.hxx"
24
25 extern "C"
26 {
27 #include "localization.h"
28 #include "Scierror.h"
29 #include "sciprint.h"
30 #include "sci_malloc.h"
31 #include "getFullFilename.h"
32 }
33
34 /*--------------------------------------------------------------------------*/
35 static const char fname[] = "http_upload";
36 types::Function::ReturnValue sci_http_upload(types::typed_list &in, types::optional_list &opt, int _iRetCount, types::typed_list &out)
37 {
38     SciCurl* sciCurlObj = SciCurl::getInstance();
39     CURLcode res = CURLE_OK;
40
41     if (in.size() < 3 || in.size() > 4)
42     {
43         Scierror(77, _("%s: Wrong number of input argument(s): %d to %d expected.\n"), fname, 3, 4);
44         return types::Function::Error;
45     }
46
47     if (_iRetCount > 2)
48     {
49         Scierror(78, _("%s: Wrong number of output argument(s): %d to %d expected.\n"), fname, 1, 2);
50         return types::Function::Error;
51     }
52
53     // get URL
54     if(in[0]->isString() == false && in[0]->getAs<types::String>()->isScalar() == false)
55     {
56         Scierror(999, _("%s: Wrong type for input argument #%d: A scalar string expected.\n"), fname, 1);
57         return types::Function::Error;
58     }
59
60     CURL* curl = curl_easy_init();
61     if(curl == nullptr)
62     {
63         Scierror(999, _("%s: CURL initialization failed.\n"), fname);
64         return types::Function::Error;
65     }
66
67     char* pcURL = wide_string_to_UTF8(in[0]->getAs<types::String>()->get(0));
68     curl_easy_setopt(curl, CURLOPT_URL, pcURL);
69     FREE(pcURL);
70
71     // get input file name
72     if(in[1]->isString() == false)
73     {
74         Scierror(999, _("%s: Wrong type for input argument #%d: A matrix string expected.\n"), fname, 2);
75         return types::Function::Error;
76     }
77
78     types::String* pStrFiles = in[1]->getAs<types::String>();
79     std::vector<char*> files;
80     for(int i = 0; i < pStrFiles->getSize(); i++)
81     {
82         wchar_t* pwcFileName = getFullFilenameW(pStrFiles->get(i));
83         files.push_back(wide_string_to_UTF8(pwcFileName));
84         FREE(pwcFileName);
85     }
86
87     // get variable name server side
88     if(in[2]->isString() == false && in[2]->getAs<types::String>()->isScalar() == false)
89     {
90         Scierror(999, _("%s: Wrong type for input argument #%d: A scalar string expected.\n"), fname, 3);
91         return types::Function::Error;
92     }
93
94     char* pcVarName = wide_string_to_UTF8(in[2]->getAs<types::String>()->get(0));
95
96     struct curl_httppost *formpost = NULL;
97     struct curl_httppost *lastptr  = NULL;
98
99     // /!\ This doesn't work with nodejs multer array /!\
100     // struct curl_forms form[files.size()+1];
101     // int iter = 0;
102     // for(auto f : files)
103     // {
104     //     form[iter].option  = CURLFORM_FILE;
105     //     form[iter++].value = f;
106     // }
107     //
108     // form[iter].option = CURLFORM_END;
109     //
110     // curl_formadd(&formpost,
111     //              &lastptr,
112     //              CURLFORM_COPYNAME, pcVarName,
113     //              CURLFORM_ARRAY, form,
114     //              CURLFORM_END);
115
116     for(auto f : files)
117     {
118         curl_formadd(&formpost,
119                      &lastptr,
120                      CURLFORM_COPYNAME, pcVarName,
121                      CURLFORM_FILE, f,
122                      CURLFORM_END);
123     }
124
125     FREE(pcVarName);
126
127     if(in.size() > 3)
128     {
129         // get data
130         if(in[3]->isStruct() == false && in[3]->getAs<types::Struct>()->isScalar() == false)
131         {
132             Scierror(999, _("%s: Wrong type for input argument #%d: A structure of size %d expected.\n"), fname, 3, 1);
133             return types::Function::Error;
134         }
135
136         types::Struct* pStruct = in[3]->getAs<types::Struct>();
137         std::unordered_map<std::wstring, int> fieldsMap = pStruct->get(0)->getFields();
138         std::vector<types::InternalType*> pITData = pStruct->get(0)->getData();
139         for (const auto & field : fieldsMap)
140         {
141             char* pcFieldName = wide_string_to_UTF8(field.first.data());
142             std::string strData(toJSON(pITData[field.second]));
143             //remove " @start and @end of string
144             if (strData.front() == '\"' && strData.back() == '\"')
145             {
146                 strData = strData.substr(1);
147                 strData.pop_back();
148             }
149             sciprint("|%s|: |%s|\n", pcFieldName, strData.data());
150             curl_formadd(&formpost,
151                          &lastptr,
152                          CURLFORM_COPYNAME, pcFieldName,
153                          CURLFORM_COPYCONTENTS, strData.data(),
154                          CURLFORM_END);
155
156             FREE(pcFieldName);
157         }
158     }
159
160     curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
161
162     // set proxy information
163     if(sciCurlObj->setProxy(curl))
164     {
165         Scierror(999, _("%s: Wrong proxy information, please check in the 'internet' Scilab preference.\n"), fname);
166         return types::Function::Error;
167     }
168
169     // common optional argument
170     if(checkCommonOpt((void*)curl, opt, fname))
171     {
172         return types::Function::Error;
173     }
174
175     // specific optional argument
176     for (const auto& o : opt)
177     {
178         if (o.first == L"method")
179         {
180             if(o.second->isString() == false && o.second->getAs<types::String>()->isScalar() == false)
181             {
182                 Scierror(999, _("%s: Wrong type for input argument #%s: A scalar string expected.\n"), fname, o.first.data());
183                 return types::Function::Error;
184             }
185
186             wchar_t* pMeth = o.second->getAs<types::String>()->get(0);
187             if(wcscmp(pMeth, L"PUT") == 0)
188             {
189                 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
190             }
191             else if(wcscmp(pMeth, L"POST") != 0)
192             {
193                 Scierror(999, _("%s: Wrong value for input argument #%s: 'PUT' or 'POST' expected.\n"), fname, o.first.data());
194                 return types::Function::Error;
195             }
196         }
197     }
198
199     sciCurlObj->getResultAsObject(curl);
200     res = curl_easy_perform(curl);
201     for(auto f : files)
202     {
203         FREE(f);
204     }
205
206     curl_formfree(formpost);
207
208     if(res != CURLE_OK)
209     {
210         Scierror(999, _("%s: CURL execution failed.\n%s\n"), fname, curl_easy_strerror(res));
211         sciCurlObj->clear();
212         return types::Function::Error;
213     }
214
215     out.push_back(sciCurlObj->getResult());
216
217     if(_iRetCount == 2)
218     {
219         long http_code = 0;
220         curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
221         out.push_back(new types::Double((double)http_code));
222     }
223
224     curl_easy_cleanup(curl);
225     return types::Function::OK;
226 }