/******************************************************************************* * File: MySock.cpp * * Version: 0.2 * * Date: 20081002 * * Author: Gaspar Fernández (helyo@totaki.com) * * * * Copyright (C) 2008 Gaspar Fernández * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * 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 program. If not, see . * * * ******************************************************************************** * Description: * Class and functions to create socket connections and fetch information * from http servers. It is part of another project I'm in. (Some comments are * in Spanish. * * Change History: * Date Author Modification * 10.02.2008 Gaspar Fernández Initial release * 31.10.2010 Gaspar Fernández Bug Corrections ********************************************************************************/ #include "MySock.h" #include /************************************************************* * Function: extract_key_value * ************************************************************* * Description: * * Extract information separated with a white space. * * We consider KEY before the whitespace and VALUE * * after that. * * * * Input: * * string str - String containing the relation * * * * Output: * * TKey_Value struct containing separated key and value. * * * * Change History: * * Date Author Modification * * * *************************************************************/ TKey_Value extract_key_value(string str) { TKey_Value data; char buf[512]; // Max characters try // Sólo si es posible { sscanf(str.data(), "%s", buf); // Extract header data.value=str.substr(strlen(buf)); data.key=buf; } catch(exception& e) { // Do nothing } return data; } /************************************************************* * Constructor MySock() * ************************************************************* * Description: * * Creates MySock class and initialize it. The second * * constructor uses an HTTP URI to read information stored * * there. * * * * Input: * * string uri (in the second one) * * * * Change History: * * Date Author Modification * * * *************************************************************/ MySock::MySock() { this->error=NO_ERROR; this->connected=false; } MySock::MySock(string uri) { int port; string host; string params; string protostr; string req; this->error=NO_ERROR; string::size_type pos = uri.find("//", 0); string::size_type pos2; if ((pos!=string::npos) && (pos==uri.find("/", 0)) && (pos>0)) // Sin barras antes { protostr = uri.substr(0,(pos-1)); if (protostr=="http") { port=80; pos2=uri.find("/", (pos+2)); if (pos2==string::npos) pos2=uri.length(); host=uri.substr(pos+2, pos2-pos-2); params=uri.substr(pos2); this->MakeConnection(host, port); req="GET "+params+" HTTP/1.0"+CRLF+ "Host: "+host+"\r\n"+ CRLF; if (this->connected) this->SendData(req); } else { this->error=NO_VALID_PROTOCOL; } } else this->error=NO_VALID_PROTOCOL; } /************************************************************* * Constructor MySock() * ************************************************************* * Description: * * Creates MySock class and initialize it. Connect with * * a remote server using specified port. * * * * Input: * * string server - Server to connect to * * int port - Port in the remote server * * * * Change History: * * Date Author Modification * * * *************************************************************/ MySock::MySock(string server, int port) { this->MakeConnection(server, port); } /************************************************************* * Method: MakeConnection() * ************************************************************* * Description: * * Makes a connection to an specified server using a * * remote port * * * * Input: * * string server * * int port * * * * Output: * * Nothing * * * * Change History: * * Date Author Modification * * * *************************************************************/ void MySock::MakeConnection(string server, int port) { struct sockaddr_in serverAddress; struct hostent *hostInfo; this->connected=false; this->error=NO_ERROR; hostInfo = gethostbyname(server.data()); this->error=(hostInfo==NULL)?CANT_RESOLVE_HOST:NO_ERROR; if (this->error==0) { this->sockd = socket(AF_INET, SOCK_STREAM, 0); this->error = (this->sockd<0)?CANT_CREATE_SOCKET:NO_ERROR; } // Connect to server. First we have to set some fields in the // serverAddress structure. The system will assign me an arbitrary // local port that is not in use. if (this->error==0) { serverAddress.sin_family = hostInfo->h_addrtype; memcpy((char *) &serverAddress.sin_addr.s_addr, hostInfo->h_addr_list[0], hostInfo->h_length); serverAddress.sin_port = htons(port); this->error= (connect(this->sockd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0)?CANT_CONNECT:NO_ERROR; } this->connected=(error==NO_ERROR); } /************************************************************* * Method: SendData() * ************************************************************* * Description: * * Sends information through the already stablished * * connection. * * * * Input: * * string data - Data being sent * * * * Output: * * Nothing * * * * Change History: * * Date Author Modification * * * *************************************************************/ int MySock::SendData(string data) { if (this->error==0) this->error = (write(this->sockd,data.data(),strlen(data.data()))<0)?CANT_SEND_DATA:NO_ERROR; return this->error; } /************************************************************* * Method: GetTextData() * ************************************************************* * Description: * * Read received data * * * * Input: * * Nothing * * * * Output: * * string - returns data received. * * * * Change History: * * Date Author Modification * * * *************************************************************/ string MySock::GetTextData() { int n; char buffer[1024]; string out(""); if (this->error==0) { bzero(buffer,1024); while ((n=read(this->sockd,buffer,1023)>0)) { if (n>=0) out+=buffer; else { error=CANT_READ_DATA; break; } bzero(buffer,1024); } return out; } return NULL; } /************************************************************* * Method: GetHTTPData() * ************************************************************* * Description: * * We connect to an HTTP server and We Want to Read and * * identify headers and contents. We use this function * * * * Input: * * Nothing * * * * Output: * * HTTP_Request structure - Returns headers and data * * * * Change History: * * Date Author Modification * * * *************************************************************/ HTTP_Request *MySock::GetHTTPData() { string txtData; int i=0; // Cuenta líneas string::size_type pos, pos2; HTTP_Request *http; string header; string value; bool err, data; char buf[255], buf2[255]; TKey_Value hdata; if (this->error==NO_ERROR) { txtData=this->GetTextData(); // Close the socket, we will analyze data this->closeConnection(); data=false; err=false; pos=0; http=new HTTP_Request; while ((!data) && (!err)) { pos2=txtData.find("\r\n",pos); if (pos2==string::npos) err=true; else if (pos2==pos) data=true; else { header=txtData.substr(pos,pos2-pos); // cout<statusstr=header; sscanf(header.data(),"%s %s", buf, buf2); // HTTP/1.0 200 http->status=atoi(buf2); } else { hdata=extract_key_value(header); if (hdata.key=="Date:") http->date=hdata.value; else if (hdata.key=="Server: ") http->server=hdata.value; else if (hdata.key=="Last-Modified") http->last_modified=hdata.value; else if (hdata.key=="Content-Type") http->content_type=hdata.value; else if (hdata.key=="Content-Length") http->content_length=hdata.value; } pos=pos2+2; i++; } } if (err) { this->error=NO_VALID_HEADERS; return NULL; } else http->data=txtData.substr(pos2+2); // Skip \r\n return http; } return NULL; } void MySock::closeConnection() { if (connected) { close(sockd); connected=false; } } MySock::~MySock() { closeConnection(); } // It is a test function to use MySock class. It was written for DWGO // int main(int argc, char *argv[]) // { // string servidor; // MySock sock; // int portno; // HTTP_Request *http; // if (argc < 3) { // fprintf(stderr,"usage %s hostname port\n", argv[0]); // exit(0); // } // portno = atoi(argv[2]); // servidor=argv[1]; // // sock = MySock(servidor, portno); // // sock.SendData("GET /pub/data/observations/metar/decoded/LEMG.TXT HTTP/1.0\n\n"); // // cout<status<<"Fecha: "<date<data; // return 0; // }