TFCweb  1.0.4 $Rev: 483 $
TFC Primavera 2012: Nucli d'un servidor web
AutoIndex.cc
Veure la documentació d'aquest fitxer.
1 
8 /*
9  * Copyright (c) 2012 Toni Corvera
10  *
11  * This file is part of TFCWeb.
12  *
13  * TFCWeb is free software: you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation, either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * TFCWeb is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with TFCWeb. If not, see <http://www.gnu.org/licenses/>.
25  */
26 
27 #include "AutoIndex.h"
28 #include "Servidor.h"
29 #include "TipusMIME.h"
30 #include "utils.h"
31 
32 #include <algorithm>
33 #include <sstream>
34 #include <vector>
35 #include <boost/date_time.hpp>
36 #include <boost/lexical_cast.hpp>
37 
38 using namespace boost::filesystem;
39 using namespace std;
40 using namespace tfc::utils;
41 
42 using boost::lexical_cast;
43 
44 namespace MIME = tfc::RegistreTipusMIME;
45 
46 namespace {
47 
49 enum {
50  AMPLE_DATA = 11,
51  AMPLE_MIDA = 12,
52  AMPLE_MIME = 15
53 };
54 
55 // Drecera
56 inline string mtime(const time_t & t) {
57  return tfc::utils::formata_data(t, "%d %b %Y");
58 }
59 
60 string mida(const size_t s) {
61  if (s > (1<<20)) {
62  const size_t mb = s / (1<<20);
63  const size_t kb = s % (1<<20);
64  return lexical_cast<string>(mb)+"."+lexical_cast<string>(kb)[0]+" MiB";
65  }
66  else if (s > (1<<10)) {
67  const size_t kb = s / (1<<10);
68  const size_t b = s % (1<<10);
69  return lexical_cast<string>(kb)+"."+lexical_cast<string>(b)[0]+" KiB";
70  }
71  return lexical_cast<string>(s)+" B";
72 }
73 
74 } // ns anònim
75 
76 namespace tfc {
77 
78 string AutoIndex::html() const {
79  if (!cache_.empty()) { // Si ja s'ha generat no cal repetir-ho
80  return cache_;
81  }
82 
83  stringstream ss;
84 
85  const bool barra_final = ( !url_.ruta().empty() && '/' == utils::final(url_.ruta()));
86  const string base_dir = url_.ruta() + ( barra_final ? "" : "/");
87  /*
88  * Formats:
89  * Apache:
90  * Llistat de noms enllaçats
91  * Apache FancyIndex:
92  * Name, Last Modified, Size, Description
93  * Webfsd:
94  * access, user, group, date, size, name
95  */
96  // FIXME: Abstraure?
97  ss << "<!DOCTYPE html>\n"
98  "<html>\n"
99  "<head><title>Índex per a " << base_dir << "</title></head>\n"
100  << "<body>\n"
101  "<h1>Índex per a " << base_dir << "</h1>\n"
102  "<pre>"
103  << lpad("data", AMPLE_DATA)
104  << rpad(" tipus", AMPLE_MIME)
105  << lpad("mida", AMPLE_MIDA)
106  << " nom"
107  "</pre>"
108  "<hr />\n"
109  "<pre>";
110  // El llistat de directoris obté els fitxers desordenats, cal un pas intermig per ordenar-los
111  // <http://www.boost.org/doc/libs/1_49_0/libs/filesystem/v3/doc/tutorial.html#Using-path-decomposition>
112  vector<path> paths;
113 
114  //paths.push_back(path("."));
115  paths.push_back(path(".."));
116  copy(directory_iterator(directori()), directory_iterator(), back_inserter(paths));
117  sort(paths.begin(), paths.end());
118 
119  //const string base = "http://" + to_string(host_);
120 
121  const string str_dir = string(AMPLE_MIDA-5, ' ')+"&lt;DIR&gt;";
122  // FIXME: Ignorar dotfiles i il·legibles (Permisos? -Windows: attrib-)
123  for (auto it = paths.begin(); it != paths.end(); ++it) {
124  if (!es_llegible(*it)) {
125  continue;
126  }
127  // La conversió implícita a string afegeix cometes
128  const string fn = it->filename().string();
129  const bool es_dir = is_directory(*it);
130  const string str_mida = (es_dir ? str_dir : mida(file_size(*it)));
131  const string data = mtime(last_write_time(*it));
132  const string extra = (es_dir ? "/" : "");
133  const string mime = MIME::tipus(*it);
134  const string url = base_dir + fn;
135  ss
136  << lpad(data, AMPLE_DATA)
137  << rpad(" "+mime, AMPLE_MIME)
138  << lpad(str_mida, AMPLE_MIDA)
139  << " <a href=\"" << url << "\">" // FIXME: Construir path relatiu per enllaç
140  << fn << extra << "</a>"
141  << "\n";
142  }
143 
144  ss << "</pre>\n"
145  "<hr />\n"
146  "<address>" << Servidor::signatura()
147  << ". Servidor " << host_.address() << ", port " << host_.port()
148  << "</address>\n"
149  "</body>\n"
150  "</html>";
151  cache_ = ss.str();
152  return cache_;
153 }
154 
155 void AutoIndex::envia(boost::asio::ip::tcp::socket & socket) const
156  throw (runtime_error)
157 {
158  this->envia(socket, html());
159 }
160 
161 AutoIndex::tipus_capsaleres AutoIndex::capsaleres() const {
162  tipus_capsaleres capsaleres = RecursDinamic::capsaleres();
163  const string content_length = lexical_cast<string>(html().length());
164  capsaleres.push_back( make_pair("Content-Length", content_length) );
165  // FIXME: Accept-Charset?? vs UTF-8
166  // TODO: espai entre MIME i charset?
167  capsaleres.push_back( make_pair("Content-Type", "text/html; charset=UTF-8") );
168  // TODO: Afegir Last-Modified?
169  return capsaleres;
170 }
171 
172 } // ns tfc
173 
174 // vim:set ts=4 et ai: //