OSDN Git Service

fix for FreeBSD 11.1.
[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 size_t  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             ::write (fp.fd, &b[0], s);
74             size -= s;
75             mapsize += s;
76         }
77 //      fp.close ();    mmapした後にクローズする。
78 //      std::cerr << "post-file[" << mypid << ":" << env->scriptName << "]: done    " << mapsize << "Bytes\n";          // 出力サイズが大きいと、デッドロックする。
79         mapdata = (char*)mmap (NULL, mapsize, PROT_READ, MAP_PRIVATE, fp.fd, 0);
80 #ifdef DEBUG2
81         std::cerr << (void*) mapdata << ": " << mapsize << "\n";
82 #endif /* DEBUG */
83     } else {
84         throw (ustring (CharConst ("configuration error: can't open temporary file.")));
85     }
86     return true;
87 }
88
89 void  CGIFormFile::unlinkTmpFile () {
90     if (mapdata != NULL) {
91         munmap (mapdata, mapsize);
92         mapdata = NULL;
93     }
94     if (tmpfile.size () > 0) {
95 //      unlink (tmpfile.c_str ());
96         tmpfile.resize (0);
97     }
98 }
99
100 void  CGIFormFile::searchPart (MotorEnv* env) {
101     char*  b = mapdata;
102     char*  e = mapdata + mapsize;
103     char*  x;
104     boost::match_results<char*>  m;
105     ustring  disp;
106     ustring  name;
107     ustring  filename;
108     ustring  type;
109     ustring  v;
110     size_t  size;
111
112 #ifdef DEBUG2
113     std::cerr << "b:" << (void*)b << " e:" << (void*)e << "\n";
114     std::cerr << "mapdata:" << ustring (b, b + 40) << "\n";
115 #endif /* DEBUG */
116     if (b != e && regex_search (b, e, m, re1, boost::regex_constants::match_single_line)) {
117 #ifdef DEBUG2
118         std::cerr << "match:" << ustring (m[0].first, m[0].second) << "\n";
119 #endif /* DEBUG */
120         b = m[0].second;
121         while (b != e && regex_search (b, e, m, reN, boost::regex_constants::match_single_line)) {
122             x = m[0].first;
123 #ifdef DEBUG2
124             std::cerr << "match:" << ustring (m[0].first, m[0].second) << "\n";
125 #endif /* DEBUG */
126             readMimeHead (b, x, disp, name, filename, type);
127 #ifdef DEBUG2
128             std::cerr << "disp:" << disp << " name:" << name << " filename:" << filename << " type:" << type << "\n";
129 #endif /* DEBUG */
130             if (filename.size () > 0) {
131                 int  k1, k2;
132                 k2 = parts.size ();
133                 fix (name);
134                 parts.push_back (part (b, x));
135                 fix (filename);
136                 fix (type);
137                 k1 = insert (iarg, name, filePart_osSafe (filename));
138                 datamap.insert (sary_t::value_type (k1, k2));
139                 typemap.insert (tary_t::value_type (k2, type));
140 #ifdef DEBUG2
141                 std::cerr << "insert(" << k1 << "," << k2 << ")\n";
142 #endif /* DEBUG */
143             } else {
144                 // no filename
145                 size = x - b;
146                 if (size < env->appenv->postlimit) {
147                     v = ustring (b, x);
148                     name = omitNul (name);
149                     fix (name);
150                     v = omitNul (v);
151                     fix (v);
152                     insert (iarg, name, v);
153                 } else {
154                     *env->log << "form variable '" << name << "': size limit.\n";
155                 }
156             }
157
158             b = m[0].second;
159             if (e - b < 2) {
160                 break;
161             } else if (b[0] == '-' && b[1] == '-') {
162 #ifdef DEBUG2
163                 std::cerr << "break\n";
164 #endif /* DEBUG */
165                 break;
166             } else if (b[0] == '\r' && b[1] == '\n') {
167                 b += 2;
168             } else {
169                 break;          // format error;
170             }
171         }
172     }
173 }
174
175 void  CGIFormFile::compileReg () {
176     ustring  a;
177     ustring  t = escape_re (boundary);
178
179     a.append (CharConst ("^--"));
180     a.append (t);
181     a.append (uCRLF);
182 #ifdef DEBUG2
183     std::cerr << "re1:" << a << "\n";
184 #endif /* DEBUG */
185     re1.assign (a);
186     a = uCRLF;
187     a.append (CharConst ("--"));
188     a.append (t);
189 #ifdef DEBUG2
190     std::cerr << "reN:" << a << "\n";
191 #endif /* DEBUG */
192     reN.assign (a);
193 }
194
195 class  ChSplitterNL {
196  public:
197     char*  b;           // 先頭
198     char*  t;           // 区切り文字列先頭
199     char*  u;           // 区切り文字列末尾
200     char*  e;           // 末尾
201
202     ChSplitterNL (char* _begin, char* _end) {
203         b = t = u = _begin;
204         e = _end;
205     };
206     ~ChSplitterNL () {};
207
208     bool  isEnd () {
209         return b == e;
210     };
211     ustring  pre () {
212         return ustring (b, t);
213     };
214     bool  next () {
215         b = t = u;
216         if (b < e) {
217             if (findNL ()) {
218             } else {
219                 t = u = e;
220             }
221             return true;
222         } else {
223             return false;
224         }
225     };
226     bool  nextSep () {
227         b = t = u;
228         if (b < e) {
229             if (findNL ()) {
230                 return true;
231             } else {
232                 t = u = e;
233                 return false;
234             }
235         } else {
236             t = u = e;
237             return false;
238         }
239     };
240     bool  findNL () {
241         for (; t < e; ++ t) {
242             if (*t == '\n') {
243                 u = t + 1;
244                 return true;
245             } else if (*t == '\r') {
246                 u = t + 1;
247                 if (u < e && *u == '\n')
248                     ++ u;
249                 return true;
250             }
251         }
252         return false;
253     };
254 };
255
256 void  CGIFormFile::readMimeHead (char*& b, char* e, ustring& disp, ustring& name, ustring& filename, ustring& type) {
257     ChSplitterNL  sp (b, e);
258     boost::match_results<char*>  m2;
259     static uregex  re_disp1 ("^Content-Disposition:\\s*(.*);\\s*name=\"(.*)\";\\s*filename=\"(.*)\"$");
260     static uregex  re_disp2 ("^Content-Disposition:\\s*(.*);\\s*name=\"(.*)\"$");
261     static uregex  re_type ("^Content-Type:\\s*([a-zA-Z_0-9/.+-]*)(;\\s*(.*))?$");
262
263     disp.resize (0);
264     name.resize (0);
265     filename.resize (0);
266     type.resize (0);
267     while (sp.next ()) {
268 #ifdef DEBUG2
269         std::cerr << "line:" << sp.pre () << "\n";
270 #endif /* DEBUG */
271         if (sp.b == sp.t) {
272             b = sp.u;
273             break;
274         }
275         if (regex_search (sp.b, sp.t, m2, re_disp1, boost::regex_constants::match_single_line)) {
276             disp.assign (m2[1].first, m2[1].second - m2[1].first);
277             name.assign (m2[2].first, m2[2].second - m2[2].first);
278             filename.assign (m2[3].first, m2[3].second - m2[3].first);
279         } else if (regex_search (sp.b, sp.t, m2, re_disp2, boost::regex_constants::match_single_line)) {
280             disp.assign (m2[1].first, m2[1].second - m2[1].first);
281             name.assign (m2[2].first, m2[2].second - m2[2].first);
282         } else if (regex_search (sp.b, sp.t, m2, re_type, boost::regex_constants::match_single_line)) {
283             type.assign (m2[1].first, m2[1].second - m2[1].first);
284         } else {
285 #ifdef DEBUG2
286             std::cerr << "not match:" << sp.pre () << "\n";
287 #endif /* DEBUG */
288         }
289     }
290 }
291
292 bool  CGIFormFile::saveFile (int i, const ustring& path, size_t max) {
293     static size_t  bsize = 65536;
294     part  p;
295     char*  b;
296     size_t  s, size;
297     FileMacro  f;
298
299     if (0 <= i && i < parts.size ()) {
300         p = parts[i];
301         assert (mapdata <= p.first && p.first <= p.second && p.second < mapdata + mapsize);
302         b = p.first;
303         size = p.second - p.first;
304         if (max > 0 && size > max)
305             return false;
306
307 #ifdef DEBUG2
308         std::cerr << "saveFile(" << i << "," << path << "): " << (void*)p.first << "--" << (p.second - p.first) << "\n";
309 #endif /* DEBUG */
310         f.openWrite (path.c_str ());
311 #ifdef DEBUG2
312         std::cerr << "write:" << path << "\n";
313 #endif /* DEBUG */
314         while (size > 0) {
315             s = (size < bsize) ? size : bsize;
316             s = ::write (f.fd, b, s);
317             size -= s;
318             b += s;
319         }
320         f.close ();
321         return true;
322     }
323     return false;
324 }
325