1 /*
2  * Copyright 2006-2008 The FLWOR Foundation.
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 #include <curl/curl.h>
17 #include <map>
18 #include <zorba/zorba.h>
19 #include <zorba/serializer.h>
20 #include <zorba/external_module.h>
21 #include <zorba/function.h>
22 #include <zorba/empty_sequence.h>
23 #include <zorba/user_exception.h>
24 
25 #include "http_request_handler.h"
26 #include "request_parser.h"
27 #include "http_response_handler.h"
28 #include "http_response_parser.h"
29 
30 #ifdef WIN32
31 # include <Windows.h>
32 # define MAX_BUF_SIZE 2048
33 #endif
34 
35 namespace zorba {
36 
37   namespace http_client {
38 #ifdef WIN32
set_cacert(CURL * lCurl,std::string aPath)39 		static void set_cacert(CURL* lCurl, std::string aPath) {
40 			TCHAR path[MAX_BUF_SIZE];
41 			int r = GetModuleFileName(NULL, path, 2048);
42 			if (r == -1)
43 				return;
44 #	ifdef UNICODE
45 			char buf[MAX_BUF_SIZE];
46 			memset(buf, 0, MAX_BUF_SIZE);
47 			for (int i = 0; i <= r; ++i) {
48 				buf[i] = (char) path[i];
49 			}
50 			std::string lPath(buf);
51 #	else
52 			std::string lPath(path);
53 #	endif
54 			aPath = lPath.substr(0, lPath.rfind('\\'));
55 			aPath += "\\cacert.pem";
56       if(GetFileAttributesA(aPath.c_str()) != INVALID_FILE_ATTRIBUTES)
57 			  curl_easy_setopt(lCurl, CURLOPT_CAINFO, aPath.c_str());
58       else
59         curl_easy_setopt(lCurl, CURLOPT_SSL_VERIFYPEER, 0L);
60 		}
61 #endif //WIN32
62 
63     class HttpSendFunction : public ContextualExternalFunction {
64     protected:
65       const ExternalModule*     theModule;
66       ItemFactory*              theFactory;
67 
68     public:
HttpSendFunction(const ExternalModule * aModule)69       HttpSendFunction(const ExternalModule* aModule)
70       : theModule(aModule),
71       theFactory(Zorba::getInstance(0)->getItemFactory()) {}
72 
~HttpSendFunction()73       virtual ~HttpSendFunction() {}
74 
75     public:
76       virtual String
getURI() const77       getURI() const { return theModule->getURI(); }
78 
79       virtual String
getLocalName() const80       getLocalName() const { return "http-sequential-impl"; }
81 
82       virtual ItemSequence_t
83       evaluate(const ExternalFunction::Arguments_t& args,
84                const StaticContext* aStaticContext, const DynamicContext* aDynamicContext)
85       const;
86     };
87 
88     class HttpReadFunction : public HttpSendFunction {
89     public:
HttpReadFunction(const ExternalModule * aModule)90       HttpReadFunction(const ExternalModule* aModule)
91       : HttpSendFunction(aModule) {}
92 
~HttpReadFunction()93       virtual ~HttpReadFunction() {}
94 
95     public:
96       virtual String
getLocalName() const97       getLocalName() const { return "http-nondeterministic-impl"; }
98 
99     };
100 
101     class HttpClientModule : public ExternalModule {
102     protected:
103       class ltstr
104       {
105       public:
operator ()(const String & s1,const String & s2) const106         bool operator()(const String& s1, const String& s2) const
107         {
108           return s1.compare(s2) < 0;
109         }
110       };
111 
112       typedef std::map<String, ExternalFunction*, ltstr> FuncMap_t;
113 
114       FuncMap_t theFunctions;
115 
116     public:
117       virtual ~HttpClientModule();
118 
HttpClientModule()119       HttpClientModule() : theModuleUri("http://www.zorba-xquery.com/modules/http-client")
120       {
121         for (FuncMap_t::const_iterator lIter = theFunctions.begin();
122              lIter != theFunctions.end(); ++lIter) {
123           delete lIter->second;
124         }
125         theFunctions.clear();
126       }
127 
128       virtual String
getURI() const129       getURI() const { return theModuleUri; }
130 
131       virtual ExternalFunction*
getExternalFunction(const String & aLocalname)132       getExternalFunction(const String& aLocalname)
133       {
134         ExternalFunction*& lFunc = theFunctions[aLocalname];
135         if (!lFunc) {
136           if (aLocalname == "http-sequential-impl") {
137             lFunc = new HttpSendFunction(this);
138           } else if (aLocalname == "http-nondeterministic-impl") {
139             lFunc = new HttpReadFunction(this);
140           }
141         }
142         return lFunc;
143       }
144 
145       virtual void
destroy()146       destroy()
147       {
148         if (!dynamic_cast<HttpClientModule*>(this)) {
149           return;
150         }
151         delete this;
152       }
153 
154     private:
155       String theModuleUri;
156     };
157 
158     ItemSequence_t
general_evaluate(const ExternalFunction::Arguments_t & args,const StaticContext * aStaticContext,const DynamicContext * aDynamicContext,ItemFactory * aFactory)159     general_evaluate(
160         const ExternalFunction::Arguments_t& args,
161         const StaticContext* aStaticContext,
162         const DynamicContext* aDynamicContext,
163         ItemFactory* aFactory)
164     {
165       CURL* lCURL = curl_easy_init();
166 
167       Item lRequest;
168       Item lHref;
169       Item lContent;
170 
171       Iterator_t arg0_iter = args[0]->getIterator();
172       arg0_iter->open();
173       bool lReqSet = arg0_iter->next(lRequest);
174       arg0_iter->close();
175       Iterator_t arg1_iter = args[1]->getIterator();
176       arg1_iter->open();
177       bool lHrefSet = arg1_iter->next(lHref);
178       arg1_iter->close();
179 
180       std::string lData;
181 
182       std::auto_ptr<HttpRequestHandler> lHandler;
183       std::auto_ptr<RequestParser> lParser;
184       struct curl_slist* lHeaderList = 0;
185 
186       ErrorThrower thrower(aFactory, &lHeaderList);
187 
188       if (lReqSet) {
189         lHandler.reset(new HttpRequestHandler(lCURL, args[2]));
190         lParser.reset(new RequestParser(lHandler.get(), thrower));
191         lParser->parse(lRequest);
192       }
193       if (lHrefSet) {
194         curl_easy_setopt(lCURL, CURLOPT_URL, lHref.getStringValue().c_str());
195       }
196       curl_easy_setopt(lCURL, CURLOPT_USERAGENT, "libcurl-agent/1.0");
197       //curl_easy_setopt(lCURL, CURLOPT_PROXY, "localhost:8888");
198 #ifdef WIN32
199       std::string caCertPath;
200       set_cacert(lCURL, caCertPath);
201 #endif
202       HttpResponseHandler lRespHandler(aFactory, lHeaderList);
203       String lOverrideContentType;
204       if (lHandler.get())
205         lHandler->getOverrideContentType(lOverrideContentType);
206       bool lStatusOnly =
207           lHandler.get() == NULL ? false : (lHandler->isStatusOnly() || lHandler->isHeadRequest());
208       // This gives the ownership of lCurl to the HttpResponseParser
209       std::auto_ptr<HttpResponseParser> lRespParser(new HttpResponseParser(lRespHandler, lCURL, thrower,
210         lOverrideContentType.c_str(), lStatusOnly));
211       int lRetCode = lRespParser->parse();
212 
213        if (lRetCode) {
214         thrower.raiseException("http://expath.org/ns/error", "HC001", "An HTTP error occurred");
215       }
216 
217       // If the Parser is "self contained", that means it didn't create any
218       // objects with a lifecycle longer than itself; therefore we should free
219       // it (by letting auto_ptr delete it). If the Parser is not self contained,
220       // then it will have arranged for some other memory manager to free it
221       // later when appropriate; therefore we should NOT let auto_ptr delete it
222       // now.
223       if ( ! lRespParser->selfContained()) {
224         lRespParser.release();
225       }
226       return ItemSequence_t(lRespHandler.releaseResult());
227     }
228 
229     ItemSequence_t
evaluate(const ExternalFunction::Arguments_t & args,const StaticContext * aStaticContext,const DynamicContext * aDynamicContext) const230     HttpSendFunction::evaluate(const ExternalFunction::Arguments_t& args,
231       const StaticContext* aStaticContext, const DynamicContext* aDynamicContext) const
232     {
233       return general_evaluate(args, aStaticContext, aDynamicContext, theFactory);
234     }
235 
~HttpClientModule()236     HttpClientModule::~HttpClientModule()
237     {
238       for (FuncMap_t::const_iterator lIter = theFunctions.begin();
239            lIter != theFunctions.end(); ++lIter) {
240         delete lIter->second;
241       }
242       theFunctions.clear();
243     }
244   } // namespace http_request
245 } // namespace zorba
246 
247 #ifdef WIN32
248 #  define DLL_EXPORT __declspec(dllexport)
249 #else
250 #  define DLL_EXPORT __attribute__ ((visibility("default")))
251 #endif
252 
createModule()253 extern "C" DLL_EXPORT zorba::ExternalModule* createModule() {
254   return new zorba::http_client::HttpClientModule();
255 }
256 
257