Implemented first bits of http_file filter which serves plain
[metaproxy-moved-to-github.git] / src / filter_http_file.cpp
1 /* $Id: filter_http_file.cpp,v 1.1 2006-01-19 21:44:26 adam Exp $
2    Copyright (c) 2005, Index Data.
3
4 %LICENSE%
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 yf = yp2::filter;
31
32 namespace yp2 {
33     namespace filter {
34         struct HttpFile::Area {
35             std::string m_url_path_prefix;
36             std::string m_file_root;
37         };
38         class HttpFile::Mime {
39             friend class Rep;
40             std::string m_type;
41         public:
42             Mime(std::string type);
43             Mime();
44         };
45         class HttpFile::Rep {
46             friend class HttpFile;
47
48             typedef std::list<Area> AreaList;
49             typedef std::map<std::string,Mime> MimeMap;
50
51             MimeMap m_ext_to_map;
52             AreaList m_area_list;
53             void fetch_uri(yp2::Session &session,
54                            Z_HTTP_Request *req, yp2::Package &package);
55             void fetch_file(yp2::Session &session,
56                             Z_HTTP_Request *req,
57                             std::string &fname, yp2::Package &package);
58             std::string get_mime_type(std::string &fname);
59         };
60     }
61 }
62
63 yf::HttpFile::Mime::Mime() {}
64
65 yf::HttpFile::Mime::Mime(std::string type) : m_type(type) {}
66
67 yf::HttpFile::HttpFile() : m_p(new Rep)
68 {
69     m_p->m_ext_to_map["html"] = Mime("text/html");
70     m_p->m_ext_to_map["htm"] = Mime("text/html");
71     m_p->m_ext_to_map["png"] = Mime("image/png");
72     m_p->m_ext_to_map["txt"] = Mime("text/plain");
73     m_p->m_ext_to_map["text"] = Mime("text/plain");
74     m_p->m_ext_to_map["asc"] = Mime("text/plain");
75     m_p->m_ext_to_map["xml"] = Mime("application/xml");
76     m_p->m_ext_to_map["xsl"] = Mime("application/xml");
77
78     Area a;
79     a.m_url_path_prefix = "/etc";
80     a.m_file_root = "..";
81     m_p->m_area_list.push_back(a);
82 }
83
84 yf::HttpFile::~HttpFile()
85 {
86 }
87
88 std::string yf::HttpFile::Rep::get_mime_type(std::string &fname)
89 {
90     std::string file_part = fname;
91     std::string::size_type p = fname.find_last_of('/');
92     
93     if (p != std::string::npos)
94         file_part = fname.substr(p+1);
95
96     p = file_part.find_last_of('.');
97     std::string content_type;
98     if (p != std::string::npos)
99     {
100         std::string ext = file_part.substr(p+1);
101         MimeMap::const_iterator it = m_ext_to_map.find(ext);
102
103         if (it != m_ext_to_map.end())
104             content_type = it->second.m_type;
105     }
106     if (content_type.length() == 0)
107         content_type = "application/octet-stream";
108     return content_type;
109 }
110
111 void yf::HttpFile::Rep::fetch_file(yp2::Session &session,
112                                    Z_HTTP_Request *req,
113                                    std::string &fname, yp2::Package &package)
114 {
115     struct stat sbuf;
116     yp2::odr o;
117     
118     if (stat(fname.c_str(), &sbuf))
119     {
120         Z_GDU *gdu = o.create_HTTP_Response(session, req, 404);
121         package.response() = gdu;
122         return;
123     }
124     if ((sbuf.st_mode & S_IFMT) != S_IFREG)
125     {
126         Z_GDU *gdu = o.create_HTTP_Response(session, req, 404);
127         package.response() = gdu;
128         return;
129     }
130     if (sbuf.st_size > (off_t) 1000000)
131     {
132         Z_GDU *gdu = o.create_HTTP_Response(session, req, 404);
133         package.response() = gdu;
134         return;
135     }
136     
137     FILE *f = fopen(fname.c_str(), "rb");
138     if (!f)
139     {
140         Z_GDU *gdu = o.create_HTTP_Response(session, req, 404);
141         package.response() = gdu;
142         return;
143     }
144     Z_GDU *gdu = o.create_HTTP_Response(session, req, 200);
145
146     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
147     hres->content_len = sbuf.st_size;
148     hres->content_buf = (char*) odr_malloc(o, hres->content_len);
149     fread(hres->content_buf, 1, hres->content_len, f);
150
151     fclose(f);
152     
153     std::string content_type = get_mime_type(fname);
154
155     z_HTTP_header_add(o, &hres->headers,
156                       "Content-Type", content_type.c_str());
157     package.response() = gdu;
158 }
159
160 void yf::HttpFile::Rep::fetch_uri(yp2::Session &session,
161                                   Z_HTTP_Request *req, yp2::Package &package)
162 {
163     bool sane = true;
164     std::string path = req->path;
165
166     // we don't consider ?, # yet..
167
168     // we don't allow ..
169     std::string::size_type p = path.find("..");
170     if (p != std::string::npos)
171         sane = false;
172
173     if (sane)
174     {
175         AreaList::const_iterator it;
176         for (it = m_area_list.begin(); it != m_area_list.end(); it++)
177         {
178             std::string::size_type l = it->m_url_path_prefix.length();
179
180             if (path.compare(0, l, it->m_url_path_prefix) == 0)
181             {
182                 std::string fname = it->m_file_root + path;
183                 fetch_file(session, req, fname, package);
184                 return;
185             }
186         }
187     }
188     yp2::odr o;
189     Z_GDU *gdu = o.create_HTTP_Response(session, req, 404);
190     package.response() = gdu;
191 }
192                          
193 void yf::HttpFile::process(yp2::Package &package) const
194 {
195     Z_GDU *gdu = package.request().get();
196     if (gdu && gdu->which == Z_GDU_HTTP_Request)
197         m_p->fetch_uri(package.session(), gdu->u.HTTP_Request, package);
198     else
199         package.move();
200 }
201
202 static yp2::filter::Base* filter_creator()
203 {
204     return new yp2::filter::HttpFile;
205 }
206
207 extern "C" {
208     struct yp2_filter_struct yp2_filter_http_file = {
209         0,
210         "http_file",
211         filter_creator
212     };
213 }
214
215
216 /*
217  * Local variables:
218  * c-basic-offset: 4
219  * indent-tabs-mode: nil
220  * c-file-style: "stroustrup"
221  * End:
222  * vim: shiftwidth=4 tabstop=8 expandtab
223  */