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 resultToParse = this.injectionModel.getMediatorEngine().getEngine().transform(resultToParse);
302
303
304 var regexSearch = Pattern.compile(
305 DataAccess.MODE
306 + DataAccess.ENCLOSE_VALUE_RGX
307 + DataAccess.CELL_TABLE
308 + DataAccess.ENCLOSE_VALUE_RGX
309 )
310 .matcher(resultToParse);
311
312 this.injectionModel.sendToViews(new Seal.EndIndeterminateProgress(table));
313
314 if (!regexSearch.find()) {
315 throw new InjectionFailureException("No match while injecting columns");
316 }
317
318 regexSearch.reset();
319
320
321 while (regexSearch.find()) {
322 String nameColumn = regexSearch.group(1);
323 var column = new Column(nameColumn, table);
324 columns.add(column);
325 }
326
327 this.injectionModel.sendToViews(new Seal.AddColumns(columns));
328 return columns;
329 }
330
331
332
333
334
335
336
337
338
339
340
341 public String[][] listValues(List<Column> columnsBean) throws JSqlException {
342 var databaseBean = (Database) columnsBean.getFirst().getParent().getParent();
343 var tableBean = (Table) columnsBean.getFirst().getParent();
344 int rowCount = columnsBean.getFirst().getParent().getChildCount();
345
346
347 this.injectionModel.sendToViews(new Seal.StartProgress(tableBean));
348
349
350 List<String> columnsName = new ArrayList<>();
351 for (AbstractElementDatabase columnBean: columnsBean) {
352 columnsName.add(columnBean.toString());
353 }
354
355
356
357
358 String[] columns = columnsName.toArray(new String[0]);
359
360 List<List<String>> listValues = this.getRows(databaseBean, tableBean, rowCount, columns);
361
362
363 columnsName.addFirst(StringUtils.EMPTY);
364 columnsName.addFirst(StringUtils.EMPTY);
365
366 String[][] table = this.getTable(columnsName, listValues);
367
368 columns = columnsName.toArray(new String[0]);
369
370 this.injectionModel.sendToViews(new Seal.CreateValuesTab(columns, table, tableBean));
371 this.injectionModel.sendToViews(new Seal.EndProgress(tableBean));
372 return table;
373 }
374
375 private List<List<String>> getRows(Database database, Table table, int rowCount, String[] columns) throws InjectionFailureException {
376 String resultToParse = StringUtils.EMPTY;
377
378 try {
379 var pageSource = new String[]{ StringUtils.EMPTY };
380 resultToParse = new SuspendableGetRows(this.injectionModel).run(new Input(
381 this.injectionModel.getMediatorEngine().getEngine().instance().sqlRows(columns, database, table),
382 pageSource,
383 true,
384 rowCount,
385 table,
386 "rows"
387 ));
388 } catch (AbstractSlidingException e) {
389 resultToParse = DataAccess.getPartialResultAndLog(e, resultToParse);
390 } catch (Exception e) {
391 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
392 }
393 return SuspendableGetRows.parse(resultToParse);
394 }
395
396 private static String getPartialResultAndLog(AbstractSlidingException e, String resultToParse) {
397 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage());
398
399
400 String resultFromWindow = resultToParse;
401 if (StringUtils.isNotEmpty(e.getSlidingWindowAllRows())) {
402 resultFromWindow = e.getSlidingWindowAllRows();
403 } else if (StringUtils.isNotEmpty(e.getSlidingWindowCurrentRows())) {
404 resultFromWindow = e.getSlidingWindowCurrentRows();
405 }
406 return resultFromWindow;
407 }
408
409 private String[][] getTable(List<String> columnsName, List<List<String>> values) {
410
411 var table = new String[values.size()][columnsName.size()];
412
413 for (var indexRow = 0 ; indexRow < values.size() ; indexRow++) {
414 var isIncomplete = false;
415
416 for (var indexColumn = 0 ; indexColumn < columnsName.size() ; indexColumn++) {
417 try {
418 table[indexRow][indexColumn] = values.get(indexRow).get(indexColumn);
419 } catch (IndexOutOfBoundsException e) {
420 isIncomplete = true;
421 LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, I18nUtil.valueByKey("LOG_LIST_VALUES_INCOMPLETE"));
422 LOGGER.log(LogLevelUtil.IGNORE, e);
423 }
424 }
425
426 if (isIncomplete) {
427 int logIndexRow = indexRow;
428 LOGGER.log(
429 LogLevelUtil.CONSOLE_ERROR,
430 "{}{}: ",
431 () -> I18nUtil.valueByKey("LOG_LIST_VALUES_TOO_LONG"),
432 () -> logIndexRow + 1
433 );
434
435 LOGGER.log(
436 LogLevelUtil.CONSOLE_ERROR,
437 () -> String.join(
438 ", ",
439 values.get(logIndexRow).toArray(new String[0])
440 )
441 );
442 }
443 }
444 return table;
445 }
446 }