1 // mysql_db.cpp:  MySQL database interface ActionScript objects, for Gnash.
2 //
3 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 //   Free Software Foundation, Inc
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 
20 #ifdef HAVE_CONFIG_H
21 #include "gnashconfig.h"
22 #endif
23 
24 #include "mysql_db.h"
25 
26 #include "namedStrings.h"
27 #include <errmsg.h>
28 #include <mysql.h>
29 #include <vector>
30 
31 #include "log.h"
32 #include "Array_as.h"
33 #include "as_value.h"
34 #include "fn_call.h"
35 #include "Global_as.h"
36 #include "as_function.h"
37 
38 namespace gnash {
39 
40 as_value mysql_connect(const fn_call& fn);
41 as_value mysql_qetData(const fn_call& fn);
42 as_value mysql_disconnect(const fn_call& fn);
43 
44 as_value mysql_query(const fn_call& fn);
45 as_value mysql_row(const fn_call& fn);
46 as_value mysql_fields(const fn_call& fn);
47 as_value mysql_fetch(const fn_call& fn);
48 as_value mysql_store(const fn_call& fn);
49 as_value mysql_free(const fn_call& fn);
50 
51 LogFile& dbglogfile = LogFile::getDefaultInstance();
52 
53 static void
attachInterface(as_object & obj)54 attachInterface(as_object& obj)
55 {
56     Global_as& gl = getGlobal(obj);
57 
58     obj.init_member("connect", gl.createFunction(mysql_connect));
59     obj.init_member("qetData", gl.createFunction(mysql_qetData));
60     obj.init_member("disconnect", gl.createFunction(mysql_disconnect));
61     obj.init_member("query", gl.createFunction(mysql_query));
62     obj.init_member("fetch_row", gl.createFunction(mysql_fetch));
63     obj.init_member("num_fields", gl.createFunction(mysql_fields));
64     obj.init_member("free_result", gl.createFunction(mysql_free));
65     obj.init_member("store_results", gl.createFunction(mysql_store));
66 }
67 
68 class MySQL : public Relay
69 {
70 public:
71     typedef std::vector< std::vector<const char *> > query_t;
72     MySQL();
73     ~MySQL();
74     bool connect(const char *host, const char *dbname, const char *user, const char *passwd);
75     int getData(const char *sql, query_t &result);
76     bool disconnect();
77 
78     // These are wrappers for the regular MySQL API
79     bool guery(MYSQL *db, const char *sql);
80     bool guery(const char *sql);
81     int num_fields();
82     int num_fields(MYSQL_RES *result);
83     MYSQL_ROW fetch_row();
84     MYSQL_ROW fetch_row(MYSQL_RES *result);
85     void free_result();
86     void free_result(MYSQL_RES *result);
87     MYSQL_RES *store_result();
88     MYSQL_RES *store_result(MYSQL *db);
89 private:
90     MYSQL *_db;
91     MYSQL_RES *_result;
92     MYSQL_ROW _row;
93 };
94 
95 static as_value
mysql_ctor(const fn_call & fn)96 mysql_ctor(const fn_call& fn)
97 {
98     as_object* obj = ensure<ValidThis>(fn);
99     obj->setRelay(new MySQL());
100     return as_value();
101 }
102 
103 
MySQL()104 MySQL::MySQL() :
105     _db(nullptr),
106     _result(nullptr),
107     _row(nullptr)
108 {
109 }
110 
~MySQL()111 MySQL::~MySQL()
112 {
113     disconnect();
114 }
115 
116 int
num_fields()117 MySQL::num_fields()
118 {
119     if (_result) {
120         return num_fields(_result);
121     }
122     return -1;
123 }
124 
125 int
num_fields(MYSQL_RES * result)126 MySQL::num_fields(MYSQL_RES *result)
127 {
128     return mysql_num_fields(result);
129 }
130 
131 MYSQL_ROW
fetch_row()132 MySQL::fetch_row()
133 {
134     if (_result) {
135         return fetch_row(_result);
136     }
137     return nullptr;
138 }
139 
140 MYSQL_ROW
fetch_row(MYSQL_RES * result)141 MySQL::fetch_row(MYSQL_RES *result)
142 {
143     return mysql_fetch_row(result);
144 }
145 
146 void
free_result()147 MySQL::free_result()
148 {
149     if (_result) {
150         free_result(_result);
151     }
152 }
153 
154 void
free_result(MYSQL_RES * result)155 MySQL::free_result(MYSQL_RES *result)
156 {
157     mysql_free_result(result);
158 }
159 
160 MYSQL_RES *
store_result()161 MySQL::store_result()
162 {
163     if (_db) {
164         return store_result(_db);
165     }
166     return nullptr;
167 }
168 
169 MYSQL_RES *
store_result(MYSQL * db)170 MySQL::store_result(MYSQL *db)
171 {
172     _result = mysql_store_result(db);
173     return _result;
174 }
175 
176 bool
connect(const char * host,const char * dbname,const char * user,const char * passwd)177 MySQL::connect(const char* host, const char* dbname, const char* user, const char* passwd)
178 {
179 //    GNASH_REPORT_FUNCTION;
180 
181     // Closes a previously opened connection &
182     // also deallocates the connection handle
183     disconnect();
184 
185     if ((_db = mysql_init(nullptr)) == nullptr ) {
186         log_error(_("Couldn't initialize database"));
187         return false;
188     }
189 
190     if (mysql_real_connect(_db, host, user, passwd, dbname, 0, nullptr, 0) == nullptr) {
191 	log_error(_("Couldn't connect to database"));
192 	return false;
193     }
194 
195     return true;
196 }
197 
198 bool
guery(const char * sql)199 MySQL::guery(const char *sql)
200 {
201 //    GNASH_REPORT_FUNCTION;
202     if (_db) {
203         return guery(_db, sql);
204     }
205     return -1;
206 }
207 
208 bool
guery(MYSQL * db,const char * sql)209 MySQL::guery(MYSQL *db, const char *sql)
210 {
211 //    GNASH_REPORT_FUNCTION;
212     int res = mysql_real_query(db, sql, strlen(sql));
213     switch (res) {
214       case CR_SERVER_LOST:
215       case CR_COMMANDS_OUT_OF_SYNC:
216       case CR_SERVER_GONE_ERROR:
217 	  log_error (_("MySQL connection error: %s"), mysql_error(_db));
218 	  // Try to reconnect to the database
219 // 	  closeDB();
220 // 	  openDB();
221 	  break;
222       case -1:
223       case CR_UNKNOWN_ERROR:
224 	  log_error (_("MySQL error on query for:\n\t%s\nQuery was: %s"),
225 		     mysql_error(_db), sql);
226 	  return false;
227 	  break;
228        default:
229  	  return true;
230     }
231     return false;
232 }
233 
234 int
getData(const char * sql,query_t & qresult)235 MySQL::getData(const char *sql, query_t &qresult)
236 {
237 //    GNASH_REPORT_FUNCTION;
238 
239     bool qstatus = false;
240     int res = mysql_real_query(_db, sql, strlen(sql));
241     switch (res) {
242       case CR_SERVER_LOST:
243       case CR_COMMANDS_OUT_OF_SYNC:
244       case CR_SERVER_GONE_ERROR:
245 	  log_error(_("MySQL connection error: %s"), mysql_error(_db));
246 	  // Try to reconnect to the database
247 // 	  closeDB();
248 // 	  openDB();
249 	  break;
250       case -1:
251       case CR_UNKNOWN_ERROR:
252 	  log_error (_("MySQL error on query for:\n\t%s\nQuery was: %s"),
253 		     mysql_error(_db), sql);
254 //	  return false;
255 	  break;
256 //       default:
257 // 	  return true;
258     }
259 
260     _result = mysql_store_result(_db);
261 //    int nrows = mysql_num_rows(result);
262 
263 #if 0
264     for (size_t i=0; i<mysql_num_fields(_result); i++) {
265 	MYSQL_FIELD *fields = mysql_fetch_fields(_result);
266 	log_debug(_("Field name is: %s: "), fields->name);
267     }
268 #endif
269 
270     while((_row = mysql_fetch_row(_result))) {
271 	std::vector<const char *> row_vec;
272 	for (size_t i=0; i<mysql_num_fields(_result); i++) {
273 //	    log_debug("Column[%d] is: \"%s\"", i, row[i]);
274 	    row_vec.push_back(_row[i]);
275         }
276 	qresult.push_back(row_vec);
277 	qstatus = true;
278     }
279 
280     mysql_free_result(_result);
281     return(qstatus);
282 }
283 
284 bool
disconnect()285 MySQL::disconnect()
286 {
287 //    GNASH_REPORT_FUNCTION;
288     if (_db != nullptr) {
289         mysql_close(_db);
290         _db = nullptr;
291     }
292     return true;
293 }
294 
295 
296 // Entry points for ActionScript methods
297 as_value
mysql_connect(const fn_call & fn)298 mysql_connect(const fn_call& fn)
299 {
300 
301     MySQL* ptr = ensure<ThisIsNative<MySQL> >(fn);
302 
303     if (fn.nargs == 4) {
304         std::string host = fn.arg(0).to_string();
305         std::string db = fn.arg(1).to_string();
306         std::string user = fn.arg(2).to_string();
307         std::string passwd = fn.arg(3).to_string();
308         return as_value(ptr->connect(host.c_str(), db.c_str(),
309                          user.c_str(), passwd.c_str()));
310     }
311 
312     return as_value(false);
313 }
314 
315 as_value
mysql_qetData(const fn_call & fn)316 mysql_qetData(const fn_call& fn)
317 {
318 //    GNASH_REPORT_FUNCTION;
319 
320     if (fn.nargs > 0) {
321         // TODO: maybe use this argument.
322         //std::string sql = fn.arg(0).to_string();
323 	    as_object* arr = toObject(fn.arg(1), getVM(fn));
324 
325         MySQL::query_t qresult;
326 
327         for (size_t i=0; i<qresult.size(); i++) {
328             std::vector<const char *> row;
329             row = qresult[i];
330             for (size_t j=0; j< row.size(); j++) {
331                 as_value entry = row[j];
332                 callMethod(arr, NSV::PROP_PUSH, entry);
333             }
334         }
335         return as_value(true);
336     }
337     log_aserror("Mysql.getData(): missing arguments");
338  	return as_value(false);
339 }
340 
341 as_value
mysql_free(const fn_call & fn)342 mysql_free(const fn_call& fn)
343 {
344 //    GNASH_REPORT_FUNCTION;
345     MySQL* ptr = ensure<ThisIsNative<MySQL> >(fn);
346     ptr->free_result();
347     return as_value(true);
348 }
349 
350 as_value
mysql_fields(const fn_call & fn)351 mysql_fields(const fn_call& fn)
352 {
353 //    GNASH_REPORT_FUNCTION;
354     MySQL* ptr = ensure<ThisIsNative<MySQL> >(fn);
355     return as_value(ptr->num_fields());
356 }
357 
358 as_value
mysql_fetch(const fn_call & fn)359 mysql_fetch(const fn_call& fn)
360 {
361     MySQL* ptr = ensure<ThisIsNative<MySQL> >(fn);
362     if (fn.nargs > 0) {
363         MYSQL_ROW res = ptr->fetch_row();
364         as_value aaa = *res;
365         Global_as& gl = getGlobal(fn);
366         as_object* arr = gl.createArray();
367         callMethod(arr, NSV::PROP_PUSH, aaa);
368         return as_value(arr);
369     }
370     log_aserror("Mysql.fetch(): missing arguments");
371     return as_value();
372 }
373 
374 as_value
mysql_store(const fn_call & fn)375 mysql_store(const fn_call& fn)
376 {
377 //    GNASH_REPORT_FUNCTION;
378     MySQL* ptr = ensure<ThisIsNative<MySQL> >(fn);
379     as_object* obj = reinterpret_cast<as_object*>(ptr->store_result());
380     return as_value(obj);
381 }
382 
383 as_value
mysql_query(const fn_call & fn)384 mysql_query(const fn_call& fn)
385 {
386 //    GNASH_REPORT_FUNCTION;
387     MySQL* ptr = ensure<ThisIsNative<MySQL> >(fn);
388     if (fn.nargs > 0) {
389         std::string sql = fn.arg(0).to_string();
390         return as_value(ptr->guery(sql.c_str()));
391     }
392     log_aserror("Missing arguments to MySQL.query");
393     return as_value();
394 }
395 
396 as_value
mysql_disconnect(const fn_call & fn)397 mysql_disconnect(const fn_call& fn)
398 {
399     MySQL* ptr = ensure<ThisIsNative<MySQL> >(fn);
400     return as_value(ptr->disconnect());
401 }
402 
403 extern "C" {
404 
mysql_class_init(as_object & obj)405 void mysql_class_init(as_object &obj)
406 {
407     Global_as& gl = getGlobal(obj);
408     as_object* proto = createObject(gl);
409 	as_object *cl = gl.createClass(&mysql_ctor, proto);
410     attachInterface(*proto);
411 	obj.init_member("MySQL", cl);
412 }
413 
414 }
415 
416 } // end of gnash namespace
417 
418 
419 // Local Variables:
420 // mode: C++
421 // indent-tabs-mode: t
422 // End:
423