OSDN Git Service

942bfe377f0317b27afbe0ee624969f905d16502
[hmh/hhml.git] / modules / ml-neon.cc
1 #include "ml-neon.h"
2 #include "config.h"
3 #include "ml-http.h"
4 #include "ml.h"
5 #include "mlenv.h"
6 #include "motorenv.h"
7 #include "motoroutput.h"
8 #include "util_check.h"
9 #include "util_string.h"
10 #include "util_random.h"
11 #include "util_mimetype.h"
12 #include "util_file.h"
13 #include "util_base64.h"
14 #include "http-iconv.h"
15 #include "expr.h"
16 #include "utf8.h"
17 #include "ustring.h"
18 #include "filemacro.h"
19 #include <fstream>
20 #include <neon/ne_session.h>
21 #include <neon/ne_request.h>
22 #include <neon/ne_utils.h>
23 #include <neon/ne_uri.h>
24
25 class  NEONInitOnce {
26 public:
27     int  count;
28
29     NEONInitOnce () {
30         count = 0;
31     };
32     virtual  ~NEONInitOnce () {
33         if (count > 0) {
34             ne_sock_exit ();
35         }
36     };
37
38     void  init () {
39         if (count == 0) {
40             ne_sock_init ();
41             ++ count;
42 #ifdef DEBUG
43             std::cerr << "neon feature:"
44                       << (ne_has_support (NE_FEATURE_SSL) ? " FEATURE_SSL" : "")
45                       << (ne_has_support (NE_FEATURE_ZLIB) ? " FEATURE_ZLIB" : "")
46                       << (ne_has_support (NE_FEATURE_IPV6) ? " FEATURE_IPV6" : "")
47                       << (ne_has_support (NE_FEATURE_LFS) ? " FEATURE_LFS" : "")
48                       << (ne_has_support (NE_FEATURE_SOCKS) ? " FEATURE_SOCKS" : "")
49                       << (ne_has_support (NE_FEATURE_TS_SSL) ? " FEATURE_TS_SSL" : "")
50                       << "\n";
51 #endif /* DEBUG */
52         }
53     }
54 };
55 NEONInitOnce  NeonInit;
56
57 //============================================================
58 void  NeonPostBodyProvider::pushFile (const ustring& name, const ustring& path) {
59     if (name.length () > 0 && isPlainFile (path))
60         postFile.push_back (std::pair<ustring, ustring> (name, path));
61 }
62
63 void  NeonPostBodyProvider::makeSeparator () {
64     separator.assign (CharConst ("--------")).append (randomKey ()).append (randomKey ());
65 }
66
67 ustring  NeonPostBodyProvider::separatorLine () {
68     return ustring (CharConst ("--")).append (separator).append (uCRLF);
69 }
70
71 ustring  NeonPostBodyProvider::separatorHeader () {
72     return ustring (CharConst (kMIME_FORMDATA "; boundary=")).append (separator);
73 }
74
75 ustring  NeonPostBodyProvider::textSeparator (const ustring& name) {
76     ustring  ans;
77     ans.assign (CharConst ("--")).append (separator).append (uCRLF);
78     ans.append (CharConst (kRES_DISP ": form-data; name=" kQ2)).append (percentEncode (query->cv (name))).append (CharConst (kQ2 kCRLF kCRLF));
79     return ans;
80 }
81
82 ustring  NeonPostBodyProvider::fileSeparator (const ustring& name, const ustring& filename) {
83     ustring  ans;
84     ans.assign (CharConst ("--")).append (separator).append (uCRLF);
85     ans.append (CharConst (kRES_DISP ": form-data; name=" kQ2)).append (percentEncode (query->cv (name))).append (CharConst (kQ2 "; filename=" kQ2)).append (slashEncode (filename)).append (CharConst (kCRLF));
86     ans.append (CharConst (kRES_TYPE ": ")).append (mimetype (getExt (filename))).append (CharConst (kCRLF kCRLF));
87     return ans;
88 }
89
90 ustring  NeonPostBodyProvider::tailSeparator () {
91     ustring  ans;
92     ans.assign (CharConst ("--")).append (separator).append (CharConst ("--" kCRLF));
93     return ans;
94 }
95
96 ne_off_t  NeonPostBodyProvider::calcLength () {
97     ne_off_t  ans = 0;
98     MNode*  a;
99     ustring  u;
100     off_t  s;
101
102     tp = query->queryParam ();
103     if (isCons (tp)) {
104         while (tp) {
105             a = tp->car ();
106             if (isCons (a)) {
107                 ans += textSeparator (to_string (a->car ())).length () + to_string (a->cdr ()).length () + 2;
108             }
109             nextNode (tp);
110         }
111     }
112     bp = postFile.begin ();
113     ep = postFile.end ();
114     for (; bp < ep; ++ bp) {
115         u = filePart_osSafe ((*bp).second);
116         if (! fileSize (u, s))
117             s = 0;
118         ans += fileSeparator ((*bp).first, u).length () + s + 2;
119     }
120     ans += tailSeparator ().length ();
121 #ifdef DEBUG
122     std::cerr << "calcLength(): " << ans << "\n";
123 #endif /* DEBUG */
124     return ans;
125 }
126
127 char*  NeonPostBodyProvider::bodyProvider (char* buffer, size_t buflen) {
128     ssize_t  ans = 0;
129     if (buflen == 0) {
130         tp = query->queryParam ();
131         bp = postFile.begin ();
132         ep = postFile.end ();
133         if (isCons (tp) || bp < ep) {
134             state = S_TEXT;
135         } else {
136             state = S_DONE;
137         }
138         offset = 0;
139         // 正常なら0を返す
140     } else {
141 #ifdef DEBUG
142         std::cerr << "state:" << state << ", offset:" << offset << ", buflen:" << buflen << "\n";
143 #endif /* DEBUG */
144         switch (state) {
145         case S_TEXT:
146             if (offset > 0) {
147                 ans = bodyProviderText (buffer, buflen);
148             } else if (isCons (tp)) {
149                 MNode*  a = tp->car ();
150                 nextNode (tp);
151                 ubuf = textSeparator (to_string (a->car ())) + to_string (a->cdr ()) + uCRLF;
152 #ifdef DEBUG
153                 std::cerr << "ubuf:" << omitNonAscii (ubuf.substr (0, 8)) << "\n";
154 #endif /* DEBUG */
155                 ans = bodyProviderText (buffer, buflen);
156             } else {
157                 state = S_FILEHEAD;
158                 ans = bodyProvider (buffer, buflen) - buffer;
159             }
160             break;
161         case S_FILEHEAD:
162             if (offset > 0) {
163                 ans = bodyProviderText (buffer, buflen);
164                 if (offset == 0)
165                     state = S_FILEBODY;
166             } else if (bp < ep) {
167                 ustring  u = filePart_osSafe ((*bp).second);
168                 ubuf = fileSeparator ((*bp).first, u);
169 #ifdef DEBUG
170                 std::cerr << "ubuf:" << omitNonAscii (ubuf.substr (0, 8)) << "\n";
171 #endif /* DEBUG */
172                 ans = bodyProviderText (buffer, buflen);
173                 if (offset == 0)
174                     state = S_FILEBODY;
175             } else {
176                 state = S_TAIL;
177                 ans = bodyProvider (buffer, buflen) - buffer;
178             }
179             break;
180         case S_FILEBODY:
181             if (offset > 0) {
182                 ans = fd.read (buffer, buflen);
183 #ifdef DEBUG
184                 std::cerr << "read\n";
185 #endif /* DEBUG */
186                 if (ans <= 0) {
187                     fd.close ();
188 #ifdef DEBUG
189                     std::cerr << "close\n";
190 #endif /* DEBUG */
191                     offset = 0;
192                     ++ bp;
193                     state = S_FILETERM;
194                     ans = bodyProvider (buffer, buflen) - buffer;
195                 } else {
196                     offset += ans;
197                 }
198             } else {    // offset == 0
199                 ustring  u = filePart_osSafe ((*bp).second);
200 #ifdef DEBUG
201                 std::cerr << "open:" << (*bp).second << "\n";
202 #endif /* DEBUG */
203                 if (fd.openRead ((*bp).second.c_str ())) {
204                     ans = fd.read (buffer, buflen);
205                     if (ans <= 0) {
206                         fd.close ();
207 #ifdef DEBUG
208                         std::cerr << "close\n";
209 #endif /* DEBUG */
210                         ++ bp;
211                         state = S_FILETERM;
212                         ans = bodyProvider (buffer, buflen) - buffer;
213                     } else {
214                         offset = ans;
215                     }
216                 } else {
217 #ifdef DEBUG
218                     std::cerr << "open failed\n";
219 #endif /* DEBUG */
220                     ++ bp;
221                     state = S_FILETERM;
222                     ans = bodyProvider (buffer, buflen) - buffer;
223                 }
224             }
225             break;
226         case S_FILETERM:
227             if (offset > 0) {
228                 ans = bodyProviderText (buffer, buflen);
229             } else {
230                 ubuf = uCRLF;
231 #ifdef DEBUG
232                 std::cerr << "ubuf:CRLF\n";
233 #endif /* DEBUG */
234                 ans = bodyProviderText (buffer, buflen);
235             }
236             if (offset == 0)
237                 state = S_FILEHEAD;
238             break;
239         case S_TAIL:
240             if (offset > 0) {
241                 ans = bodyProviderText (buffer, buflen);
242                 if (offset == 0)
243                     state = S_DONE;
244             } else {
245                 ubuf = tailSeparator ();
246                 ans = bodyProviderText (buffer, buflen);
247                 if (offset == 0)
248                     state = S_DONE;
249             }
250             break;
251         case S_DONE:
252             // 0を返す
253             break;
254         default:;
255         }
256     }
257
258     if (ans > 0 && buflen > ans)
259         return bodyProvider (buffer + ans, buflen - ans);
260 #ifdef DEBUG
261     std::cerr << "bodyProvider ():" << ans << "\n";
262 #endif /* DEBUG */
263     return buffer + ans;
264 }
265
266 ssize_t  NeonPostBodyProvider::bodyProviderText (char* buffer, size_t buflen) {
267     ssize_t  ans = ubuf.length () - offset;
268 #ifdef DEBUG
269     std::cerr << "bodyProviderText: buflen:" << buflen << ", offset:" << offset << ", text:" << omitNonAsciiWord (ubuf.substr (offset, offset + 6)) << "\n";
270 #endif /* DEBUG */
271     if (ans <= buflen) {
272         memcpy (buffer, ubuf.data () + offset, ans);
273         offset = 0;
274     } else {
275         memcpy (buffer, ubuf.data () + offset, buflen);
276         offset += buflen;
277         ans = buflen;
278     }
279     return ans;
280 }
281
282 //============================================================
283 static int  ignoreVerifyFn (void* neon, int failures, const ne_ssl_certificate* cert) {
284 #ifdef DEBUG2
285     std::cerr << "ignoreVerifyFn ()\n";
286 #endif /* DEBUG */
287     return 0;
288 }
289
290 NeonSession::NeonSession (proto_t _proto, const ustring& host, unsigned int port) {
291     const char*  protoName;
292
293     proto = _proto;
294     switch (proto) {
295     case PROTO_HTTP:
296         protoName = "http";
297         break;
298     case PROTO_HTTPS:
299         protoName = "https";
300         break;
301     default:
302         assert (0);
303     }
304     session = ne_session_create (protoName, host.c_str (), port);
305     if (! session)
306         return;
307     if (proto == PROTO_HTTPS)
308         ne_ssl_trust_default_ca (session);
309 #ifdef DEBUG
310     std::cerr << "session flag:"
311               << (ne_get_session_flag (session, NE_SESSFLAG_PERSIST) ? " NE_SESSFLAG_PERSIST" : "")
312               << (ne_get_session_flag (session, NE_SESSFLAG_ICYPROTO) ? " NE_SESSFLAG_ICYPROTO" : "")
313               << (ne_get_session_flag (session, NE_SESSFLAG_SSLv2) ? " NE_SESSFLAG_SSLv2" : "")
314               << (ne_get_session_flag (session, NE_SESSFLAG_RFC4918) ? " NE_SESSFLAG_RFC4918" : "")
315               << (ne_get_session_flag (session, NE_SESSFLAG_CONNAUTH) ? " NE_SESSFLAG_CONNAUTH" : "")
316               << (ne_get_session_flag (session, NE_SESSFLAG_TLS_SNI) ? " NE_SESSFLAG_TLS_SNI" : "")
317               << (ne_get_session_flag (session, NE_SESSFLAG_EXPECT100) ? " NE_SESSFLAG_EXPECT100" : "")
318               << "\n";
319 #endif /* DEBUG */
320 }
321
322 NeonSession::~NeonSession () {
323     ne_session_destroy (session);
324     session = NULL;
325 }
326
327 void  NeonSession::setNoVerify () {
328     if (proto == PROTO_HTTPS)
329         ne_ssl_set_verify (session, ignoreVerifyFn, this);
330 }
331
332 void  NeonSession::setProxy (const ustring& host, int port) {
333     if (checkHostname (host) && port > 0 && port < 65536) {
334 #ifdef DEBUG2
335         std::cerr << "set proxy " << host << ":" << port << "\n";
336 #endif /* DEBUG */
337         ne_session_proxy (session, host.c_str (), port);
338     } else {
339         throw (ustring (CharConst ("bad proxy host.")));
340     }
341 }
342
343 //============================================================
344 void  NeonQuery::closeReq () {
345     if (req) {
346         switch (mode) {
347         case MODE_DISPATCHED:
348             ne_discard_response (req);
349         case MODE_RECEIVED:
350             ne_end_request (req);
351         case MODE_SETUP:;
352         }
353         mode = MODE_SETUP;
354         ne_request_destroy (req);
355         req = NULL;
356     }
357 }
358
359 void  NeonQuery::setIConv (const char* name) {
360     cv_in.reset (new UIConv (kCODE_UTF8, name));
361     cv_out.reset (new UIConv (name, kCODE_UTF8));
362 }
363
364 static ssize_t  qbodyProvider (void* userdata, char* buffer, size_t buflen) {
365     NeonPostBodyProvider*  obj = (NeonPostBodyProvider*)userdata;
366     return obj->bodyProvider (buffer, buflen) - buffer;
367 }
368
369 void  NeonQuery::submit () {
370     FileMacro  fd;
371     ustring  uri = filterPath ();
372     ustring  getQuery = buildGetQuery ();
373
374     errorMsg.resize (0);
375     if (getQuery.length () > 0) {
376         uri.append (CharConst ("?"));
377         uri.append (getQuery);
378     }
379     closeReq ();
380     req = ne_request_create (session, methodStr (), uri.c_str ());
381     buildCookie ();
382     buildHeader ();
383     switch (method) {
384     case METHOD_POST:
385         if (! rawquery.isEmpty ()) {
386             setRawQuery (fd);
387         } else {
388             ustring  data;
389             buildQuery (queryParam (), data);
390             setFormType_urlencoded ();
391             ne_set_request_body_buffer (req, data.c_str (), data.size ());
392         }
393         break;
394     case METHOD_FILE:
395         ne_add_request_header (req, kRES_TYPE, qbody->separatorHeader ().c_str ());
396         ne_set_request_body_provider (req, qbody->calcLength (), qbodyProvider, qbody.get ());
397         break;
398     case METHOD_PUT:
399         if (! rawquery.isEmpty ()) {
400             setRawQuery (fd);
401         }
402         break;
403     default:;
404     }
405     int  rc = ne_begin_request (req);
406     if (rc == NE_OK) {
407         mode = MODE_DISPATCHED;
408     } else {
409         errorMsg.assign (ne_get_error (session));
410 #ifdef DEBUG
411         std::cerr << "error: " << errorMsg << "\n";
412 #endif /* DEBUG */
413     }
414 }
415
416 void  NeonQuery::readBody (MotorOutput* out) {
417     if (mode == MODE_DISPATCHED) {
418         char  buf[65536];
419         ssize_t  s;
420         while ((s = ne_read_response_block (req, buf, 65536)) > 0) {
421             out->out_raw (buf, s);
422         }
423         if (s == 0) {           // success
424             mode = MODE_RECEIVED;
425         } else if (s < 0) {     // error
426             mode = MODE_RECEIVED;
427         }
428     }
429 }
430
431 int  NeonQuery::getStatus () {
432     if (req) {
433         const ne_status*  st = ne_get_status (req);
434         if (st)
435             return st->code;
436     }
437     return 0;                   // error
438 }
439
440 const char*  NeonQuery::methodStr () {
441     switch (method) {
442     case METHOD_GET:
443         return kMETHOD_GET;
444     case METHOD_POST:
445         return kMETHOD_POST;
446     case METHOD_PUT:
447         return kMETHOD_PUT;
448     case METHOD_DELETE:
449         return kMETHOD_DELETE;
450     case METHOD_HEAD:
451         return kMETHOD_HEAD;
452     case METHOD_FILE:
453         return kMETHOD_POST;
454     default:
455         assert (0);
456     }
457 }
458
459 ustring  NeonQuery::buildGetQuery () {
460     ustring  ans;
461
462     if (! isNil (queryParamGet ())) {
463         buildQuery (queryParamGet (), ans);
464     } else if (method == METHOD_GET) {
465         if (! rawquery.isEmpty () && rawquery.isText ()) {
466             ans = rawquery.read ();
467         } else if (! isNil (queryParam ())) {
468             buildQuery (queryParam (), ans);
469         }
470     }
471     return ans;
472 }
473
474 void  NeonQuery::buildQuery (MNode* e, ustring& out) {
475     int  c = 0;
476     MNode*  a;
477
478     if (isCons (e)) {
479         while (e) {
480             a = e->car ();
481             if (isCons (a)) {
482                 if (c > 0)
483                     out.append (uAmp);
484                 out.append (percentEncode (cv (to_string (a->car ()))));
485                 if (! isNil (a->cdr ())) {
486                     out.append (uEq);
487                     out.append (percentEncode (cv (to_string (a->cdr ()))));
488                 }
489                 ++ c;
490             }
491             nextNode (e);
492         }
493     }
494 }
495
496 ustring  NeonQuery::buildMimeSeparator_text (const ustring& name) {
497     ustring  ans;
498     ans.assign (qbody->separatorLine ());
499     ans.append (CharConst ("Content-Disposition: form-data; name=" kQ2)).append (percentEncode (cv (name))).append (CharConst (kQ2 kCRLF kCRLF));
500     return ans;
501 }
502
503 ustring  NeonQuery::buildMimeSeparator_file (const ustring& name, const ustring& filename) {
504     ustring  ans;
505     ans.assign (qbody->separatorLine ());
506     ans.append (CharConst ("Content-Disposition: form-data; name=" kQ2)).append (percentEncode (cv (name))).append (CharConst (kQ2 "; filename=" kQ2)).append (slashEncode (filename)).append (CharConst (kQ2 kCRLF));
507     ans.append (CharConst ("Content-Type: ")).append (mimetype (getExt (filename))).append (CharConst (kCRLF kCRLF));
508     return ans;
509 }
510
511 ustring  NeonQuery::cv (const ustring& src) {
512     if (cv_out.get ()) {
513         return cv_out->cv (src);
514     } else {
515         return src;
516     }
517 }
518
519 ustring  NeonQuery::rcv (const ustring& src) {
520     if (cv_in.get ()) {
521         return cv_in->cv (src);
522     } else {
523         return src;
524     }
525 }
526
527 ustring  NeonQuery::getResponseHeader (const char* name) {
528     const char*  ans = ne_get_response_header (req, name);
529     if (ans)
530         return ustring (ans);
531     else
532         return uEmpty;
533 }
534
535 MNode*  NeonQuery::getResponseHeaderAll () {
536     MNodeList  ans;
537     void*  csr = NULL;
538     const char*  name;
539     const char*  val;
540     do {
541         csr = ne_response_header_iterate (req, csr, &name, &val);
542         if (csr)
543             ans.append (newMNode_cons (newMNode_str (new ustring (name)), newMNode_str (new ustring (val))));
544         else
545             break;
546     } while (1);
547     return ans.release ();
548 }
549
550 ustring  NeonQuery::getResponseCookie (const ustring& name) {
551     parseCookie ();
552     for (int i = 0; i < replyCookie.size (); ++ i) {
553         if (replyCookie[i].key == name)
554             return replyCookie[i].value;
555     }
556     return uEmpty;
557 }
558
559 MNode*  NeonQuery::getResponseCookieAll () {
560     MNodeList  ans;
561     parseCookie ();
562     for (int i = 0; i < replyCookie.size (); ++ i) {
563         ans.append (newMNode_cons (newMNode_str (new ustring (replyCookie[i].key)), newMNode_str (new ustring (replyCookie[i].value))));
564     }
565     return ans.release ();
566 }
567
568 ustring  NeonQuery::filterPath () {
569     return percentEncode_path (omitCtrl (path));
570 }
571
572 void  NeonQuery::buildCookie () {
573     MNode*  e = cookie ();
574     MNode*  a;
575     ustring  data;
576     size_t  off = 0;
577     ustring  u;
578
579     if (isCons (e)) {
580         while (e) {
581             a = e->car ();
582             if (isNil (a)) {
583             } else if (isCons (a)) {
584                 u.assign (cookieencode (to_string (a->car ()))).append (uEq).append (cookieencode (to_string (a->cdr ())));
585                 if (off > 800) {
586                     data.append (CharConst (";" kCRLF " ")).append (u);
587                     off = u.length ();
588                 } else if (off > 0) {
589                     data.append (CharConst ("; ")).append (u);
590                     off += u.length () + 2;
591                 } else {
592                     data.append (u);
593                     off += u.length ();
594                 }
595             } else {
596                 throw (cookie ()->dump_string_short () + uErrorBadParam);
597             }
598             nextNode (e);
599         }
600     }
601     if (data.length () > 0)
602         ne_add_request_header (req, "Cookie", data.c_str ());
603 }
604
605 void  NeonQuery::buildHeader () {
606     MNode*  e = header ();
607     MNode*  a;
608     ustring  key, val;
609     static uregex  re1 ("[\\x00-\\x1f: ]");
610     static uregex  re2 ("[\\x00-\\x1f]");
611
612     if (isCons (e)) {
613         while (e) {
614             a = e->car ();
615             if (isNil (a)) {
616             } else if (a->isCons ()) {
617                 key = to_string (a->car ());
618                 val = to_string (a->cdr ());
619                 if (! checkRe (key, re1) && ! checkRe (val, re2) && key.length () + val.length () < 2048) {
620                     ne_add_request_header (req, key.c_str (), val.c_str ());
621                 }
622             } else {
623                 throw (cookie ()->dump_string_short () + uErrorBadParam);
624             }
625             nextNode (e);
626         }
627     }
628     if (basicID.length () > 0)
629         buildBasicAuthHeader ();
630 }
631
632 void  NeonQuery::buildBasicAuthHeader () {
633     ustring  idpw;
634     ustring  auth;
635     idpw.assign (basicID).append (uColon).append (basicPW);
636     auth.assign (CharConst ("Basic ")).append (base64Encode (idpw.begin (), idpw.end ()));
637     ne_add_request_header (req, "Authorization", auth.c_str ());
638 }
639
640 void  NeonQuery::setFormType () {
641     if (querytype.length () > 0) {
642         ne_add_request_header(req, "Content-type", querytype.c_str ());
643     } else {
644         setFormType_urlencoded ();
645     }
646 }
647
648 void  NeonQuery::setFormType_urlencoded () {
649     ne_add_request_header(req, "Content-type", kMIME_URLENCODED);
650 }
651
652 void  NeonQuery::setFormType_formdata () {
653     ne_add_request_header(req, "Content-type", kMIME_FORMDATA);
654 }
655
656 void  NeonQuery::parseCookie () {
657     if (! replyCookieDone) {
658         replyCookieDone = true;
659         ustring  u = getResponseHeader ("set-cookie");
660         uiterator  b = u.begin ();
661         uiterator  e = u.end ();
662         uregex  re1 ("[=,;]");
663         uregex  re2 ("[,;]");
664         uregex  re3 ("^path=|expires=|domain=|secure");
665         uregex  re4 ("^..., *[0-9]+[^,;]+");
666         umatch  m;
667         while (b < e && usearch (b, e, m, re1)) {       // = | , | ;
668             CookieInfo  info;
669             switch (*m[0].first) {
670             case '=':
671                 info.key.assign (b, m[0].first);
672                 b = m[0].second;
673                 if (usearch (b, e, m, re2)) {   // , | ;
674                     info.value.assign (b, m[0].first);
675                     if (*m[0].first == ';') {
676                         b = m[0].second;
677                         while (b < e) {
678                             if (usearch (b, e, m, re3)) {       // path=,expires=,...
679                                 if (*m[0].first == 'e') {
680                                     if (usearch (b, e, m, re4)) {
681                                         b = m[0].second;
682                                         if (*(b - 1) == ',') {
683                                             skipSpace (b, e);
684                                             break;
685                                         } else {        // ;
686                                             skipSpace (b, e);
687                                         }
688                                     } else if (usearch (b, e, m, re2)) {
689                                         b = m[0].second;
690                                         skipSpace (b, e);
691                                         if (*m[0].first == ',')
692                                             break;
693                                     } else {
694                                         b = e;
695                                     }
696                                 } else if (usearch (b, e, m, re2)) {
697                                     b = m[0].second;
698                                     skipSpace (b, e);
699                                     if (*m[0].first == ',')
700                                         break;
701                                 } else {
702                                     b = e;
703                                 }
704                             } else if (usearch (b, e, m, re2)) {        // , | ;
705                                 b = m[0].second;
706                                 skipSpace (b, e);
707                                 if (*m[0].first == ',')
708                                     break;      // while
709                             } else {
710                                 b = e;
711                             }
712                         }
713                     }
714                 } else {
715                     info.value.assign (b, e);
716                 }
717                 replyCookie.push_back (info);
718                 break;
719             case ';':
720                 b = m[0].second;
721                 skipNextToChar (b, e, ',');
722                 skipSpace (b, e);
723                 break;
724             case ',':
725             default:
726                 b = m[0].second;
727                 skipSpace (b, e);
728             }
729         }
730     }
731 }
732
733 void  NeonQuery::setRawQuery (FileMacro& fd) {
734     setFormType ();
735     if (rawquery.isText ()) {
736         ne_set_request_body_buffer (req, rawquery.param.c_str (), rawquery.param.size ());
737     } else {
738         ustring  src = rawquery.src ();
739         if (src.length () > 0) {
740             if (fd.openRead (src.c_str ())) {
741                 ne_set_request_body_fd (req, fd.fd, 0, fd.size ());
742             }
743         }
744     }
745 }
746
747 //============================================================
748 void  MLNeon::newSession (MlEnv* mlenv) {
749     session.reset (new NeonSession (proto, host, port));
750     // *** User-Agent
751     query.reset (new NeonQuery (session->get (), mlenv));
752 }
753
754 //============================================================
755 /*DOC:
756 ==neon library==
757
758 */
759 /*DOC:
760 ===$neon==
761  ($neon [#http | #https] Host Port
762         [:proxy '(HOSTNAME . PORT)] [:proxy-user '(ID . PASSWORD)]
763         [:on-error FUNCTION]
764         [#no-verify]
765         [SUBFUNCTION...]) -> LAST VALUE
766
767 */
768 //#MFUNC        $neon   ml_neon cMLNeonID
769 MNode*  ml_neon (MNode* cell, MlEnv* mlenv) {
770     MNode*  arg = cell->cdr ();
771     NeonInit.init ();
772     MLNeon  obj (mlenv);
773     MNodePtr  errfn;
774     MNodePtr  ans;
775     MNodePtr  t;
776     bool  b;
777     std::vector<MNode*>  params;
778     std::vector<MNode*>  keywords;
779     MNode*  rest;
780     static paramList  kwlist[] = {
781         {CharConst ("http"), true},             // 0
782         {CharConst ("https"), true},            // 1
783         {CharConst ("proxy"), false},           // 2
784         {CharConst ("proxy-user"), false},      // 3
785         {CharConst ("on-error"), false},        // 4
786         {CharConst ("no-verify"), true},        // 5
787         {NULL, 0, 0}
788     };
789
790     setParams (arg, 2, &params, kwlist, &keywords, &rest);
791     obj.host = eval_str (params[0], mlenv);
792     obj.port = eval_int (params[1], mlenv);
793     if (evkw_bool (0, b))               // 0:http
794         obj.proto = NeonSession::PROTO_HTTP;
795     if (evkw_bool (1, b))               // 1:https
796         obj.proto = NeonSession::PROTO_HTTPS;
797     if (evkw (2, t)) {                  // 2:proxy
798         if (isCons (t ())) {
799             obj.proxyhost = to_string (t ()->car ());
800             obj.proxyport = to_int (t ()->cdr ());
801         } else {
802             throw (t ()->dump_string_short () + uErrorBadParam);
803         }
804     }
805     if (evkw (3, t)) {                  // 3:proxy-user
806         if (isCons (t ())) {
807             obj.proxyid = to_string (t ()->car ());
808             obj.proxypw = to_string (t ()->cdr ());
809         } else {
810             throw (t ()->dump_string_short () + uErrorBadParam);
811         }
812     }
813     evkw (4, errfn);                    // 4:on-error
814     evkw_bool (5, obj.fnoverify);       // 5:no-verify
815
816     if (! checkHostname (obj.host))
817         throw (obj.host + ": bad hostname.");
818     if (obj.port <= 0 || obj.port >= 65536)
819         throw (to_ustring (obj.port) + ": bad port number.");
820
821     //****** proxy-userが未実装
822     obj.newSession (mlenv);
823     if (obj.proxyhost.length () > 0)
824         obj.session->setProxy (obj.proxyhost, obj.proxyport);
825     if (obj.fnoverify)
826         obj.session->setNoVerify ();
827
828     mlenv->setMStack (&obj);
829     try {
830         ans = progn (rest, mlenv);
831     } catch (ustring& msg) {
832         if (errfn ()) {
833             onErrorFn (errfn (), mlenv);
834         } else {
835             throw (msg);
836         }
837     }
838     mlenv->stopBreak (cell->car ());
839
840     return ans.release ();
841 }
842
843 static void  buildFileList (NeonQuery* query, MNode* postFileSerial, MNode* postFileNamed, MNode* postFileStatic, MlEnv* mlenv) {
844     MNode*  e;
845     MNode*  a;
846     ustring  name;
847     ustring  val;
848     ustring  path;
849
850     e = postFileSerial;
851     if (isCons (e)) {
852         while (e) {
853             if ((a = e->car ()) && isCons (a)) {
854                 name = to_string (a->car ());
855                 val = to_string (a->cdr ());
856                 path = mlenv->env->path_store_file (val);
857                 query->qbody->pushFile (name, path);
858             }
859             nextNode (e);
860         }
861     }
862     e = postFileNamed;
863     if (isCons (e)) {
864         while (e) {
865             if ((a = e->car ()) && isCons (a)) {
866                 name = to_string (a->car ());
867                 val = to_string (a->cdr ());
868                 path = mlenv->env->path_storage_file (val);
869                 query->qbody->pushFile (name, path);
870             }
871             nextNode (e);
872         }
873     }
874     e = postFileStatic;
875     if (isCons (e)) {
876         while (e) {
877             if ((a = e->car ()) && isCons (a)) {
878                 name = to_string (a->car ());
879                 val = to_string (a->cdr ());
880                 path = mlenv->env->path_static_file (val);
881                 query->qbody->pushFile (name, path);
882             }
883             nextNode (e);
884         }
885     }
886 }
887
888 /*DOC:
889 ===subfunctions of $neon===
890
891 */
892 static MNode*  checkConsList (MNode* e) {
893     MNodeList  ans;
894     MNode*  a;
895     if (isCons (e)) {
896         while (e) {
897             if (isCons ((a = e->car ())))
898                 ans.append (a);
899             nextNode (e);
900         }
901     }
902     return ans.release ();
903 }
904
905 /*DOC:
906 ====http-request====
907  (http-request PATH [#get] [#post] [#put] [#delete] [#head] [#file]
908         [:basic-user '(ID . PASSWORD)]
909         [:query '((NAME . VALUE) ...)] [:get-query '((NAME . VALUE) ...)] [#no-encode]
910         [:post-file-serial '((NAME . FILE) ...)]
911         [:post-file-named '((NAME . FILE) ...)]
912         [:post-file-static '((NAME . FILE) ...)]
913         [:raw-query TEXT] [:raw-file-serial FILE] [:raw-file-named FILE] [:raw-file-static FILE]
914         [:query-type MIMETYPE]
915         [:cookie '((NAME . VALUE) ...)] [:header '((NAME . VALUE) ...)]
916         [#sjis] [#euc-jp] [:iconv NAME]
917 */
918 //#SFUNC        http-request    ml_neon_http_request
919 MNode*  ml_neon_http_request (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
920     MNode*  arg = cell->cdr ();
921     MLNeon*  obj = (MLNeon*)mobj;
922     MNodePtr  postFileSerial;
923     MNodePtr  postFileNamed;
924     MNodePtr  postFileStatic;
925     bool  f;
926     MNodePtr  t;
927     std::vector<MNode*>  params;
928     std::vector<MNode*>  keywords;
929     static paramList  kwlist[] = {
930         {CharConst ("get"), true},                      // 0
931         {CharConst ("post"), true},                     // 1
932         {CharConst ("put"), true},                      // 2
933         {CharConst ("delete"), true},                   // 3
934         {CharConst ("head"), true},                     // 4
935         {CharConst ("file"), true},                     // 5
936         {CharConst ("basic-user"), false},              // 6
937         {CharConst ("query"), false},                   // 7
938         {CharConst ("get-query"), false},               // 8
939         {CharConst ("post-file-serial"), false},        // 9
940         {CharConst ("post-file-named"), false},         // 10
941         {CharConst ("post-file-static"), false},        // 11
942         {CharConst ("raw-query"), false},               // 12
943         {CharConst ("raw-file-serial"), false},         // 13
944         {CharConst ("raw-file-named"), false},          // 14
945         {CharConst ("raw-file-static"), false},         // 15
946         {CharConst ("query-type"), false},              // 16
947         {CharConst ("cookie"), false},                  // 17
948         {CharConst ("header"), false},                  // 18
949         {CharConst ("sjis"), true},                     // 19
950         {CharConst ("euc-jp"), true},                   // 20
951         {CharConst ("iconv"), false},                   // 21
952         {NULL, 0, 0}
953     };
954
955     setParams (arg, 1, &params, kwlist, &keywords, NULL);
956     obj->query->path = eval_str (params[0], mlenv);
957     if (evkw_bool (0, f))       // 0:get
958         obj->query->method = NeonQuery::METHOD_GET;
959     if (evkw_bool (1, f))       // 1:post
960         obj->query->method = NeonQuery::METHOD_POST;
961     if (evkw_bool (2, f))       // 2:put
962         obj->query->method = NeonQuery::METHOD_PUT;
963     if (evkw_bool (3, f))       // 3:delete
964         obj->query->method = NeonQuery::METHOD_DELETE;
965     if (evkw_bool (4, f))       // 4:head
966         obj->query->method = NeonQuery::METHOD_HEAD;
967     if (evkw_bool (5, f))       // 5:file / pseudo method
968         obj->query->method = NeonQuery::METHOD_FILE;
969     if (evkw (6, t)) {          // 6:basic-user
970         if (isCons (t ())) {
971             obj->query->basicID = to_string (t ()->car ());
972             obj->query->basicPW = to_string (t ()->cdr ());
973         } else {
974             throw (t ()->dump_string_short () + uErrorBadParam);
975         }
976     }
977     if (evkw (7, t))                            // 7:query
978         obj->query->queryParam = checkConsList (t ());
979     if (evkw (8, t))                            // 8:get-query
980         obj->query->queryParamGet = checkConsList (t ());
981     if (evkw (9, t))                            // 9:post-file-serial
982         postFileSerial = checkConsList (t ());
983     if (evkw (10, t))                           // 10:post-file-named
984         postFileNamed = checkConsList (t ());
985     if (evkw (11, t))                           // 11:post-file-static
986         postFileStatic = checkConsList (t ());
987     if (evkw (12, t))           // 12:raw-query
988         obj->query->rawquery.srcText (to_string (t ()));
989     if (evkw (13, t))           // 13:raw-file-serial
990         obj->query->rawquery.srcSerial (to_string (t ()));
991     if (evkw (14, t))           // 14:raw-file-named
992         obj->query->rawquery.srcNamed (to_string (t ()));
993     if (evkw (15, t))           // 15:raw-file-static
994         obj->query->rawquery.srcStatic (to_string (t ()));
995     if (evkw (16, t)) {         // 16:query-type
996         obj->query->querytype = to_string (t ());
997         if (!checkASCII (obj->query->querytype))
998             throw (obj->query->querytype + ustring (CharConst (": bad type")));
999     }
1000     evkw (17, obj->query->cookie);      // 17:cookie
1001     evkw (18, obj->query->header);      // 18:header
1002     if (evkw_bool (19, f)) {            // 19:sjis
1003         obj->query->setIConv ("SHIFT_JIS");
1004     } else if (evkw_bool (20, f)) {     // 20:euc-jp
1005         obj->query->setIConv ("EUC-JP");
1006     } else if (evkw (21, t)) {  // 21:iconv
1007         ustring  code = to_string (t ());
1008         uregex  re ("^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$");
1009         umatch  m;
1010         if (usearch (code, m, re)) {
1011             obj->query->setIConv (code.c_str ());
1012         } else {
1013             throw (ustring (code).append (CharConst (": unknown encoding.")));
1014         }
1015     }
1016
1017     if (obj->query->method == NeonQuery::METHOD_FILE)
1018         buildFileList (obj->query.get (), postFileSerial (), postFileNamed (), postFileStatic (), mlenv);
1019
1020     obj->query->submit ();
1021
1022     return NULL;
1023 }
1024
1025 /*DOC:
1026 ====http-status====
1027  (http-status) -> NUMBER
1028
1029 */
1030 //#SFUNC        http-status     ml_neon_http_status
1031 MNode*  ml_neon_http_status (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1032     MNode*  arg = cell->cdr ();
1033     MLNeon*  obj = (MLNeon*)mobj;
1034     
1035     if (arg)
1036         throw (uErrorWrongNumber);
1037
1038     return newMNode_num (obj->query->getStatus ());
1039 }
1040
1041 /*DOC:
1042 ====http-response====
1043  (http-response) -> STRING
1044
1045 */
1046 //#SFUNC        http-response   ml_neon_http_response
1047 MNode*  ml_neon_http_response (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1048     MNode*  arg = cell->cdr ();
1049     MLNeon*  obj = (MLNeon*)mobj;
1050     ustring  ans;
1051
1052     if (arg)
1053         throw (uErrorWrongNumber);
1054
1055     std::stringstream  ostr;
1056     MotorOutputOStream  out (&ostr);
1057     obj->query->readBody (&out);
1058     return newMNode_str (new ustring (fixUTF8 (obj->query->rcv (ostr.str ()))));
1059 }
1060
1061 /*DOC:
1062 ====http-response-file====
1063  (http-response-file FILENAME) -> NIL
1064
1065 */
1066 //#SFUNC        http-response-file      ml_neon_http_response_file
1067 MNode*  ml_neon_http_response_file (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1068     MNode*  arg = cell->cdr ();
1069     MLNeon*  obj = (MLNeon*)mobj;
1070     ustring  name;
1071     ustring  tgt, tmp;
1072
1073     if (! arg)
1074         throw (uErrorWrongNumber);
1075     name = eval_str (arg->car (), mlenv);
1076     nextNode (arg);
1077     if (arg)
1078         throw (uErrorWrongNumber);
1079
1080     tgt = mlenv->env->path_store_file (name);
1081     tmp.assign (tgt).append (CharConst ("-tmp")).append (to_ustring (getpid ()));
1082     {
1083         std::ofstream  stream;
1084         MotorOutputOStream  out (&stream);
1085         stream.open (tmp.c_str (), std::ios_base::out | std::ios_base::binary);
1086         obj->query->readBody (&out);
1087         stream.close ();
1088         rename (tmp.c_str (), tgt.c_str ());
1089     }
1090
1091     return NULL;
1092 }
1093
1094 /*DOC:
1095 ====http-response-output====
1096  (http-response-output [#continue | :continue BOOL]) -> NIL
1097
1098 */
1099 //#SFUNC        http-response-output    ml_neon_http_response_output
1100 MNode*  ml_neon_http_response_output (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1101     MNode*  arg = cell->cdr ();
1102     MLNeon*  obj = (MLNeon*)mobj;
1103     bool  cflag = false;
1104     std::vector<MNode*>  keywords;
1105     static paramList  kwlist[] = {
1106         {CharConst ("continue"), true},
1107         {NULL, 0, 0}
1108     };
1109
1110     setParams (arg, 0, NULL, kwlist, &keywords, NULL);
1111     if (keywords[0] && eval_bool (keywords[0], mlenv))
1112         cflag = true;
1113
1114     if (! mlenv->env->responseDone)
1115         mlenv->env->standardResponse_html ();
1116     obj->query->readBody (mlenv->env->output);
1117
1118     if (! cflag)
1119         mlenv->breakProg ();
1120
1121     return NULL;
1122 }
1123
1124 /*DOC:
1125 ====get-cookie====
1126  (get-cookie NAME) -> STRING
1127
1128 */
1129 //#SFUNC        get-cookie      ml_neon_get_cookie
1130 MNode*  ml_neon_get_cookie (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1131     MNode*  arg = cell->cdr ();
1132     MLNeon*  obj = (MLNeon*)mobj;
1133     ustring  name;
1134
1135     if (! arg)
1136         throw (uErrorWrongNumber);
1137     name = eval_str (arg->car (), mlenv);
1138     nextNode (arg);
1139     if (arg)
1140         throw (uErrorWrongNumber);
1141
1142     ustring  ans = obj->query->getResponseCookie (name);
1143     if (ans.length () > 0) {
1144         return newMNode_str (new ustring (ans));
1145     } else {
1146         return NULL;
1147     }
1148 }
1149
1150 /*DOC:
1151 ====get-cookie-all====
1152  (get-cookie-all) -> LIST of (NAME . VALUE)
1153
1154 */
1155 //#SFUNC        get-cookie-all  ml_neon_get_cookie_all
1156 MNode*  ml_neon_get_cookie_all (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1157     MNode*  arg = cell->cdr ();
1158     MLNeon*  obj = (MLNeon*)mobj;
1159
1160     if (arg)
1161         throw (uErrorWrongNumber);
1162
1163     return obj->query->getResponseCookieAll ();
1164 }
1165
1166 /*DOC:
1167 ====http-content-type====
1168  (http-content-type) -> STRING
1169
1170 */
1171 //#SFUNC        http-content-type       ml_neon_http_content_type
1172 MNode*  ml_neon_http_content_type (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1173     MNode*  arg = cell->cdr ();
1174     MLNeon*  obj = (MLNeon*)mobj;
1175     ustring  u = obj->query->getResponseHeader ("content-type");
1176
1177     if (u.length () > 0) {
1178         uiterator  b, e, m;
1179         b = u.begin ();
1180         e = u.end ();
1181         if (splitChar (b, e, ';', m)) {
1182             return newMNode_str (new ustring (toLower (b, m)));
1183         } else {
1184             return newMNode_str (new ustring (toLower (b, e)));
1185         }
1186     } else {
1187         return NULL;
1188     }
1189 }
1190
1191 /*DOC:
1192 ====http-get-header====
1193  (http-get-header NAME) -> STRING
1194
1195 */
1196 //#SFUNC        http-get-header ml_neon_http_get_header
1197 MNode*  ml_neon_http_get_header (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1198     MNode*  arg = cell->cdr ();
1199     MLNeon*  obj = (MLNeon*)mobj;
1200     ustring  name;
1201     ustring  u;
1202
1203     if (! arg)
1204         throw (uErrorWrongNumber);
1205     name = eval_str (arg->car (), mlenv);
1206     nextNode (arg);
1207     if (arg)
1208         throw (uErrorWrongNumber);
1209
1210     u = obj->query->getResponseHeader (name.c_str ());
1211     if (u.length () > 0) {
1212         return newMNode_str (new ustring (u));
1213     } else {
1214         return NULL;
1215     }
1216 }
1217
1218 /*DOC:
1219 ====http-get-header-all====
1220  (http-get-header-all) -> LIST of (NAME . VALUE)
1221
1222 */
1223 //#SFUNC        http-get-header-all     ml_neon_http_get_header_all
1224 MNode*  ml_neon_http_get_header_all (MNode* cell, MlEnv* mlenv, MLFunc* mobj) {
1225     MNode*  arg = cell->cdr ();
1226     MLNeon*  obj = (MLNeon*)mobj;
1227
1228     if (arg)
1229         throw (uErrorWrongNumber);
1230
1231     return obj->query->getResponseHeaderAll ();
1232 }
1233