1 /* An XMLRPC client and server library for GNet
2 *
3 * Copyright (c) 2006 Dov Grobgeld <dov.grobgeld@gmail.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 */
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include "xmlrpc.h"
26
27 typedef struct {
28 GNetXmlRpcClient client;
29 GTcpSocket* socket;
30 GString *xmlrpc_string;
31 gboolean in_header;
32 gint header_lineno;
33 gint message_size;
34 gint error;
35 } GNetXmlRpcClientPrivate;
36
37 static gchar *build_xmlrpc_message(const gchar *method,
38 const gchar *param);
39
gnet_xmlrpc_client_new(const gchar * hostname,int port)40 GNetXmlRpcClient *gnet_xmlrpc_client_new(const gchar *hostname,
41 int port)
42 {
43 GNetXmlRpcClientPrivate *client = g_new0(GNetXmlRpcClientPrivate, 1);
44 GInetAddr* addr;
45
46 addr = gnet_inetaddr_new (hostname, port);
47 if (!addr)
48 {
49 fprintf (stderr, "Error: Name lookup for %s failed\n", hostname);
50 return NULL;
51 }
52
53 /* Create the socket */
54 client->socket = gnet_tcp_socket_new (addr);
55 gnet_inetaddr_delete (addr);
56 if (!client->socket)
57 {
58 fprintf (stderr, "Error: Could not connect to %s:%d\n", hostname, port);
59 return NULL;
60 }
61
62 client->client.gnet_channel = gnet_tcp_socket_get_io_channel (client->socket);
63 g_assert (client->client.gnet_channel != NULL);
64
65 client->xmlrpc_string = g_string_new("");
66 client->in_header = TRUE;
67 client->header_lineno = 0;
68 client->message_size = 0;
69 client->error = 0;
70
71 return (GNetXmlRpcClient*)client;
72 }
73
gnet_xmlrpc_client_delete(GNetXmlRpcClient * _client)74 void gnet_xmlrpc_client_delete(GNetXmlRpcClient *_client)
75 {
76 GNetXmlRpcClientPrivate *client = (GNetXmlRpcClientPrivate*)_client;
77
78 g_string_free(client->xmlrpc_string, TRUE);
79 gnet_tcp_socket_delete (client->socket);
80 g_free(client);
81 }
82
gnet_xmlrpc_client_call(GNetXmlRpcClient * _client,const gchar * method,const gchar * param,gchar ** reply)83 int gnet_xmlrpc_client_call(GNetXmlRpcClient *_client,
84 const gchar *method,
85 const gchar *param,
86 // output
87 gchar **reply)
88 {
89 GNetXmlRpcClientPrivate *client = (GNetXmlRpcClientPrivate*)_client;
90 gchar *xmlrpc_message = build_xmlrpc_message(method, param);
91 gchar *msg_start, *msg_end;
92 gchar *p;
93 GString *xmlrpc_string = g_string_new("");
94 GString *reply_string = g_string_new("");
95 gsize n;
96 gchar buffer[1024];
97 gint error;
98
99 n = strlen(xmlrpc_message);
100 // printf("Writing...\n"); fflush(stdout);
101 error = gnet_io_channel_writen (client->client.gnet_channel,
102 xmlrpc_message, n, &n);
103 // printf("error = %d\n", error); fflush(stdout);
104 if (error != G_IO_ERROR_NONE)
105 return -1;
106
107 // fprintf(stderr, "entering while loop\n");
108 while (gnet_io_channel_readline (client->client.gnet_channel, buffer, sizeof(buffer), &n) == G_IO_ERROR_NONE)
109 {
110 if (client->in_header)
111 {
112 // Check for a valid response
113 if (client->header_lineno == 0)
114 {
115 // If we don't have HTTP we've got a problem
116 if (g_strstr_len(buffer, 5, "HTTP") == NULL)
117 {
118 return -1;
119 }
120 }
121 else if (n==1)
122 {
123 client->in_header = FALSE;
124 }
125 else
126 {
127 // Look for the content-length string case independant.
128 char *p;
129
130 // Lower case buf
131 p = buffer;
132 while(*p)
133 {
134 *p = g_ascii_tolower(*p);
135 p++;
136 }
137
138 // Search for string
139 if ((p = g_strstr_len(buffer, n-1,
140 "content-length:")) != NULL)
141 {
142 p += strlen("Content-length:");
143 client->message_size = atoi(p);
144 }
145 }
146 client->header_lineno++;
147 }
148 // If we are not in the header then append the line to the xmlrpc string.
149 else
150 {
151 g_string_append_len(xmlrpc_string, buffer, n-1);
152 g_string_append_c(xmlrpc_string, '\n');
153 }
154
155 // Check if we are finished
156 if (xmlrpc_string->len
157 && xmlrpc_string->len >= client->message_size)
158 {
159 // Quit and reset parsing for next message
160 client->in_header = 1;
161 client->header_lineno = 0;
162 break;
163 }
164 }
165
166 // Extract the response. Should be exchanged to some more robust
167 // XML parsing.
168 msg_start = g_strstr_len(xmlrpc_string->str,
169 xmlrpc_string->len,
170 "<value><string>");
171 if (msg_start == NULL)
172 return -1;
173
174 msg_start += strlen("<value><string>");
175 msg_end = g_strrstr(msg_start, "</string></value>");
176
177 // Decode the response
178 p = msg_start;
179 while(*p != '<')
180 {
181 gchar c = *p++;
182
183 if (c == '&')
184 {
185 if (g_strstr_len(p, 4, "amp;") == p)
186 {
187 g_string_append_c(reply_string, '&');
188 p+= 4;
189 }
190 else if (g_strstr_len(p, 3, "lt;") == p)
191 {
192 g_string_append_c(reply_string, '<');
193 p+= 3;
194 }
195 else if (g_strstr_len(p, 3, "gt;") == p)
196 {
197 g_string_append_c(reply_string, '>');
198 p+= 3;
199 }
200 else
201 {
202 // Don't know what to do. Just add the ampersand..
203 g_string_append_c(reply_string, '&');
204 }
205 }
206 else
207 g_string_append_c(reply_string, c);
208 }
209 *reply = reply_string->str;
210 g_string_free(reply_string, FALSE);
211 g_string_free(xmlrpc_string, TRUE);
212
213 return 0;
214 }
215
build_xmlrpc_message(const gchar * method,const gchar * param)216 static gchar *build_xmlrpc_message(const gchar *method,
217 const gchar *param)
218 {
219 GString *xmlrpc_msg = g_string_new("");
220 GString *req_string = g_string_new("");
221 const gchar *p;
222 gchar *ret;
223
224 g_string_append_printf(xmlrpc_msg,
225 "<?xml version=\"1.0\"?>\n"
226 "<methodCall>\n"
227 "<methodName>%s</methodName>\n"
228 "<params>\n"
229 "<param><value><string>",
230 method);
231
232 // Encode the param string
233 p = param;
234 while(*p)
235 {
236 gchar c = *p++;
237 switch (c)
238 {
239 case '&' : g_string_append(xmlrpc_msg, "&"); break;
240 case '<': g_string_append(xmlrpc_msg, "<"); break;
241 case '>': g_string_append(xmlrpc_msg, ">"); break;
242 default:
243 g_string_append_c(xmlrpc_msg, c);
244 }
245 }
246
247 g_string_append(xmlrpc_msg,
248 "</string></value></param>\n"
249 "</params>\n"
250 "</methodCall>\n");
251
252 g_string_append_printf(req_string,
253 "POST /RPC2 HTTP/1.0\n"
254 "User-Agent: Frontier/5.1.2 (WinNT)\n"
255 "Host: localhost\n"
256 "Content-Type: text/xml\n"
257 "Content-length: %u\n\n",
258 (guint) xmlrpc_msg->len);
259 g_string_append_len(req_string,
260 xmlrpc_msg->str,
261 xmlrpc_msg->len);
262 g_string_free(xmlrpc_msg, TRUE);
263
264 ret = req_string->str;
265 g_string_free(req_string, FALSE);
266
267 return ret;
268 }
269