TFCweb  1.0.4 $Rev: 483 $
TFC Primavera 2012: Nucli d'un servidor web
DataHTTP.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 "DataHTTP.h"
28 #include "portabilitat.h"
29 #include "utils.h"
30 
31 #include <cstdlib>
32 #include <boost/algorithm/string.hpp>
33 #include <boost/assign.hpp>
34 #include <boost/regex.hpp>
35 #include <boost/unordered_map.hpp>
36 
37 using namespace boost::algorithm;
38 using namespace boost::gregorian;
39 using namespace boost::posix_time;
40 using namespace std;
41 using boost::lexical_cast;
42 using boost::unordered_map;
43 
44 namespace {
45 
46 using namespace tfc;
47 
49 const string FORMAT_HTTP("%a, %d %b %Y %H:%M:%S GMT"); // %T no parseja
50 
51 // Formats acceptats en la recepció (RFC 2068, 3.3.1):
52 // Sun, 06 Nov 1994 08:49:37 GMT // RFC 822 <=> FORMAT_HTTP
53 // Sunday, 06-Nov-94 08:49:37 GMT // RFC 850
54 // Sun Nov 6 08:49:37 1994 // ANSI C asctime()
55 
56 // Extracció dels valors a partir de expressions regulars
57 const boost::regex RE_RFC822(
58  // 1: dia setmana, 2: dia mes, 3: mes, 4: any (xxxx), 5,6,7: hms
59  "^(\\w{3}), (\\d{2}) (\\w{3}) (\\d{4}) (\\d{2}):(\\d{2}):(\\d{2}) GMT$",
60  boost::regbase::perl);
61 
62 void extreu_rfc822(const string & s,
63  string & dia_sem, string & dia_mes,
64  string & mes, string & any,
65  string & hora, string & minut, string & segon)
66 {
67  assert( boost::regex_match(s, RE_RFC822) ); // PRECONDICIÓ
68  boost::smatch m;
69  boost::regex_match(s, m, RE_RFC822);
70  dia_sem = m[1];
71  dia_mes = m[2];
72  mes = m[3];
73  any = m[4];
74  hora = m[5];
75  minut = m[6];
76  segon = m[7];
77 }
78 
79 const boost::regex RE_RFC850(
80  // 1: dia setmana, 2: dia mes, 3: mes, 4: any (xx), 5,6,7: hms
81  "^(\\w+), (\\d{2})-(\\w{3})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2}) GMT$",
82  boost::regbase::perl);
83 
84 void extreu_rfc850(const string & s,
85  string & dia_sem, string & dia_mes,
86  string & mes, string & any,
87  string & hora, string & minut, string & segon)
88 {
89  assert( boost::regex_match(s, RE_RFC850) ); // PRECONDICIÓ
90  boost::smatch m;
91  boost::regex_match(s, m, RE_RFC850);
92  dia_sem = m[1];
93  dia_mes = m[2];
94  mes = m[3];
95  any = "19"+m[4];
96  hora = m[5];
97  minut = m[6];
98  segon = m[7];
99 }
100 
101 const boost::regex RE_ASCTIME(
102  // 1: dia setmana, 2: mes, 3: dia mes (' 'x ó xx), 4,5,6: hms, 7: any (xxxx)
103  "^(\\w{3}) (\\w{3}) ?(\\d{1,2}) (\\d{2}):(\\d{2}):(\\d{2}) (\\d{4})$",
104  boost::regbase::perl);
105 
106 void extreu_asctime(const string & s,
107  string & dia_sem, string & dia_mes,
108  string & mes, string & any,
109  string & hora, string & minut, string & segon)
110 {
111  assert( boost::regex_match(s, RE_ASCTIME) ); // PRECONDICIÓ
112  boost::smatch m;
113  boost::regex_match(s, m, RE_ASCTIME);
114  dia_sem = m[1];
115  mes = m[2];
116  dia_mes = m[3];
117  hora = m[4];
118  minut = m[5];
119  segon = m[6];
120  any = m[7];
121 }
122 // Traduccions de dates textuals
124 const unordered_map<string, int> DIES_SETMANA = boost::assign::map_list_of
125  ( "mon", 0 ) ( "monday", 0 )
126  ( "tue", 1 ) ( "tuesday", 1 )
127  ( "wed", 2 ) ( "wednesday", 2 )
128  ( "thu", 3 ) ( "thursday", 3 )
129  ( "fri", 4 ) ( "friday", 4 )
130  ( "sat", 5 ) ( "saturday", 5 )
131  ( "sun", 6 ) ( "sunday", 6 );
133 const unordered_map<string, int> MESOS = boost::assign::map_list_of
134  ( "jan", 0 )
135  ( "feb", 1 )
136  ( "mar", 2 )
137  ( "apr", 3 )
138  ( "may", 4 )
139  ( "jun", 5 )
140  ( "jul", 6 )
141  ( "aug", 7 )
142  ( "sep", 8 )
143  ( "oct", 9 )
144  ( "nov", 10 )
145  ( "dec", 11 );
146 
147 // Vegeu llegeix_data_http per detalls
148 inline time_t tfc_timegm(std::tm & t) {
149  bool existia;
150  const string tz = portabilitat::getenv("TZ", &existia);
151  portabilitat::setenv("TZ", "UTC");
152  const time_t r = mktime(&t);
153  // Restaurem
154  if (existia) {
155  portabilitat::setenv("TZ", tz);
156  }
157  else {
159  }
160  return r;
161 }
162 
163 } // ns anònim
164 
165 namespace tfc {
166 
167 string data_http(time_t t) {
168  return utils::formata_data(t, FORMAT_HTTP);
169 }
170 
171 string data_http() {
172  return utils::formata_data(second_clock::universal_time(), FORMAT_HTTP);
173 }
174 
175 // La lectura de dates amb Boost Date Time és força complicada per l'aplicació
176 // de fusos horaris i horaris d'estiu, com que el format de les dates HTTP
177 // és rígid i molt fàcil d'interpretar he optat per implementar la lectura
178 // TODO: Utilitzar date time si m'aclaro
179 time_t llegeix_data_http(const string & s) throw (ErrorFormatDataIncorrecte) {
180  const bool es_rfc822 = boost::regex_match(s, RE_RFC822),
181  es_rfc850 = boost::regex_match(s, RE_RFC850),
182  es_asctime = boost::regex_match(s, RE_ASCTIME);
183 
184  if (!es_rfc822 && !es_rfc850 && !es_asctime) {
186  }
187  string dia_sem, dia_mes, mes, any, hora, minut, segon;
188  void (*implementacio)(const string&,string&,string&,string&,string&,string&,string&,string&);
189  if (es_rfc822) {
190  implementacio = extreu_rfc822;
191  }
192  else if (es_rfc850) {
193  implementacio = extreu_rfc850;
194  }
195  else {
196  assert( es_asctime );
197  implementacio = extreu_asctime;
198  }
199  implementacio(s, dia_sem, dia_mes, mes, any, hora, minut, segon);
200  assert( any.length() == 4 ); // Any amb 4 xifres sempre
201 
202  const auto it_mes = MESOS.find(to_lower_copy(mes));
203  const auto it_dsem = DIES_SETMANA.find(to_lower_copy(dia_sem));
204  if (it_mes == MESOS.end() || it_dsem == DIES_SETMANA.end()) {
206  }
207  // mktime considera que el tm que rep està en temps local, no universal
208  // en UNIX hi ha timegm que el considera universal, però no és portable
209  // (http://www.gnu.org/software/libc/manual/html_node/Broken_002ddown-Time.html)
210  // L'alternativa portable és canviar la variable d'entorn TZ a UTC abans de cridar mktime
211  std::tm t = { 0 };
212  t.tm_sec = lexical_cast<int>(segon);
213  t.tm_min = lexical_cast<int>(minut);
214  t.tm_hour = lexical_cast<int>(hora);
215  t.tm_mday = lexical_cast<int>(dia_mes);
216  t.tm_mon = it_mes->second;
217  t.tm_year = lexical_cast<int>(any) - 1900; // Anys desde 1900!
218  t.tm_wday = it_dsem->second;
219  // tm_yday <- ignorat
220  t.tm_isdst = 0; // UTC <-> Mai DST
221 
222  return tfc_timegm(t);
223 }
224 
225 } // ns tfc
226 
227 // vim:set ts=4 et ai: //