use pbd's gstdio compatibility wrapper (GUI)
[ardour.git] / gtk2_ardour / pingback.cc
1 /*
2     Copyright (C) 2012 Paul Davis 
3     Inspired by code from Ben Loftis @ Harrison Consoles
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program 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
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include <string>
22 #include <iostream>
23 #include <fstream>
24 #include <cstring>
25
26 #ifdef PLATFORM_WINDOWS
27 #include <windows.h>
28 #include <glibmm.h>
29 #else
30 #include <sys/utsname.h>
31 #endif
32
33 #include <curl/curl.h>
34
35 #include <pbd/gstdio_compat.h>
36 #include <glibmm/miscutils.h>
37
38 #include "pbd/compose.h"
39 #include "pbd/pthread_utils.h"
40
41 #include "ardour/filesystem_paths.h"
42 #include "ardour/rc_configuration.h"
43
44 #include "pingback.h"
45
46 using std::string;
47 using namespace ARDOUR;
48
49 static size_t
50 curl_write_data (char *bufptr, size_t size, size_t nitems, void *ptr)
51 {
52         /* we know its a string */
53
54         string* sptr = (string*) ptr;
55
56         for (size_t i = 0; i < nitems; ++i) {
57                 for (size_t n = 0; n < size; ++n) {
58                         if (*bufptr == '\n') {
59                                 break;
60                         }
61
62                         (*sptr) += *bufptr++;
63                 }
64         }
65
66         return size * nitems;
67 }
68
69 struct ping_call {
70     std::string version;
71     std::string announce_path;
72
73     ping_call (const std::string& v, const std::string& a)
74             : version (v), announce_path (a) {}
75 };
76
77 #ifdef PLATFORM_WINDOWS
78 static bool
79 _query_registry (const char *regkey, const char *regval, std::string &rv) {
80         HKEY key;
81         DWORD size = PATH_MAX;
82         char tmp[PATH_MAX+1];
83
84         if (   (ERROR_SUCCESS == RegOpenKeyExA (HKEY_LOCAL_MACHINE, regkey, 0, KEY_READ, &key))
85             && (ERROR_SUCCESS == RegQueryValueExA (key, regval, 0, NULL, reinterpret_cast<LPBYTE>(tmp), &size))
86                  )
87         {
88                 rv = Glib::locale_to_utf8 (tmp);
89                 return true;
90         }
91
92         if (   (ERROR_SUCCESS == RegOpenKeyExA (HKEY_LOCAL_MACHINE, regkey, 0, KEY_READ | KEY_WOW64_32KEY, &key))
93             && (ERROR_SUCCESS == RegQueryValueExA (key, regval, 0, NULL, reinterpret_cast<LPBYTE>(tmp), &size))
94                  )
95         {
96                 rv = Glib::locale_to_utf8 (tmp);
97                 return true;
98         }
99
100         return false;
101 }
102 #endif
103
104
105 static void*
106 _pingback (void *arg)
107 {
108         ping_call* cm = static_cast<ping_call*> (arg);
109         CURL* c;
110         string return_str;
111         //initialize curl
112
113         curl_global_init (CURL_GLOBAL_NOTHING);
114         c = curl_easy_init ();
115         
116         curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, curl_write_data); 
117         curl_easy_setopt (c, CURLOPT_WRITEDATA, &return_str); 
118         char errbuf[CURL_ERROR_SIZE];
119         curl_easy_setopt (c, CURLOPT_ERRORBUFFER, errbuf); 
120
121         string url;
122
123 #ifdef __APPLE__
124         url = Config->get_osx_pingback_url ();
125 #elif defined PLATFORM_WINDOWS
126         url = Config->get_windows_pingback_url ();
127 #else
128         url = Config->get_linux_pingback_url ();
129 #endif
130
131         if (url.compare (0, 4, "http") != 0) {
132                 delete cm;
133                 return 0;
134         }
135
136         char* v = curl_easy_escape (c, cm->version.c_str(), cm->version.length());
137         url += v;
138         url += '?';
139         free (v);
140
141 #ifndef PLATFORM_WINDOWS
142         struct utsname utb;
143
144         if (uname (&utb)) {
145                 delete cm;
146                 return 0;
147         }
148
149         //string uts = string_compose ("%1 %2 %3 %4", utb.sysname, utb.release, utb.version, utb.machine);
150         string s;
151         char* query;
152
153         query = curl_easy_escape (c, utb.sysname, strlen (utb.sysname));
154         s = string_compose ("s=%1", query);
155         url += s;
156         url += '&';
157         free (query);
158
159         query = curl_easy_escape (c, utb.release, strlen (utb.release));
160         s = string_compose ("r=%1", query);
161         url += s;
162         url += '&';
163         free (query);
164
165         query = curl_easy_escape (c, utb.machine, strlen (utb.machine));
166         s = string_compose ("m=%1", query);
167         url += s;
168         free (query);
169 #else
170         std::string val;
171         if (_query_registry("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "ProductName", val)) {
172                 char* query = curl_easy_escape (c, val.c_str(), strlen (val.c_str()));
173                 url += "r=";
174                 url += query;
175                 url += '&';
176                 free (query);
177         } else {
178                 url += "r=&";
179         }
180
181         if (_query_registry("Hardware\\Description\\System\\CentralProcessor\\0", "Identifier", val)) {
182                 // remove "Family X Model YY Stepping Z" tail
183                 size_t cut = val.find (" Family ");
184                 if (string::npos != cut) {
185                         val = val.substr (0, cut);
186                 }
187                 char* query = curl_easy_escape (c, val.c_str(), strlen (val.c_str()));
188                 url += "m=";
189                 url += query;
190                 url += '&';
191                 free (query);
192         } else {
193                 url += "m=&";
194         }
195
196 # if ( defined(__x86_64__) || defined(_M_X64) )
197         url += "s=Windows64";
198 # else
199         url += "s=Windows32";
200 # endif
201
202 #endif /* PLATFORM_WINDOWS */
203
204         curl_easy_setopt (c, CURLOPT_URL, url.c_str());
205
206         return_str = "";
207
208         if (curl_easy_perform (c) == 0) {
209                 long http_status; 
210
211                 curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &http_status);
212
213                 if (http_status != 200) {
214                         std::cerr << "Bad HTTP status" << std::endl;
215                         return 0;
216                 }
217
218                 if ( return_str.length() > 140 ) { // like a tweet :)
219                         std::cerr << "Announcement string is too long (probably behind a proxy)." << std::endl;
220                 } else {
221                         std::cerr << "Announcement is: " << return_str << std::endl;
222                         
223                         //write announcements to local file, even if the
224                         //announcement is empty
225
226                         FILE* fout = g_fopen (cm->announce_path.c_str(), "wb");
227
228                         if (fout) {
229                                 fwrite (return_str.c_str(), sizeof(char), return_str.length (), fout);
230                                 fclose (fout);
231                         }
232                 }
233         } else {
234                 std::cerr << "curl failed: " << errbuf << std::endl;
235         }
236
237         curl_easy_cleanup (c);
238         delete cm;
239         return 0;
240 }
241
242 namespace ARDOUR {
243
244 void pingback (const string& version, const string& announce_path) 
245 {
246         ping_call* cm = new ping_call (version, announce_path);
247         pthread_t thread;
248
249         pthread_create_and_store ("pingback", &thread, _pingback, cm);
250 }
251
252 }