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.suspendable.Input;
16 import com.jsql.view.subscriber.Seal;
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(new Input(
107 this.injectionModel.getMediatorEngine().getEngine().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 LOGGER.log(
130 LogLevelUtil.CONSOLE_SUCCESS,
131 "Database [{}] on {} [{}] for user [{}]",
132 nameDatabase,
133 this.injectionModel.getMediatorEngine().getEngine(),
134 versionDatabase,
135 username
136 );
137 } catch (ArrayIndexOutOfBoundsException e) {
138 LOGGER.log(LogLevelUtil.CONSOLE_ERROR,"{}: {}", I18nUtil.valueByKey("LOG_DB_METADATA_INCORRECT"), resultToParse);
139 LOGGER.log(LogLevelUtil.CONSOLE_INFORM, I18nUtil.valueByKey("LOG_DB_METADATA_WARN"));
140 }
141 }
142
143
144
145
146
147
148
149
150
151
152 public List<Database> listDatabases() throws JSqlException {
153 LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, () -> I18nUtil.valueByKey("LOG_FETCHING_DATABASES"));
154 List<Database> databases = new ArrayList<>();
155 String resultToParse = StringUtils.EMPTY;
156
157 try {
158 var sourcePage = new String[]{ StringUtils.EMPTY };
159 resultToParse = new SuspendableGetRows(this.injectionModel).run(new Input(
160 this.injectionModel.getMediatorEngine().getEngine().instance().sqlDatabases(),
161 sourcePage,
162 true,
163 0,
164 MockElement.MOCK,
165 "databases"
166 ));
167 } catch (AbstractSlidingException e) {
168 resultToParse = DataAccess.getPartialResultAndLog(e, resultToParse);
169 } catch (Exception e) {
170 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
171 }
172
173
174 var regexSearch = Pattern.compile(
175 DataAccess.MODE
176 + DataAccess.ENCLOSE_VALUE_RGX
177 + DataAccess.CELL_TABLE
178 + DataAccess.ENCLOSE_VALUE_RGX
179 )
180 .matcher(resultToParse);
181 if (!regexSearch.find()) {
182 throw new InjectionFailureException("No match while injecting databases");
183 }
184
185 regexSearch.reset();
186
187 while (regexSearch.find()) {
188 String databaseName = regexSearch.group(1);
189 String tableCount = regexSearch.group(2);
190
191 var newDatabase = new Database(databaseName, tableCount);
192 databases.add(newDatabase);
193 }
194
195 this.injectionModel.sendToViews(new Seal.AddDatabases(databases));
196 return databases;
197 }
198
199
200
201
202
203
204
205
206
207
208
209 public List<Table> listTables(Database database) throws JSqlException {
210
211
212 this.injectionModel.setIsStoppedByUser(false);
213
214
215 this.injectionModel.sendToViews(new Seal.StartProgress(database));
216
217 var tableCount = Integer.toString(database.getChildCount());
218
219 String resultToParse = StringUtils.EMPTY;
220 try {
221 var pageSource = new String[]{ StringUtils.EMPTY };
222 resultToParse = new SuspendableGetRows(this.injectionModel).run(new Input(
223 this.injectionModel.getMediatorEngine().getEngine().instance().sqlTables(database),
224 pageSource,
225 true,
226 Integer.parseInt(tableCount),
227 database,
228 "tables"
229 ));
230 } catch (AbstractSlidingException e) {
231 resultToParse = DataAccess.getPartialResultAndLog(e, resultToParse);
232 } catch (Exception e) {
233 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
234 }
235
236
237 var regexSearch = Pattern.compile(
238 DataAccess.MODE
239 + DataAccess.ENCLOSE_VALUE_RGX
240 + DataAccess.CELL_TABLE
241 + DataAccess.ENCLOSE_VALUE_RGX
242 )
243 .matcher(resultToParse);
244
245 this.injectionModel.sendToViews(new Seal.EndProgress(database));
246
247 if (!regexSearch.find()) {
248 throw new InjectionFailureException("No match while injecting tables");
249 }
250 regexSearch.reset();
251
252 List<Table> tables = new ArrayList<>();
253
254
255 while (regexSearch.find()) {
256 String tableName = regexSearch.group(1);
257 String rowCount = regexSearch.group(2);
258
259 var newTable = new Table(tableName, rowCount, database);
260 tables.add(newTable);
261 }
262
263 this.injectionModel.sendToViews(new Seal.AddTables(tables));
264 return tables;
265 }
266
267
268
269
270
271
272
273
274
275
276
277 public List<Column> listColumns(Table table) throws JSqlException {
278 List<Column> columns = new ArrayList<>();
279
280
281 this.injectionModel.sendToViews(new Seal.StartIndeterminateProgress(table));
282
283 String resultToParse = StringUtils.EMPTY;
284
285 try {
286 var pageSource = new String[]{ StringUtils.EMPTY };
287 resultToParse = new SuspendableGetRows(this.injectionModel).run(new Input(
288 this.injectionModel.getMediatorEngine().getEngine().instance().sqlColumns(table),
289 pageSource,
290 true,
291 0,
292 table,
293 "columns"
294 ));
295 } catch (AbstractSlidingException e) {
296 resultToParse = DataAccess.getPartialResultAndLog(e, resultToParse);
297 } catch (Exception e) {
298 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
299 }
300
301
302 if (this.injectionModel.getMediatorEngine().isSqlite()) {
303 resultToParse = this.injectionModel.getMediatorEngine().getSqlite().transformSqlite(resultToParse);
304 }
305
306
307 var regexSearch = Pattern.compile(
308 DataAccess.MODE
309 + DataAccess.ENCLOSE_VALUE_RGX
310 + DataAccess.CELL_TABLE
311 + DataAccess.ENCLOSE_VALUE_RGX
312 )
313 .matcher(resultToParse);
314
315 this.injectionModel.sendToViews(new Seal.EndIndeterminateProgress(table));
316
317 if (!regexSearch.find()) {
318 throw new InjectionFailureException("No match while injecting columns");
319 }
320
321 regexSearch.reset();
322
323
324 while (regexSearch.find()) {
325 String nameColumn = regexSearch.group(1);
326 var column = new Column(nameColumn, table);
327 columns.add(column);
328 }
329
330 this.injectionModel.sendToViews(new Seal.AddColumns(columns));
331 return columns;
332 }
333
334
335
336
337
338
339
340
341
342
343
344 public String[][] listValues(List<Column> columnsBean) throws JSqlException {
345 var databaseBean = (Database) columnsBean.getFirst().getParent().getParent();
346 var tableBean = (Table) columnsBean.getFirst().getParent();
347 int rowCount = columnsBean.getFirst().getParent().getChildCount();
348
349
350 this.injectionModel.sendToViews(new Seal.StartProgress(tableBean));
351
352
353 List<String> columnsName = new ArrayList<>();
354 for (AbstractElementDatabase columnBean: columnsBean) {
355 columnsName.add(columnBean.toString());
356 }
357
358
359
360
361 String[] columns = columnsName.toArray(new String[0]);
362
363 List<List<String>> listValues = this.getRows(databaseBean, tableBean, rowCount, columns);
364
365
366 columnsName.addFirst(StringUtils.EMPTY);
367 columnsName.addFirst(StringUtils.EMPTY);
368
369 String[][] table = this.getTable(columnsName, listValues);
370
371 columns = columnsName.toArray(new String[0]);
372
373 this.injectionModel.sendToViews(new Seal.CreateValuesTab(columns, table, tableBean));
374 this.injectionModel.sendToViews(new Seal.EndProgress(tableBean));
375 return table;
376 }
377
378 private List<List<String>> getRows(Database database, Table table, int rowCount, String[] columns) throws InjectionFailureException {
379 String resultToParse = StringUtils.EMPTY;
380
381 try {
382 var pageSource = new String[]{ StringUtils.EMPTY };
383 resultToParse = new SuspendableGetRows(this.injectionModel).run(new Input(
384 this.injectionModel.getMediatorEngine().getEngine().instance().sqlRows(columns, database, table),
385 pageSource,
386 true,
387 rowCount,
388 table,
389 "rows"
390 ));
391 } catch (AbstractSlidingException e) {
392 resultToParse = DataAccess.getPartialResultAndLog(e, resultToParse);
393 } catch (Exception e) {
394 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
395 }
396 return SuspendableGetRows.parse(resultToParse);
397 }
398
399 private static String getPartialResultAndLog(AbstractSlidingException e, String resultToParse) {
400 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage());
401
402
403 String resultFromWindow = resultToParse;
404 if (StringUtils.isNotEmpty(e.getSlidingWindowAllRows())) {
405 resultFromWindow = e.getSlidingWindowAllRows();
406 } else if (StringUtils.isNotEmpty(e.getSlidingWindowCurrentRows())) {
407 resultFromWindow = e.getSlidingWindowCurrentRows();
408 }
409 return resultFromWindow;
410 }
411
412 private String[][] getTable(List<String> columnsName, List<List<String>> values) {
413
414 var table = new String[values.size()][columnsName.size()];
415
416 for (var indexRow = 0 ; indexRow < values.size() ; indexRow++) {
417 var isIncomplete = false;
418
419 for (var indexColumn = 0 ; indexColumn < columnsName.size() ; indexColumn++) {
420 try {
421 table[indexRow][indexColumn] = values.get(indexRow).get(indexColumn);
422 } catch (IndexOutOfBoundsException e) {
423 isIncomplete = true;
424 LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, I18nUtil.valueByKey("LOG_LIST_VALUES_INCOMPLETE"));
425 LOGGER.log(LogLevelUtil.IGNORE, e);
426 }
427 }
428
429 if (isIncomplete) {
430 int logIndexRow = indexRow;
431 LOGGER.log(
432 LogLevelUtil.CONSOLE_ERROR,
433 "{}{}: ",
434 () -> I18nUtil.valueByKey("LOG_LIST_VALUES_TOO_LONG"),
435 () -> logIndexRow + 1
436 );
437
438 LOGGER.log(
439 LogLevelUtil.CONSOLE_ERROR,
440 () -> String.join(
441 ", ",
442 values.get(logIndexRow).toArray(new String[0])
443 )
444 );
445 }
446 }
447 return table;
448 }
449 }