1 /***************************************************************************
2                ulxr_http_protocol.cpp  -  http prootocol
3                              -------------------
4     begin                : Mon May 3 2004
5     copyright            : (C) 2002-2007 by Ewald Arnold
6     email                : ulxmlrpcpp@ewald-arnold.de
7 
8     $Id: ulxr_http_protocol.cpp 1164 2010-01-06 10:03:51Z ewald-arnold $
9 
10  ***************************************************************************/
11 
12 /**************************************************************************
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU Lesser General Public License as
16  * published by the Free Software Foundation; either version 2 of the License,
17  * or (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27  *
28  ***************************************************************************/
29 
30 // #define ULXR_SHOW_TRACE
31 // #define ULXR_DEBUG_OUTPUT
32 // #define ULXR_SHOW_HTTP
33 // #define ULXR_SHOW_READ
34 // #define ULXR_SHOW_WRITE
35 // #define ULXR_SHOW_XML
36 
37 
38 #define ULXR_NEED_EXPORTS
39 #include <ulxmlrpcpp/ulxmlrpcpp.h> // always first
40 
41 #include <cstdlib>
42 #include <cstdio>
43 #include <ctime>
44 #include <sys/stat.h>
45 
46 #include <cstring>
47 
48 #if defined(__BORLANDC__) || defined (_MSC_VER)
49 #include <utility>
50 #endif
51 
52 #include <ulxmlrpcpp/ulxr_http_protocol.h>
53 #include <ulxmlrpcpp/ulxr_tcpip_connection.h>
54 #include <ulxmlrpcpp/ulxr_except.h>
55 #include <ulxmlrpcpp/ulxr_response.h>
56 #include <ulxmlrpcpp/ulxr_call.h>
57 
58 #ifndef ULXR_OMIT_REENTRANT_PROTECTOR
59 #include <ulxmlrpcpp/ulxr_mutex.h>
60 #endif
61 
62 
63 namespace ulxr
64 {
65 
66 struct HttpProtocol::PImpl
67 {
68    CppString    proxy_user;
69    CppString    proxy_pass;
70    CppString    useragent;
71    CppString    header_firstline;
72    CppString    header_buffer;
73    CppString    hostname;
74    unsigned     hostport;
75 
76    bool         useconnect;
77    bool         connected;
78 
79    ConnectorWrapperBase  *connector;
80 
81    bool            bChunkedEncoding;
82    int             chunk_size;
83    bool            chunk_terminated;
84    bool            chunk_in_header;
85    Cpp8BitString   chunk_data;
86    unsigned        chunked_block;
87    unsigned        chunk_body_skip;
88 
89    bool                              bAcceptcookies;
90    std::map<CppString, CppString>    cookies;
91    CppString                         serverCookie;
92    CppString                         clientCookie;
93    std::vector<CppString>            userTempFields;
94    header_property                   headerprops;
95 };
96 
97 
HttpProtocol(const HttpProtocol & prot)98 ULXR_API_IMPL0 HttpProtocol::HttpProtocol(const HttpProtocol &prot)
99   : Protocol(prot)
100   , pimpl(new PImpl)
101 {
102   *pimpl = *prot.pimpl;
103 }
104 
105 
106 ULXR_API_IMPL0
HttpProtocol(Connection * conn,const CppString & hn,unsigned hp)107   HttpProtocol::HttpProtocol(Connection *conn, const CppString &hn, unsigned hp)
108   : Protocol (conn)
109   , pimpl(new PImpl)
110 {
111     pimpl->hostname = hn;
112     pimpl->hostport = hp;
113     ULXR_TRACE(ULXR_PCHAR("HttpProtocol(conn, name, port)"));
114     init();
115 }
116 
117 
118 ULXR_API_IMPL0
HttpProtocol(TcpIpConnection * conn)119   HttpProtocol::HttpProtocol(TcpIpConnection *conn)
120   : Protocol (conn)
121   , pimpl(new PImpl)
122 {
123     pimpl->hostname = conn->getPeerName();
124     pimpl->hostport = conn->getPort();
125     ULXR_TRACE(ULXR_PCHAR("HttpProtocol(conn)"));
126     init();
127 }
128 
129 
~HttpProtocol()130 ULXR_API_IMPL0 HttpProtocol::~HttpProtocol()
131 {
132     ULXR_TRACE(ULXR_PCHAR("~HttpProtocol"));
133     delete pimpl->connector;
134     delete pimpl;
135     pimpl = 0;
136 }
137 
138 
ULXR_API_IMPL(HttpProtocol *)139 ULXR_API_IMPL(HttpProtocol *) HttpProtocol::clone() const
140 {
141     ULXR_TRACE(ULXR_PCHAR("HttpProtocol::clone()"));
142     return new HttpProtocol(*this);
143 }
144 
145 
ULXR_API_IMPL(Protocol *)146 ULXR_API_IMPL(Protocol *) HttpProtocol::detach()
147 {
148     ULXR_TRACE(ULXR_PCHAR("HttpProtocol::detach()"));
149     HttpProtocol *cloneprot = this->clone();
150     cloneprot->setConnection(getConnection()->detach());
151     cloneprot->pimpl->connector = new ConnectorWrapper<HttpProtocol>(cloneprot, &HttpProtocol::doConnect);
152     return cloneprot;  // return previous and running connection
153 }
154 
155 
ULXR_API_IMPL(void)156 ULXR_API_IMPL(void) HttpProtocol::init()
157 {
158     ULXR_TRACE(ULXR_PCHAR("init"));
159     pimpl->connector = new ConnectorWrapper<HttpProtocol>(this, &HttpProtocol::doConnect);
160     getConnection()->setConnector(pimpl->connector);
161     pimpl->useconnect = false;
162     pimpl->connected = false;
163     ULXR_TRACE(ULXR_PCHAR("init"));
164     pimpl->headerprops.clear();
165     pimpl->useragent = ULXR_GET_STRING(ULXR_PACKAGE) + ULXR_PCHAR("/") + ULXR_GET_STRING(ULXR_VERSION);
166     pimpl->userTempFields.clear();
167     pimpl->bAcceptcookies = false;
168     pimpl->bChunkedEncoding = false;
169     pimpl->chunk_data.clear();
170     pimpl->chunk_size = 0;
171     pimpl->chunk_body_skip = 0;
172     setChunkedTransfer(false);
173 }
174 
175 
ULXR_API_IMPL(void)176 ULXR_API_IMPL(void) HttpProtocol::clearHttpInfo()
177 {
178     ULXR_TRACE(ULXR_PCHAR("clearHttpInfo"));
179     pimpl->header_firstline = ULXR_PCHAR("");
180     pimpl->header_buffer = ULXR_PCHAR("");
181     pimpl->headerprops.clear();
182     pimpl->cookies.clear();
183     pimpl->bChunkedEncoding = false;
184     pimpl->chunk_data.clear();
185     pimpl->chunk_size = 0;
186     pimpl->chunk_body_skip = 0;
187     pimpl->chunk_terminated = false;
188     pimpl->chunk_in_header = true;
189 }
190 
191 
ULXR_API_IMPL(void)192 ULXR_API_IMPL(void) HttpProtocol::resetConnection()
193 {
194     ULXR_TRACE(ULXR_PCHAR("resetConnection"));
195     Protocol::resetConnection();
196     clearHttpInfo();
197     //loadCookie(peername);
198 }
199 
200 
ULXR_API_IMPL(void)201 ULXR_API_IMPL(void) HttpProtocol::close()
202 {
203     ULXR_TRACE(ULXR_PCHAR("close"));
204     Protocol::close();
205     pimpl->connected = false;
206 //    storeCookie(peername);
207 }
208 
209 
ULXR_API_IMPL(void)210 ULXR_API_IMPL(void) HttpProtocol::shutdown(int mode)
211 {
212     ULXR_TRACE(ULXR_PCHAR("shutdown"));
213     if (getConnection() != 0)
214       getConnection()->shutdown(mode);
215 }
216 
217 
ULXR_API_IMPL(CppString)218 ULXR_API_IMPL(CppString) HttpProtocol::getHttpProperty(const CppString &in_name) const
219 {
220     ULXR_TRACE(ULXR_PCHAR("getHttpProperty ") << in_name);
221     CppString name = in_name;
222     makeLower(name);
223     header_property::const_iterator it;
224 
225     if ((it = pimpl->headerprops.find(name)) == pimpl->headerprops.end() )
226         throw ConnectionException(NotConformingError,
227                                   ulxr_i18n(ULXR_PCHAR("Http property field not available: "))+name, 400);
228 
229     return (*it).second;
230 }
231 
232 
ULXR_API_IMPL(bool)233 ULXR_API_IMPL(bool) HttpProtocol::hasHttpProperty(const CppString &in_name) const
234 {
235     CppString name = in_name;
236     makeLower(name);
237     bool b = pimpl->headerprops.find(name) != pimpl->headerprops.end();
238     ULXR_TRACE(ULXR_PCHAR("hasHttpProperty: ") << in_name << ULXR_PCHAR(" ") << b);
239     return b;
240 }
241 
242 
ULXR_API_IMPL(void)243 ULXR_API_IMPL(void) HttpProtocol::parseHeaderLine()
244 {
245     ULXR_TRACE(ULXR_PCHAR("parseHeaderLine"));
246 
247     if (pimpl->header_firstline.length() == 0)
248     {
249         pimpl->header_firstline = pimpl->header_buffer;
250         ULXR_DOUT_HTTP(ULXR_PCHAR("firstline: <") << pimpl->header_firstline << ULXR_PCHAR(">"));
251     }
252     else
253     {
254         CppString nm, cont;
255         std::size_t pos = pimpl->header_buffer.find(':');
256         if (pos == CppString::npos)
257         {
258             nm = pimpl->header_buffer;
259             cont = ULXR_PCHAR("");
260         }
261         else
262         {
263             nm = pimpl->header_buffer.substr(0, pos);
264             cont = pimpl->header_buffer.substr(pos+1);
265         }
266 
267         makeLower(nm);
268         cont = stripWS(cont);
269         nm = stripWS(nm);
270         pimpl->headerprops.insert(std::make_pair(nm, cont));
271 
272         if (pimpl->bAcceptcookies && (nm == ULXR_PCHAR("set-cookie")))  // distinguish between cookie / set-cookie?
273           setCookie(cont);
274 
275         else if (pimpl->bAcceptcookies && (nm == ULXR_PCHAR("cookie")))
276           setCookie(cont);
277 
278         ULXR_DOUT_HTTP(ULXR_PCHAR("headerprop: <") << nm
279                        << ULXR_PCHAR("> + <") << cont << ULXR_PCHAR("> "));
280     }
281     pimpl->header_buffer = ULXR_PCHAR("");
282 }
283 
284 
hasClosingProperty()285 bool HttpProtocol::hasClosingProperty()
286 {
287   bool do_close = false;
288   if (hasHttpProperty(ULXR_PCHAR("connection")))
289   {
290       CppString sConnect = getHttpProperty(ULXR_PCHAR("connection"));
291       makeLower(sConnect);
292       if (sConnect == CppString(ULXR_PCHAR("close")))
293           do_close = true;
294   }
295 
296   if (hasHttpProperty(ULXR_PCHAR("proxy-connection")))
297   {
298       CppString sConnect = getHttpProperty(ULXR_PCHAR("proxy-connection"));
299       makeLower(sConnect);
300       if (sConnect == CppString(ULXR_PCHAR("close")))
301           do_close = true;
302   }
303   return do_close;
304 }
305 
306 
ULXR_API_IMPL(bool)307 ULXR_API_IMPL(bool) HttpProtocol::checkContinue()
308 {
309     ULXR_TRACE(ULXR_PCHAR("checkContinue"));
310     CppString head_version;
311     unsigned head_status = 500;
312     CppString head_phrase;
313     splitHeaderLine(head_version, head_status, head_phrase);
314     if (head_status == 100)
315     {
316       ULXR_TRACE(ULXR_PCHAR("Ignoring header 100-Continue"));
317       setConnectionState(ConnStart);
318       return true;
319     }
320     else
321       return false;
322 }
323 
324 
ULXR_API_IMPL(Protocol::State)325 ULXR_API_IMPL(Protocol::State)
326 HttpProtocol::connectionMachine(char * &buffer, long &len)
327 {
328     /*
329       Each invokation of this state machine tries to parse one single
330       http header line of the buffer. If the content of the buffer is too small
331       (no linefeed found) the content is cached in an internal string
332       and used the next time.
333       buffer points to the beginning of the next line at return if
334       a linefeed has been found. In the message body nothing is done.
335     */
336     ULXR_TRACE(ULXR_PCHAR("connectionMachine with ") << len << ULXR_PCHAR(" bytes"));
337     if (len == 0 || buffer == 0)
338         return getConnectionState();
339 
340     char *chunk_cursor = buffer;
341     char *chunk_start = buffer;
342 
343     while (len > 0)
344     {
345         const unsigned state = getConnectionState();
346         switch (state)
347         {
348         case ConnStart:
349             setConnectionState(ConnHeaderLine);
350             clearHttpInfo();
351         break;
352 
353         case ConnPendingCR:
354             if (*buffer == '\n') // CR+LF
355             {
356                 --len;
357                 ++buffer;
358             }
359 
360             if (pimpl->header_buffer.length() == 0)
361                 setConnectionState(ConnSwitchToBody);
362             else
363                 setConnectionState(ConnPendingHeaderLine);
364         break;
365 
366         case ConnPendingHeaderLine:
367             if (pimpl->header_buffer.length() == 0)
368                 setConnectionState(ConnSwitchToBody);
369 
370             else if (*buffer != ' ')  // continuation line of current header field?
371             {
372                 parseHeaderLine();
373                 setConnectionState(ConnHeaderLine);
374             }
375             else
376               setConnectionState(ConnHeaderLine);
377         break;
378 
379         case ConnHeaderLine:
380 //            ULXR_TRACE(ULXR_PCHAR("ConnHeaderLine:"));
381 
382             if (*buffer == '\r')
383                 setConnectionState(ConnPendingCR);
384 
385             else if (*buffer == '\n')
386             {
387                 if (pimpl->header_buffer.length() == 0)
388                     setConnectionState(ConnSwitchToBody);
389                 else
390                     setConnectionState(ConnPendingHeaderLine);
391             }
392 
393             else
394                 pimpl->header_buffer += *buffer;
395 
396             ++buffer;
397             --len;
398         break;
399 
400         case ConnSwitchToBody:
401           machine_switchToBody(buffer, len, chunk_start, chunk_cursor);
402         break;
403 
404         case ConnChunkHeader:
405         {
406           ULXR_TRACE(ULXR_PCHAR("ConnChunkHeader:"));
407 
408           char c = *buffer;
409           if (c != '\n' && c != '\r')
410             pimpl->chunk_data += c;
411 
412           ++buffer;
413           --len;
414 
415           if (c == '\n')
416           {
417             char* pStop;
418             pimpl->chunk_size = std::strtol(pimpl->chunk_data.c_str(), &pStop, 16);
419             ULXR_TRACE(ULXR_PCHAR("chunk with ")
420                        << pimpl->chunk_size
421                        << ULXR_PCHAR(" bytes announced"));
422 
423             if (   *pStop != ' '
424                 && *pStop != 0
425                 && *pStop != ';')
426             {
427                 setConnectionState(ConnError);
428                 throw ConnectionException(SystemError, ulxr_i18n(ULXR_PCHAR("chunk size is followed by a bad character: ")) + *pStop, 500);
429             }
430 
431             if (getContentLength() > 0)
432               setRemainingContentLength(getRemainingContentLength() - pimpl->chunk_size);
433             else if(getContentLength() == 0)
434               setRemainingContentLength(pimpl->chunk_size);
435 
436             if (pimpl->chunk_size < 1)  // chunk size == 0 terminates data block
437             {
438               pimpl->chunk_in_header = false;
439               setConnectionState(State(ConnHeaderLine));
440             }
441             else
442               setConnectionState(State(ConnChunkBody));
443           }
444 
445           if (len <= 0)
446           {
447             len = chunk_cursor - chunk_start;
448             buffer = chunk_start;
449             if (len != 0)
450               return ConnBody;  // fake regular body
451             else
452               return State(ConnChunkHeader);
453           }
454         }
455         break;
456 
457         case ConnChunkBodySkip:
458           buffer++;
459           len--;
460           if (--pimpl->chunk_body_skip <= 0)
461           {
462             pimpl->chunk_data.clear();
463             setConnectionState(State(ConnChunkHeader));
464           }
465         break;
466 
467         case ConnChunkBody:
468           ULXR_TRACE(ULXR_PCHAR("ConnChunkBody:"));
469           while (pimpl->chunk_size > 0 && len > 0)
470           {
471             *chunk_cursor++ = *buffer++;
472             --pimpl->chunk_size;
473             --len;
474           }
475 
476           if (pimpl->chunk_size <= 0)
477           {
478             pimpl->chunk_body_skip = 2;
479             setConnectionState(State(ConnChunkBodySkip));
480           }
481         break;
482 
483         case ConnChunkTerminated:
484             ULXR_TRACE(ULXR_PCHAR("ConnChunkTerminated:"));
485             return State(ConnChunkTerminated);
486         /*break; */
487 
488         case ConnBody:
489             ULXR_TRACE(ULXR_PCHAR("ConnBody:"));
490             return ConnBody;
491         /*break; */
492 
493         case ConnError:
494             ULXR_TRACE(ULXR_PCHAR("ConnError:"));
495             return ConnError;
496         /*break; */
497 
498         default:
499             setConnectionState(ConnError);
500             throw ConnectionException(SystemError, ulxr_i18n(ULXR_PCHAR("connectionMachine(): unknown state")), 500);
501         }
502     }
503 
504     if (getConnectionState() == ConnSwitchToBody)
505       machine_switchToBody(buffer, len, chunk_start, chunk_cursor);
506 
507     ULXR_TRACE(ULXR_PCHAR("/connectionMachine"));
508 
509     if (pimpl->bChunkedEncoding)
510     {
511       len = chunk_cursor - chunk_start;
512       buffer = chunk_start;
513       if (len != 0)
514         return ConnBody;
515     }
516 
517     return getConnectionState();
518 }
519 
520 
machine_switchToBody(char * & buffer,long & len,char * & chunk_start,char * & chunk_cursor)521 void HttpProtocol::machine_switchToBody(char * &buffer,
522                                         long   &len,
523                                         char * &chunk_start,
524                                         char * &chunk_cursor)
525 {
526   ULXR_TRACE(ULXR_PCHAR("ConnSwitchToBody:"));
527   if (pimpl->chunk_in_header)
528   {
529     if (!checkContinue())
530     {
531       if (hasHttpProperty(ULXR_PCHAR("transfer-encoding")))
532       {
533           CppString sEncoding = getHttpProperty(ULXR_PCHAR("transfer-encoding"));
534           if (sEncoding == ULXR_PCHAR("chunked"))
535           {
536               setRemainingContentLength(-1);
537               setContentLength(-1);
538               ULXR_TRACE(ULXR_PCHAR("have chunked transfer encoding"));
539               pimpl->bChunkedEncoding = true;
540               pimpl->chunk_size = 0;
541               pimpl->chunk_data.clear();
542           }
543       }
544 
545       if (!pimpl->bChunkedEncoding)
546       {
547         if (hasHttpProperty(ULXR_PCHAR("content-length")))
548         {
549             determineContentLength();
550 
551             ULXR_TRACE(ULXR_PCHAR("content_length: ") << getContentLength());
552             ULXR_TRACE(ULXR_PCHAR("len: ") << len);
553 
554             if (getContentLength() >= 0)
555                 setRemainingContentLength(getContentLength() - len);
556         }
557         setConnectionState(ConnBody);
558       }
559       else
560         setConnectionState(State(ConnChunkHeader));
561     }
562   }
563   else
564   {
565     len = chunk_cursor - chunk_start;
566     buffer = chunk_start;
567     setConnectionState(State(ConnChunkTerminated));
568     pimpl->chunk_terminated = true;
569   }
570 
571   if (hasClosingProperty())
572     setPersistent(false);
573   else
574     setPersistent(true);
575 }
576 
577 
ULXR_API_IMPL(bool)578 ULXR_API_IMPL(bool) HttpProtocol::hasBytesToRead() const
579 {
580   bool b = false;
581   if (pimpl->bChunkedEncoding)
582     b = !pimpl->chunk_terminated;
583   else
584     b = getRemainingContentLength() != 0;
585 
586   ULXR_TRACE(ULXR_PCHAR("hasBytesToRead: " << b));
587   return b;
588 }
589 
590 
ULXR_API_IMPL(void)591 ULXR_API_IMPL(void) HttpProtocol::determineContentLength()
592 {
593     ULXR_TRACE(ULXR_PCHAR("determineContentLength"));
594 
595     header_property::iterator it;
596     if ((it = pimpl->headerprops.find(ULXR_PCHAR("content-length"))) != pimpl->headerprops.end() )
597     {
598         ULXR_TRACE(ULXR_PCHAR(" content-length: ") << (*it).second);
599         setContentLength(ulxr_atoi(getLatin1((*it).second).c_str()));
600         ULXR_TRACE(ULXR_PCHAR(" length: ") << getContentLength());
601     }
602     else
603     {
604       if (pimpl->bChunkedEncoding)
605         setContentLength(0); // set with next chunk header
606       else
607         throw ConnectionException(NotConformingError,
608                                   ulxr_i18n(ULXR_PCHAR("Content-Length of message not available")), 411);
609 
610     }
611 
612     setRemainingContentLength(getContentLength());
613     ULXR_TRACE(ULXR_PCHAR(" content_length: ") << getContentLength());
614 }
615 
616 
ULXR_API_IMPL(void)617 ULXR_API_IMPL(void)
618 HttpProtocol::sendResponseHeader(int code,
619                                  const CppString &phrase,
620                                  const CppString &type,
621                                  unsigned long len,
622                                  bool wbxml_mode)
623 {
624     // doConnect(); must already be pimpl->connected
625 
626     ULXR_TRACE(ULXR_PCHAR("sendResponseHeader"));
627     char stat[40];
628     ulxr_sprintf(stat, "%d", code );
629 
630     char contlen[40];
631     ulxr_sprintf(contlen, "%ld", len );
632 
633     CppString ps = phrase;
634 
635     std::size_t pos = 0;
636     while ((pos = ps.find('\n', pos)) != CppString::npos)
637     {
638         ps.replace(pos, 1, ULXR_PCHAR(" "));
639         pos += 1;
640     }
641 
642     pos = 0;
643     while ((pos = ps.find(ULXR_CHAR('\r'), pos)) != CppString::npos)
644     {
645         ps.replace(pos, 1, ULXR_PCHAR(" "));
646         pos += 1;
647     }
648 
649     CppString http_str = (CppString) ULXR_PCHAR("HTTP/1.1 ")
650                          + ULXR_GET_STRING(stat) + ULXR_PCHAR(" ") + ps
651                          + ULXR_PCHAR("\r\n");
652 
653     if (!isPersistent())
654         http_str += ULXR_PCHAR("Connection: Close\r\n");
655     else
656         http_str += ULXR_PCHAR("Proxy-Connection: Keep-Alive\r\n");
657 
658     if (len != 0 && type.length() != 0)
659         http_str  += ULXR_PCHAR("Content-Type: ") + type + ULXR_PCHAR("\r\n");
660 
661     for (unsigned i = 0; i < pimpl->userTempFields.size(); ++i)
662         http_str += pimpl->userTempFields[i] + ULXR_CHAR("\r\n");
663     pimpl->userTempFields.clear();
664 
665     if (hasServerCookie())
666       http_str += ULXR_PCHAR("Set-Cookie: ") + getServerCookie() + ULXR_PCHAR("\r\n");
667 
668     if (isChunkedTransfer())
669     {
670       http_str += ULXR_PCHAR("Transfer-Encoding: chunked\r\n");
671 #ifdef ULXR_DEBUG_OUTPUT
672       http_str += ULXR_PCHAR("X-Content-Length: ") + ULXR_GET_STRING(contlen) + ULXR_PCHAR("\r\n");
673 #endif
674     }
675     else
676       http_str += ULXR_PCHAR("Content-Length: ") + ULXR_GET_STRING(contlen) + ULXR_PCHAR("\r\n");
677 
678     if (!wbxml_mode)
679     {
680         http_str += ULXR_PCHAR("X-Powered-By: ") + getUserAgent() + ULXR_PCHAR("\r\n")
681                     + ULXR_PCHAR("Server: ") + pimpl->hostname + ULXR_PCHAR("\r\n")
682                     + ULXR_PCHAR("Date: ") + getDateStr() + ULXR_PCHAR("\r\n");
683     }
684 
685     http_str += ULXR_PCHAR("\r\n");    // empty line at end of header
686 
687     ULXR_DOUT_HTTP(ULXR_PCHAR("resp: \n") << http_str.c_str());
688 
689 #ifdef ULXR_UNICODE
690     Cpp8BitString utf = unicodeToUtf8(http_str);
691     writeRaw(utf.data(), utf.length());
692 #else
693     writeRaw(http_str.data(), http_str.length());
694 #endif
695 }
696 
697 
ULXR_API_IMPL(void)698 ULXR_API_IMPL(void) HttpProtocol::enableConnect(bool enable)
699 {
700   ULXR_TRACE(ULXR_PCHAR("enableConnect ") << enable);
701   pimpl->useconnect = enable;
702 }
703 
704 
ULXR_API_IMPL(bool)705 ULXR_API_IMPL(bool) HttpProtocol::isConnectEnabled() const
706 {
707   ULXR_TRACE(ULXR_PCHAR("isConnectEnabled ") << pimpl->useconnect);
708   return pimpl->useconnect;
709 }
710 
711 
ULXR_API_IMPL(bool)712 ULXR_API_IMPL(bool) HttpProtocol::isConnected() const
713 {
714   ULXR_TRACE(ULXR_PCHAR("ispimpl->connected ") << pimpl->connected);
715   return pimpl->connected;
716 }
717 
718 
ULXR_API_IMPL(void)719 ULXR_API_IMPL(void) HttpProtocol::awaitConnect()
720 {
721     ULXR_TRACE(ULXR_PCHAR("awaitConnect"));
722 
723     char buffer[ULXR_RECV_BUFFER_SIZE];
724     char *buff_ptr;
725     bool done = false;
726     long readed;
727     while (!done && hasBytesToRead()
728                 && ((readed = readRaw(buffer, sizeof(buffer))) > 0) )
729     {
730       buff_ptr = buffer;
731       ULXR_TRACE(ULXR_PCHAR("loop"));
732 
733       if (readed > 0)
734       {
735         State state = connectionMachine(buff_ptr, readed);
736         if (state == ConnError)
737         {
738           ULXR_TRACE(ULXR_PCHAR("ConnError"));
739           done = true;
740           throw ConnectionException(TransportError, ulxr_i18n(ULXR_PCHAR("network problem occured")), 400);
741         }
742 
743         else if (state == ConnSwitchToBody)
744         {
745           ULXR_TRACE(ULXR_PCHAR("ConnSwitchToBody"));
746           done = true;
747         }
748 
749         else if (state == ConnBody)
750         {
751           ULXR_TRACE(ULXR_PCHAR("ConnBody"));
752           done = true;
753         }
754       }
755     }
756 
757     CppString head_version;
758     unsigned head_status = 500;
759     CppString head_phrase = ULXR_PCHAR("Internal error");
760     splitHeaderLine(head_version, head_status, head_phrase);
761 
762     if (head_status != 200)
763       throw ConnectionException(TransportError, head_phrase, head_status);
764 
765     pimpl->connected = true;
766     ULXR_TRACE(ULXR_PCHAR("awaitConnect: pimpl->connected"));
767 }
768 
769 
ULXR_API_IMPL(void)770 ULXR_API_IMPL(void) HttpProtocol::tryConnect()
771 {
772     ULXR_TRACE(ULXR_PCHAR("performConnect"));
773 
774     char ports[40];
775     ulxr_sprintf(ports, ":%d", pimpl->hostport);
776     CppString resource = pimpl->hostname + ULXR_GET_STRING(ports);
777     CppString http_str = ULXR_PCHAR("CONNECT ") + resource + ULXR_PCHAR(" HTTP/1.1\r\n");
778 
779     http_str += ULXR_PCHAR("User-Agent: ") + getUserAgent() + ULXR_PCHAR("\r\n");
780     http_str += ULXR_PCHAR("Proxy-Connection: Keep-Alive\r\n");
781     http_str += ULXR_PCHAR("Host: ") + pimpl->hostname + ULXR_PCHAR("\r\n");
782 
783     if (pimpl->proxy_user.length() + pimpl->proxy_pass.length() != 0)
784        http_str += ULXR_PCHAR("Proxy-Authorization: Basic ")
785                    + encodeBase64(pimpl->proxy_user + ULXR_PCHAR(":") + pimpl->proxy_pass);
786 
787     http_str += ULXR_PCHAR("\r\n");    // empty line at end of header
788 
789     ULXR_DOUT_HTTP(ULXR_PCHAR("connect: \n") << http_str.c_str());
790 
791 #ifdef ULXR_UNICODE
792     Cpp8BitString utf = unicodeToUtf8(http_str);
793     writeRaw(utf.data(), utf.length());
794 #else
795     writeRaw(http_str.data(), http_str.length());
796 #endif
797 }
798 
799 
ULXR_API_IMPL(void)800 ULXR_API_IMPL(void) HttpProtocol::doConnect()
801 {
802     if (isConnectEnabled() && !isConnected())
803     {
804       resetConnection();
805       tryConnect();
806       awaitConnect();
807       resetConnection();
808     }
809 }
810 
811 
ULXR_API_IMPL(void)812 ULXR_API_IMPL(void)
813 HttpProtocol::sendRequestHeader(const CppString &method,
814                                 const CppString &in_resource,
815                                 const CppString &type,
816                                 unsigned long len,
817                                 bool wbxml_mode)
818 {
819     doConnect();
820     pimpl->bChunkedEncoding = false;
821     ULXR_TRACE(ULXR_PCHAR("sendRequestHeader"));
822     char contlen[40];
823     ulxr_sprintf(contlen, "%ld", len );
824 
825     char ports[40];
826     ulxr_sprintf(ports, "%d", pimpl->hostport);
827     CppString resource = ULXR_PCHAR("http://") + pimpl->hostname + ULXR_PCHAR(":") + ULXR_GET_STRING(ports) + in_resource;
828     CppString http_str = method + ULXR_PCHAR(" ") + resource + ULXR_PCHAR(" HTTP/1.1\r\n");
829     http_str += ULXR_PCHAR("Host: ") + pimpl->hostname + ULXR_PCHAR("\r\n");
830 
831     if(!wbxml_mode)
832         http_str += ULXR_PCHAR("User-Agent: ") + getUserAgent() + ULXR_PCHAR("\r\n");
833 
834     if (pimpl->proxy_user.length() + pimpl->proxy_pass.length() != 0)
835        http_str += ULXR_PCHAR("Proxy-Authorization: Basic ")
836                    + encodeBase64(pimpl->proxy_user + ULXR_PCHAR(":") + pimpl->proxy_pass);
837 
838     if (!isPersistent())
839         http_str += ULXR_PCHAR("Connection: Close\r\n");
840     else
841         http_str += ULXR_PCHAR("Proxy-Connection: Keep-Alive\r\n");
842 
843     if (len != 0 && type.length() != 0)
844         http_str += ULXR_PCHAR("Content-Type: ") + type + ULXR_PCHAR("\r\n");
845 
846     for (unsigned i = 0; i < pimpl->userTempFields.size(); ++i)
847         http_str += pimpl->userTempFields[i] + ULXR_CHAR("\r\n");
848     pimpl->userTempFields.clear();
849 
850     if(!wbxml_mode)
851       http_str += ULXR_PCHAR("Date: ") + getDateStr() + ULXR_PCHAR("\r\n");
852 
853     if (isChunkedTransfer())
854     {
855       http_str += ULXR_PCHAR("Transfer-Encoding: chunked\r\n");
856 #ifdef ULXR_DEBUG_OUTPUT
857       http_str += ULXR_PCHAR("X-Content-Length: ") + ULXR_GET_STRING(contlen) + ULXR_PCHAR("\r\n");
858 #endif
859     }
860     else
861       http_str += ULXR_PCHAR("Content-Length: ") + ULXR_GET_STRING(contlen) + ULXR_PCHAR("\r\n");
862 
863     if (hasClientCookie())
864       http_str += ULXR_PCHAR("Cookie: ") + getClientCookie() + ULXR_PCHAR("\r\n");
865 
866     http_str += ULXR_PCHAR("\r\n");    // empty line at end of header
867 
868     ULXR_DOUT_HTTP(ULXR_PCHAR("req: \n") << http_str.c_str());
869 
870 #ifdef ULXR_UNICODE
871     Cpp8BitString utf = unicodeToUtf8(http_str);
872     writeRaw(utf.data(), utf.length());
873 #else
874     writeRaw(http_str.data(), http_str.length());
875 #endif
876 };
877 
878 
ULXR_API_IMPL(CppString)879 ULXR_API_IMPL(CppString) HttpProtocol::getDateStr()
880 {
881     ULXR_TRACE(ULXR_PCHAR("getDateStr"));
882     time_t tm;
883     time(&tm);
884 
885 #ifndef HAVE_CTIME_R
886 
887 #ifndef ULXR_OMIT_REENTRANT_PROTECTOR   // todo: optionally replace with ctime_r
888     Mutex::Locker lock(ctimeMutex);
889 #endif
890 
891     char * ct = ulxr_ctime(&tm);
892     CppString s = ULXR_GET_STRING(ct);  // "\n" already included!
893 
894 #else
895 
896     char buff[40];
897     char * ct = ::ctime_r(&tm, buff);
898     CppString s = ULXR_GET_STRING(ct);  // "\n" already included!
899 
900 #endif
901 
902     s.erase(s.length()-1);              // remove it
903     return s;
904 }
905 
906 
ULXR_API_IMPL(void)907 ULXR_API_IMPL(void)
908 HttpProtocol::sendNegativeResponse(int status,
909                                    const CppString &phrase,
910                                    const CppString &info)
911 {
912     ULXR_TRACE(ULXR_PCHAR("sendNegativeResponse"));
913 
914     // doConnect(); must already be pimpl->connected
915 
916     char stat[40];
917     ulxr_sprintf(stat, "%d", status );
918 
919     CppString msg = ulxr_i18n(ULXR_PCHAR("<html>")
920                          ULXR_PCHAR("<head><title>Error occured</title></head>")
921                          ULXR_PCHAR("<body>")
922                          ULXR_PCHAR("<b>Sorry, error occured: ")) + ULXR_GET_STRING(stat)
923                     + ULXR_PCHAR(", ") + phrase;
924 
925     if (info.length() != 0)
926         msg += ULXR_PCHAR("<br />") + info;
927 
928     msg += ulxr_i18n(ULXR_PCHAR("</b>")
929                 ULXR_PCHAR("<hr /><p>")
930                 ULXR_PCHAR("This cute little server is powered by")
931                 ULXR_PCHAR(" <a href=\"http://ulxmlrpcpp.sourceforge.net\">"));
932 
933     msg += ULXR_GET_STRING(ULXR_PACKAGE)
934            + ULXR_PCHAR("/v") + ULXR_GET_STRING(ULXR_VERSION)
935            + ULXR_PCHAR("</a>")
936            + ULXR_PCHAR("</p>")
937            ULXR_PCHAR("</body>")
938            ULXR_PCHAR("</html>");
939 
940     ULXR_DOUT_RESP(ULXR_PCHAR("msg:\n") << msg);
941 
942 #ifdef ULXR_UNICODE
943     Cpp8BitString utf = unicodeToUtf8(msg);
944     sendResponseHeader(status, phrase, ULXR_PCHAR("text/html"), utf.length());
945     writeRaw(utf.data(), utf.length());
946 #else
947     sendResponseHeader(status, phrase, ULXR_PCHAR("text/html"), msg.length());
948     writeRaw(msg.data(), msg.length());
949 #endif
950 }
951 
952 
ULXR_API_IMPL(void)953 ULXR_API_IMPL(void) HttpProtocol::sendRpcResponse(const MethodResponse &resp, bool wbxml_mode)
954 {
955     ULXR_TRACE(ULXR_PCHAR("sendRpcResponse"));
956 
957     // doConnect(); must already be pimpl->connected
958 
959     if (wbxml_mode)
960     {
961         std::string xml = resp.getWbXml();
962         ULXR_DOUT_XML(binaryDebugOutput(xml));
963         sendResponseHeader(200, ULXR_PCHAR("OK"), ULXR_PCHAR("application/x-wbxml-ulxr"), xml.length(), wbxml_mode);
964         writeBody(xml.data(), xml.length());
965     }
966     else
967     {
968         CppString xml = resp.getXml(0)+ULXR_PCHAR("\n");
969         ULXR_DOUT_XML(xml);
970 
971 #ifdef ULXR_UNICODE
972         Cpp8BitString utf = unicodeToUtf8(xml);
973         sendResponseHeader(200, ULXR_PCHAR("OK"), ULXR_PCHAR("text/xml"), utf.length(), wbxml_mode);
974         writeBody(utf.data(), utf.length());
975 #else
976         sendResponseHeader(200, ULXR_PCHAR("OK"), ULXR_PCHAR("text/xml"), xml.length(), wbxml_mode);
977         writeBody(xml.data(), xml.length());
978 #endif
979 
980     }
981 }
982 
ULXR_API_IMPL(void)983 ULXR_API_IMPL(void) HttpProtocol::writeChunk(const char *data, unsigned long len)
984 {
985   if (!isChunkedTransfer())
986     throw ConnectionException(NotConformingError,
987                               ulxr_i18n(ULXR_PCHAR("Protocol is not prepared for chunked encoding: ")), 400);
988 
989   if (len != 0)
990   {
991     char stat[40];
992     ulxr_sprintf(stat, "%lx", len);
993     ULXR_TRACE(ULXR_PCHAR("HttpProtocol::writeChunk() chunk with 0x")
994                << ULXR_GET_STRING(stat)
995                << ULXR_PCHAR(" bytes"));
996     writeRaw(stat, strlen(stat));
997     writeRaw("\r\n", 2);
998     writeRaw(data, len);
999     writeRaw("\r\n", 2);
1000   }
1001   else
1002   {
1003     ULXR_TRACE(ULXR_PCHAR("HttpProtocol::writeChunk() last chunk with 0 bytes"));
1004     writeRaw("0\r\n\r\n", 5);  // terminator
1005   }
1006 }
1007 
1008 
ULXR_API_IMPL(void)1009 ULXR_API_IMPL(void) HttpProtocol::writeBody(const char *data, unsigned long len)
1010 {
1011   if (!isChunkedTransfer())
1012     writeRaw(data, len);
1013   else
1014   {
1015     writeChunk(data, len);
1016     writeChunk(data, 0);
1017   }
1018 }
1019 
1020 
ULXR_API_IMPL(void)1021 ULXR_API_IMPL(void) HttpProtocol::sendRpcCall(const MethodCall &call,
1022                                            const CppString &resource,
1023                                            bool wbxml_mode)
1024 {
1025     ULXR_TRACE(ULXR_PCHAR("sendRpcCall"));
1026     doConnect();
1027 
1028     if (wbxml_mode)
1029     {
1030         std::string xml = call.getWbXml();
1031         ULXR_DOUT_XML(binaryDebugOutput(xml));
1032         sendRequestHeader(ULXR_PCHAR("POST"), resource, ULXR_PCHAR("application/x-wbxml-ulxr"), xml.length(), wbxml_mode);
1033         writeBody(xml.data(), xml.length());
1034     }
1035     else
1036     {
1037         CppString xml = call.getXml(0)+ULXR_PCHAR("\n");
1038         ULXR_DOUT_XML(xml);
1039 
1040 #ifdef ULXR_UNICODE
1041         Cpp8BitString utf = unicodeToUtf8(xml);
1042         sendRequestHeader(ULXR_PCHAR("POST"), resource, ULXR_PCHAR("text/xml"), utf.length(), wbxml_mode);
1043         writeBody(utf.data(), utf.length());
1044 #else
1045         sendRequestHeader(ULXR_PCHAR("POST"), resource, ULXR_PCHAR("text/xml"), xml.length(), wbxml_mode);
1046         writeBody(xml.data(), xml.length());
1047 #endif
1048 
1049     }
1050 };
1051 
1052 
ULXR_API_IMPL(bool)1053 ULXR_API_IMPL(bool) HttpProtocol::responseStatus(CppString &phrase) const
1054 {
1055     ULXR_TRACE(ULXR_PCHAR("responseStatus"));
1056 
1057     CppString s = stripWS(getFirstHeaderLine());
1058     if (s.length() == 0)
1059     {
1060         s = ulxr_i18n(ULXR_PCHAR("No connection status available"));
1061         return false;
1062     }
1063 
1064     std::size_t pos = s.find(' ');
1065     if (pos != CppString::npos)  // skip version
1066         s.erase(0, pos+1);
1067     else
1068         s = ULXR_PCHAR("");
1069 
1070     CppString stat;
1071     s = stripWS(s);
1072     pos = s.find(ULXR_CHAR(' '));
1073     if (pos != CppString::npos)
1074     {
1075         stat = s.substr(0, pos);
1076         s.erase(0, pos+1);
1077     }
1078     else
1079     {
1080         stat = s;
1081         s = ULXR_PCHAR("");
1082     }
1083 
1084     phrase = stripWS(s);
1085 
1086     return stat == ULXR_PCHAR("200");
1087 }
1088 
1089 
ULXR_API_IMPL(bool)1090 ULXR_API_IMPL(bool) HttpProtocol::determineClosing(const CppString &http_ver)
1091 {
1092     ULXR_TRACE(ULXR_PCHAR("determineClosing"));
1093     if (   http_ver == ULXR_PCHAR("0.9")
1094             || http_ver == ULXR_PCHAR("1.0"))
1095     {
1096         if (hasHttpProperty(ULXR_PCHAR("connection")))
1097         {
1098             CppString s = getHttpProperty(ULXR_PCHAR("connection"));
1099             makeLower(s);
1100             return !(s == ULXR_PCHAR("keep-alive"));
1101         }
1102         ULXR_TRACE(ULXR_PCHAR("determineClosing: true"));
1103         return true;  // close by default
1104     }
1105     else  // 1.1 and later
1106     {
1107         return hasClosingProperty();
1108 /*
1109         ULXR_TRACE(ULXR_PCHAR("determineClosing: false"));
1110         return false;  // keep open by default
1111 */
1112     }
1113     /*return true; avoids warning */
1114 }
1115 
1116 
ULXR_API_IMPL(bool)1117 ULXR_API_IMPL(bool) HttpProtocol::getUserPass(CppString &user,
1118         CppString &pass) const
1119 {
1120     ULXR_TRACE(ULXR_PCHAR("getUserPass"));
1121     user = ULXR_PCHAR("");
1122     pass = ULXR_PCHAR("");
1123 
1124     if (hasHttpProperty(ULXR_PCHAR("authorization")) )
1125     {
1126         CppString auth = getHttpProperty(ULXR_PCHAR("authorization"));
1127 
1128         ULXR_TRACE(ULXR_PCHAR("getUserPass: ") + auth);
1129         ULXR_TRACE(ULXR_PCHAR("getUserPass: basic?"));
1130 
1131         // only know basic auth
1132         CppString auth_id = auth.substr(0, 6);
1133         makeLower(auth_id);
1134         if (auth_id != ULXR_PCHAR("basic "))
1135             return false;
1136 
1137         auth.erase(0, 6);
1138         auth = decodeBase64(auth);
1139         ULXR_TRACE(ULXR_PCHAR("getUserPass: ':'? ")  + auth);
1140         std::size_t pos = auth.find(':');
1141         if (pos != CppString::npos)
1142         {
1143             user = stripWS(auth.substr(0, pos));
1144             pass = stripWS(auth.substr(pos+1));
1145             ULXR_TRACE(ULXR_PCHAR("getUserPass: user=") +user + ULXR_PCHAR(", pass=")+pass);
1146             return true;
1147         }
1148     }
1149 
1150     return false;
1151 }
1152 
1153 
ULXR_API_IMPL(void)1154 ULXR_API_IMPL(void) HttpProtocol::rejectAuthentication(const CppString &realm)
1155 {
1156     ULXR_TRACE(ULXR_PCHAR("rejectAuthentication: ") + realm);
1157     addOneTimeHttpField(ULXR_PCHAR("WWW-Authenticate"),
1158                         ULXR_PCHAR("Basic realm=\"") + realm +ULXR_PCHAR("\""));
1159     sendNegativeResponse(401, ULXR_PCHAR("Authentication required for realm \"")+ realm + ULXR_PCHAR("\""));
1160 }
1161 
1162 
ULXR_API_IMPL(void)1163 ULXR_API_IMPL(void) HttpProtocol::addOneTimeHttpField(const CppString &name, const CppString &value)
1164 {
1165     ULXR_TRACE(ULXR_PCHAR("addOneTimeHttpField: ") + name + ULXR_PCHAR(": ") + value);
1166     pimpl->userTempFields.push_back(stripWS(name) + ULXR_PCHAR(": ") + stripWS(value));
1167 }
1168 
1169 
ULXR_API_IMPL(void)1170 ULXR_API_IMPL(void) HttpProtocol::setMessageAuthentication(const CppString &user,
1171                                                         const CppString &pass)
1172 {
1173     ULXR_TRACE(ULXR_PCHAR("setMessageAuthentication"));
1174     CppString s = ULXR_PCHAR("Basic ");
1175     s += encodeBase64(user + ULXR_PCHAR(":") + pass);
1176     addOneTimeHttpField(ULXR_PCHAR("Authorization"), s);
1177 }
1178 
1179 
ULXR_API_IMPL(void)1180 ULXR_API_IMPL(void) HttpProtocol::setProxyAuthentication(const CppString &user,
1181                                                       const CppString &pass)
1182 {
1183     ULXR_TRACE(ULXR_PCHAR("setProxyAuthentication"));
1184     pimpl->proxy_user = user;
1185     pimpl->proxy_pass = pass;
1186 }
1187 
1188 
ULXR_API_IMPL(void)1189 ULXR_API_IMPL(void) HttpProtocol::setTransmitOnly()
1190 {
1191     ULXR_TRACE(ULXR_PCHAR("setTransmitOnly"));
1192     addOneTimeHttpField(ULXR_PCHAR("X-TransmitOnly"), ULXR_PCHAR("true"));
1193 }
1194 
1195 
ULXR_API_IMPL(bool)1196 ULXR_API_IMPL(bool) HttpProtocol::isTransmitOnly()
1197 {
1198     ULXR_TRACE(ULXR_PCHAR("isTransmitOnly"));
1199     return hasHttpProperty(ULXR_PCHAR("X-TransmitOnly"))
1200            && (getHttpProperty(ULXR_PCHAR("X-TransmitOnly")) == ULXR_PCHAR("true"));
1201 }
1202 
1203 
ULXR_API_IMPL(CppString)1204 ULXR_API_IMPL(CppString) HttpProtocol::getProtocolName()
1205 {
1206     return ULXR_PCHAR("http");
1207 }
1208 
1209 
hasCookie() const1210 bool HttpProtocol::hasCookie() const
1211 {
1212   bool b = !pimpl->cookies.empty();
1213   ULXR_TRACE(ULXR_PCHAR("hasCookie: ") << b);
1214   return b;
1215 }
1216 
1217 
setCookie(const CppString & in_cont)1218 void HttpProtocol::setCookie(const CppString &in_cont)
1219 {
1220   ULXR_TRACE(ULXR_PCHAR("setCookie: ") << in_cont);
1221   CppString cont = in_cont;
1222   std::size_t uEnd = cont.find(';');
1223   while (uEnd != CppString::npos)
1224   {
1225     CppString sKV = cont.substr(0, uEnd);
1226     cont.erase(0, uEnd+1);
1227     std::size_t uEq = sKV.find('=');
1228     if (uEq != CppString::npos)
1229     {
1230         CppString sKey = stripWS(sKV.substr(0, uEq));
1231         CppString sVal = stripWS(sKV.substr(uEq+1));
1232         ULXR_TRACE(ULXR_PCHAR("setCookie: ") << sKey << ULXR_PCHAR(" ") << sVal);
1233         pimpl->cookies[sKey] = sVal;
1234     }
1235     uEnd = cont.find(';');
1236   }
1237 
1238   std::size_t uEq = cont.find('=');
1239   if (uEq != CppString::npos)
1240   {
1241       CppString sKey = stripWS(cont.substr(0, uEq));
1242       CppString sVal = stripWS(cont.substr(uEq+1));
1243       ULXR_TRACE(ULXR_PCHAR("setCookie: ") << sKey << ULXR_PCHAR(" ") << sVal);
1244       pimpl->cookies[sKey] = sVal;
1245   }
1246 }
1247 
1248 
getCookie() const1249 CppString HttpProtocol::getCookie() const
1250 {
1251   CppString ret;
1252   for (std::map<CppString, CppString>::const_iterator iCookie = pimpl->cookies.begin();
1253         iCookie != pimpl->cookies.end();
1254         ++iCookie)
1255   {
1256       if (iCookie != pimpl->cookies.begin())
1257           ret += ULXR_PCHAR("; ");
1258       ret += (*iCookie).first + ULXR_PCHAR("=") + (*iCookie).second;
1259   }
1260   ULXR_TRACE(ULXR_PCHAR("getCookie: ") << ret);
1261   return ret;
1262 }
1263 
1264 
ULXR_API_IMPL(void)1265 ULXR_API_IMPL(void) HttpProtocol::setAcceptCookies(bool bAccept)
1266 {
1267   ULXR_TRACE(ULXR_PCHAR("setAcceptCookies: ") << bAccept);
1268   pimpl->bAcceptcookies = bAccept;
1269 }
1270 
1271 
ULXR_API_IMPL(bool)1272 ULXR_API_IMPL(bool) HttpProtocol::isAcceptCookies() const
1273 {
1274   ULXR_TRACE(ULXR_PCHAR("isAcceptCookies: ") << pimpl->bAcceptcookies);
1275   return pimpl->bAcceptcookies;
1276 }
1277 
1278 
ULXR_API_IMPL(void)1279 ULXR_API_IMPL(void) HttpProtocol::setServerCookie(const CppString &cookie)
1280 {
1281   ULXR_TRACE(ULXR_PCHAR("setServerCookie: ") << cookie);
1282   pimpl->serverCookie = cookie;
1283 }
1284 
1285 
ULXR_API_IMPL(CppString)1286 ULXR_API_IMPL(CppString) HttpProtocol::getServerCookie() const
1287 {
1288   ULXR_TRACE(ULXR_PCHAR("getServerCookie: ") << pimpl->serverCookie);
1289   return pimpl->serverCookie;
1290 }
1291 
1292 
ULXR_API_IMPL(bool)1293 ULXR_API_IMPL(bool) HttpProtocol::hasServerCookie() const
1294 {
1295   bool b = pimpl->serverCookie.length() != 0;
1296   ULXR_TRACE(ULXR_PCHAR("hasServerCookie: ") << b);
1297   return b;
1298 }
1299 
1300 
ULXR_API_IMPL(void)1301 ULXR_API_IMPL(void) HttpProtocol::setClientCookie(const CppString &cookie)
1302 {
1303   ULXR_TRACE(ULXR_PCHAR("setClientCookie: ") << cookie);
1304   pimpl->clientCookie = cookie;
1305 }
1306 
1307 
ULXR_API_IMPL(CppString)1308 ULXR_API_IMPL(CppString) HttpProtocol::getClientCookie() const
1309 {
1310   ULXR_TRACE(ULXR_PCHAR("getClientCookie: ") << pimpl->clientCookie);
1311   return pimpl->clientCookie;
1312 }
1313 
1314 
ULXR_API_IMPL(bool)1315 ULXR_API_IMPL(bool) HttpProtocol::hasClientCookie() const
1316 {
1317   bool b = pimpl->clientCookie.length() != 0;
1318   ULXR_TRACE(ULXR_PCHAR("hasClientCookie: ") << b);
1319   return b;
1320 }
1321 
1322 
ULXR_API_IMPL(void)1323 ULXR_API_IMPL(void) HttpProtocol::setUserAgent(const CppString &ua)
1324 {
1325   pimpl->useragent = ua;
1326 }
1327 
1328 
ULXR_API_IMPL(CppString)1329 ULXR_API_IMPL(CppString) HttpProtocol::getUserAgent() const
1330 {
1331   return pimpl->useragent;
1332 }
1333 
1334 
ULXR_API_IMPL(CppString)1335 ULXR_API_IMPL(CppString) HttpProtocol::getFirstHeaderLine() const
1336 {
1337   return pimpl->header_firstline;
1338 }
1339 
1340 
ULXR_API_IMPL(void)1341 ULXR_API_IMPL(void)
1342   HttpProtocol::splitHeaderLine(CppString &head_version, unsigned  &head_status, CppString &head_phrase)
1343 {
1344    head_version = ULXR_PCHAR("");
1345    head_status = 500;
1346    head_phrase = ULXR_PCHAR("Internal error");
1347 
1348    CppString s = stripWS(getFirstHeaderLine());
1349    std::size_t pos = s.find(' ');
1350    if (pos != CppString::npos)
1351    {
1352      head_version = s.substr(0, pos);
1353      s.erase(0, pos+1);
1354    }
1355    else
1356    {
1357      head_version = s;
1358      s = ULXR_PCHAR("");
1359    }
1360    pos = head_version.find('/');
1361    if (pos != CppString::npos)
1362      head_version.erase(0, pos+1);
1363 
1364    CppString stat;
1365    s = stripWS(s);
1366    pos = s.find(' ');
1367    if (pos != CppString::npos)
1368    {
1369      stat = s.substr(0, pos);
1370      s.erase(0, pos+1);
1371    }
1372    else
1373    {
1374      stat = s;
1375      s = ULXR_PCHAR("");
1376    }
1377    head_status = ulxr_atoi(getLatin1(stat).c_str());
1378 
1379    s = stripWS(s);
1380    head_phrase = s;
1381 }
1382 
1383 
ULXR_API_IMPL(void)1384 ULXR_API_IMPL(void) HttpProtocol::setChunkedTransfer(bool chunked)
1385 {
1386   ULXR_TRACE(ULXR_PCHAR("setChunkedTransfer() ") << chunked);
1387   pimpl->chunked_block = chunked;
1388 }
1389 
1390 
ULXR_API_IMPL(bool)1391 ULXR_API_IMPL(bool) HttpProtocol::isChunkedTransfer() const
1392 {
1393   ULXR_TRACE(ULXR_PCHAR("isChunkedTransfer() ") << pimpl->chunked_block);
1394   return pimpl->chunked_block;
1395 }
1396 
1397 
1398 }  // namespace ulxr
1399 
1400