core/vil/vil_stream_url.cxx
Go to the documentation of this file.
00001 // This is core/vil/vil_stream_url.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // \author fsm
00008 
00009 #include "vil_stream_url.h"
00010 #include <vil/vil_stream_core.h>
00011 
00012 #include <vcl_cassert.h>
00013 #undef sprintf
00014 #include <vcl_cstdio.h>  // sprintf()
00015 #include <vcl_cstring.h>
00016 #include <vcl_cstdlib.h>
00017 #include <vcl_string.h>
00018 #include <vcl_iostream.h>
00019 #include <vcl_fstream.h>
00020 
00021 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
00022 
00023 # include <unistd.h>       // read(), write(), close()
00024 # include <netdb.h>        // gethostbyname(), sockaddr_in()
00025 # include <sys/socket.h>
00026 # include <netinet/in.h>   // htons()
00027 # ifdef __alpha
00028 #  include <fp.h>           // htons() [ on e.g. DEC alpha, htons is in machine/endian.h]
00029 # endif
00030 # define SOCKET int
00031 #elif defined (VCL_WIN32) && !defined(__CYGWIN__)
00032 # include <winsock2.h>
00033 #endif
00034 
00035 
00036 static const
00037 char base64_encoding[]=
00038 {
00039   'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
00040   'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
00041   'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
00042   'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
00043 };
00044 
00045 static char out_buf[4];
00046 
00047 static const char * encode_triplet(char data[3], unsigned n)
00048 {
00049   assert (n>0 && n <4);
00050   out_buf[0] = base64_encoding[(data[0] & 0xFC) >> 2];
00051 
00052   if (n==1)
00053   {
00054     out_buf[2] = out_buf[3] = '=';
00055     return out_buf;
00056   }
00057 
00058   out_buf[1] = base64_encoding[
00059     ((data[0] & 0x3) << 4) + ((data[1] & 0xf0)>>4)];
00060   out_buf[2] = base64_encoding[
00061     ((data[1] & 0xf) << 2) + ((data[2] & 0xc0)>>6)];
00062 
00063   if (n==2)
00064   {
00065     out_buf[3] = '=';
00066     return out_buf;
00067   }
00068 
00069   out_buf[3] = base64_encoding[ (data[2] & 0x3f) ];
00070   return out_buf;
00071 }
00072 
00073 //=======================================================================
00074 
00075 static vcl_string encode_base64(const vcl_string& in)
00076 {
00077   vcl_string out;
00078   unsigned int i = 0, line_octets = 0;
00079   const unsigned int l = (unsigned int)(in.size());
00080   char data[3];
00081   while (i < l)
00082   {
00083     data[0] = in[i++];
00084     data[1] = data[2] = 0;
00085 
00086     if (i == l)
00087     {
00088       out.append(encode_triplet(data,1),4);
00089       return out;
00090     }
00091 
00092     data[1] = in[i++];
00093 
00094     if (i == l)
00095     {
00096       out.append(encode_triplet(data,2),4);
00097       return out;
00098     }
00099 
00100     data[2] = in[i++];
00101 
00102     out.append(encode_triplet(data,3),4);
00103 
00104     if (line_octets >= 68/4) // print carriage return
00105     {
00106       out.append("\r\n",2);
00107       line_octets = 0;
00108     }
00109     else
00110       ++line_octets;
00111   }
00112 
00113   return out;
00114 }
00115 
00116 
00117 vil_stream_url::vil_stream_url(char const *url)
00118   : u_(0)
00119 {
00120   if (vcl_strncmp(url, "http://", 7) != 0)
00121     return; // doesn't look like a URL to me....
00122 
00123   char const *p = url+7;
00124   while (*p && *p!='/')
00125     ++p;
00126 
00127   // split URL into auth, host, path and port number.
00128   vcl_string host = vcl_string(url+7, p);
00129   vcl_string path = (*p) ? p+1 : "";
00130   vcl_string auth;
00131   int port = 80; // default
00132 
00133   // authentication
00134   for (unsigned int i=0; i<host.size(); ++i)
00135     if (host[i] == '@') {
00136       auth = vcl_string(host.c_str(), host.c_str()+i);
00137       host = vcl_string(host.c_str()+i+1, host.c_str() + host.size());
00138       break;
00139     }
00140 
00141   // port?
00142   if (host.size() > 0)
00143   for (unsigned int i=(unsigned int)(host.size()-1); i>0; --i)
00144     if (host[i] == ':') {
00145       port = vcl_atoi(host.c_str() + i + 1);
00146       host = vcl_string(host.c_str(), host.c_str() + i);
00147       break;
00148     }
00149 
00150   // do character translation
00151   for (unsigned k =0; k < path.size(); ++k)
00152     if (path[k] == ' ')
00153       path.replace(k, 1, "%20");
00154     else if (path[k] == '%')
00155       path.replace(k, 1, "%25");
00156 
00157   // so far so good.
00158 #ifdef DEBUG
00159   vcl_cerr << "auth = \'" << auth << "\'\n"
00160            << "host = \'" << host << "\'\n"
00161            << "path = \'" << path << "\'\n"
00162            << "port = " << port << vcl_endl;
00163 #endif
00164 
00165 #if defined(VCL_WIN32) && !defined(__CYGWIN__)
00166   static int called_WSAStartup;
00167   if (called_WSAStartup==0)
00168   {
00169     WORD wVersionRequested;
00170     WSADATA wsaData;
00171 
00172     wVersionRequested = MAKEWORD( 2, 2 );
00173 
00174     /* int err = */ WSAStartup( wVersionRequested, &wsaData );
00175   }
00176 #endif
00177 
00178   // create socket endpoint.
00179   SOCKET tcp_socket = socket(PF_INET,      // IPv4 protocols.
00180                              SOCK_STREAM,  // two-way, reliable, connection-based stream socket.
00181                              PF_UNSPEC);   // protocol number.
00182 
00183 #if defined(VCL_WIN32) && !defined(__CYGWIN__)
00184   if (tcp_socket == INVALID_SOCKET) {
00185     vcl_cerr << __FILE__ ": failed to create socket.\n";
00186 # ifndef NDEBUG
00187     vcl_cerr << "error code : " << WSAGetLastError() << vcl_endl;
00188 # endif
00189     return;
00190   }
00191 #else
00192   if (tcp_socket < 0)
00193     vcl_cerr << __FILE__ ": failed to create socket.\n";
00194 #endif
00195 
00196 #ifdef DEBUG
00197   vcl_cerr << __FILE__ ": tcp_sockect = " << tcp_socket << vcl_endl;
00198 #endif
00199 
00200   // get network address of server.
00201   hostent *hp = gethostbyname(host.c_str());
00202   if (! hp) {
00203     vcl_cerr << __FILE__ ": failed to lookup host\n";
00204 #if defined(VCL_WIN32) && !defined(__CYGWIN__)
00205     closesocket(tcp_socket);
00206 #else
00207     close(tcp_socket);
00208 #endif
00209     return;
00210   }
00211 
00212   // make socket address.
00213   sockaddr_in my_addr;
00214   my_addr.sin_family = AF_INET;
00215   my_addr.sin_port = htons(port);  // convert port number to network byte order..
00216   vcl_memcpy(&my_addr.sin_addr, hp->h_addr_list[0], hp->h_length);
00217 
00218   // connect to server.
00219   if (connect(tcp_socket , (sockaddr *) &my_addr, sizeof my_addr) < 0) {
00220     vcl_cerr << __FILE__ ": failed to connect to host\n";
00221     //perror(__FILE__);
00222 #if defined(VCL_WIN32) && !defined(__CYGWIN__)
00223     closesocket(tcp_socket);
00224 #else
00225     close(tcp_socket);
00226 #endif
00227     return;
00228   }
00229 
00230   // buffer for data transfers over socket.
00231 
00232   char buffer[4096];
00233 
00234   // send HTTP 1.1 request.
00235   vcl_snprintf(buffer, 4090, "GET /%s / HTTP/1.1\r\n", path.c_str());
00236   if (auth != "")
00237     vcl_snprintf(buffer+vcl_strlen(buffer), 4090-vcl_strlen(buffer),
00238                  "Authorization:  Basic %s\n", encode_base64(auth).c_str());
00239 
00240   if (vcl_snprintf(buffer+vcl_strlen(buffer), 4090-vcl_strlen(buffer), "\r\n") < 0)
00241   {
00242     vcl_cerr << "ERROR: vil_stream_url buffer overflow.";
00243     vcl_abort();
00244   }
00245 
00246 #if defined(VCL_WIN32) && !defined(__CYGWIN__)
00247   if (send(tcp_socket, buffer, (int)vcl_strlen(buffer), 0) < 0)
00248   {
00249     vcl_cerr << __FILE__ ": error sending HTTP request\n";
00250     closesocket(tcp_socket);
00251     return;
00252   }
00253 #else
00254   if (::write(tcp_socket, buffer, vcl_strlen(buffer)) < 0)
00255   {
00256     vcl_cerr << __FILE__ ": error sending HTTP request\n";
00257     close(tcp_socket);
00258     return;
00259   }
00260 #endif
00261 
00262 
00263 //  vcl_ofstream test2("/test2.jpg", vcl_ios_binary);
00264 
00265   // read from socket into memory.
00266   u_ = new vil_stream_core;
00267   u_->ref();
00268   {
00269     unsigned entity_marker = 0; // count end of header CR and LFs
00270     vil_streampos n;
00271 #if defined(VCL_WIN32) && !defined(__CYGWIN__)
00272     while ((n = recv(tcp_socket, buffer, sizeof buffer,0 )) > 0L)
00273 #else
00274     while ((n = ::read(tcp_socket, buffer, sizeof buffer)) > 0L)
00275 #endif
00276     {
00277       // search for the CRLFCRLF sequence that marks the end
00278       // of the http response header
00279       assert (entity_marker < 5);
00280       if (entity_marker==4)
00281       {
00282         u_->write(buffer, n);
00283 //      test2.write(buffer, n);
00284       }
00285       else
00286       {
00287         for (vil_streampos i=0; i<n; ++i)
00288         {
00289           if ((entity_marker==2||entity_marker==0) && buffer[i]=='\r') entity_marker++;
00290           else if (entity_marker==1 && buffer[i]=='\n') entity_marker++;
00291           else if (entity_marker==3 && buffer[i]=='\n')
00292           {
00293             entity_marker++;
00294             u_->write(buffer+i+1, n-i-1);
00295 //            test2.write(buffer+i+1, n-i-1);
00296             break;
00297           }
00298           else entity_marker=0;
00299         }
00300       }
00301     }
00302   }
00303 
00304 #if 0 // useful for figuring out where the error is
00305   char btest[4096];
00306   vcl_ofstream test("/test.jpg", vcl_ios_binary);
00307   u_->seek(0L);
00308   while (vil_streampos bn = u_->read(btest, 4096L))
00309     test.write(btest, bn);
00310   test.close();
00311 #endif
00312 
00313 
00314   // close connection to server.
00315 #if defined(VCL_WIN32) && !defined(__CYGWIN__)
00316   closesocket(tcp_socket);
00317 #else
00318   close(tcp_socket);
00319 #endif
00320 }
00321 
00322 vil_stream_url::~vil_stream_url()
00323 {
00324   if (u_) {
00325     u_->unref();
00326     u_ = 0;
00327   }
00328 }