OSDN Git Service

minor fix.
[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 void  CGIFormFile::read_multipart (MotorEnv* env) {
26 #ifdef DEBUG2
27     std::cerr << "boundary:" << boundary << "\n";
28 #endif /* DEBUG */
29     compileReg ();
30     if (saveData (env)) {
31         searchPart (env);
32 #ifdef DEBUG2
33         dump (std::cerr);
34         for (int i = 0; i < parts.size (); i ++) {
35             std::cerr << "file:" << i << " (" << (void*)parts[i].first << ", " << (void*)parts[i].second << ") filename:" << filenames[i] << "\n";
36         }
37 #endif /* DEBUG */
38     }
39 }
40
41 bool  CGIFormFile::saveData (MotorEnv* env) {
42     static const int  bsize = 65536 * 4;
43     size_t  size = postSize ();
44     ustring  b;
45     size_t  s;
46     pid_t  mypid = getpid ();
47
48     b.resize (bsize);
49     if (size == 0 || size > env->appenv->postfilelimit)
50         return false;           // NG
51     tmpfile = env->path_to_posttemp ();
52     mapsize = 0;
53     if (fp.openReadWrite (tmpfile.c_str ())) {
54         unlink (tmpfile.c_str ());
55         while (size > 0) {
56             s = (size < bsize) ? size : bsize;
57             s = std::cin.read (&b[0], s).gcount ();
58             if (s <= 0)
59                 break;
60             s = ::write (fp.fd, &b[0], s);
61             size -= s;
62             mapsize += s;
63             if (size > 0) {
64                 std::cerr << "post-file[" << mypid << ":" << env->scriptName << "]: reading " << mapsize << "Bytes...\n";
65             }
66         }
67 //      fp.close ();
68         close (0);              // cin
69         std::cerr << "post-file[" << mypid << ":" << env->scriptName << "]: done    " << mapsize << "Bytes\n";
70         mapdata = (char*)mmap (NULL, mapsize, PROT_READ, MAP_PRIVATE, fp.fd, 0);
71 #ifdef DEBUG2
72         std::cerr << (void*) mapdata << ": " << mapsize << "\n";
73 #endif /* DEBUG */
74     } else {
75         throw (ustring (CharConst ("configuration error: can't open temporary file.")));
76     }
77     return true;
78 }
79
80 void  CGIFormFile::unlinkTmpFile () {
81     if (mapdata > 0) {
82         munmap (mapdata, mapsize);
83         mapdata = NULL;
84     }
85     if (tmpfile.size () > 0) {
86 //      unlink (tmpfile.c_str ());
87         tmpfile.resize (0);
88     }
89 }
90
91 bool  CGIFormFile::isMultipart () {
92     ustring  e = getenvString (kCONTENT_TYPE);
93     umatch  m;
94     static uregex  re ("^" kMIME_FORMDATA "\\s*;\\s*boundary=\"?([ '()+,./0-9:=?A-Z_a-z-]*['()+,./0-9:=?A-Z_a-z-])");
95
96     if (usearch (e, m, re)) {
97         boundary = ustring (m[1]);
98         return true;
99     } else {
100         return false;
101     }
102 }
103
104 void  CGIFormFile::searchPart (MotorEnv* env) {
105     char*  b = mapdata;
106     char*  e = mapdata + mapsize;
107     char*  x;
108     boost::match_results<char*>  m;
109     ustring  disp;
110     ustring  name;
111     ustring  filename;
112     ustring  type;
113     ustring  v;
114     size_t  size;
115
116 #ifdef DEBUG2
117     std::cerr << "b:" << (void*)b << " e:" << (void*)e << "\n";
118     std::cerr << "mapdata:" << ustring (b, b + 40) << "\n";
119 #endif /* DEBUG */
120     if (b != e && regex_search (b, e, m, re1, boost::regex_constants::match_single_line)) {
121 #ifdef DEBUG2
122         std::cerr << "match:" << ustring (m[0].first, m[0].second) << "\n";
123 #endif /* DEBUG */
124         b = m[0].second;
125         while (b != e && regex_search (b, e, m, reN, boost::regex_constants::match_single_line)) {
126             x = m[0].first;
127 #ifdef DEBUG2
128             std::cerr << "match:" << ustring (m[0].first, m[0].second) << "\n";
129 #endif /* DEBUG */
130             readMimeHead (b, x, disp, name, filename, type);
131 #ifdef DEBUG2
132             std::cerr << "disp:" << disp << " name:" << name << " filename:" << filename << " type:" << type << "\n";
133 #endif /* DEBUG */
134             if (filename.size () > 0) {
135                 int  k1, k2;
136 //              v = to_ustring (parts.size ());
137                 k2 = parts.size ();
138                 fix (name);
139 //              insert (filearg, name, v);
140                 parts.push_back (part (b, x));
141                 fix (filename);
142                 k1 = insert (iarg, name, filePart_osSafe (filename));
143                 datamap.insert (sary_t::value_type (k1, k2));
144 #ifdef DEBUG2
145                 std::cerr << "insert(" << k1 << "," << k2 << ")\n";
146 #endif /* DEBUG */
147             } else {
148                 // no filename
149                 size = x - b;
150                 if (size < env->appenv->postlimit) {
151                     v = ustring (b, x);
152                     name = omitNul (name);
153                     fix (name);
154                     v = omitNul (v);
155                     fix (v);
156                     insert (iarg, name, v);
157                 } else {
158                     *env->log << "form variable '" << name << "': size limit.\n";
159                 }
160             }
161
162             b = m[0].second;
163             if (e - b < 2) {
164                 break;
165             } else if (b[0] == '-' && b[1] == '-') {
166 #ifdef DEBUG2
167                 std::cerr << "break\n";
168 #endif /* DEBUG */
169                 break;
170             } else if (b[0] == '\r' && b[1] == '\n') {
171                 b += 2;
172             } else {
173                 break;          // format error;
174             }
175         }
176     }
177 }
178
179 void  CGIFormFile::compileReg () {
180     ustring  a;
181     ustring  t = escape_re (boundary);
182
183     a.append (CharConst ("^--"));
184     a.append (t);
185     a.append (uCRLF);
186 #ifdef DEBUG2
187     std::cerr << "re1:" << a << "\n";
188 #endif /* DEBUG */
189     re1.assign (a);
190     a = uCRLF;
191     a.append (CharConst ("--"));
192     a.append (t);
193 #ifdef DEBUG2
194     std::cerr << "reN:" << a << "\n";
195 #endif /* DEBUG */
196     reN.assign (a);
197 }
198
199 void  CGIFormFile::readMimeHead (char*& b, char* e, ustring& disp, ustring& name, ustring& filename, ustring& type) {
200     boost::match_results<char*>  m;
201     boost::match_results<char*>  m2;
202     char*  x;
203     static uregex  re_disp1 ("^Content-Disposition:\\s*(.*);\\s*name=\"(.*)\";\\s*filename=\"(.*)\"$");
204     static uregex  re_disp2 ("^Content-Disposition:\\s*(.*);\\s*name=\"(.*)\"$");
205     static uregex  re_type ("^Content-Type:\\s*([a-zA-Z_0-9/.+-]*)(;\\s*(.*))?$");
206
207     disp.resize (0);
208     name.resize (0);
209     filename.resize (0);
210     type.resize (0);
211     while (b != e && regex_search (b, e, m, re_nl, boost::regex_constants::match_single_line)) {
212         x = m[0].first;
213 #ifdef DEBUG2
214         std::cerr << "line:" << ustring (b, x) << "\n";
215 #endif /* DEBUG */
216         if (b == x) {           // empty line
217             b = m[0].second;
218             break;
219         }
220         if (regex_search (b, x, m2, re_disp1, boost::regex_constants::match_single_line)) {
221             disp.assign (m2[1].first, m2[1].second - m2[1].first);
222             name.assign (m2[2].first, m2[2].second - m2[2].first);
223             filename.assign (m2[3].first, m2[3].second - m2[3].first);
224         } else if (regex_search (b, x, m2, re_disp2, boost::regex_constants::match_single_line)) {
225             disp.assign (m2[1].first, m2[1].second - m2[1].first);
226             name.assign (m2[2].first, m2[2].second - m2[2].first);
227         } else if (regex_search (b, x, m2, re_type, boost::regex_constants::match_single_line)) {
228             type.assign (m2[1].first, m2[1].second - m2[1].first);
229         } else {
230 #ifdef DEBUG2
231             std::cerr << "not match:" << ustring (b, x) << "\n";
232 #endif /* DEBUG */
233         }
234         b = m[0].second;
235     }
236 }
237
238 #if 0
239 bool  CGIFormFile::readFilename (int i, ustring& filename) {
240     if (0 <= i && i < parts.size ()) {
241         filename = filenames[i];
242         return true;
243     }
244     return false;
245 }
246 #endif
247
248 bool  CGIFormFile::saveFile (int i, const ustring& path, size_t max) {
249     static size_t  bsize = 65536;
250     part  p;
251     char*  b;
252     size_t  s, size;
253     FileMacro  f;
254
255     if (0 <= i && i < parts.size ()) {
256         p = parts[i];
257         assert (mapdata <= p.first && p.first <= p.second && p.second < mapdata + mapsize);
258         b = p.first;
259         size = p.second - p.first;
260         if (max > 0 && size > max)
261             return false;
262
263 #ifdef DEBUG2
264         std::cerr << "saveFile(" << i << "," << path << "): " << (void*)p.first << "--" << (p.second - p.first) << "\n";
265 #endif /* DEBUG */
266         f.openWrite (path.c_str ());
267 #ifdef DEBUG2
268         std::cerr << "write:" << path << "\n";
269 #endif /* DEBUG */
270         while (size > 0) {
271             s = (size < bsize) ? size : bsize;
272             s = ::write (f.fd, b, s);
273             size -= s;
274             b += s;
275         }
276         f.close ();
277         return true;
278     }
279     return false;
280 }
281