1 /* $Id: ncbi_conn_stream.cpp 624530 2021-02-01 13:22:09Z ivanov $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Authors:  Anton Lavrentiev, Denis Vakatov
27  *
28  * File Description:
29  *   CONN-based C++ streams
30  *
31  *   See file <connect/ncbi_conn_stream.hpp> for more detailed information.
32  *
33  */
34 
35 #include <ncbi_pch.hpp>
36 #include "ncbi_ansi_ext.h"
37 #include "ncbi_conn_streambuf.hpp"
38 #include "ncbi_servicep.h"
39 #include "ncbi_socketp.h"
40 #include <connect/ncbi_conn_exception.hpp>
41 #include <connect/ncbi_conn_stream.hpp>
42 #include <connect/ncbi_file_connector.h>
43 #include <connect/ncbi_util.h>
44 #include <stdlib.h>
45 
46 
47 BEGIN_NCBI_SCOPE
48 
49 
CConn_IOStream(const TConnector & connector,const STimeout * timeout,size_t buf_size,TConn_Flags flgs,CT_CHAR_TYPE * ptr,size_t size)50 CConn_IOStream::CConn_IOStream(const TConnector& connector,
51                                const STimeout* timeout,
52                                size_t buf_size, TConn_Flags flgs,
53                                CT_CHAR_TYPE* ptr, size_t size)
54     : CNcbiIostream(0), m_CSb(new CConn_Streambuf(connector.first,
55                                                   connector.second,
56                                                   timeout, buf_size, flgs,
57                                                   ptr, size))
58 {
59     init(Status() != eIO_Success
60          ? 0  // according to the standard (27.4.4.1.3), badbit is set here
61          : m_CSb);
62 }
63 
64 
CConn_IOStream(CONN conn,bool close,const STimeout * timeout,size_t buf_size,TConn_Flags flgs,CT_CHAR_TYPE * ptr,size_t size)65 CConn_IOStream::CConn_IOStream(CONN conn, bool close,
66                                const STimeout* timeout,
67                                size_t buf_size, TConn_Flags flgs,
68                                CT_CHAR_TYPE* ptr, size_t size)
69     : CNcbiIostream(0), m_CSb(new CConn_Streambuf(conn, close,
70                                                   timeout, buf_size, flgs,
71                                                   ptr, size))
72 {
73     init(Status() != eIO_Success
74          ? 0  // according to the standard (27.4.4.1.3), badbit is set here
75          : m_CSb);
76 }
77 
78 
~CConn_IOStream()79 CConn_IOStream::~CConn_IOStream()
80 {
81     x_Destroy();
82 }
83 
84 
85 #define GET_CONN(sb)  ((sb) ? (sb)->GetCONN() : 0)
86 
87 
GetCONN(void) const88 CONN CConn_IOStream::GetCONN(void) const
89 {
90     return GET_CONN(m_CSb);
91 }
92 
93 
GetType(void) const94 string CConn_IOStream::GetType(void) const
95 {
96     CONN        conn = GET_CONN(m_CSb);
97     const char* type = conn ? CONN_GetType(conn) : 0;
98     return type ? type : kEmptyStr;
99 }
100 
101 
GetDescription(void) const102 string CConn_IOStream::GetDescription(void) const
103 {
104     CONN   conn = GET_CONN(m_CSb);
105     char*  text = conn ? CONN_Description(conn) : 0;
106     string retval(text ? text : kEmptyStr);
107     if (text)
108         free(text);
109     return retval;
110 }
111 
112 
SetTimeout(EIO_Event direction,const STimeout * timeout) const113 EIO_Status CConn_IOStream::SetTimeout(EIO_Event       direction,
114                                       const STimeout* timeout) const
115 {
116     CONN conn = GET_CONN(m_CSb);
117     return conn ? CONN_SetTimeout(conn, direction, timeout) : eIO_NotSupported;
118 }
119 
120 
GetTimeout(EIO_Event direction) const121 const STimeout* CConn_IOStream::GetTimeout(EIO_Event direction) const
122 {
123     CONN conn = GET_CONN(m_CSb);
124     return conn ? CONN_GetTimeout(conn, direction) : kDefaultTimeout;
125 }
126 
127 
Status(EIO_Event dir) const128 EIO_Status CConn_IOStream::Status(EIO_Event dir) const
129 {
130     return m_CSb ? m_CSb->Status(dir) : eIO_NotSupported;
131 }
132 
133 
Fetch(const STimeout * timeout)134 EIO_Status CConn_IOStream::Fetch(const STimeout* timeout)
135 {
136     EIO_Status status;
137     CONN conn = GET_CONN(m_CSb);
138     if (!conn) {
139         setstate(NcbiBadbit);
140         status = eIO_NotSupported;
141     } else
142         status = m_CSb->Fetch(timeout);
143     return status;
144 }
145 
146 
Pushback(const CT_CHAR_TYPE * data,streamsize size)147 EIO_Status CConn_IOStream::Pushback(const CT_CHAR_TYPE* data, streamsize size)
148 {
149     EIO_Status status = m_CSb ? m_CSb->Pushback(data, size) : eIO_NotSupported;
150     if (status != eIO_Success)
151         clear(NcbiBadbit);
152     return status;
153 }
154 
155 
GetSOCK(void)156 SOCK CConn_IOStream::GetSOCK(void)
157 {
158     SOCK sock;
159     CONN conn = GET_CONN(m_CSb);
160     if (!conn  ||  CONN_GetSOCK(conn, &sock) != eIO_Success)
161         sock = 0;
162     return sock;
163 }
164 
165 
Close(void)166 EIO_Status CConn_IOStream::Close(void)
167 {
168     if (!m_CSb)
169         return eIO_Closed;
170     EIO_Status status = m_CSb->Close();
171     if (status != eIO_Success  &&  status != eIO_Closed)
172         clear(NcbiBadbit);
173     return status;
174 }
175 
176 
x_Destroy(void)177 void CConn_IOStream::x_Destroy(void)
178 {
179     CConn_Streambuf* sb = m_CSb;
180     m_CSb = 0;
181     delete sb;
182 }
183 
184 
SetCanceledCallback(const ICanceled * canceled)185 EIO_Status CConn_IOStream::SetCanceledCallback(const ICanceled* canceled)
186 {
187     CONN conn = GET_CONN(m_CSb);
188     if (!conn)
189         return eIO_NotSupported;
190 
191     bool isset = m_Canceled.NotNull() ? 1 : 0;
192 
193     if (canceled) {
194         SCONN_Callback cb;
195         m_Canceled = canceled;
196         memset(&cb, 0, sizeof(cb));
197         cb.func = (FCONN_Callback) x_IsCanceled;
198         cb.data = this;
199         CONN_SetCallback(conn, eCONN_OnOpen,  &cb,      isset ? 0 : &m_CB[0]);
200         CONN_SetCallback(conn, eCONN_OnRead,  &cb,      isset ? 0 : &m_CB[1]);
201         CONN_SetCallback(conn, eCONN_OnWrite, &cb,      isset ? 0 : &m_CB[2]);
202         CONN_SetCallback(conn, eCONN_OnFlush, &cb,      isset ? 0 : &m_CB[3]);
203     } else if (isset) {
204         CONN_SetCallback(conn, eCONN_OnFlush, &m_CB[3],         0);
205         CONN_SetCallback(conn, eCONN_OnWrite, &m_CB[2],         0);
206         CONN_SetCallback(conn, eCONN_OnRead,  &m_CB[1],         0);
207         CONN_SetCallback(conn, eCONN_OnOpen,  &m_CB[0],         0);
208         m_Canceled = 0;
209     }
210 
211     return eIO_Success;
212 }
213 
214 
x_IsCanceled(CONN conn,TCONN_Callback type,void * data)215 EIO_Status CConn_IOStream::x_IsCanceled(CONN           conn,
216                                         TCONN_Callback type,
217                                         void*          data)
218 {
219     _ASSERT(conn  &&  data);
220     CConn_IOStream* io = reinterpret_cast<CConn_IOStream*>(data);
221     if (/* io && */ io->m_Canceled.NotNull()  &&  io->m_Canceled->IsCanceled())
222         return eIO_Interrupt;
223     int n = (int) type & (int) eIO_ReadWrite;
224     _ASSERT(0 <= n  &&  (size_t) n < sizeof(io->m_CB) / sizeof(io->m_CB[0]));
225     _ASSERT((n == 0  &&  type == eCONN_OnOpen)   ||
226             (n == 1  &&  type == eCONN_OnRead)   ||
227             (n == 2  &&  type == eCONN_OnWrite)  ||
228             (n == 3  &&  type == eCONN_OnFlush));
229     return io->m_CB[n].func
230         ? io->m_CB[n].func(conn, type, io->m_CB[n].data)
231         : eIO_Success;
232 
233 }
234 
235 
CConn_SocketStream(const string & host,unsigned short port,unsigned short max_try,const STimeout * timeout,size_t buf_size)236 CConn_SocketStream::CConn_SocketStream(const string&   host,
237                                        unsigned short  port,
238                                        unsigned short  max_try,
239                                        const STimeout* timeout,
240                                        size_t          buf_size)
241     : CConn_IOStream(TConnector(SOCK_CreateConnector(host.c_str(),
242                                                      port,
243                                                      max_try)),
244                      timeout, buf_size)
245 {
246     return;
247 }
248 
249 
CConn_SocketStream(const string & host,unsigned short port,const void * data,size_t size,TSOCK_Flags flgs,unsigned short max_try,const STimeout * timeout,size_t buf_size)250 CConn_SocketStream::CConn_SocketStream(const string&   host,
251                                        unsigned short  port,
252                                        const void*     data,
253                                        size_t          size,
254                                        TSOCK_Flags     flgs,
255                                        unsigned short  max_try,
256                                        const STimeout* timeout,
257                                        size_t          buf_size)
258     : CConn_IOStream(TConnector(SOCK_CreateConnectorEx(host.c_str(),
259                                                        port,
260                                                        max_try,
261                                                        data,
262                                                        size,
263                                                        flgs)),
264                      timeout, buf_size)
265 {
266     return;
267 }
268 
269 
CConn_SocketStream(SOCK sock,EOwnership if_to_own,const STimeout * timeout,size_t buf_size)270 CConn_SocketStream::CConn_SocketStream(SOCK            sock,
271                                        EOwnership      if_to_own,
272                                        const STimeout* timeout,
273                                        size_t          buf_size)
274     : CConn_IOStream(TConnector(SOCK_CreateConnectorOnTop(sock,
275                                                           if_to_own
276                                                           != eNoOwnership)),
277                      timeout, buf_size)
278 {
279     return;
280 }
281 
282 
s_GrabSOCK(CSocket & socket)283 static SOCK s_GrabSOCK(CSocket& socket)
284 {
285     SOCK sock = socket.GetSOCK();
286     if (!sock) {
287         NCBI_THROW(CIO_Exception, eInvalidArg,
288                    "CConn_SocketStream::CConn_SocketStream(): "
289                    " Socket may not be empty");
290     }
291     if (socket.SetOwnership(eNoOwnership) == eNoOwnership) {
292         NCBI_THROW(CIO_Exception, eInvalidArg,
293                    "CConn_SocketStream::CConn_SocketStream(): "
294                    " Socket must be owned");
295     }
296     socket.Reset(0/*empty*/,
297                  eNoOwnership/*irrelevant*/,
298                  eCopyTimeoutsFromSOCK/*irrelevant*/);
299     return sock;
300 }
301 
302 
CConn_SocketStream(CSocket & socket,const STimeout * timeout,size_t buf_size)303 CConn_SocketStream::CConn_SocketStream(CSocket&        socket,
304                                        const STimeout* timeout,
305                                        size_t          buf_size)
306     : CConn_IOStream(TConnector(SOCK_CreateConnectorOnTop(s_GrabSOCK(socket),
307                                                           1/*own*/)),
308                      timeout, buf_size)
309 {
310     return;
311 }
312 
313 
314 template<>
315 struct Deleter<SConnNetInfo>
316 {
DeleteDeleter317     static void Delete(SConnNetInfo* net_info)
318     { ConnNetInfo_Destroy(net_info); }
319 };
320 
321 
322 static CConn_IOStream::TConnector
s_SocketConnectorBuilder(const SConnNetInfo * net_info,const STimeout * timeout,const void * data,size_t size,TSOCK_Flags flgs)323 s_SocketConnectorBuilder(const SConnNetInfo* net_info,
324                          const STimeout*     timeout,
325                          const void*         data,
326                          size_t              size,
327                          TSOCK_Flags         flgs)
328 {
329     EIO_Status status = eIO_Success;
330     bool       proxy = false;
331     SOCK       sock = 0;
332 
333     _ASSERT(net_info);
334     if ((flgs & (fSOCK_LogOn | fSOCK_LogDefault)) == fSOCK_LogDefault
335         &&  net_info->debug_printout == eDebugPrintout_Data) {
336         flgs &= ~fSOCK_LogDefault;
337         flgs |=  fSOCK_LogOn;
338     }
339     if (net_info->http_proxy_host[0]  &&  net_info->http_proxy_port) {
340         status = HTTP_CreateTunnel(net_info, fHTTP_NoAutoRetry, &sock);
341         _ASSERT(!sock ^ !(status != eIO_Success));
342         if (status == eIO_Success
343             &&  (size  ||  (flgs & ~(fSOCK_LogOn | fSOCK_LogDefault)))) {
344             SSOCK_Init init;
345             memset(&init, 0, sizeof(init));
346             init.data = data;
347             init.size = size;
348             init.host = net_info->host;
349             init.cred = net_info->credentials;
350             SOCK s;
351             status = SOCK_CreateOnTopInternal(sock, 0, &s, &init, flgs);
352             _ASSERT(!s ^ !(status != eIO_Success));
353             SOCK_Destroy(sock);
354             sock = s;
355         }
356         proxy = true;
357     }
358     if (!sock  &&  (!proxy  ||  net_info->http_proxy_leak)) {
359         if (timeout == kDefaultTimeout)
360             timeout  = net_info->timeout;
361         if (!proxy  &&  net_info->debug_printout) {
362             AutoPtr<SConnNetInfo> x_net_info(ConnNetInfo_Clone(net_info));
363             if (x_net_info.get()) {
364                 // manual cleanup of most fields req'd
365                 x_net_info->scheme = eURL_Unspec;
366                 x_net_info->req_method = eReqMethod_Any;
367                 x_net_info->external = 0;
368                 x_net_info->firewall = 0;
369                 x_net_info->stateless = 0;
370                 x_net_info->lb_disable = 0;
371                 x_net_info->http_version = 0;
372                 x_net_info->http_push_auth = 0;
373                 x_net_info->http_proxy_leak = 0;
374                 x_net_info->user[0] = '\0';
375                 x_net_info->pass[0] = '\0';
376                 x_net_info->path[0] = '\0';
377                 x_net_info->http_proxy_host[0] = '\0';
378                 x_net_info->http_proxy_port    =   0;
379                 x_net_info->http_proxy_user[0] = '\0';
380                 x_net_info->http_proxy_pass[0] = '\0';
381                 ConnNetInfo_SetUserHeader(x_net_info.get(), 0);
382                 if (x_net_info->http_referer) {
383                     free((void*) x_net_info->http_referer);
384                     x_net_info->http_referer = 0;
385                 }
386                 x_net_info->timeout = timeout;
387             }
388             ConnNetInfo_Log(x_net_info.get(), eLOG_Note, CORE_GetLOG());
389         }
390         SSOCK_Init init;
391         memset(&init, 0, sizeof(init));
392         init.data = data;
393         init.size = size;
394         init.host = net_info->host;
395         init.cred = net_info->credentials;
396         status = SOCK_CreateInternal(net_info->host, net_info->port, timeout,
397                                      &sock, &init, flgs);
398         _ASSERT(!sock ^ !(status != eIO_Success));
399     }
400     string hostport(net_info->host);
401     hostport += ':';
402     hostport += NStr::UIntToString(net_info->port);
403     CONNECTOR c = SOCK_CreateConnectorOnTopEx(sock, 1/*own*/,hostport.c_str());
404     if (!c) {
405         SOCK_Abort(sock);
406         SOCK_Close(sock);
407         status = eIO_Unknown;
408     }
409     return CConn_IOStream::TConnector(c, status);
410 }
411 
412 
CConn_SocketStream(const SConnNetInfo & net_info,const void * data,size_t size,TSOCK_Flags flgs,const STimeout * timeout,size_t buf_size)413 CConn_SocketStream::CConn_SocketStream(const SConnNetInfo& net_info,
414                                        const void*         data,
415                                        size_t              size,
416                                        TSOCK_Flags         flgs,
417                                        const STimeout*     timeout,
418                                        size_t              buf_size)
419     : CConn_IOStream(s_SocketConnectorBuilder(&net_info, timeout,
420                                               data, size, flgs),
421                      timeout, buf_size)
422 {
423     return;
424 }
425 
426 
427 // NB:  Must never be upcalled (directly or indirectly) from any stream ctor!
Parse(const char * header)428 EHTTP_HeaderParse SHTTP_StatusData::Parse(const char* header)
429 {
430     int c, n;
431     m_Code = 0;
432     m_Text.clear();
433     m_Header = header;
434     if (sscanf(header, "%*s %u%n", &c, &n) < 1)
435         return eHTTP_HeaderError;
436     const char* str = m_Header.c_str() + n;
437     str += strspn(str, " \t");
438     const char* eol = strchr(str, '\n');
439     if (!eol)
440         eol = str + strlen(str);
441     while (eol > str) {
442         if (!isspace((unsigned char) eol[-1]))
443             break;
444         --eol;
445     }
446     m_Code = c;
447     m_Text.assign(str, (size_t)(eol - str));
448     return eHTTP_HeaderSuccess;
449 }
450 
451 
452 typedef AutoPtr< char, CDeleter<char> >  TTempCharPtr;
453 
454 
455 static CConn_IOStream::TConnector
s_HttpConnectorBuilder(const SConnNetInfo * net_info,EReqMethod method,const char * url,const char * host,unsigned short port,const char * path,const char * args,const char * user_header,void * x_data,FHTTP_Adjust x_adjust,FHTTP_Cleanup x_cleanup,FHTTP_ParseHeader x_parse_header,THTTP_Flags flgs,const STimeout * timeout,void ** user_data_ptr,FHTTP_Cleanup * user_cleanup_ptr,void * user_data=0,FHTTP_Cleanup user_cleanup=0)456 s_HttpConnectorBuilder(const SConnNetInfo* net_info,
457                        EReqMethod          method,
458                        const char*         url,
459                        const char*         host,
460                        unsigned short      port,
461                        const char*         path,
462                        const char*         args,
463                        const char*         user_header,
464                        void*               x_data,
465                        FHTTP_Adjust        x_adjust,
466                        FHTTP_Cleanup       x_cleanup,
467                        FHTTP_ParseHeader   x_parse_header,
468                        THTTP_Flags         flgs,
469                        const STimeout*     timeout,
470                        void**              user_data_ptr,
471                        FHTTP_Cleanup*      user_cleanup_ptr,
472                        void*               user_data    = 0,
473                        FHTTP_Cleanup       user_cleanup = 0)
474 {
475     EReqMethod x_req_method;
476     AutoPtr<SConnNetInfo> x_net_info(net_info
477                                      ? ConnNetInfo_Clone(net_info)
478                                      : ConnNetInfo_CreateInternal(0));
479     if (!x_net_info.get()) {
480         NCBI_THROW(CIO_Exception, eUnknown,
481                    "CConn_HttpStream::CConn_HttpStream(): "
482                    " Out of memory");
483     }
484     x_req_method = (EReqMethod)(method & ~eReqMethod_v1);
485     if (x_req_method == eReqMethod_Connect) {
486         NCBI_THROW(CIO_Exception, eInvalidArg,
487                    "CConn_HttpStream::CConn_HttpStream(): "
488                    " Bad request method (CONNECT)");
489     }
490     if (x_req_method)
491         x_net_info->req_method = method;
492     else if (method/*ANY/1.1*/)
493         x_net_info->http_version = 1;
494     if (url  &&  !ConnNetInfo_ParseURL(x_net_info.get(), url)) {
495         NCBI_THROW(CIO_Exception, eInvalidArg,
496                    "CConn_HttpStream::CConn_HttpStream(): "
497                    " Bad URL \"" + string(url) + '"');
498     }
499     if (host) {
500         size_t len;
501         if ((len = *host ? strlen(host) : 0) >= sizeof(x_net_info->host)) {
502             NCBI_THROW(CIO_Exception, eInvalidArg,
503                        "CConn_HttpStream::CConn_HttpStream(): "
504                        " Host too long \"" + string(host) + '"');
505         }
506         memcpy(x_net_info->host, host, ++len);
507     }
508     if (port)
509         x_net_info->port = port;
510     if (path  &&  !ConnNetInfo_SetPath(x_net_info.get(), path)) {
511         NCBI_THROW(CIO_Exception, eInvalidArg,
512                    "CConn_HttpStream::CConn_HttpStream(): "
513                    " Path too long \"" + string(path) + '"');
514     }
515     if (args  &&  !ConnNetInfo_SetArgs(x_net_info.get(), args)) {
516         NCBI_THROW(CIO_Exception, eInvalidArg,
517                    "CConn_HttpStream::CConn_HttpStream(): "
518                    " Args too long \"" + string(args) + '"');
519     }
520     if (user_header  &&  *user_header
521         &&  !ConnNetInfo_OverrideUserHeader(x_net_info.get(), user_header)) {
522         int x_dynamic = 0;
523         const char* x_message = NcbiMessagePlusError(&x_dynamic,
524                                                      "Cannot set user header",
525                                                      errno, 0);
526         TTempCharPtr msg_ptr(const_cast<char*> (x_message),
527                              x_dynamic ? eTakeOwnership : eNoOwnership);
528         NCBI_THROW(CIO_Exception, eUnknown,
529                    "CConn_HttpStream::CConn_HttpStream(): "
530                    " " + string(msg_ptr.get()));
531     }
532     if (timeout != kDefaultTimeout)
533         x_net_info->timeout = timeout;
534     // NB: need these two inited now in case of early CONNECTOR->destroy() call
535     *user_data_ptr    = user_data;
536     *user_cleanup_ptr = user_cleanup;
537     CONNECTOR c = HTTP_CreateConnectorEx(x_net_info.get(),
538                                          flgs,
539                                          x_parse_header,
540                                          x_data,
541                                          x_adjust,
542                                          x_cleanup);
543     return CConn_IOStream::TConnector(c);
544 }
545 
546 
CConn_HttpStream(const string & host,const string & path,const string & args,const string & user_header,unsigned short port,THTTP_Flags flgs,const STimeout * timeout,size_t buf_size)547 CConn_HttpStream::CConn_HttpStream(const string&   host,
548                                    const string&   path,
549                                    const string&   args,
550                                    const string&   user_header,
551                                    unsigned short  port,
552                                    THTTP_Flags     flgs,
553                                    const STimeout* timeout,
554                                    size_t          buf_size)
555     : CConn_IOStream(s_HttpConnectorBuilder(0,
556                                             eReqMethod_Any,
557                                             0,
558                                             host.c_str(),
559                                             port,
560                                             path.c_str(),
561                                             args.c_str(),
562                                             user_header.c_str(),
563                                             this,
564                                             x_Adjust,
565                                             0/*x_Cleanup*/,
566                                             x_ParseHeader,
567                                             flgs,
568                                             timeout,
569                                             &m_UserData,
570                                             &m_UserCleanup),
571                      timeout, buf_size),
572       m_UserAdjust(0), m_UserParseHeader(0)
573 {
574     return;
575 }
576 
577 
CConn_HttpStream(const string & url,THTTP_Flags flgs,const STimeout * timeout,size_t buf_size)578 CConn_HttpStream::CConn_HttpStream(const string&   url,
579                                    THTTP_Flags     flgs,
580                                    const STimeout* timeout,
581                                    size_t          buf_size)
582     : CConn_IOStream(s_HttpConnectorBuilder(0,
583                                             eReqMethod_Any,
584                                             url.c_str(),
585                                             0,
586                                             0,
587                                             0,
588                                             0,
589                                             0,
590                                             this,
591                                             x_Adjust,
592                                             0/*x_Cleanup*/,
593                                             x_ParseHeader,
594                                             flgs,
595                                             timeout,
596                                             &m_UserData,
597                                             &m_UserCleanup),
598                      timeout, buf_size),
599       m_UserAdjust(0), m_UserParseHeader(0)
600 {
601     return;
602 }
603 
604 
CConn_HttpStream(const string & url,EReqMethod method,const string & user_header,THTTP_Flags flgs,const STimeout * timeout,size_t buf_size)605 CConn_HttpStream::CConn_HttpStream(const string&   url,
606                                    EReqMethod      method,
607                                    const string&   user_header,
608                                    THTTP_Flags     flgs,
609                                    const STimeout* timeout,
610                                    size_t          buf_size)
611     : CConn_IOStream(s_HttpConnectorBuilder(0,
612                                             method,
613                                             url.c_str(),
614                                             0,
615                                             0,
616                                             0,
617                                             0,
618                                             user_header.c_str(),
619                                             this,
620                                             x_Adjust,
621                                             0/*x_Cleanup*/,
622                                             x_ParseHeader,
623                                             flgs,
624                                             timeout,
625                                             &m_UserData,
626                                             &m_UserCleanup),
627                      timeout, buf_size),
628       m_UserAdjust(0),  m_UserParseHeader(0)
629 {
630     return;
631 }
632 
633 
CConn_HttpStream(const string & url,const SConnNetInfo * net_info,const string & user_header,FHTTP_ParseHeader parse_header,void * user_data,FHTTP_Adjust adjust,FHTTP_Cleanup cleanup,THTTP_Flags flgs,const STimeout * timeout,size_t buf_size)634 CConn_HttpStream::CConn_HttpStream(const string&       url,
635                                    const SConnNetInfo* net_info,
636                                    const string&       user_header,
637                                    FHTTP_ParseHeader   parse_header,
638                                    void*               user_data,
639                                    FHTTP_Adjust        adjust,
640                                    FHTTP_Cleanup       cleanup,
641                                    THTTP_Flags         flgs,
642                                    const STimeout*     timeout,
643                                    size_t              buf_size)
644     : CConn_IOStream(s_HttpConnectorBuilder(net_info,
645                                             eReqMethod_Any,
646                                             url.c_str(),
647                                             0,
648                                             0,
649                                             0,
650                                             0,
651                                             user_header.c_str(),
652                                             this,
653                                                       x_Adjust,
654                                             cleanup ? x_Cleanup : 0,
655                                             x_ParseHeader,
656                                             flgs,
657                                             timeout,
658                                             &m_UserData,
659                                             &m_UserCleanup,
660                                             user_data,
661                                             cleanup),
662                      timeout, buf_size),
663       m_UserAdjust(adjust), m_UserParseHeader(parse_header)
664 {
665     return;
666 }
667 
668 
CConn_HttpStream(const SConnNetInfo * net_info,const string & user_header,FHTTP_ParseHeader parse_header,void * user_data,FHTTP_Adjust adjust,FHTTP_Cleanup cleanup,THTTP_Flags flgs,const STimeout * timeout,size_t buf_size)669 CConn_HttpStream::CConn_HttpStream(const SConnNetInfo* net_info,
670                                    const string&       user_header,
671                                    FHTTP_ParseHeader   parse_header,
672                                    void*               user_data,
673                                    FHTTP_Adjust        adjust,
674                                    FHTTP_Cleanup       cleanup,
675                                    THTTP_Flags         flgs,
676                                    const STimeout*     timeout,
677                                    size_t              buf_size)
678     : CConn_IOStream(s_HttpConnectorBuilder(net_info,
679                                             eReqMethod_Any,
680                                             0,
681                                             0,
682                                             0,
683                                             0,
684                                             0,
685                                             user_header.c_str(),
686                                             this,
687                                                       x_Adjust,
688                                             cleanup ? x_Cleanup : 0,
689                                             x_ParseHeader,
690                                             flgs,
691                                             timeout,
692                                             &m_UserData,
693                                             &m_UserCleanup,
694                                             user_data,
695                                             cleanup),
696                      timeout, buf_size),
697       m_UserAdjust(adjust), m_UserParseHeader(parse_header)
698 {
699     return;
700 }
701 
702 
~CConn_HttpStream()703 CConn_HttpStream::~CConn_HttpStream()
704 {
705     // Explicitly destroy so that the callbacks are not called out of context
706     x_Destroy();
707 }
708 
709 
710 // WARNING: must not be called from CConn_HttpStream ctor (directly or not)!
x_ParseHeader(const char * header,void * data,int code)711 EHTTP_HeaderParse CConn_HttpStream::x_ParseHeader(const char* header,
712                                                   void*       data,
713                                                   int         code)
714 {
715     CConn_HttpStream* http = reinterpret_cast<CConn_HttpStream*>(data);
716     EHTTP_HeaderParse rv = http->m_StatusData.Parse(header);
717     if (rv != eHTTP_HeaderSuccess)
718         return rv;
719     _ASSERT(!code  ||  code == http->m_StatusData.m_Code);
720     return http->m_UserParseHeader
721         ? http->m_UserParseHeader(header, http->m_UserData, code)
722         : eHTTP_HeaderSuccess;
723 }
724 
725 
x_Adjust(SConnNetInfo * net_info,void * data,unsigned int count)726 int CConn_HttpStream::x_Adjust(SConnNetInfo* net_info,
727                                void*         data,
728                                unsigned int  count)
729 {
730     int retval;
731     bool modified;
732     CConn_HttpStream* http = reinterpret_cast<CConn_HttpStream*>(data);
733     if (count == (unsigned int)(-1)  &&  !http->m_URL.empty()) {
734         http->m_StatusData.Clear();
735         if (!ConnNetInfo_ParseURL(net_info, http->m_URL.c_str()))
736             return 0/*failure*/;
737         http->m_URL.erase();
738         modified = true;
739     } else
740         modified = false;
741     if (http->m_UserAdjust) {
742         if (!(retval = http->m_UserAdjust(net_info, http->m_UserData, count)))
743             return 0/*failure*/;
744         if (retval < 0  &&  modified)
745             retval = 1;
746     } else
747         retval = modified ? 1 : -1;
748     return retval/*success*/;
749 }
750 
751 
x_Cleanup(void * data)752 void CConn_HttpStream::x_Cleanup(void* data)
753 {
754     CConn_HttpStream* http = reinterpret_cast<CConn_HttpStream*>(data);
755     http->m_UserCleanup(http->m_UserData);
756 }
757 
758 
759 static CConn_IOStream::TConnector
s_ServiceConnectorBuilder(const char * service,TSERV_Type types,const SConnNetInfo * net_info,const char * user_header,const SSERVICE_Extra * extra,CConn_ServiceStream::SSERVICE_CBData * cbdata,FSERVICE_Reset x_reset,FHTTP_Adjust x_adjust,FSERVICE_Cleanup x_cleanup,FHTTP_ParseHeader x_parse_header,FSERVICE_GetNextInfo x_get_next_info,const STimeout * timeout)760 s_ServiceConnectorBuilder(const char*                          service,
761                           TSERV_Type                           types,
762                           const SConnNetInfo*                  net_info,
763                           const char*                          user_header,
764                           const SSERVICE_Extra*                extra,
765                           CConn_ServiceStream::SSERVICE_CBData*cbdata,
766                           FSERVICE_Reset                       x_reset,
767                           FHTTP_Adjust                         x_adjust,
768                           FSERVICE_Cleanup                     x_cleanup,
769                           FHTTP_ParseHeader                    x_parse_header,
770                           FSERVICE_GetNextInfo                 x_get_next_info,
771                           const STimeout*                      timeout)
772 {
773     AutoPtr<SConnNetInfo> x_net_info(net_info
774                                      ? ConnNetInfo_Clone(net_info)
775                                      : ConnNetInfo_Create(service));
776     if (!x_net_info.get()) {
777         NCBI_THROW(CIO_Exception, eUnknown,
778                    "CConn_ServiceStream::CConn_ServiceStream(): "
779                    " Out of memory");
780     }
781     if (user_header  &&  *user_header
782         &&  !ConnNetInfo_OverrideUserHeader(x_net_info.get(), user_header)) {
783         int x_dynamic = 0;
784         const char* x_message = NcbiMessagePlusError(&x_dynamic,
785                                                      "Cannot set user header",
786                                                      errno, 0);
787         TTempCharPtr msg_ptr(const_cast<char*> (x_message),
788                              x_dynamic ? eTakeOwnership : eNoOwnership);
789         NCBI_THROW(CIO_Exception, eUnknown,
790                    "CConn_ServiceStream::CConn_ServiceStream(): "
791                    " " + string(msg_ptr.get()));
792     }
793     if (timeout != kDefaultTimeout)
794         x_net_info->timeout = timeout;
795     if (extra)
796         memcpy(&cbdata->extra, extra, sizeof(cbdata->extra));
797     else
798         memset(&cbdata->extra, 0,     sizeof(cbdata->extra));
799     _ASSERT(!x_reset          ||  (extra  &&  extra->reset));
800     _ASSERT(!x_adjust         ||  (extra  &&  extra->adjust));
801     _ASSERT(!x_cleanup        ||  (extra  &&  extra->cleanup));
802     _ASSERT(!x_get_next_info  ||  (extra  &&  extra->get_next_info));
803     SSERVICE_Extra x_extra;
804     memset(&x_extra, 0, sizeof(x_extra));
805     x_extra.data          = cbdata;
806     x_extra.reset         = x_reset;
807     x_extra.adjust        = x_adjust;
808     x_extra.cleanup       = x_cleanup;
809     x_extra.parse_header  = x_parse_header;
810     x_extra.get_next_info = x_get_next_info;
811     x_extra.flags         = extra ? extra->flags : 0;
812     CONNECTOR c = SERVICE_CreateConnectorEx(service,
813                                             types,
814                                             x_net_info.get(),
815                                             &x_extra);
816     return CConn_IOStream::TConnector(c);
817 }
818 
819 
CConn_ServiceStream(const string & service,TSERV_Type types,const SConnNetInfo * net_info,const SSERVICE_Extra * extra,const STimeout * timeout,size_t buf_size)820 CConn_ServiceStream::CConn_ServiceStream(const string&         service,
821                                          TSERV_Type            types,
822                                          const SConnNetInfo*   net_info,
823                                          const SSERVICE_Extra* extra,
824                                          const STimeout*       timeout,
825                                          size_t                buf_size)
826     : CConn_IOStream(s_ServiceConnectorBuilder(service.c_str(),
827                                                types,
828                                                net_info,
829                                                0, // user_header
830                                                extra,
831                                                &m_CBD,
832                                                extra  &&  extra->reset
833                                                ? x_Reset : 0,
834                                                extra  &&  extra->adjust
835                                                ? x_Adjust : 0,
836                                                extra  &&  extra->cleanup
837                                                ? x_Cleanup : 0,
838                                                x_ParseHeader,
839                                                extra  &&  extra->get_next_info
840                                                ? x_GetNextInfo : 0,
841                                                timeout),
842                      timeout, buf_size,
843                      types & fSERV_DelayOpen ? fConn_DelayOpen : 0)
844 {
845     return;
846 }
847 
848 
CConn_ServiceStream(const string & service,const string & user_header,TSERV_Type types,const SSERVICE_Extra * extra,const STimeout * timeout,size_t buf_size)849 CConn_ServiceStream::CConn_ServiceStream(const string&         service,
850                                          const string&         user_header,
851                                          TSERV_Type            types,
852                                          const SSERVICE_Extra* extra,
853                                          const STimeout*       timeout,
854                                          size_t                buf_size)
855     : CConn_IOStream(s_ServiceConnectorBuilder(service.c_str(),
856                                                types,
857                                                0, // net_info
858                                                user_header.c_str(),
859                                                extra,
860                                                &m_CBD,
861                                                extra  &&  extra->reset
862                                                ? x_Reset : 0,
863                                                extra  &&  extra->adjust
864                                                ? x_Adjust : 0,
865                                                extra  &&  extra->cleanup
866                                                ? x_Cleanup : 0,
867                                                x_ParseHeader,
868                                                extra  &&  extra->get_next_info
869                                                ? x_GetNextInfo : 0,
870                                                timeout),
871                      timeout, buf_size,
872                      types & fSERV_DelayOpen ? fConn_DelayOpen : 0)
873 {
874     return;
875 }
876 
877 
~CConn_ServiceStream()878 CConn_ServiceStream::~CConn_ServiceStream()
879 {
880     // Explicitly destroy so that the callbacks are not called out of context
881     x_Destroy();
882 }
883 
884 
x_ParseHeader(const char * header,void * data,int code)885 EHTTP_HeaderParse CConn_ServiceStream::x_ParseHeader(const char* header,
886                                                      void* data, int code)
887 {
888     SSERVICE_CBData* cbd = reinterpret_cast<SSERVICE_CBData*>(data);
889     EHTTP_HeaderParse rv = cbd->status.Parse(header);
890     if (rv != eHTTP_HeaderSuccess)
891         return rv;
892     _ASSERT(!code  ||  code == cbd->status.m_Code);
893     return cbd->extra.parse_header
894         ? cbd->extra.parse_header(header, cbd->extra.data, code)
895         : eHTTP_HeaderSuccess;
896 }
897 
898 
x_Adjust(SConnNetInfo * net_info,void * data,unsigned int count)899 int/*bool*/ CConn_ServiceStream::x_Adjust(SConnNetInfo* net_info,
900                                           void*         data,
901                                           unsigned int  count)
902 {
903     SSERVICE_CBData* cbd = reinterpret_cast<SSERVICE_CBData*>(data);
904     if (count != (unsigned int)(-1))
905         cbd->status.Clear();  // never call from CConn_ServiceStream ctor!
906     return cbd->extra.adjust(net_info, cbd->extra.data, count);
907 }
908 
909 
x_Reset(void * data)910 void CConn_ServiceStream::x_Reset(void* data)
911 {
912     SSERVICE_CBData* cbd = reinterpret_cast<SSERVICE_CBData*>(data);
913     cbd->extra.reset(cbd->extra.data);
914 }
915 
916 
x_Cleanup(void * data)917 void CConn_ServiceStream::x_Cleanup(void* data)
918 {
919     SSERVICE_CBData* cbd = reinterpret_cast<SSERVICE_CBData*>(data);
920     cbd->extra.cleanup(cbd->extra.data);
921 }
922 
923 
x_GetNextInfo(void * data,SERV_ITER iter)924 const SSERV_Info* CConn_ServiceStream::x_GetNextInfo(void* data,SERV_ITER iter)
925 {
926     SSERVICE_CBData* cbd = reinterpret_cast<SSERVICE_CBData*>(data);
927     return cbd->extra.get_next_info(cbd->extra.data, iter);
928 }
929 
930 
CConn_MemoryStream(size_t buf_size)931 CConn_MemoryStream::CConn_MemoryStream(size_t buf_size)
932     : CConn_IOStream(TConnector(MEMORY_CreateConnector()),
933                      kInfiniteTimeout/*0*/, buf_size),
934       m_Ptr(0)
935 {
936     return;
937 }
938 
939 
CConn_MemoryStream(BUF buf,EOwnership owner,size_t buf_size)940 CConn_MemoryStream::CConn_MemoryStream(BUF        buf,
941                                        EOwnership owner,
942                                        size_t     buf_size)
943     : CConn_IOStream(TConnector(MEMORY_CreateConnectorEx(buf,
944                                                          owner
945                                                          == eTakeOwnership
946                                                          ? 1/*true*/
947                                                          : 0/*false*/)),
948                      kInfiniteTimeout/*0*/, buf_size, 0/*flags*/,
949                      0, BUF_Size(buf)),
950       m_Ptr(0)
951 {
952     return;
953 }
954 
955 
CConn_MemoryStream(const void * ptr,size_t size,EOwnership owner,size_t buf_size)956 CConn_MemoryStream::CConn_MemoryStream(const void* ptr,
957                                        size_t      size,
958                                        EOwnership  owner,
959                                        size_t      buf_size)
960     : CConn_IOStream(TConnector(MEMORY_CreateConnector()),
961                      kInfiniteTimeout/*0*/, buf_size, 0/*flags*/,
962                      (CT_CHAR_TYPE*) ptr, size),
963       m_Ptr(owner == eTakeOwnership ? ptr : 0)
964 {
965     return;
966 }
967 
968 
~CConn_MemoryStream()969 CConn_MemoryStream::~CConn_MemoryStream()
970 {
971     // Explicitly call x_Destroy() to avoid using deleted m_Ptr otherwise.
972     x_Destroy();
973     delete[] (CT_CHAR_TYPE*) m_Ptr;
974 }
975 
976 
ToString(string * str)977 void CConn_MemoryStream::ToString(string* str)
978 {
979     if (!str) {
980         NCBI_THROW(CIO_Exception, eInvalidArg,
981                    "CConn_MemoryStream::ToString(NULL) is not allowed");
982     }
983     CConn_Streambuf* sb = dynamic_cast<CConn_Streambuf*>(rdbuf());
984     size_t size = sb  &&  good() ? (size_t)(tellp() - tellg()) : 0;
985     str->resize(size);
986     if (sb) {
987         size_t s = (size_t) sb->sgetn(&(*str)[0], size);
988 #ifdef NCBI_COMPILER_WORKSHOP
989         if (s < 0) {
990             s = 0; // WS6 weirdness to sometimes return -1 from sgetn() :-/
991         } else
992 #endif //NCBI_COMPILER_WORKSHOP
993         _ASSERT(s == size);
994         str->resize(s);  // NB: just in case, essentially NOOP when s == size
995     }
996 }
997 
998 
ToVector(vector<char> * vec)999 void CConn_MemoryStream::ToVector(vector<char>* vec)
1000 {
1001     if (!vec) {
1002         NCBI_THROW(CIO_Exception, eInvalidArg,
1003                    "CConn_MemoryStream::ToVector(NULL) is not allowed");
1004     }
1005     CConn_Streambuf* sb = dynamic_cast<CConn_Streambuf*>(rdbuf());
1006     size_t size = sb  &&  good() ? (size_t)(tellp() - tellg()) : 0;
1007     vec->resize(size);
1008     if (sb) {
1009         size_t s = (size_t) sb->sgetn(&(*vec)[0], size);
1010 #ifdef NCBI_COMPILER_WORKSHOP
1011         if (s < 0) {
1012             s = 0;  // WS6 weirdness to sometimes return -1 from sgetn() :-/
1013         } else
1014 #endif //NCBI_COMPILER_WORKSHOP
1015         _ASSERT(s == size);
1016         vec->resize(s);  // NB: just in case, essentially NOOP when s == size
1017     }
1018 }
1019 
1020 
1021 static CConn_IOStream::TConnector
s_PipeConnectorBuilder(const string & cmd,const vector<string> & args,CPipe::TCreateFlags flgs,size_t pipe_size,CPipe * & pipe)1022 s_PipeConnectorBuilder(const string&         cmd,
1023                        const vector<string>& args,
1024                        CPipe::TCreateFlags   flgs,
1025                        size_t                pipe_size,
1026                        CPipe*&               pipe)
1027 {
1028     pipe = new CPipe(pipe_size);
1029     CONNECTOR c = PIPE_CreateConnector(cmd, args, flgs, pipe, eNoOwnership);
1030     return CConn_IOStream::TConnector(c);
1031 }
1032 
1033 
CConn_PipeStream(const string & cmd,const vector<string> & args,CPipe::TCreateFlags flgs,size_t pipe_size,const STimeout * timeout,size_t buf_size)1034 CConn_PipeStream::CConn_PipeStream(const string&         cmd,
1035                                    const vector<string>& args,
1036                                    CPipe::TCreateFlags   flgs,
1037                                    size_t                pipe_size,
1038                                    const STimeout*       timeout,
1039                                    size_t                buf_size)
1040     : CConn_IOStream(s_PipeConnectorBuilder(cmd, args, flgs, pipe_size,
1041                                             m_Pipe),
1042                      timeout, buf_size), m_ExitCode(-1)
1043 {
1044     return;
1045 }
1046 
1047 
~CConn_PipeStream()1048 CConn_PipeStream::~CConn_PipeStream()
1049 {
1050     // Explicitly do x_Destroy() to avoid using dead m_Pipe in base class dtor
1051     x_Destroy();
1052     delete m_Pipe;
1053 }
1054 
1055 
Close(void)1056 EIO_Status CConn_PipeStream::Close(void)
1057 {
1058     if (!flush())
1059         return Status(eIO_Write);
1060     // NB:  This can lead to wrong order for a close callback, if any fired by
1061     // CConn_IOStream::Close():  the callback will be late and actually coming
1062     // _after_ the pipe gets closed here.  There's no easy way to avoid this...
1063     EIO_Status status = m_Pipe->Close(&m_ExitCode);
1064     (void) CConn_IOStream::Close();
1065     return status;
1066 }
1067 
1068 
CConn_NamedPipeStream(const string & pipename,size_t pipesize,const STimeout * timeout,size_t buf_size)1069 CConn_NamedPipeStream::CConn_NamedPipeStream(const string&   pipename,
1070                                              size_t          pipesize,
1071                                              const STimeout* timeout,
1072                                              size_t          buf_size)
1073     : CConn_IOStream(TConnector(NAMEDPIPE_CreateConnector(pipename, pipesize)),
1074                      timeout, buf_size)
1075 {
1076     return;
1077 }
1078 
1079 
1080 static CConn_IOStream::TConnector
s_FtpConnectorBuilder(const SConnNetInfo * net_info,TFTP_Flags flgs,const SFTP_Callback * cmcb,const STimeout * timeout)1081 s_FtpConnectorBuilder(const SConnNetInfo*  net_info,
1082                       TFTP_Flags           flgs,
1083                       const SFTP_Callback* cmcb,
1084                       const STimeout*      timeout)
1085 {
1086     _ASSERT(net_info);
1087     if (timeout == kDefaultTimeout)
1088         timeout  = net_info->timeout;
1089     const SConnNetInfo* x_net_info;
1090     if (timeout != net_info->timeout) {
1091         SConnNetInfo* xx_net_info = ConnNetInfo_Clone(net_info);
1092         if (xx_net_info)
1093             xx_net_info->timeout = timeout;
1094         x_net_info = xx_net_info;
1095     } else
1096         x_net_info = net_info;
1097     CONNECTOR c = FTP_CreateConnector(x_net_info, flgs, cmcb);
1098     if (x_net_info != net_info)
1099         ConnNetInfo_Destroy((SConnNetInfo*) x_net_info);
1100     return CConn_IOStream::TConnector(c);
1101 }
1102 
1103 
1104 /* For data integrity and unambigous interpretation, FTP streams are not
1105  * buffered at the level of the C++ STL streambuf because of the way they
1106  * execute read / write operations on the mix of FTP commands and data.
1107  * There should be a little impact on performance of byte-by-byte I/O (such as
1108  * formatted input, which is not expected very often for this kind of streams,
1109  * anyways), and almost none for block I/O (such as read / readsome / write).
1110  */
CConn_FtpStream(const string & host,const string & user,const string & pass,const string & path,unsigned short port,TFTP_Flags flgs,const SFTP_Callback * cmcb,const STimeout * timeout,size_t buf_size)1111 CConn_FtpStream::CConn_FtpStream(const string&        host,
1112                                  const string&        user,
1113                                  const string&        pass,
1114                                  const string&        path,
1115                                  unsigned short       port,
1116                                  TFTP_Flags           flgs,
1117                                  const SFTP_Callback* cmcb,
1118                                  const STimeout*      timeout,
1119                                  size_t               buf_size)
1120     : CConn_IOStream(TConnector(FTP_CreateConnectorSimple(host.c_str(),
1121                                                           port,
1122                                                           user.c_str(),
1123                                                           pass.c_str(),
1124                                                           path.c_str(),
1125                                                           flgs,
1126                                                           cmcb)),
1127                      timeout, buf_size,
1128                      fConn_Untie | fConn_WriteUnbuffered)
1129 {
1130     return;
1131 }
1132 
1133 
CConn_FtpStream(const SConnNetInfo & net_info,TFTP_Flags flgs,const SFTP_Callback * cmcb,const STimeout * timeout,size_t buf_size)1134 CConn_FtpStream::CConn_FtpStream(const SConnNetInfo&  net_info,
1135                                  TFTP_Flags           flgs,
1136                                  const SFTP_Callback* cmcb,
1137                                  const STimeout*      timeout,
1138                                  size_t               buf_size)
1139     : CConn_IOStream(s_FtpConnectorBuilder(&net_info,
1140                                            flgs,
1141                                            cmcb,
1142                                            timeout),
1143                      timeout, buf_size,
1144                      fConn_Untie | fConn_WriteUnbuffered)
1145 {
1146     return;
1147 }
1148 
1149 
Drain(const STimeout * timeout)1150 EIO_Status CConn_FtpStream::Drain(const STimeout* timeout)
1151 {
1152     const STimeout* r_timeout = kInfiniteTimeout/*0*/;
1153     const STimeout* w_timeout = kInfiniteTimeout/*0*/;
1154     static char sink[16384]; /*NB:shared sink*/
1155     CONN conn = GetCONN();
1156     if (conn) {
1157         size_t n;
1158         r_timeout = CONN_GetTimeout(conn, eIO_Read);
1159         w_timeout = CONN_GetTimeout(conn, eIO_Write);
1160         _VERIFY(SetTimeout(eIO_Read,  timeout) == eIO_Success);
1161         _VERIFY(SetTimeout(eIO_Write, timeout) == eIO_Success);
1162         // Cause any upload-in-progress to abort
1163         CONN_Read(conn, sink, sizeof(sink), &n, eIO_ReadPlain);
1164         // Cause any command-in-progress to abort
1165         CONN_Write(conn, "NOOP\n", 5, &n, eIO_WritePersist);
1166     }
1167     clear();
1168     while (read(sink, sizeof(sink)))
1169         ;
1170     if (!conn)
1171         return eIO_Closed;
1172     EIO_Status status;
1173     do {
1174         size_t n;
1175         status = CONN_Read(conn, sink, sizeof(sink), &n, eIO_ReadPersist);
1176     } while (status == eIO_Success);
1177     _VERIFY(CONN_SetTimeout(conn, eIO_Read,  r_timeout) == eIO_Success);
1178     _VERIFY(CONN_SetTimeout(conn, eIO_Write, w_timeout) == eIO_Success);
1179     clear();
1180     return status == eIO_Closed ? eIO_Success : status;
1181 }
1182 
1183 
CConn_FTPDownloadStream(const string & host,const string & file,const string & user,const string & pass,const string & path,unsigned short port,TFTP_Flags flgs,const SFTP_Callback * cmcb,Uint8 offset,const STimeout * timeout,size_t buf_size)1184 CConn_FTPDownloadStream::CConn_FTPDownloadStream(const string&        host,
1185                                                  const string&        file,
1186                                                  const string&        user,
1187                                                  const string&        pass,
1188                                                  const string&        path,
1189                                                  unsigned short       port,
1190                                                  TFTP_Flags           flgs,
1191                                                  const SFTP_Callback* cmcb,
1192                                                  Uint8                offset,
1193                                                  const STimeout*      timeout,
1194                                                  size_t               buf_size)
1195     : CConn_FtpStream(host, user, pass, path, port, flgs, cmcb,
1196                       timeout, buf_size)
1197 {
1198     if (!file.empty())
1199         x_InitDownload(file, offset);
1200 }
1201 
1202 
CConn_FTPDownloadStream(const SConnNetInfo & net_info,TFTP_Flags flgs,const SFTP_Callback * cmcb,Uint8 offset,const STimeout * timeout,size_t buf_size)1203 CConn_FTPDownloadStream::CConn_FTPDownloadStream(const SConnNetInfo&  net_info,
1204                                                  TFTP_Flags           flgs,
1205                                                  const SFTP_Callback* cmcb,
1206                                                  Uint8                offset,
1207                                                  const STimeout*      timeout,
1208                                                  size_t               buf_size)
1209     : CConn_FtpStream(net_info, flgs | fFTP_IgnorePath, cmcb,
1210                       timeout, buf_size)
1211 {
1212     if (net_info.path[0])
1213         x_InitDownload(net_info.path, offset);
1214 }
1215 
1216 
x_InitDownload(const string & file,Uint8 offset)1217 void CConn_FTPDownloadStream::x_InitDownload(const string& file, Uint8 offset)
1218 {
1219     // Use '\n' here instead of NcbiFlush to avoid (and thus make silent)
1220     // flush errors on retrieval of nonexistent (or bad) files / directories...
1221     EIO_Status status;
1222     if (offset) {
1223         write("REST ", 5) << NStr::UInt8ToString(offset) << '\n';
1224         status  = Status(eIO_Write);
1225     } else
1226         status  = eIO_Success;
1227     if (good()  &&  status == eIO_Success) {
1228         bool directory = NStr::EndsWith(file, '/');
1229         write(directory ? "NLST " : "RETR ", 5) << file << '\n';
1230         status  = Status(eIO_Write);
1231     }
1232     if (status != eIO_Success)
1233         clear(NcbiBadbit);
1234 }
1235 
1236 
CConn_FTPUploadStream(const string & host,const string & user,const string & pass,const string & file,const string & path,unsigned short port,TFTP_Flags flgs,Uint8 offset,const STimeout * timeout)1237 CConn_FTPUploadStream::CConn_FTPUploadStream(const string&   host,
1238                                              const string&   user,
1239                                              const string&   pass,
1240                                              const string&   file,
1241                                              const string&   path,
1242                                              unsigned short  port,
1243                                              TFTP_Flags      flgs,
1244                                              Uint8           offset,
1245                                              const STimeout* timeout)
1246     : CConn_FtpStream(host, user, pass, path, port, flgs, 0/*cmcb*/,
1247                       timeout)
1248 {
1249     if (!file.empty())
1250         x_InitUpload(file, offset);
1251 }
1252 
1253 
CConn_FTPUploadStream(const SConnNetInfo & net_info,TFTP_Flags flgs,Uint8 offset,const STimeout * timeout)1254 CConn_FTPUploadStream::CConn_FTPUploadStream(const SConnNetInfo& net_info,
1255                                              TFTP_Flags          flgs,
1256                                              Uint8               offset,
1257                                              const STimeout*     timeout)
1258     : CConn_FtpStream(net_info, flgs | fFTP_IgnorePath, 0/*cmcb*/,
1259                       timeout)
1260 {
1261     if (net_info.path[0])
1262         x_InitUpload(net_info.path, offset);
1263 }
1264 
1265 
x_InitUpload(const string & file,Uint8 offset)1266 void CConn_FTPUploadStream::x_InitUpload(const string& file, Uint8 offset)
1267 {
1268     EIO_Status status;
1269     if (offset) {
1270         write("REST ", 5) << NStr::UInt8ToString(offset) << NcbiFlush;
1271         status  = Status(eIO_Write);
1272     } else
1273         status  = eIO_Success;
1274     if (good()  &&  status == eIO_Success) {
1275         write("STOR ", 5) << file << NcbiFlush;
1276         status  = Status(eIO_Write);
1277     }
1278     if (status != eIO_Success)
1279         clear(NcbiBadbit);
1280 }
1281 
1282 
1283 /* non-public class */
1284 class CConn_FileStream : public CConn_IOStream
1285 {
1286 public:
CConn_FileStream(const string & ifname,const string & ofname=kEmptyStr,SFILE_ConnAttr * attr=0)1287     CConn_FileStream(const string&   ifname,
1288                      const string&   ofname = kEmptyStr,
1289                      SFILE_ConnAttr* attr   = 0)
1290         : CConn_IOStream(TConnector(FILE_CreateConnectorEx(ifname.c_str(),
1291                                                            ofname.c_str(),
1292                                                            attr)),
1293                          0/*timeout*/, 0/*unbuffered*/,
1294                          fConn_Untie)
1295     {
1296         return;
1297     }
1298 };
1299 
1300 
x_IsIdentifier(const string & str)1301 static bool x_IsIdentifier(const string& str)
1302 {
1303     const char* s = str.c_str();
1304     if (!isalpha((unsigned char)(*s)))
1305         return false;
1306     for (++s;  *s;  ++s) {
1307         if (!isalnum((unsigned char)(*s))  &&  *s != '_')
1308             return false;
1309     }
1310     return true;
1311 }
1312 
1313 
NcbiOpenURL(const string & url,size_t buf_size)1314 extern CConn_IOStream* NcbiOpenURL(const string& url, size_t buf_size)
1315 {
1316     {
1317         class CInPlaceConnIniter : protected CConnIniter
1318         {
1319         } conn_initer;  /*NCBI_FAKE_WARNING*/
1320     }
1321     bool svc = x_IsIdentifier(url);
1322 
1323     AutoPtr<SConnNetInfo> net_info
1324         (ConnNetInfo_CreateInternal
1325          (svc
1326           ? TTempCharPtr(SERV_ServiceName(url.c_str())).get()
1327           : NStr::StartsWith(url, "ftp://", NStr::eNocase)
1328           ? "_FTP" : 0));
1329     if (svc)
1330         return new CConn_ServiceStream(url, fSERV_Any, net_info.get());
1331 
1332     unsigned int   host;
1333     unsigned short port;
1334     if (url.size() == CSocketAPI::StringToHostPort(url, &host, &port)
1335         &&  port  &&  net_info.get()) {
1336         net_info->req_method = eReqMethod_Connect;
1337     }
1338 
1339     if (ConnNetInfo_ParseURL(net_info.get(), url.c_str())) {
1340         _ASSERT(net_info);  // otherwise ConnNetInfo_ParseURL() would've failed
1341         if (net_info->req_method == eReqMethod_Connect) {
1342             return new CConn_SocketStream(*net_info, 0, 0, fSOCK_LogDefault,
1343                                           net_info->timeout, buf_size);
1344         }
1345         switch (net_info->scheme) {
1346         case eURL_Https:
1347         case eURL_Http:
1348             return new CConn_HttpStream(net_info.get(), kEmptyStr, 0, 0, 0, 0,
1349                                         fHTTP_AutoReconnect,
1350                                         kDefaultTimeout, buf_size);
1351         case eURL_File:
1352             if (*net_info->host  ||  net_info->port)
1353                 break; // not supported
1354             if (net_info->debug_printout) {
1355                 // manual cleanup of most fields req'd
1356                 net_info->req_method = eReqMethod_Any;
1357                 net_info->external = 0;
1358                 net_info->firewall = 0;
1359                 net_info->stateless = 0;
1360                 net_info->lb_disable = 0;
1361                 net_info->http_version = 0;
1362                 net_info->http_push_auth = 0;
1363                 net_info->http_proxy_leak = 0;
1364                 net_info->user[0] = '\0';
1365                 net_info->pass[0] = '\0';
1366                 net_info->http_proxy_host[0] = '\0';
1367                 net_info->http_proxy_port    =   0;
1368                 net_info->http_proxy_user[0] = '\0';
1369                 net_info->http_proxy_pass[0] = '\0';
1370                 net_info->max_try = 0;
1371                 net_info->timeout = kInfiniteTimeout/*0*/;
1372                 ConnNetInfo_SetUserHeader(net_info.get(), 0);
1373                 if (net_info->http_referer) {
1374                     free((void*) net_info->http_referer);
1375                     net_info->http_referer = 0;
1376                 }
1377                 ConnNetInfo_Log(net_info.get(), eLOG_Note, CORE_GetLOG());
1378             }
1379             return new CConn_FileStream(net_info->path);
1380         case eURL_Ftp:
1381             if (!net_info->user[0]) {
1382                 strcpy(net_info->user, "ftp");
1383                 if (!net_info->pass[0])
1384                     strcpy(net_info->pass, "-none@");
1385             }
1386             return new CConn_FTPDownloadStream(*net_info, 0, 0, 0,
1387                                                net_info->timeout, buf_size);
1388         default:
1389             break;
1390         }
1391     }
1392     return 0;
1393 }
1394 
1395 
1396 END_NCBI_SCOPE
1397