1
2
3
4
5
6
7
8
9
10
11 package com.jsql.model.accessible;
12
13 import com.jsql.model.InjectionModel;
14 import com.jsql.model.bean.database.*;
15 import com.jsql.model.bean.util.Interaction;
16 import com.jsql.model.bean.util.Request;
17 import com.jsql.model.exception.AbstractSlidingException;
18 import com.jsql.model.exception.InjectionFailureException;
19 import com.jsql.model.exception.JSqlException;
20 import com.jsql.model.suspendable.SuspendableGetRows;
21 import com.jsql.util.I18nUtil;
22 import com.jsql.util.LogLevelUtil;
23 import org.apache.commons.lang3.StringUtils;
24 import org.apache.logging.log4j.LogManager;
25 import org.apache.logging.log4j.Logger;
26
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.regex.Pattern;
30
31
32
33
34
35 public class DataAccess {
36
37
38
39
40 private static final Logger LOGGER = LogManager.getRootLogger();
41
42
43
44
45
46
47 public static final String TRAIL_RGX = "\\x01\\x03\\x03\\x07";
48
49
50
51
52
53
54
55 public static final String SEPARATOR_CELL_RGX = "\\x06";
56
57
58
59
60
61
62 public static final String SEPARATOR_QTE_RGX = "\\x05";
63
64
65
66
67
68
69
70 public static final String ENCLOSE_VALUE_RGX = "\\x04";
71
72 public static final String LEAD = "SqLi";
73 public static final String SHELL_LEAD = "${shell.lead}";
74 public static final String TRAIL = "iLQS";
75 public static final String SHELL_TRAIL = "${shell.trail}";
76
77
78
79
80 public static final String MODE = "(?si)";
81
82
83
84
85
86
87
88
89
90 public static final String CELL_TABLE = "([^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]*)"+ DataAccess.SEPARATOR_QTE_RGX +"([^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]*)(\\x08)?";
91
92 private final InjectionModel injectionModel;
93
94 public DataAccess(InjectionModel injectionModel) {
95 this.injectionModel = injectionModel;
96 }
97
98
99
100
101
102 public void getDatabaseInfos() {
103 LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, () -> I18nUtil.valueByKey("LOG_FETCHING_INFORMATIONS"));
104
105 var sourcePage = new String[]{ StringUtils.EMPTY };
106
107 var resultToParse = StringUtils.EMPTY;
108 try {
109 resultToParse = new SuspendableGetRows(this.injectionModel).run(
110 this.injectionModel.getMediatorVendor().getVendor().instance().sqlInfos(),
111 sourcePage,
112 false,
113 0,
114 MockElement.MOCK,
115 "metadata"
116 );
117 } catch (AbstractSlidingException e) {
118 resultToParse = DataAccess.getPartialResultAndLog(e, resultToParse);
119 } catch (Exception e) {
120 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
121 }
122
123 if (StringUtils.isEmpty(resultToParse)) {
124 this.injectionModel.sendResponseFromSite("Incorrect metadata", sourcePage[0].trim());
125 }
126
127 try {
128 String versionDatabase = resultToParse.split(DataAccess.ENCLOSE_VALUE_RGX)[0].replaceAll("\\s+", StringUtils.SPACE);
129 String nameDatabase = resultToParse.split(DataAccess.ENCLOSE_VALUE_RGX)[1];
130 String username = resultToParse.split(DataAccess.ENCLOSE_VALUE_RGX)[2];
131
132 var infos = String.format(
133 "Database [%s] on %s [%s] for user [%s]",
134 nameDatabase,
135 this.injectionModel.getMediatorVendor().getVendor(),
136 versionDatabase,
137 username
138 );
139 LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, infos);
140 } catch (ArrayIndexOutOfBoundsException e) {
141 LOGGER.log(
142 LogLevelUtil.CONSOLE_ERROR,
143 String.format("%s: %s", I18nUtil.valueByKey("LOG_DB_METADATA_INCORRECT"), resultToParse)
144 );
145 LOGGER.log(LogLevelUtil.CONSOLE_INFORM, I18nUtil.valueByKey("LOG_DB_METADATA_WARN"));
146 }
147 }
148
149
150
151
152
153
154
155
156
157
158 public List<Database> listDatabases() throws JSqlException {
159 LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, () -> I18nUtil.valueByKey("LOG_FETCHING_DATABASES"));
160 List<Database> databases = new ArrayList<>();
161 String resultToParse = StringUtils.EMPTY;
162
163 try {
164 var sourcePage = new String[]{ StringUtils.EMPTY };
165 resultToParse = new SuspendableGetRows(this.injectionModel).run(
166 this.injectionModel.getMediatorVendor().getVendor().instance().sqlDatabases(),
167 sourcePage,
168 true,
169 0,
170 MockElement.MOCK,
171 "databases"
172 );
173 } catch (AbstractSlidingException e) {
174 resultToParse = DataAccess.getPartialResultAndLog(e, resultToParse);
175 } catch (Exception e) {
176 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
177 }
178
179
180 var regexSearch = Pattern.compile(
181 DataAccess.MODE
182 + DataAccess.ENCLOSE_VALUE_RGX
183 + DataAccess.CELL_TABLE
184 + DataAccess.ENCLOSE_VALUE_RGX
185 )
186 .matcher(resultToParse);
187 if (!regexSearch.find()) {
188 throw new InjectionFailureException("No match while injecting databases");
189 }
190
191 regexSearch.reset();
192
193 while (regexSearch.find()) {
194 String databaseName = regexSearch.group(1);
195 String tableCount = regexSearch.group(2);
196
197 var newDatabase = new Database(databaseName, tableCount);
198 databases.add(newDatabase);
199 }
200
201 var request = new Request();
202 request.setMessage(Interaction.ADD_DATABASES);
203 request.setParameters(databases);
204 this.injectionModel.sendToViews(request);
205 return databases;
206 }
207
208
209
210
211
212
213
214
215
216
217
218 public List<Table> listTables(Database database) throws JSqlException {
219
220
221 this.injectionModel.setIsStoppedByUser(false);
222
223
224 var requestStartProgress = new Request();
225 requestStartProgress.setMessage(Interaction.START_PROGRESS);
226 requestStartProgress.setParameters(database);
227 this.injectionModel.sendToViews(requestStartProgress);
228
229 var tableCount = Integer.toString(database.getChildCount());
230
231 String resultToParse = StringUtils.EMPTY;
232 try {
233 var pageSource = new String[]{ StringUtils.EMPTY };
234 resultToParse = new SuspendableGetRows(this.injectionModel).run(
235 this.injectionModel.getMediatorVendor().getVendor().instance().sqlTables(database),
236 pageSource,
237 true,
238 Integer.parseInt(tableCount),
239 database,
240 "tables"
241 );
242 } catch (AbstractSlidingException e) {
243 resultToParse = DataAccess.getPartialResultAndLog(e, resultToParse);
244 } catch (Exception e) {
245 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
246 }
247
248
249 var regexSearch = Pattern.compile(
250 DataAccess.MODE
251 + DataAccess.ENCLOSE_VALUE_RGX
252 + DataAccess.CELL_TABLE
253 + DataAccess.ENCLOSE_VALUE_RGX
254 )
255 .matcher(resultToParse);
256
257 var requestEndProgress = new Request();
258 requestEndProgress.setMessage(Interaction.END_PROGRESS);
259 requestEndProgress.setParameters(database);
260 this.injectionModel.sendToViews(requestEndProgress);
261
262 if (!regexSearch.find()) {
263 throw new InjectionFailureException("No match while injecting tables");
264 }
265 regexSearch.reset();
266
267 List<Table> tables = new ArrayList<>();
268
269
270 while (regexSearch.find()) {
271 String tableName = regexSearch.group(1);
272 String rowCount = regexSearch.group(2);
273
274 var newTable = new Table(tableName, rowCount, database);
275 tables.add(newTable);
276 }
277
278 var requestAddTables = new Request();
279 requestAddTables.setMessage(Interaction.ADD_TABLES);
280 requestAddTables.setParameters(tables);
281 this.injectionModel.sendToViews(requestAddTables);
282 return tables;
283 }
284
285
286
287
288
289
290
291
292
293
294
295 public List<Column> listColumns(Table table) throws JSqlException {
296 List<Column> columns = new ArrayList<>();
297
298
299 var requestStartProgress = new Request();
300 requestStartProgress.setMessage(Interaction.START_INDETERMINATE_PROGRESS);
301 requestStartProgress.setParameters(table);
302 this.injectionModel.sendToViews(requestStartProgress);
303
304 String resultToParse = StringUtils.EMPTY;
305
306 try {
307 var pageSource = new String[]{ StringUtils.EMPTY };
308 resultToParse = new SuspendableGetRows(this.injectionModel).run(
309 this.injectionModel.getMediatorVendor().getVendor().instance().sqlColumns(table),
310 pageSource,
311 true,
312 0,
313 table,
314 "columns"
315 );
316 } catch (AbstractSlidingException e) {
317 resultToParse = DataAccess.getPartialResultAndLog(e, resultToParse);
318 } catch (Exception e) {
319 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
320 }
321
322
323 if (this.injectionModel.getMediatorVendor().isSqlite()) {
324 resultToParse = this.injectionModel.getMediatorVendor().getSqlite().transformSqlite(resultToParse);
325 }
326
327
328 var regexSearch = Pattern.compile(
329 DataAccess.MODE
330 + DataAccess.ENCLOSE_VALUE_RGX
331 + DataAccess.CELL_TABLE
332 + DataAccess.ENCLOSE_VALUE_RGX
333 )
334 .matcher(resultToParse);
335
336 var requestEndProgress = new Request();
337 requestEndProgress.setMessage(Interaction.END_INDETERMINATE_PROGRESS);
338 requestEndProgress.setParameters(table);
339 this.injectionModel.sendToViews(requestEndProgress);
340
341 if (!regexSearch.find()) {
342 throw new InjectionFailureException("No match while injecting columns");
343 }
344
345 regexSearch.reset();
346
347
348 while (regexSearch.find()) {
349 String nameColumn = regexSearch.group(1);
350 var column = new Column(nameColumn, table);
351 columns.add(column);
352 }
353
354 var requestAddColumns = new Request();
355 requestAddColumns.setMessage(Interaction.ADD_COLUMNS);
356 requestAddColumns.setParameters(columns);
357 this.injectionModel.sendToViews(requestAddColumns);
358 return columns;
359 }
360
361
362
363
364
365
366
367
368
369
370
371 public String[][] listValues(List<Column> columnsBean) throws JSqlException {
372 var databaseBean = (Database) columnsBean.get(0).getParent().getParent();
373 var tableBean = (Table) columnsBean.get(0).getParent();
374 int rowCount = columnsBean.get(0).getParent().getChildCount();
375
376
377 var request = new Request();
378 request.setMessage(Interaction.START_PROGRESS);
379 request.setParameters(tableBean);
380 this.injectionModel.sendToViews(request);
381
382
383 List<String> columnsName = new ArrayList<>();
384 for (AbstractElementDatabase columnBean: columnsBean) {
385 columnsName.add(columnBean.toString());
386 }
387
388
389
390
391 String[] columns = columnsName.toArray(new String[0]);
392
393 List<List<String>> listValues = this.getRows(databaseBean, tableBean, rowCount, columns);
394
395
396 columnsName.add(0, StringUtils.EMPTY);
397 columnsName.add(0, StringUtils.EMPTY);
398
399 String[][] table = this.getTable(columnsName, listValues);
400
401 columns = columnsName.toArray(new String[0]);
402
403
404 var objectData = new Object[]{ columns, table, tableBean };
405
406 var requestCreateValuesTab = new Request();
407 requestCreateValuesTab.setMessage(Interaction.CREATE_VALUES_TAB);
408 requestCreateValuesTab.setParameters(objectData);
409 this.injectionModel.sendToViews(requestCreateValuesTab);
410
411 var requestEndProgress = new Request();
412 requestEndProgress.setMessage(Interaction.END_PROGRESS);
413 requestEndProgress.setParameters(tableBean);
414 this.injectionModel.sendToViews(requestEndProgress);
415 return table;
416 }
417
418 private List<List<String>> getRows(Database database, Table table, int rowCount, String[] columns) throws InjectionFailureException {
419 String resultToParse = StringUtils.EMPTY;
420
421 try {
422 var pageSource = new String[]{ StringUtils.EMPTY };
423 resultToParse = new SuspendableGetRows(this.injectionModel).run(
424 this.injectionModel.getMediatorVendor().getVendor().instance().sqlRows(columns, database, table),
425 pageSource,
426 true,
427 rowCount,
428 table,
429 "rows"
430 );
431 } catch (AbstractSlidingException e) {
432 resultToParse = DataAccess.getPartialResultAndLog(e, resultToParse);
433 } catch (Exception e) {
434 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
435 }
436 return SuspendableGetRows.parse(resultToParse);
437 }
438
439 private static String getPartialResultAndLog(AbstractSlidingException e, String resultToParse) {
440 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage());
441
442
443 if (StringUtils.isNotEmpty(e.getSlidingWindowAllRows())) {
444 resultToParse = e.getSlidingWindowAllRows();
445 } else if (StringUtils.isNotEmpty(e.getSlidingWindowCurrentRows())) {
446 resultToParse = e.getSlidingWindowCurrentRows();
447 }
448 return resultToParse;
449 }
450
451 private String[][] getTable(List<String> columnsName, List<List<String>> values) {
452
453 var table = new String[values.size()][columnsName.size()];
454
455 for (var indexRow = 0 ; indexRow < values.size() ; indexRow++) {
456 var isIncomplete = false;
457
458 for (var indexColumn = 0 ; indexColumn < columnsName.size() ; indexColumn++) {
459 try {
460 table[indexRow][indexColumn] = values.get(indexRow).get(indexColumn);
461 } catch (IndexOutOfBoundsException e) {
462 isIncomplete = true;
463 LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, I18nUtil.valueByKey("LOG_LIST_VALUES_INCOMPLETE"));
464 LOGGER.log(LogLevelUtil.IGNORE, e);
465 }
466 }
467
468 if (isIncomplete) {
469 int logIndexRow = indexRow;
470 LOGGER.log(
471 LogLevelUtil.CONSOLE_ERROR,
472 "{}{}: ",
473 () -> I18nUtil.valueByKey("LOG_LIST_VALUES_TOO_LONG"),
474 () -> logIndexRow + 1
475 );
476
477 LOGGER.log(
478 LogLevelUtil.CONSOLE_ERROR,
479 () -> String.join(
480 ", ",
481 values.get(logIndexRow).toArray(new String[0])
482 )
483 );
484 }
485 }
486 return table;
487 }
488 }