/* ** Copyright (c) 2007 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public ** License version 2 as published by the Free Software Foundation. ** ** This program 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 ** General Public License for more details. ** ** You should have received a copy of the GNU 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. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code that implements the client-side HTTP protocol */ #include "config.h" #include "http.h" #ifdef __MINGW32__ # include # include #else # include # include # include # include #endif #include #include #include /* ** Persistent information about the HTTP connection. */ #ifdef __MINGW32__ static WSADATA ws_info; static int pSocket = 0; /* The socket on which we talk to the server on */ #else static FILE *pSocket = 0; /* The socket filehandle on which we talk to the server */ #endif /* ** Winsock must be initialize before use. This helper method allows us to ** always call ws_init in our code regardless of platform but only actually ** initialize winsock on the windows platform. */ static void ws_init(){ #ifdef __MINGW32__ if (WSAStartup(MAKEWORD(2,0), &ws_info) != 0){ fossil_panic("can't initialize winsock"); } #endif } /* ** Like ws_init, winsock must also be cleaned up after. */ static void ws_cleanup(){ #ifdef __MINGW32__ WSACleanup(); #endif } /* ** Open a socket connection to the server. Return 0 on success and ** non-zero if an error occurs. */ static int http_open_socket(void){ static struct sockaddr_in addr; /* The server address */ static int addrIsInit = 0; /* True once addr is initialized */ int s; ws_init(); if( !addrIsInit ){ addr.sin_family = AF_INET; addr.sin_port = htons(g.urlPort); *(int*)&addr.sin_addr = inet_addr(g.urlName); if( -1 == *(int*)&addr.sin_addr ){ #ifndef FOSSIL_STATIC_LINK struct hostent *pHost; pHost = gethostbyname(g.urlName); if( pHost!=0 ){ memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); }else #endif { fossil_panic("can't resolve host name: %s\n", g.urlName); } } addrIsInit = 1; /* Set the Global.zIpAddr variable to the server we are talking to. ** This is used to populate the ipaddr column of the rcvfrom table, ** if any files are received from the server. */ g.zIpAddr = mprintf("%s", inet_ntoa(addr.sin_addr)); } s = socket(AF_INET,SOCK_STREAM,0); if( s<0 ){ fossil_panic("cannot create a socket"); } if( connect(s,(struct sockaddr*)&addr,sizeof(addr))<0 ){ fossil_panic("cannot connect to host %s:%d", g.urlName, g.urlPort); } #ifdef __MINGW32__ pSocket = s; #else pSocket = fdopen(s,"r+"); signal(SIGPIPE, SIG_IGN); #endif return 0; } #ifdef __MINGW32__ /* ** Read the socket until a newline '\n' is found. Return the number ** of characters read. pSockId contains the socket handel. pOut ** contains a pointer to the buffer to write to. pOutSize contains ** the maximum size of the line that pOut can handle. */ static int socket_recv_line(int pSockId, char* pOut, int pOutSize){ int received=0; char letter; memset(pOut,0,pOutSize); for(; received0 ){ pOut[received]=letter; if( letter=='\n' ){ break; } }else{ break; } } return received; } /* ** Initialize a blob to the data on an input socket. return ** the number of bytes read into the blob. Any prior content ** of the blob is discarded, not freed. ** ** The function was placed here in http.c due to it's socket ** nature and we did not want to introduce socket headers into ** the socket netural blob.c file. */ int socket_read_blob(Blob *pBlob, int pSockId, int nToRead){ int i=0,read=0; char rbuf[50]; blob_zero(pBlob); while ( i0 && g.urlPath[i-1]=='/' ){ zSep = ""; }else{ zSep = "/"; } blob_appendf(&hdr, "POST %s%sxfer HTTP/1.1\r\n", g.urlPath, zSep); blob_appendf(&hdr, "Host: %s\r\n", g.urlHostname); if( g.fHttpTrace ){ blob_appendf(&hdr, "Content-Type: application/x-fossil-debug\r\n"); }else{ blob_appendf(&hdr, "Content-Type: application/x-fossil\r\n"); } blob_appendf(&hdr, "Content-Length: %d\r\n\r\n", blob_size(&payload)); if( g.fHttpTrace ){ /* When tracing, write the transmitted HTTP message both to standard ** output and into a file. The file can then be used to drive the ** server-side like this: ** ** ./fossil http =2 ){ fossil_fatal("connection to server failed"); } blob_reset(&hdr); blob_reset(&payload); if( g.fHttpTrace ){ printf("HTTP RECEIVE:\n%s\n=======================\n", blob_str(pRecv)); }else{ blob_uncompress(pRecv, pRecv); } } /* ** Make sure the socket to the HTTP server is closed */ void http_close(void){ if( pSocket ){ #ifdef __MINGW32__ closesocket(pSocket); #else fclose(pSocket); #endif pSocket = 0; } /* ** This is counter productive. Each time we open a connection we initialize ** winsock and then when closing we cleanup. It would be better to ** initialize winsock once at application start when we know we are going to ** use the socket interface and then cleanup once at application exit when ** we are all done with all socket operations. */ ws_cleanup(); }