OSDN Git Service

reduce regex's.
[hmh/hhml.git] / lib / formfile.cc
1 #include "formfile.h"
2 #include "motorenv.h"
3 #include "util_const.h"
4 #include "util_string.h"
5 #include "ustring.h"
6 #include "filemacro.h"
7 #include <sys/types.h>
8 #include <unistd.h>
9 #include <sys/mman.h>
10 #include <assert.h>
11
12 int  CGIFormFile::partAt (int i) {
13     if (i >= 0) {
14         sary_t::iterator  it = datamap.find (i);
15         if (it == datamap.end ()) {
16             return -1;
17         } else {
18             return it->second;
19         }
20     } else {
21         return -1;
22     }
23 }
24
25 ustring  CGIFormFile::typeAt (int i) {
26     if (i >= 0) {
27         tary_t::iterator  it = typemap.find (i);
28         if (it == typemap.end ()) {
29             return uEmpty;
30         } else {
31             return it->second;
32         }
33     } else {
34         return uEmpty;
35     }
36 }
37
38 void  CGIFormFile::read_multipart (MotorEnv* env) {
39 #ifdef DEBUG2
40     std::cerr << "boundary:" << boundary << "\n";
41 #endif /* DEBUG */
42     compileReg ();
43     if (saveData (env)) {
44         searchPart (env);
45 #ifdef DEBUG2
46         dump (std::cerr);
47         for (int i = 0; i < parts.size (); i ++) {
48             std::cerr << "file:" << i << " (" << (void*)parts[i].first << ", " << (void*)parts[i].second << ") filename:" << filenames[i] << "\n";
49         }
50 #endif /* DEBUG */
51     }
52 }
53
54 bool  CGIFormFile::saveData (MotorEnv* env) {
55     static const int  bsize = 65536 * 4;
56     size_t  size = postSize ();
57     ustring  b;
58     size_t  s;
59     pid_t  mypid = getpid ();
60
61     b.resize (bsize);
62     if (size == 0 || size > env->appenv->postfilelimit)
63         return false;           // NG
64     tmpfile = env->path_to_posttemp ();
65     mapsize = 0;
66     if (fp.openReadWrite (tmpfile.c_str ())) {
67         unlink (tmpfile.c_str ());
68         while (size > 0) {
69             s = (size < bsize) ? size : bsize;
70             s = std::cin.read (&b[0], s).gcount ();
71             if (s <= 0)
72                 break;
73             s = ::write (fp.fd, &b[0], s);
74             size -= s;
75             mapsize += s;
76             if (size > 0) {
77                 std::cerr << "post-file[" << mypid << ":" << env->scriptName << "]: reading " << mapsize << "Bytes...\n"; // cerrはバッファリングされない
78             }
79         }
80 //      fp.close ();
81         std::cerr << "post-file[" << mypid << ":" << env->scriptName << "]: done    " << mapsize << "Bytes\n";
82         mapdata = (char*)mmap (NULL, mapsize, PROT_READ, MAP_PRIVATE, fp.fd, 0);
83 #ifdef DEBUG2
84         std::cerr << (void*) mapdata << ": " << mapsize << "\n";
85 #endif /* DEBUG */
86     } else {
87         throw (ustring (CharConst ("configuration error: can't open temporary file.")));
88     }
89     return true;
90 }
91
92 void  CGIFormFile::unlinkTmpFile () {
93     if (mapdata > 0) {
94         munmap (mapdata, mapsize);
95         mapdata = NULL;
96     }
97     if (tmpfile.size () > 0) {
98 //      unlink (tmpfile.c_str ());
99         tmpfile.resize (0);
100     }
101 }
102
103 void  CGIFormFile::searchPart (MotorEnv* env) {
104     char*  b = mapdata;
105     char*  e = mapdata + mapsize;
106     char*  x;
107     boost::match_results<char*>  m;
108     ustring  disp;
109     ustring  name;
110     ustring  filename;
111     ustring  type;
112     ustring  v;
113     size_t  size;
114
115 #ifdef DEBUG2
116     std::cerr << "b:" << (void*)b << " e:" << (void*)e << "\n";
117     std::cerr << "mapdata:" << ustring (b, b + 40) << "\n";
118 #endif /* DEBUG */
119     if (b != e && regex_search (b, e, m, re1, boost::regex_constants::match_single_line)) {
120 #ifdef DEBUG2
121         std::cerr << "match:" << ustring (m[0].first, m[0].second) << "\n";
122 #endif /* DEBUG */
123         b = m[0].second;
124         while (b != e && regex_search (b, e, m, reN, boost::regex_constants::match_single_line)) {
125             x = m[0].first;
126 #ifdef DEBUG2
127             std::cerr << "match:" << ustring (m[0].first, m[0].second) << "\n";
128 #endif /* DEBUG */
129             readMimeHead (b, x, disp, name, filename, type);
130 #ifdef DEBUG2
131             std::cerr << "disp:" << disp << " name:" << name << " filename:" << filename << " type:" << type << "\n";
132 #endif /* DEBUG */
133             if (filename.size () > 0) {
134                 int  k1, k2;
135                 k2 = parts.size ();
136                 fix (name);
137                 parts.push_back (part (b, x));
138                 fix (filename);
139                 fix (type);
140                 k1 = insert (iarg, name, filePart_osSafe (filename));
141                 datamap.insert (sary_t::value_type (k1, k2));
142                 typemap.insert (tary_t::value_type (k2, type));
143 #ifdef DEBUG2
144                 std::cerr << "insert(" << k1 << "," << k2 << ")\n";
145 #endif /* DEBUG */
146             } else {
147                 // no filename
148                 size = x - b;
149                 if (size < env->appenv->postlimit) {
150                     v = ustring (b, x);
151                     name = omitNul (name);
152                     fix (name);
153                     v = omitNul (v);
154                     fix (v);
155                     insert (iarg, name, v);
156                 } else {
157                     *env->log << "form variable '" << name << "': size limit.\n";
158                 }
159             }
160
161             b = m[0].second;
162             if (e - b < 2) {
163                 break;
164             } else if (b[0] == '-' && b[1] == '-') {
165 #ifdef DEBUG2
166                 std::cerr << "break\n";
167 #endif /* DEBUG */
168                 break;
169             } else if (b[0] == '\r' && b[1] == '\n') {
170                 b += 2;
171             } else {
172                 break;          // format error;
173             }
174         }
175     }
176 }
177
178 void  CGIFormFile::compileReg () {
179     ustring  a;
180     ustring  t = escape_re (boundary);
181
182     a.append (CharConst ("^--"));
183     a.append (t);
184     a.append (uCRLF);
185 #ifdef DEBUG2
186     std::cerr << "re1:" << a << "\n";
187 #endif /* DEBUG */
188     re1.assign (a);
189     a = uCRLF;
190     a.append (CharConst ("--"));
191     a.append (t);
192 #ifdef DEBUG2
193     std::cerr << "reN:" << a << "\n";
194 #endif /* DEBUG */
195     reN.assign (a);
196 }
197
198 class  ChSplitterNL {
199  public:
200     char*  b;           // 先頭
201     char*  t;           // 区切り文字列先頭
202     char*  u;           // 区切り文字列末尾
203     char*  e;           // 末尾
204
205     ChSplitterNL (char* _begin, char* _end) {
206         b = t = u = _begin;
207         e = _end;
208     };
209     ~ChSplitterNL () {};
210
211     bool  isEnd () {
212         return b == e;
213     };
214     ustring  pre () {
215         return ustring (b, t);
216     };
217     bool  next () {
218         b = t = u;
219         if (b < e) {
220             if (findNL ()) {
221             } else {
222                 t = u = e;
223             }
224             return true;
225         } else {
226             return false;
227         }
228     };
229     bool  nextSep () {
230         b = t = u;
231         if (b < e) {
232             if (findNL ()) {
233                 return true;
234             } else {
235                 t = u = e;
236                 return false;
237             }
238         } else {
239             t = u = e;
240             return false;
241         }
242     };
243     bool  findNL () {
244         for (; t < e; ++ t) {
245             if (*t == '\n') {
246                 u = t + 1;
247                 return true;
248             } else if (*t == '\r') {
249                 u = t + 1;
250                 if (u < e && *u == '\n')
251                     ++ u;
252                 return true;
253             }
254         }
255         return false;
256     };
257 };
258
259 void  CGIFormFile::readMimeHead (char*& b, char* e, ustring& disp, ustring& name, ustring& filename, ustring& type) {
260 //    boost::match_results<char*>  m;
261     ChSplitterNL  sp (b, e);
262     boost::match_results<char*>  m2;
263 //    char*  x;
264     static uregex  re_disp1 ("^Content-Disposition:\\s*(.*);\\s*name=\"(.*)\";\\s*filename=\"(.*)\"$");
265     static uregex  re_disp2 ("^Content-Disposition:\\s*(.*);\\s*name=\"(.*)\"$");
266     static uregex  re_type ("^Content-Type:\\s*([a-zA-Z_0-9/.+-]*)(;\\s*(.*))?$");
267
268     disp.resize (0);
269     name.resize (0);
270     filename.resize (0);
271     type.resize (0);
272 //    while (b != e && regex_search (b, e, m, re_nl, boost::regex_constants::match_single_line)) {
273 //      x = m[0].first;
274     while (sp.next ()) {
275 #ifdef DEBUG2
276 //      std::cerr << "line:" << ustring (b, x) << "\n";
277         std::cerr << "line:" << sp.pre () << "\n";
278 #endif /* DEBUG */
279 //      if (b == x) {           // empty line
280 //          b = m[0].second;
281         if (sp.b == sp.t) {
282             b = sp.u;
283             break;
284         }
285 //      if (regex_search (b, x, m2, re_disp1, boost::regex_constants::match_single_line)) {
286         if (regex_search (sp.b, sp.t, m2, re_disp1, boost::regex_constants::match_single_line)) {
287             disp.assign (m2[1].first, m2[1].second - m2[1].first);
288             name.assign (m2[2].first, m2[2].second - m2[2].first);
289             filename.assign (m2[3].first, m2[3].second - m2[3].first);
290 //      } else if (regex_search (b, x, m2, re_disp2, boost::regex_constants::match_single_line)) {
291         } else if (regex_search (sp.b, sp.t, m2, re_disp2, boost::regex_constants::match_single_line)) {
292             disp.assign (m2[1].first, m2[1].second - m2[1].first);
293             name.assign (m2[2].first, m2[2].second - m2[2].first);
294 //      } else if (regex_search (b, x, m2, re_type, boost::regex_constants::match_single_line)) {
295         } else if (regex_search (sp.b, sp.t, m2, re_type, boost::regex_constants::match_single_line)) {
296             type.assign (m2[1].first, m2[1].second - m2[1].first);
297         } else {
298 #ifdef DEBUG2
299 //          std::cerr << "not match:" << ustring (b, x) << "\n";
300             std::cerr << "not match:" << sp.pre () << "\n";
301 #endif /* DEBUG */
302         }
303 //      b = m[0].second;
304     }
305 }
306
307 bool  CGIFormFile::saveFile (int i, const ustring& path, size_t max) {
308     static size_t  bsize = 65536;
309     part  p;
310     char*  b;
311     size_t  s, size;
312     FileMacro  f;
313
314     if (0 <= i && i < parts.size ()) {
315         p = parts[i];
316         assert (mapdata <= p.first && p.first <= p.second && p.second < mapdata + mapsize);
317         b = p.first;
318         size = p.second - p.first;
319         if (max > 0 && size > max)
320             return false;
321
322 #ifdef DEBUG2
323         std::cerr << "saveFile(" << i << "," << path << "): " << (void*)p.first << "--" << (p.second - p.first) << "\n";
324 #endif /* DEBUG */
325         f.openWrite (path.c_str ());
326 #ifdef DEBUG2
327         std::cerr << "write:" << path << "\n";
328 #endif /* DEBUG */
329         while (size > 0) {
330             s = (size < bsize) ? size : bsize;
331             s = ::write (f.fd, b, s);
332             size -= s;
333             b += s;
334         }
335         f.close ();
336         return true;
337     }
338     return false;
339 }
340