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