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