1 // ----------------------------------------------------------------------------
2 //
3 // flxmlrpc Copyright (c) 2015 by W1HKJ, Dave Freese <iam_w1hkj@w1hkj.com>
4 //
5 // XmlRpc++ Copyright (c) 2002-2008 by Chris Morley
6 //
7 // This file is part of fldigi
8 //
9 // flxmlrpc is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU Lesser General Public License as published by
11 // the Free Software Foundation; either version 3 of the License, or
12 // (at your option) any later version.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 // ----------------------------------------------------------------------------
17 
18 #include <config.h>
19 
20 #include "XmlRpcServerConnection.h"
21 
22 #include "XmlRpcDispatch.h"
23 #include "XmlRpcServer.h"
24 #include "XmlRpcSocket.h"
25 #include "XmlRpcUtil.h"
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <strings.h>
31 
32 using namespace XmlRpc;
33 
34 
35 
36 // The server delegates handling client requests to a serverConnection object.
XmlRpcServerConnection(XmlRpcSocket::Socket fd,XmlRpcServer * server,bool deleteOnClose)37 XmlRpcServerConnection::XmlRpcServerConnection(XmlRpcSocket::Socket fd,
38                                                XmlRpcServer* server,
39                                                bool deleteOnClose /*= false*/) :
40   XmlRpcSource(fd, deleteOnClose)
41 {
42   XmlRpcUtil::log(2,"XmlRpcServerConnection: new socket %d.", fd);
43   _server = server;
44   _connectionState = READ_HEADER;
45   _keepAlive = true;
46 }
47 
48 
~XmlRpcServerConnection()49 XmlRpcServerConnection::~XmlRpcServerConnection()
50 {
51   XmlRpcUtil::log(4,"XmlRpcServerConnection dtor.");
52   _server->removeConnection(this);
53 }
54 
55 
56 // Handle input on the server socket by accepting the connection
57 // and reading the rpc request. Return true to continue to monitor
58 // the socket for events, false to remove it from the dispatcher.
59 unsigned
handleEvent(unsigned)60 XmlRpcServerConnection::handleEvent(unsigned /*eventType*/)
61 {
62   if (_connectionState == READ_HEADER)
63     if ( ! readHeader()) return 0;
64 
65   if (_connectionState == READ_REQUEST)
66     if ( ! readRequest()) return 0;
67 
68   if (_connectionState == WRITE_RESPONSE)
69     if ( ! writeResponse()) return 0;
70 
71   return (_connectionState == WRITE_RESPONSE)
72         ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
73 }
74 
75 
76 bool
readHeader()77 XmlRpcServerConnection::readHeader()
78 {
79   // Read available data
80   bool eof;
81   if ( ! nbRead(_header, &eof))
82   {
83     // Its only an error if we already have read some data
84     if (_header.length() > 0)
85       XmlRpcUtil::error("XmlRpcServerConnection::readHeader: error while reading header (%s).",XmlRpcSocket::getErrorMsg().c_str());
86     return false;
87   }
88 
89   XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: read %d bytes.", _header.length());
90   char *hp = (char*)_header.c_str();  // Start of header
91   char *ep = hp + _header.length();   // End of string
92   char *bp = 0;                       // Start of body
93   char *lp = 0;                       // Start of content-length value
94   char *kp = 0;                       // Start of connection value
95 
96   for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
97 	if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
98 	  lp = cp + 16;
99 	else if ((ep - cp > 12) && (strncasecmp(cp, "Connection: ", 12) == 0))
100 	  kp = cp + 12;
101 	else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
102 	  bp = cp + 4;
103 	else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
104 	  bp = cp + 2;
105   }
106 
107   // If we haven't gotten the entire header yet, return (keep reading)
108   if (bp == 0) {
109     // EOF in the middle of a request is an error, otherwise its ok
110     if (eof) {
111       XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: EOF");
112       if (_header.length() > 0)
113         XmlRpcUtil::error("XmlRpcServerConnection::readHeader: EOF while reading header");
114       return false;   // Either way we close the connection
115     }
116 
117     return true;  // Keep reading
118   }
119 
120   // Decode content length
121   if (lp == 0) {
122     XmlRpcUtil::error("XmlRpcServerConnection::readHeader: No Content-length specified");
123     return false;   // We could try to figure it out by parsing as we read, but for now...
124   }
125 
126   _contentLength = atoi(lp);
127   if (_contentLength <= 0) {
128     XmlRpcUtil::error("XmlRpcServerConnection::readHeader: Invalid Content-length specified (%d).", _contentLength);
129     return false;
130   }
131 
132   XmlRpcUtil::log(3, "XmlRpcServerConnection::readHeader: specified content length is %d.", _contentLength);
133 
134   // Otherwise copy non-header data to request buffer and set state to read request.
135   _request = bp;
136 
137   // Parse out any interesting bits from the header (HTTP version, connection)
138   _keepAlive = true;
139   if (_header.find("HTTP/1.0") != std::string::npos) {
140     if (kp == 0 || strncasecmp(kp, "keep-alive", 10) != 0)
141       _keepAlive = false;           // Default for HTTP 1.0 is to close the connection
142   } else {
143     if (kp != 0 && strncasecmp(kp, "close", 5) == 0)
144       _keepAlive = false;
145   }
146   XmlRpcUtil::log(3, "KeepAlive: %d", _keepAlive);
147 
148 
149   _header = "";
150   _connectionState = READ_REQUEST;
151   return true;    // Continue monitoring this source
152 }
153 
154 
155 
156 bool
readRequest()157 XmlRpcServerConnection::readRequest()
158 {
159   // If we dont have the entire request yet, read available data
160   if (int(_request.length()) < _contentLength)
161   {
162     bool eof;
163     if ( ! nbRead(_request, &eof))
164     {
165       XmlRpcUtil::error("XmlRpcServerConnection::readRequest: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
166       return false;
167     }
168 
169     // If we haven't gotten the entire request yet, return (keep reading)
170     if (int(_request.length()) < _contentLength)
171     {
172       if (eof)
173       {
174         XmlRpcUtil::error("XmlRpcServerConnection::readRequest: EOF while reading request");
175         return false;   // Either way we close the connection
176       }
177       return true;
178     }
179   }
180 
181   // Otherwise, parse and dispatch the request
182   XmlRpcUtil::log(3, "XmlRpcServerConnection::readRequest read %d bytes.", _request.length());
183   //XmlRpcUtil::log(5, "XmlRpcServerConnection::readRequest:\n%s\n", _request.c_str());
184 
185   _connectionState = WRITE_RESPONSE;
186 
187   return true;    // Continue monitoring this source
188 }
189 
190 
191 
192 bool
writeResponse()193 XmlRpcServerConnection::writeResponse()
194 {
195   if (_response.length() == 0)
196   {
197     executeRequest();
198     _bytesWritten = 0;
199     if (_response.length() == 0)
200     {
201       XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: empty response.");
202       return false;
203     }
204   }
205 
206   // Try to write the response
207   if ( ! nbWrite(_response, &_bytesWritten))
208   {
209     XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
210     return false;
211   }
212   XmlRpcUtil::log(3, "XmlRpcServerConnection::writeResponse: wrote %d of %d bytes.", _bytesWritten, _response.length());
213 
214   // Prepare to read the next request
215   if (_bytesWritten == int(_response.length()))
216   {
217     _header = "";
218     _request = "";
219     _response = "";
220     _connectionState = READ_HEADER;
221   }
222 
223   return _keepAlive;    // Continue monitoring this source if true
224 }
225 
226 
227 //! Helper method to execute the client request
executeRequest()228 void XmlRpcServerConnection::executeRequest()
229 {
230   _response = _server->executeRequest(_request);
231 }
232 
233