1 /*
2 * vim:tw=80:ai:tabstop=4:softtabstop=4:shiftwidth=4:expandtab
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * (C) Copyright Kevin Timmerman 2007
19 * (C) Copyright Phil Dibowitz 2008
20 */
21
22 #include <stdarg.h>
23 #include <string.h>
24 #include "libconcord.h"
25 #include "lc_internal.h"
26 #include "hid.h"
27 #include "remote.h"
28 #include "xml_headers.h"
29
30 #ifdef _WIN32
31 #include <winsock.h>
32 #else /* non _WIN32 */
33 #include <strings.h>
34 #include <sys/socket.h>
35 #include <netdb.h>
36 #include <unistd.h>
37 #define closesocket close
38 #define SOCKET int
39 #define SOCKET_ERROR -1
40 #endif
41
42 #ifdef __FreeBSD__
43 #include <netinet/in.h>
44 #endif
45
46 static const uint8_t urlencodemap[32]={
47 0xFF, 0xFF, 0xFF, 0xFF, // 0 Control chars
48 0x7D, // 32 & % $ # " <space>
49 0x98, // 40 / , +
50 0x00, // 48
51 0xFC, // 56 ? > = < ; :
52 0x01, // 64 @
53 0x00, // 72
54 0x00, // 80
55 0x78, // 88 ^ ] \ [
56 0x01, // 96 '
57 0x00, // 104
58 0x00, // 112
59 0x78, // 120 ~ } | {
60 0xFF, 0xFF, 0xFF, 0xFF, // 128
61 0xFF, 0xFF, 0xFF, 0xFF,
62 0xFF, 0xFF, 0xFF, 0xFF,
63 0xFF, 0xFF, 0xFF, 0xFF
64 };
65
66 /*
67 * This function does C-style string formatting, but uses a C++ string and
68 * automatically handles buffer sizing. It is intended to be used in place of
69 * sprintf()/snprintf() where we don't necessarily know the required buffer
70 * size in advance. The formatted string is appended to the supplied C++
71 * string.
72 */
format_string(string * str,const char * format,...)73 void format_string(string *str, const char *format, ...)
74 {
75 va_list args;
76 va_start(args, format);
77 int size = vsnprintf(NULL, 0, format, args);
78 va_end(args);
79 size++; // Add room for \0
80 char *buffer = new char[size];
81 va_start(args, format);
82 vsnprintf(buffer, size, format, args);
83 va_end(args);
84 *str += buffer;
85 delete[] buffer;
86 }
87
UrlEncode(const char * in,string & out)88 static void UrlEncode(const char *in, string &out)
89 {
90 out = "";
91 const size_t len = strlen(in);
92 for(size_t i = 0; i < len; ++i) {
93 const char c = in[i];
94 if (c == ' ') {
95 out += '+';
96 } else if (c == '(') {
97 out += "%28";
98 } else if (c == ')') {
99 out += "%29";
100 } else if(urlencodemap[c>>3] & (1 << (c & 7))) {
101 char hex[4];
102 sprintf(hex, "%%%02X", c);
103 out += hex;
104 } else
105 out += c;
106 }
107 }
108
Zap(string & server,const char * s1,const char * s2)109 static int Zap(string &server, const char *s1, const char *s2)
110 {
111 int err;
112
113 // Get the numeric address of host
114 hostent* addr = gethostbyname(server.c_str());
115 if (!addr) {
116 report_net_error("gethostbyname()");
117 return LC_ERROR_OS_NET;
118 }
119
120 // Fill in the sockaddr structure
121 sockaddr_in sa;
122 memcpy(&(sa.sin_addr), addr->h_addr, addr->h_length);
123 sa.sin_family = AF_INET;
124 sa.sin_port = htons(80);
125
126 // Create a socket
127 // SOCK_STREAM = Stream (TCP) socket
128 SOCKET sock = socket(sa.sin_family, SOCK_STREAM, 0);
129
130 // Connect
131 if ((err = connect(sock,(struct sockaddr*)&sa, sizeof(sa)))) {
132 report_net_error("connect()");
133 return LC_ERROR_OS_NET;
134 }
135
136 debug("Connected!");
137
138 err = send(sock, s1, strlen(s1), 0);
139 if (err == SOCKET_ERROR) {
140 report_net_error("send()");
141 return LC_ERROR_OS_NET;
142 }
143
144 debug("%i bytes sent", err);
145
146 err = send(sock, s2, strlen(s2), 0);
147 if (err == SOCKET_ERROR) {
148 report_net_error("send()");
149 return LC_ERROR_OS_NET;
150 }
151
152 debug("%i bytes sent", err);
153
154 char buf[1000];
155 err = recv(sock,buf,999,0);
156
157 if (err == SOCKET_ERROR) {
158 report_net_error("recv()");
159 return LC_ERROR_OS_NET;
160 }
161
162 // Show the received received data
163 buf[err]=0;
164
165 debug("Received: %s", buf);
166
167 // Close the socket
168 if ((err = closesocket(sock))) {
169 report_net_error("closesocket()");
170 return LC_ERROR_OS_NET;
171 }
172
173 debug("done with web post");
174
175 return 0;
176 }
177
178 // find is the tag.
179 // data is the data to search through
180 // data_size is the size of data
181 // found is where we found it
182 // s, if passed, will hold the strong of the entire element, unless
183 // If find_attributes is set to true, and s is not null, will fill s with the
184 // attributes for the requested tag. For example, if you have:
185 // <ELEMENT A="B" C="D"/> and you search for a tag called "ELEMENT" the
186 // function will return A="B" C="D".
GetTag(const char * find,uint8_t * data,uint32_t data_size,uint8_t * & found,string * s=NULL,bool find_attributes=false)187 int GetTag(const char *find, uint8_t* data, uint32_t data_size, uint8_t *&found,
188 string *s=NULL, bool find_attributes=false)
189 {
190 char tag_name_end = '>';
191 char tag_start = '<';
192 char tag_end = '>';
193 if (find_attributes) {
194 tag_name_end = ' ';
195 tag_start = '/';
196 tag_end = '/';
197 }
198
199 const size_t find_len = strlen(find);
200 uint8_t * search = data;
201
202 // Consume tags until there aren't any left
203 while (1) {
204 // Loop searching for start of tag character
205 while (1) {
206 if (*search == '<') {
207 break;
208 }
209 if (search >= data + data_size) {
210 return -1;
211 }
212 search++;
213 }
214 // Validate there's enough string left to hold the tag name
215 uint32_t needed_len = find_len + 2;
216 uint32_t left_len = data_size - (search - data);
217 if (left_len < needed_len) {
218 return -1;
219 }
220 // Point past <, at tag name
221 search++;
222 // Check to see if this is the tag we want
223 if (search[find_len] == tag_name_end &&
224 !strnicmp(find, reinterpret_cast<const char*>(search), find_len)) {
225 // Point past >, at tag content
226 search += find_len + 1;
227
228 found = search;
229
230 /*
231 * If a string pointer was passed in, then add the
232 * contents of this entire tag to the string
233 */
234 if (s) {
235 *s = "";
236 /*
237 * Here we keep adding chars until the next tag
238 * which, in theory, should be the end-tag.
239 */
240 while (*search && *search != tag_start) {
241 *s += *search;
242 search++;
243 if (search >= data + data_size) {
244 break;
245 }
246 }
247 }
248 return 0;
249 }
250
251 // Loop searching for end of tag character
252 while (1) {
253 if (*search == tag_end) {
254 break;
255 }
256 if (search >= data + data_size) {
257 return -1;
258 }
259 search++;
260 }
261 }
262 }
263
264 // Given a set of XML attributes, e.g., ATTR1="VALUE1" ATTR2="VALUE2", this
265 // function will find a given attribute and return its value.
GetAttribute(const char * find,string data,string * result)266 int GetAttribute(const char *find, string data, string *result)
267 {
268 if ((find == NULL) || (result == NULL))
269 return -1;
270
271 size_t start_pos;
272 size_t end_pos;
273 string to_find = find;
274 to_find.append("=\"");
275
276 start_pos = data.find(to_find);
277 if (start_pos == string::npos)
278 return -1;
279 start_pos += to_find.length();
280
281 end_pos = data.find("\"", start_pos);
282 if (end_pos == string::npos)
283 return -1;
284
285 *result = data.substr(start_pos, end_pos - start_pos);
286 return 0;
287 }
288
encode_ir_signal(uint32_t carrier_clock,uint32_t * ir_signal,uint32_t ir_signal_length,string * learn_seq)289 int encode_ir_signal(uint32_t carrier_clock, uint32_t *ir_signal,
290 uint32_t ir_signal_length, string *learn_seq)
291 { /*
292 * Encode ir_signal into string accepted by Logitech server
293 */
294 char s[32];
295
296 if ((learn_seq == NULL) || (ir_signal == NULL) || (ir_signal_length == 0)) {
297 return LC_ERROR;
298 }
299 if (carrier_clock > 0xFFFF) {
300 sprintf(s, "F%08X", carrier_clock);
301 } else {
302 sprintf(s, "F%04X", carrier_clock);
303 }
304 *learn_seq = s;
305 for (unsigned int n = 0; n < ir_signal_length; ) {
306 if (ir_signal[n] > 0xFFFF) {
307 sprintf(s, "P%08X", ir_signal[n++]);
308 } else {
309 sprintf(s, "P%04X", ir_signal[n++]);
310 }
311 *learn_seq += s;
312 if (ir_signal[n] > 0xFFFF) {
313 sprintf(s, "S%08X", ir_signal[n++]);
314 } else {
315 sprintf(s, "S%04X", ir_signal[n++]);
316 }
317 *learn_seq += s;
318 }
319 return 0;
320 }
321
add_usbnet_headers(char * post_data,TRemoteInfo & ri)322 void add_usbnet_headers(char *post_data, TRemoteInfo &ri)
323 {
324 sprintf(post_data+strlen(post_data), post_xml_usbnet1, ri.home_id,
325 ri.node_id, ri.tid);
326 for (int i=0; i<ri.num_regions; i++) {
327 sprintf(post_data+strlen(post_data), post_xml_usbnet_region,
328 ri.region_ids[i], ri.region_versions[i]);
329 }
330 sprintf(post_data+strlen(post_data), "%s", post_xml_usbnet2);
331 sprintf(post_data+strlen(post_data), "%s", ri.xml_user_rf_setting);
332 sprintf(post_data+strlen(post_data), "%s", post_xml_usbnet3);
333 }
334
Post(uint8_t * xml,uint32_t xml_size,const char * root,TRemoteInfo & ri,bool has_userid,bool add_cookiekeyval,bool z_post,string * learn_seq,string * learn_key)335 int Post(uint8_t *xml, uint32_t xml_size, const char *root, TRemoteInfo &ri,
336 bool has_userid, bool add_cookiekeyval, bool z_post, string *learn_seq,
337 string *learn_key)
338 {
339 uint8_t *x = xml;
340 int err;
341 if ((err = GetTag(root, x, xml_size - (x - xml), x)))
342 return err;
343
344 string server, path, cookie, userid;
345
346 if ((err = GetTag("SERVER", x, xml_size - (x - xml), x, &server)))
347 return err;
348 if ((err = GetTag("PATH", x, xml_size - (x - xml), x, &path)))
349 return err;
350 if ((err = GetTag("VALUE", x, xml_size - (x - xml), x, &cookie)))
351 return err;
352 if (has_userid) {
353 uint8_t *n = 0;
354 if ((err = GetTag("VALUE", x, xml_size - (x - xml), n, &userid)))
355 return err;
356 }
357
358 /*
359 * For some architectures the website leaves one required value out of
360 * the cookie. Who knows why, but we allow the user to tell us to add
361 * it.
362 */
363 if (add_cookiekeyval) {
364 cookie += ";CookieKeyValue=";
365 cookie += ri.serial1;
366 cookie += ri.serial2;
367 cookie += ri.serial3;
368 }
369
370 debug("Connecting to: %s", server.c_str());
371 debug("Path: %s", path.c_str());
372 debug("Cookie: %s", cookie.c_str());
373 debug("UserId: %s", userid.c_str());
374
375 string post;
376 if (learn_seq == NULL) {
377 string serial;
378 format_string(&serial, "%s%s%s", ri.serial1, ri.serial2, ri.serial3);
379 string post_data;
380 if (z_post) {
381 format_string(&post_data, z_post_xml, ri.hw_ver_major,
382 ri.hw_ver_minor, ri.flash_mfg, ri.flash_id, ri.fw_ver_major,
383 ri.fw_ver_minor);
384 } else {
385 format_string(&post_data, post_xml, ri.fw_ver_major,
386 ri.fw_ver_minor, ri.fw_type, serial.c_str(),
387 ri.hw_ver_major, ri.hw_ver_minor, ri.hw_ver_micro,
388 ri.flash_mfg, ri.flash_id, ri.protocol, ri.architecture,
389 ri.skin);
390 format_string(&post_data, "%s", post_xml_trailer);
391 }
392
393 debug("post data: %s", post_data.c_str());
394
395 string post_data_encoded;
396 UrlEncode(post_data.c_str(), post_data_encoded);
397
398 post = "Data=" + post_data_encoded;
399 } else {
400 post = "IrSequence=" + *learn_seq + "&KeyName=" + *learn_key;
401 }
402
403 if (has_userid) {
404 post += "&UserId=" + userid;
405 }
406
407 debug("%s", post.c_str());
408
409 string http_header;
410 format_string(&http_header, post_header, path.c_str(), server.c_str(),
411 cookie.c_str(), post.length());
412
413 debug("%s", http_header.c_str());
414
415 return Zap(server, http_header.c_str(), post.c_str());
416 }
417