/* CVSNT Generic API Copyright (C) 2004 Tony Hoyle and March-Hare Software Ltd This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Win32 specific */ #define WIN32_LEAN_AND_MEAN #include #include #define SECURITY_WIN32 #include #include #include "../lib/api_system.h" #include "../cvs_string.h" #include "../HttpSocket.h" #include "../SSPIHandler.h" #include "../../version.h" /* ** Translation Table as described in RFC1113 */ static const unsigned char cb64[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; #include // in autoproxy bool GetProxyForHost(const char *url, const char *hostname, cvs::string& proxy, cvs::string& proxy_port); CHttpSocket::CHttpSocket() { m_bProxy = false; m_nAuthStage=0; } CHttpSocket::~CHttpSocket() { } bool CHttpSocket::create(const char *urlHost, bool auto_proxy /*= true*/, const char *proxy_address /*= NULL*/, const char *proxy_port /*= NULL*/, const char *username /*= NULL*/, const char *password /*= NULL*/) { if(!_setUrl(urlHost)) return false; m_bAutoProxy = auto_proxy; m_proxyUser = username?username:""; m_proxyPassword = password?password:""; if(proxy_address && proxy_port) { m_proxyName = proxy_address; m_proxyPort = proxy_port; m_bProxy = true; } return _create(); } bool CHttpSocket::_setUrl(const char *urlHost) { if(!urlHost || strncmp(urlHost,"http://",7)) return false; cvs::string url_string = urlHost; char *address,*port,*p; address=(char*)(url_string.data()+7); p=strpbrk(address,":/"); if(p && *p==':') { *p='\0'; port=p+1; p=strchr(port,'/'); } else port="80"; if(p) *p='\0'; // We don't process the URL location here m_urlHost = urlHost; m_port = port; m_address = address; return true; } bool CHttpSocket::_create() { m_nAuthStage=0; if(m_bAutoProxy) { m_bProxy = GetProxyForHost(m_urlHost.c_str(),m_address.c_str(),m_proxyName,m_proxyPort); } if(m_bProxy) return CSocketIO::create(m_proxyName.c_str(),m_proxyPort.c_str(),false); else return CSocketIO::create(m_address.c_str(),m_port.c_str(),false); } bool CHttpSocket::request(const char *command, const char *location, const char *content /* = NULL */, size_t content_length /* = 0 */) { CSSPIHandler sspi; cvs::string loc; if(!command || !*command || !location || location[0]!='/') return false; if(!m_activeSocket && !CSocketIO::connect()) return false; if(!content && content_length) content_length = 0; bool bAgain = false; m_requestHeaderList["Proxy-Connection"].clear(); if(m_bProxy) m_requestHeaderList["Proxy-Connection"].push_back("Keep-Alive"); do { bAgain = false; if(!_request(command,location,content,content_length)) return false; switch(m_responseCode) { case 302: // Moved // _asm int 3 loc = m_responseHeaderList["Location"][0].c_str(); location = loc.c_str(); if(!_setUrl(location)) return false; // Do proxies really change across redirects?? I suspect this is a rare case... // CSocketIO::close(); // if(!_create(); // return false; // if(!CSocketIO::connect()) // return false; location=strchr(location+7,'/'); if(!location) location="/"; bAgain = true; break; case 407: // Proxy authentication required { char *proxy_auth,*p; char *auth_type=NULL,*realm; cvs::string negotiate,neg_enc; std::vector& auth = m_responseHeaderList["Proxy-Authenticate"]; if(m_nAuthStage==-1) break; // Failed to auth if(!m_nAuthStage) { // NTLM doesn't work - there's some magic voodoo you have to do to use it // that isn't in any of the documentation, and it's impossible to find by // experimentation // // The code is all there and is correct according to the available documentation... // bool bHasNtlm = sspi.init("NTLM"); for(size_t n=0; nfirst.c_str(),"Content-Length") || !strcmp(i->first.c_str(),"Host")) continue; for(size_t j=0; jsecond.size(); j++) { if(CSocketIO::printf("%s: %s\r\n",i->first.c_str(),i->second[j].c_str())<0) return false; } } CSocketIO::printf("\r\n"); if(content_length) if(CSocketIO::send(content,(int)content_length)<0) return false; CSocketIO::getline(line); char *p,*l=(char*)line.c_str(); p=strchr(l,' '); if(p) *p='\0'; m_responseProtocol = l; if(p) { l=++p; p=strchr(p,' '); if(p) *p='\0'; m_responseCode = atoi(l); } if(p) m_responseString = p+1; m_responseHeaderList.clear(); while(CSocketIO::getline(line)) { if(!line.length()) break; l=(char*)line.c_str(); p=strchr(l,':'); if(p) { *(p++)='\0'; while(*p && isspace((unsigned char)*p)) p++; m_responseHeaderList[l].push_back(p); } else m_responseHeaderList[l].push_back(""); } if(m_responseHeaderList.find("Content-Length")!=m_responseHeaderList.end()) { size_t len = atoi(m_responseHeaderList["Content-Length"][0].c_str()); m_content.resize(len); if(len) { if(CSocketIO::recv((char*)m_content.data(),(int)len)<0) return false; } } else m_content=""; return true; } /************************************************************ * uuencode/decode functions ************************************************************/ // // Taken from NCSA HTTP and wwwlib. // // NOTE: These conform to RFC1113, which is slightly different then the Unix // uuencode and uudecode! // static const int pr2six[256]={ 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63, 52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,1,2,3,4,5,6,7,8,9, 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27, 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64 }; static const char six2pr[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M', 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', 'a','b','c','d','e','f','g','h','i','j','k','l','m', 'n','o','p','q','r','s','t','u','v','w','x','y','z', '0','1','2','3','4','5','6','7','8','9','+','/' }; bool CHttpSocket::base64Enc(const unsigned char *from, size_t len, cvs::string& to) { unsigned char *outptr; unsigned int i; // // Resize the buffer to 133% of the incoming data // to.resize(len + ((len + 3) / 3) + 4); outptr = (unsigned char *)to.data(); for (i=0; i> 2]; /* c1 */ *(outptr++) = six2pr[((*from << 4) & 060) | ((from[1] >> 4) & 017)]; /*c2*/ *(outptr++) = six2pr[((from[1] << 2) & 074) | ((from[2] >> 6) & 03)];/*c3*/ *(outptr++) = six2pr[from[2] & 077]; /* c4 */ from += 3; } /* If len was not a multiple of 3, then we have encoded too * many characters. Adjust appropriately. */ if(i == len+1) { /* There were only 2 bytes in that last group */ outptr[-1] = '='; } else if(i == len+2) { /* There was only 1 byte in that last group */ outptr[-1] = '='; outptr[-2] = '='; } *outptr = '\0'; return true; } bool CHttpSocket::base64Dec(const unsigned char *from, size_t len, cvs::string& to) { int nbytesdecoded; const unsigned char *bufin = from; unsigned char *bufout; int nprbytes; /* Figure out how many characters are in the input buffer. * If this would decode into more bytes than would fit into * the output buffer, adjust the number of input bytes downwards. */ while(pr2six[*(bufin++)] <= 63) ; nprbytes = (int)(bufin - from - 1); nbytesdecoded = ((nprbytes+3)/4) * 3; to.resize(nbytesdecoded+4); bufout = (unsigned char *)to.data(); bufin = from; while (nprbytes > 0) { *(bufout++) = (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4); *(bufout++) = (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2); *(bufout++) = (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]); bufin += 4; nprbytes -= 4; } if(nprbytes & 03) { if(pr2six[bufin[-2]] > 63) nbytesdecoded -= 2; else nbytesdecoded -= 1; } to.resize(nbytesdecoded); return true; }