OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 180 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
142 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
182 ilm 4
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
142 ilm 5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.erp.core.edm;
15
 
149 ilm 16
import org.openconcerto.erp.config.ComptaPropsConfiguration;
17
import org.openconcerto.erp.generationDoc.DocumentLocalStorageManager;
18
import org.openconcerto.erp.storage.StorageEngine;
19
import org.openconcerto.erp.storage.StorageEngines;
180 ilm 20
import org.openconcerto.sql.model.SQLInsert;
149 ilm 21
import org.openconcerto.sql.model.SQLRow;
22
import org.openconcerto.sql.model.SQLRowAccessor;
23
import org.openconcerto.sql.model.SQLRowValues;
180 ilm 24
import org.openconcerto.sql.model.SQLSelect;
149 ilm 25
import org.openconcerto.sql.model.SQLTable;
180 ilm 26
import org.openconcerto.sql.model.Where;
27
import org.openconcerto.utils.Base64;
149 ilm 28
import org.openconcerto.utils.ExceptionHandler;
29
import org.openconcerto.utils.FileUtils;
30
import org.openconcerto.utils.sync.SyncClient;
142 ilm 31
 
156 ilm 32
import java.io.BufferedInputStream;
33
import java.io.File;
34
import java.io.FileInputStream;
35
import java.io.IOException;
180 ilm 36
import java.nio.charset.StandardCharsets;
156 ilm 37
import java.nio.file.Files;
180 ilm 38
import java.security.InvalidAlgorithmParameterException;
39
import java.security.InvalidKeyException;
40
import java.security.NoSuchAlgorithmException;
156 ilm 41
import java.sql.SQLException;
42
import java.util.List;
43
 
180 ilm 44
import javax.crypto.BadPaddingException;
45
import javax.crypto.Cipher;
46
import javax.crypto.IllegalBlockSizeException;
47
import javax.crypto.KeyGenerator;
48
import javax.crypto.NoSuchPaddingException;
49
import javax.crypto.SecretKey;
50
import javax.crypto.spec.GCMParameterSpec;
51
import javax.crypto.spec.SecretKeySpec;
156 ilm 52
import javax.swing.JOptionPane;
182 ilm 53
import javax.swing.SwingUtilities;
156 ilm 54
 
142 ilm 55
public class AttachmentUtils {
56
 
180 ilm 57
    String generateBase64Key() throws NoSuchAlgorithmException {
58
        final KeyGenerator generator = KeyGenerator.getInstance("AES");
59
        generator.init(16 * 8);
60
        final SecretKey k = generator.generateKey();
61
        return Base64.encodeBytes(k.getEncoded(), Base64.DONT_BREAK_LINES);
62
    }
142 ilm 63
 
180 ilm 64
    SecretKey getSecretKey(String base64key) {
65
        byte[] b = Base64.decode(base64key.getBytes(StandardCharsets.UTF_8));
66
        if (b.length != 16) {
67
            throw new IllegalStateException("key length must be 16 bytes for AES");
68
        }
69
        return new SecretKeySpec(b, "AES");
70
    }
142 ilm 71
 
180 ilm 72
    public byte[] encrypt(SecretKey secretKey, byte[] in)
73
            throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
74
        return process(secretKey, in, Cipher.ENCRYPT_MODE);
75
    }
142 ilm 76
 
180 ilm 77
    public byte[] decrypt(SecretKey secretKey, byte[] in)
78
            throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
79
        return process(secretKey, in, Cipher.DECRYPT_MODE);
80
    }
142 ilm 81
 
180 ilm 82
    public byte[] process(SecretKey secretKey, byte[] in, int mode)
83
            throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
84
        final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
85
        final byte[] nonce = new byte[12];
86
        final byte[] e = secretKey.getEncoded();
87
        for (int i = 0; i < nonce.length; i++) {
88
            nonce[i] = (byte) (e[i] * e[i + 1] + e[i + 2]);
89
        }
90
        GCMParameterSpec spec = new GCMParameterSpec(16 * 8, nonce);
91
        cipher.init(mode, secretKey, spec);
92
        return cipher.doFinal(in);
93
    }
142 ilm 94
 
180 ilm 95
    public void uploadFile(File inFile, SQLRowAccessor rowSource, int idParent) throws SQLException {
96
        uploadFile(inFile, rowSource, idParent, null);
97
    }
142 ilm 98
 
180 ilm 99
    public void uploadFile(File inFile, SQLRowAccessor rowSource, int idParent, String nameInGed) throws SQLException {
100
 
101
        String encodeKey = fetchEncodedKey(rowSource.getTable().getTable("FWK_SCHEMA_METADATA"));
102
        // Création de la row attachment
103
        SQLRowValues rowValsAttachment = new SQLRowValues(rowSource.getTable().getTable("ATTACHMENT"));
104
        rowValsAttachment.put("SOURCE_TABLE", rowSource.getTable().getName());
105
        rowValsAttachment.put("SOURCE_ID", rowSource.getID());
106
        rowValsAttachment.put("ID_PARENT", idParent);
107
        if (encodeKey != null) {
108
            rowValsAttachment.put("ENCRYPTED", true);
109
        }
110
 
111
        SQLRow rowAttachment = rowValsAttachment.insert();
112
        int id = rowAttachment.getID();
113
 
114
        final String folderId = String.valueOf((id / 1000) * 1000);
115
        String subDir = "EDM/" + folderId;
116
        String fileNameID = String.valueOf(id);
117
        String ext = "";
118
 
119
        int i = inFile.getName().lastIndexOf('.');
120
        if (i > 0) {
121
            ext = inFile.getName().substring(i + 1);
122
        }
123
 
124
        final String fileWithIDNAme = fileNameID + "." + ext;
125
 
126
        final ComptaPropsConfiguration config = ComptaPropsConfiguration.getInstanceCompta();
127
        boolean isOnCloud = config.isOnCloud();
128
 
129
        try {
142 ilm 130
            if (isOnCloud) {
131
 
132
                String remotePath = subDir;
180 ilm 133
                final List<StorageEngine> engines = StorageEngines.getInstance().getActiveEngines();
134
                File encodedFile = inFile;
135
                if (encodeKey != null) {
136
                    encodedFile = File.createTempFile("encrypted", inFile.getName());
137
                    final byte[] enc = encrypt(getSecretKey(encodeKey), FileUtils.readBytes(inFile));
138
                    Files.write(encodedFile.toPath(), enc);
139
                }
140
 
142 ilm 141
                for (StorageEngine storageEngine : engines) {
142
                    if (storageEngine.isConfigured() && storageEngine.allowAutoStorage()) {
143
                        final String path = remotePath;
149 ilm 144
 
180 ilm 145
                        try (FileInputStream in = new FileInputStream(encodedFile)) {
142 ilm 146
                            storageEngine.connect();
149 ilm 147
                            final BufferedInputStream inStream = new BufferedInputStream(in);
142 ilm 148
                            storageEngine.store(inStream, path, fileWithIDNAme, true);
149
                            inStream.close();
150
                            storageEngine.disconnect();
151
                        }
180 ilm 152
 
142 ilm 153
                    }
154
                }
180 ilm 155
                if (encodeKey != null) {
156
                    encodedFile.delete();
157
                }
158
 
142 ilm 159
            } else {
160
                // Upload File
161
 
162
                // Get file out
163
                File dirRoot = DocumentLocalStorageManager.getInstance().getDocumentOutputDirectory(AttachmentSQLElement.DIRECTORY_PREFS);
164
                File storagePathFile = new File(dirRoot, subDir);
165
                storagePathFile.mkdirs();
166
                // TODO CHECK IF FILE EXISTS
180 ilm 167
                if (encodeKey == null) {
168
                    FileUtils.copyFile(inFile, new File(storagePathFile, fileWithIDNAme));
169
                } else {
170
                    final File encodedFile = new File(storagePathFile, fileWithIDNAme);
171
                    final byte[] enc = encrypt(getSecretKey(encodeKey), FileUtils.readBytes(inFile));
172
                    Files.write(encodedFile.toPath(), enc);
173
                }
142 ilm 174
            }
175
 
176
            // Update rowAttachment
177
            rowValsAttachment = rowAttachment.createEmptyUpdateRow();
178
 
179
            // Default is without extension
180
            String fileName = inFile.getName();
180 ilm 181
            if (nameInGed != null) {
182
                rowValsAttachment.put("NAME", nameInGed);
183
            } else {
184
                String name = fileName;
185
                int index = name.lastIndexOf('.');
186
                if (index > 0) {
187
                    name = name.substring(0, index);
188
                }
189
                rowValsAttachment.put("NAME", name);
142 ilm 190
            }
180 ilm 191
 
142 ilm 192
            rowValsAttachment.put("SOURCE_TABLE", rowSource.getTable().getName());
193
            rowValsAttachment.put("SOURCE_ID", rowSource.getID());
180 ilm 194
            rowValsAttachment.put("ID_PARENT", idParent);
156 ilm 195
            final String mimeType = Files.probeContentType(inFile.toPath());
196
            rowValsAttachment.put("MIMETYPE", mimeType != null ? mimeType : "application/octet-stream");
142 ilm 197
            rowValsAttachment.put("FILENAME", fileName);
198
            rowValsAttachment.put("FILESIZE", inFile.length());
199
            rowValsAttachment.put("STORAGE_PATH", subDir);
200
            rowValsAttachment.put("STORAGE_FILENAME", fileWithIDNAme);
180 ilm 201
            rowValsAttachment.put("ENCRYPTED", (encodeKey != null));
142 ilm 202
            // TODO THUMBNAIL
203
            // rowVals.put("THUMBNAIL", );
204
            // rowVals.put("THUMBNAIL_WIDTH", );
205
            // rowVals.put("THUMBNAIL_HEIGHT", );
206
 
207
            // needed for update count
208
 
209
            rowValsAttachment.commit();
149 ilm 210
            final Attachment a = new Attachment(rowValsAttachment);
211
            updateAttachmentsCountFromAttachment(a);
180 ilm 212
 
149 ilm 213
        } catch (Exception e) {
180 ilm 214
            if (rowAttachment != null) {
215
                config.getDirectory().getElement(AttachmentSQLElement.class).archive(rowAttachment.getID());
216
            }
217
            ExceptionHandler.handle("Impossible de sauvegarder le fichier " + inFile.getAbsolutePath(), e);
142 ilm 218
        }
180 ilm 219
 
142 ilm 220
    }
221
 
180 ilm 222
    public String fetchEncodedKey(SQLTable table) {
223
        final SQLSelect select = new SQLSelect();
224
        select.addSelect(table.getField("VALUE"));
225
        select.setWhere(new Where(table.getField("NAME"), "=", AttachmentSQLElement.EDM_KEY_METADATA));
226
        final List<?> rows = table.getDBSystemRoot().getDataSource().executeCol(select.asString());
227
        if (rows.size() == 1) {
228
            return rows.get(0).toString();
229
        }
230
        return null;
231
    }
232
 
233
    public void createKeyOnDatabase(SQLTable table) throws NoSuchAlgorithmException {
234
        final String s = fetchEncodedKey(table);
235
        if (s != null) {
236
            throw new IllegalStateException("key alread exists");
237
        }
238
 
239
        final SQLInsert insert = new SQLInsert();
240
        insert.add(table.getField("NAME"), AttachmentSQLElement.EDM_KEY_METADATA);
241
        insert.add(table.getField("VALUE"), generateBase64Key());
242
        table.getDBSystemRoot().getDataSource().execute(insert.asString());
243
    }
244
 
182 ilm 245
    public void createURL(String url, SQLRowAccessor rowSource, int idParent) throws SQLException {
246
        final SQLRowValues rowValsAttachment = new SQLRowValues(rowSource.getTable().getTable("ATTACHMENT"));
247
        rowValsAttachment.put("SOURCE_TABLE", rowSource.getTable().getName());
248
        rowValsAttachment.put("SOURCE_ID", rowSource.getID());
249
        rowValsAttachment.put("ID_PARENT", idParent);
250
        rowValsAttachment.put("NAME", url);
251
        rowValsAttachment.put("SOURCE_TABLE", rowSource.getTable().getName());
252
        rowValsAttachment.put("SOURCE_ID", rowSource.getID());
253
        rowValsAttachment.put("MIMETYPE", Attachment.MIMETYPE_URL);
254
        rowValsAttachment.put("FILENAME", url);
255
        rowValsAttachment.put("FILESIZE", 0);
256
        rowValsAttachment.put("STORAGE_PATH", "");
257
        rowValsAttachment.put("STORAGE_FILENAME", "");
258
        rowValsAttachment.commit();
259
    }
260
 
149 ilm 261
    public File getFile(Attachment attachment) {
142 ilm 262
 
263
        final ComptaPropsConfiguration config = ComptaPropsConfiguration.getInstanceCompta();
264
        boolean isOnCloud = config.isOnCloud();
265
 
149 ilm 266
        String subDir = attachment.getStoragePath();
267
        String fileName = attachment.getStorageFileName();
142 ilm 268
 
269
        String remotePath = config.getSocieteID() + File.separator + subDir;
270
 
271
        File fTemp;
272
        try {
273
            fTemp = File.createTempFile("edm_", "oc");
274
        } catch (IOException e) {
275
            ExceptionHandler.handle("Impossible de créer le fichier temporaire de réception", e);
276
            return null;
277
        }
278
        File f = new File(fTemp.getParent(), fTemp.getName() + "-dir");
279
        f.mkdirs();
280
        fTemp.delete();
281
 
282
        if (isOnCloud) {
283
            remotePath = remotePath.replace('\\', '/');
284
            final SyncClient client = new SyncClient("https://" + config.getStorageServer());
285
 
286
            client.setVerifyHost(false);
287
 
288
            try {
289
                client.retrieveFile(f, remotePath, fileName, config.getToken());
290
            } catch (Exception e) {
291
                ExceptionHandler.handle("Impossible de récupérer le fichier depuis le cloud", e);
292
                return null;
293
            }
294
 
295
        } else {
296
 
297
            // Get file out
298
            File dirRoot = DocumentLocalStorageManager.getInstance().getDocumentOutputDirectory(AttachmentSQLElement.DIRECTORY_PREFS);
299
            File storagePathFile = new File(dirRoot, subDir);
156 ilm 300
            File fileIn;
301
            try {
302
                fileIn = new File(storagePathFile, fileName).getCanonicalFile();
303
                if (fileIn.exists()) {
304
                    final File outFile = new File(f, fileName);
305
                    try {
306
                        FileUtils.copyFile(fileIn, outFile);
307
                    } catch (IOException e) {
308
                        ExceptionHandler.handle("Impossible de copier le fichier vers le fichier temporaire de réception", e);
309
                        return null;
310
                    }
311
                } else {
182 ilm 312
                    if (SwingUtilities.isEventDispatchThread()) {
313
                        JOptionPane.showMessageDialog(null, "Le fichier n'existe pas sur le serveur!\n" + fileIn.getAbsolutePath(), "Erreur fichier", JOptionPane.ERROR_MESSAGE);
314
                    } else {
315
                        SwingUtilities.invokeLater(new Runnable() {
316
 
317
                            @Override
318
                            public void run() {
319
                                JOptionPane.showMessageDialog(null, "Le fichier n'existe pas sur le serveur!\n" + fileIn.getAbsolutePath(), "Erreur fichier", JOptionPane.ERROR_MESSAGE);
320
                            }
321
                        });
322
                    }
142 ilm 323
                    return null;
324
                }
156 ilm 325
            } catch (IOException e1) {
326
                ExceptionHandler.handle("Impossible de trouver le fichier\n" + storagePathFile + File.pathSeparator + fileName, e1);
327
                return null;
142 ilm 328
            }
329
        }
330
        final File outFile = new File(f, fileName);
180 ilm 331
        if (attachment.isEncrypted() && outFile.length() > 0) {
332
            try {
333
                final byte[] bytes = FileUtils.readBytes(outFile);
334
                final String encodeKey = fetchEncodedKey(config.getRootSociete().getTable("FWK_SCHEMA_METADATA"));
335
                if (encodeKey == null) {
336
                    throw new IllegalStateException("missing key");
337
                }
338
                final byte[] decrypted = decrypt(getSecretKey(encodeKey), bytes);
339
                Files.write(outFile.toPath(), decrypted);
340
            } catch (Exception e) {
341
                ExceptionHandler.handle("Impossible de decrypter le fichier\n" + outFile.getAbsolutePath(), e);
342
                return null;
343
            }
344
 
345
        }
142 ilm 346
        outFile.setReadOnly();
347
        return outFile;
348
 
349
    }
350
 
149 ilm 351
    public void deleteFile(Attachment rowAttachment) throws SQLException, IllegalStateException {
180 ilm 352
        // Delete Row
353
        // Remove from DB first
354
        final ComptaPropsConfiguration config = ComptaPropsConfiguration.getInstanceCompta();
355
        config.getDirectory().getElement(AttachmentSQLElement.class).archive(rowAttachment.getId());
356
        updateAttachmentsCountFromAttachment(rowAttachment);
142 ilm 357
 
149 ilm 358
        if (!rowAttachment.isFolder()) {
359
            boolean isOnCloud = config.isOnCloud();
360
            // Delete File
361
            String subDir = rowAttachment.getStoragePath();
362
            String fileName = rowAttachment.getStorageFileName();
142 ilm 363
 
149 ilm 364
            String remotePath = config.getSocieteID() + File.separator + subDir;
365
            if (isOnCloud) {
366
                remotePath = remotePath.replace('\\', '/');
367
                // final SyncClient client = new SyncClient("https://" + config.getStorageServer());
368
                //
369
                // client.setVerifyHost(false);
142 ilm 370
 
149 ilm 371
                // TODO DELETE FILE ON CLOUD OR RENAME?
372
                // client.retrieveFile(f, remotePath, fileName, config.getToken());
373
            } else {
142 ilm 374
 
149 ilm 375
                File dirRoot = DocumentLocalStorageManager.getInstance().getDocumentOutputDirectory(AttachmentSQLElement.DIRECTORY_PREFS);
376
                File storagePathFile = new File(dirRoot, subDir);
377
                File f = new File(storagePathFile, fileName);
378
                if (f.exists()) {
379
                    if (!f.delete()) {
380
                        throw new IllegalStateException("Une erreur est survenue lors de la suppression du fichier");
381
                    }
142 ilm 382
                }
383
            }
384
        }
180 ilm 385
 
142 ilm 386
    }
387
 
149 ilm 388
    private void updateAttachmentsCountFromAttachment(Attachment rowAttachment) {
389
        final ComptaPropsConfiguration config = ComptaPropsConfiguration.getInstanceCompta();
390
        final SQLTable attachmentTable = config.getDirectory().getElement(AttachmentSQLElement.class).getTable();
391
        final SQLTable table = attachmentTable.getTable(rowAttachment.getSourceTable());
392
        final SQLRow source = table.getRow(rowAttachment.getSourceId());
142 ilm 393
        updateAttachmentsCountFromSource(source);
394
    }
395
 
149 ilm 396
    private void updateAttachmentsCountFromSource(SQLRow rowSource) {
397
        final SQLTable tableSource = rowSource.getTable();
398
        final SQLTable tableAtt = rowSource.getTable().getTable("ATTACHMENT");
142 ilm 399
 
400
        String req = "UPDATE " + tableSource.getSQLName().quote() + " SET " + tableSource.getField("ATTACHMENTS").getQuotedName() + "=(SELECT COUNT(*) FROM " + tableAtt.getSQLName().quote();
401
        req += " WHERE " + tableAtt.getArchiveField().getQuotedName() + "=0 AND " + tableAtt.getField("SOURCE_TABLE").getQuotedName() + "='" + tableSource.getName() + "'";
402
        req += " AND " + tableAtt.getField("SOURCE_ID").getQuotedName() + "=" + rowSource.getID() + ") WHERE " + tableSource.getKey().getQuotedName() + "=" + rowSource.getID();
403
 
404
        tableSource.getDBSystemRoot().getDataSource().execute(req);
405
    }
406
 
149 ilm 407
    public static void rename(Attachment rowAttachment, String newName) {
408
        rowAttachment.setName(newName);
409
        final ComptaPropsConfiguration config = ComptaPropsConfiguration.getInstanceCompta();
410
        final SQLTable attachmentTable = config.getDirectory().getElement(AttachmentSQLElement.class).getTable();
411
        final String req = "UPDATE " + attachmentTable.getSQLName().quote() + " SET " + attachmentTable.getField("NAME").getQuotedName() + "=" + attachmentTable.getBase().quoteString(newName)
412
                + " WHERE " + attachmentTable.getKey().getQuotedName() + "=" + rowAttachment.getId();
413
        attachmentTable.getDBSystemRoot().getDataSource().execute(req);
414
    }
415
 
416
    public void createFolder(String folderName, SQLRowAccessor rowSource, int idParent) throws SQLException {
417
        final SQLRowValues rowValsAttachment = new SQLRowValues(rowSource.getTable().getTable("ATTACHMENT"));
418
        rowValsAttachment.put("SOURCE_TABLE", rowSource.getTable().getName());
419
        rowValsAttachment.put("SOURCE_ID", rowSource.getID());
420
        rowValsAttachment.put("ID_PARENT", idParent);
421
        rowValsAttachment.put("NAME", folderName);
422
        rowValsAttachment.put("SOURCE_TABLE", rowSource.getTable().getName());
423
        rowValsAttachment.put("SOURCE_ID", rowSource.getID());
424
        rowValsAttachment.put("MIMETYPE", Attachment.MIMETYPE_FOLDER);
425
        rowValsAttachment.put("FILENAME", "");
426
        rowValsAttachment.put("FILESIZE", 0);
427
        rowValsAttachment.put("STORAGE_PATH", "");
428
        rowValsAttachment.put("STORAGE_FILENAME", "");
429
        rowValsAttachment.commit();
430
    }
431
 
432
    public void move(Attachment a, Attachment folder) {
433
        if (!folder.isFolder()) {
434
            throw new IllegalArgumentException(folder + " is not a folder");
435
        }
436
        move(a, folder.getId());
437
    }
438
 
439
    public void move(Attachment a, int folderId) {
440
        final ComptaPropsConfiguration config = ComptaPropsConfiguration.getInstanceCompta();
441
        final SQLTable attachmentTable = config.getDirectory().getElement(AttachmentSQLElement.class).getTable();
442
        final String req = "UPDATE " + attachmentTable.getSQLName().quote() + " SET " + attachmentTable.getField("ID_PARENT").getQuotedName() + "=" + folderId + " WHERE "
443
                + attachmentTable.getKey().getQuotedName() + "=" + a.getId();
444
        attachmentTable.getDBSystemRoot().getDataSource().execute(req);
180 ilm 445
 
149 ilm 446
    }
447
 
180 ilm 448
    public static void main(String[] args) throws NoSuchAlgorithmException {
449
        System.err.println("AttachmentUtils.main() key : " + new AttachmentUtils().generateBase64Key());
450
    }
142 ilm 451
}