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