OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

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