Add LICENSE file and Refer to it from the source. Include license material
[metaproxy-moved-to-github.git] / src / filter_http_file.cpp
1 /* $Id: filter_http_file.cpp,v 1.5 2006-06-10 14:29:12 adam Exp $
2    Copyright (c) 2005-2006, Index Data.
3
4    See the LICENSE file for details
5  */
6
7 #include "config.hpp"
8
9 #include "filter.hpp"
10 #include "package.hpp"
11
12 #include <boost/thread/mutex.hpp>
13
14 #include "util.hpp"
15 #include "filter_http_file.hpp"
16
17 #include <list>
18 #include <map>
19
20 #include <yaz/zgdu.h>
21
22 #if HAVE_SYS_TYPES_H
23 #include <sys/types.h>
24 #endif
25
26 #if HAVE_SYS_STAT_H
27 #include <sys/stat.h>
28 #endif
29
30 namespace mp = metaproxy_1;
31 namespace yf = mp::filter;
32
33 namespace metaproxy_1 {
34     namespace filter {
35         struct HttpFile::Area {
36             std::string m_url_path_prefix;
37             std::string m_file_root;
38         };
39         class HttpFile::Mime {
40             friend class Rep;
41             std::string m_type;
42         public:
43             Mime(std::string type);
44             Mime();
45         };
46         class HttpFile::Rep {
47             friend class HttpFile;
48
49             typedef std::list<Area> AreaList;
50             typedef std::map<std::string,Mime> MimeMap;
51
52             MimeMap m_ext_to_map;
53             AreaList m_area_list;
54             void fetch_uri(mp::Session &session,
55                            Z_HTTP_Request *req, mp::Package &package);
56             void fetch_file(mp::Session &session,
57                             Z_HTTP_Request *req,
58                             std::string &fname, mp::Package &package);
59             std::string get_mime_type(std::string &fname);
60         };
61     }
62 }
63
64 yf::HttpFile::Mime::Mime() {}
65
66 yf::HttpFile::Mime::Mime(std::string type) : m_type(type) {}
67
68 yf::HttpFile::HttpFile() : m_p(new Rep)
69 {
70 #if 0
71     m_p->m_ext_to_map["html"] = Mime("text/html");
72     m_p->m_ext_to_map["htm"] = Mime("text/html");
73     m_p->m_ext_to_map["png"] = Mime("image/png");
74     m_p->m_ext_to_map["txt"] = Mime("text/plain");
75     m_p->m_ext_to_map["text"] = Mime("text/plain");
76     m_p->m_ext_to_map["asc"] = Mime("text/plain");
77     m_p->m_ext_to_map["xml"] = Mime("application/xml");
78     m_p->m_ext_to_map["xsl"] = Mime("application/xml");
79 #endif
80 #if 0
81     Area a;
82     a.m_url_path_prefix = "/etc";
83     a.m_file_root = ".";
84     m_p->m_area_list.push_back(a);
85 #endif
86 }
87
88 yf::HttpFile::~HttpFile()
89 {
90 }
91
92 std::string yf::HttpFile::Rep::get_mime_type(std::string &fname)
93 {
94     std::string file_part = fname;
95     std::string::size_type p = fname.find_last_of('/');
96     
97     if (p != std::string::npos)
98         file_part = fname.substr(p+1);
99
100     p = file_part.find_last_of('.');
101     std::string content_type;
102     if (p != std::string::npos)
103     {
104         std::string ext = file_part.substr(p+1);
105         MimeMap::const_iterator it = m_ext_to_map.find(ext);
106
107         if (it != m_ext_to_map.end())
108             content_type = it->second.m_type;
109     }
110     if (content_type.length() == 0)
111         content_type = "application/octet-stream";
112     return content_type;
113 }
114
115 void yf::HttpFile::Rep::fetch_file(mp::Session &session,
116                                    Z_HTTP_Request *req,
117                                    std::string &fname, mp::Package &package)
118 {
119     mp::odr o;
120     
121     FILE *f = fopen(fname.c_str(), "rb");
122     if (!f)
123     {
124         Z_GDU *gdu = o.create_HTTP_Response(session, req, 404);
125         package.response() = gdu;
126         return;
127     }
128     if (fseek(f, 0L, SEEK_END) == -1)
129     {
130         fclose(f);
131         Z_GDU *gdu = o.create_HTTP_Response(session, req, 404);
132         package.response() = gdu;
133         return;
134     }
135     long sz = ftell(f);
136     if (sz > 1000000L)
137     {
138         fclose(f);
139         Z_GDU *gdu = o.create_HTTP_Response(session, req, 404);
140         package.response() = gdu;
141         return;
142     }
143     rewind(f);
144
145     Z_GDU *gdu = o.create_HTTP_Response(session, req, 200);
146
147     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
148     hres->content_len = sz;
149     hres->content_buf = (char*) odr_malloc(o, hres->content_len);
150     fread(hres->content_buf, 1, hres->content_len, f);
151
152     fclose(f);
153     
154     std::string content_type = get_mime_type(fname);
155
156     z_HTTP_header_add(o, &hres->headers,
157                       "Content-Type", content_type.c_str());
158     package.response() = gdu;
159 }
160
161 void yf::HttpFile::Rep::fetch_uri(mp::Session &session,
162                                   Z_HTTP_Request *req, mp::Package &package)
163 {
164     bool sane = true;
165     std::string path = req->path;
166
167     // we don't consider ?, # yet..
168
169     // we don't allow ..
170     std::string::size_type p = path.find("..");
171     if (p != std::string::npos)
172         sane = false;
173
174     if (sane)
175     {
176         AreaList::const_iterator it;
177         for (it = m_area_list.begin(); it != m_area_list.end(); it++)
178         {
179             std::string::size_type l = it->m_url_path_prefix.length();
180
181             if (path.compare(0, l, it->m_url_path_prefix) == 0)
182             {
183                 std::string fname = it->m_file_root + path.substr(l);
184                 std::cout << "fname = " << fname << "\n";
185                 fetch_file(session, req, fname, package);
186                 return;
187             }
188         }
189     }
190     mp::odr o;
191     Z_GDU *gdu = o.create_HTTP_Response(session, req, 404);
192     package.response() = gdu;
193 }
194                          
195 void yf::HttpFile::process(mp::Package &package) const
196 {
197     Z_GDU *gdu = package.request().get();
198     if (gdu && gdu->which == Z_GDU_HTTP_Request)
199         m_p->fetch_uri(package.session(), gdu->u.HTTP_Request, package);
200     else
201         package.move();
202 }
203
204 void mp::filter::HttpFile::configure(const xmlNode * ptr)
205 {
206     for (ptr = ptr->children; ptr; ptr = ptr->next)
207     {
208         if (ptr->type != XML_ELEMENT_NODE)
209             continue;
210         if (!strcmp((const char *) ptr->name, "mimetypes"))
211         {
212             std::string fname = mp::xml::get_text(ptr);
213
214             mp::PlainFile f;
215
216             if (!f.open(fname))
217             {
218                 throw mp::filter::FilterException
219                     ("Can not open mime types file " + fname);
220             }
221             
222             std::vector<std::string> args;
223             while (f.getline(args))
224             {
225                 size_t i;
226                 for (i = 1; i<args.size(); i++)
227                     m_p->m_ext_to_map[args[i]] = args[0];
228             }
229         }
230         else if (!strcmp((const char *) ptr->name, "area"))
231         {
232             xmlNode *a_node = ptr->children;
233             Area a;
234             for (; a_node; a_node = a_node->next)
235             {
236                 if (a_node->type != XML_ELEMENT_NODE)
237                     continue;
238                 
239                 if (mp::xml::is_element_yp2(a_node, "documentroot"))
240                     a.m_file_root = mp::xml::get_text(a_node);
241                 else if (mp::xml::is_element_yp2(a_node, "prefix"))
242                     a.m_url_path_prefix = mp::xml::get_text(a_node);
243                 else
244                     throw mp::filter::FilterException
245                         ("Bad element " 
246                          + std::string((const char *) a_node->name)
247                          + " in area section"
248                             );
249             }
250             if (a.m_file_root.length())
251             {
252                 m_p->m_area_list.push_back(a);
253             }
254         }
255         else
256         {
257             throw mp::filter::FilterException
258                 ("Bad element " 
259                  + std::string((const char *) ptr->name)
260                  + " in virt_db filter");
261         }
262     }
263 }
264
265 static mp::filter::Base* filter_creator()
266 {
267     return new mp::filter::HttpFile;
268 }
269
270 extern "C" {
271     struct metaproxy_1_filter_struct metaproxy_1_filter_http_file = {
272         0,
273         "http_file",
274         filter_creator
275     };
276 }
277
278
279 /*
280  * Local variables:
281  * c-basic-offset: 4
282  * indent-tabs-mode: nil
283  * c-file-style: "stroustrup"
284  * End:
285  * vim: shiftwidth=4 tabstop=8 expandtab
286  */