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