1 package org.sf.jlaunchpad.util;
2
3 import java.security.DigestInputStream;
4 import java.security.MessageDigest;
5 import java.security.NoSuchAlgorithmException;
6 import java.security.NoSuchProviderException;
7 import java.io.*;
8 import java.util.*;
9 import java.text.MessageFormat;
10 import java.text.ParseException;
11
12
13
14
15
16
17
18 public class Checksum {
19
20
21
22
23 private File file = null;
24
25
26
27
28
29
30 private File todir;
31
32
33
34
35 private String algorithm = "MD5";
36
37
38
39 private String provider = null;
40
41
42
43
44 private String fileext;
45
46
47
48 private String property;
49
50
51
52
53
54 private Map allDigests = new HashMap();
55
56
57
58
59
60
61 private Map relativeFilePaths = new HashMap();
62
63
64
65 private String totalproperty;
66
67
68
69
70 private boolean forceOverwrite;
71
72
73
74 private String verifyProperty;
75
76
77
78
79
80
81
82 private Hashtable includeFileMap = new Hashtable();
83
84
85
86 private MessageDigest messageDigest;
87
88
89
90 private boolean isCondition;
91
92
93
94 private int readBufferSize = 8 * 1024;
95
96
97
98
99 private MessageFormat format = FormatElement.getDefault().getFormat();
100
101
102
103
104
105
106 public void setFile(File file) {
107 this.file = file;
108 }
109
110
111
112
113
114
115
116
117 public void setTodir(File todir) {
118 this.todir = todir;
119 }
120
121
122
123
124
125
126
127 public void setAlgorithm(String algorithm) {
128 this.algorithm = algorithm;
129 }
130
131
132
133
134
135
136
137 public void setProvider(String provider) {
138 this.provider = provider;
139 }
140
141
142
143
144
145
146
147 public void setFileext(String fileext) {
148 this.fileext = fileext;
149 }
150
151
152
153
154
155
156 public void setProperty(String property) {
157 this.property = property;
158 }
159
160
161
162
163
164
165
166
167 public void setTotalproperty(String totalproperty) {
168 this.totalproperty = totalproperty;
169 }
170
171
172
173
174
175
176
177 public void setVerifyproperty(String verifyProperty) {
178 this.verifyProperty = verifyProperty;
179 }
180
181
182
183
184
185
186
187
188 public void setForceOverwrite(boolean forceOverwrite) {
189 this.forceOverwrite = forceOverwrite;
190 }
191
192
193
194
195
196
197 public void setReadBufferSize(int size) {
198 this.readBufferSize = size;
199 }
200
201
202
203
204
205
206
207 public void setFormat(FormatElement e) {
208 format = e.getFormat();
209 }
210
211
212
213
214
215
216
217
218
219 public void setPattern(String p) {
220 format = new MessageFormat(p);
221 }
222
223
224
225
226
227 public void execute() {
228 isCondition = false;
229 boolean value = validateAndExecute();
230
231
232
233
234
235
236 }
237
238
239
240
241 private boolean validateAndExecute() {
242 String savedFileExt = fileext;
243
244 if (file != null && file.exists() && file.isDirectory()) {
245 throw new RuntimeException("Checksum cannot be generated for directories");
246 }
247 if (file != null && totalproperty != null) {
248 throw new RuntimeException("File and Totalproperty cannot co-exist.");
249 }
250 if (property != null && fileext != null) {
251 throw new RuntimeException("Property and FileExt cannot co-exist.");
252 }
253 if (property != null) {
254 if (forceOverwrite) {
255 throw new RuntimeException(
256 "ForceOverwrite cannot be used when Property is specified");
257 }
258 int ct = 0;
259
260 if (file != null) {
261 ct++;
262 }
263 if (ct > 1) {
264 throw new RuntimeException(
265 "Multiple files cannot be used when Property is specified");
266 }
267 }
268 if (verifyProperty != null) {
269 isCondition = true;
270 }
271 if (verifyProperty != null && forceOverwrite) {
272 throw new RuntimeException("VerifyProperty and ForceOverwrite cannot co-exist.");
273 }
274 if (isCondition && forceOverwrite) {
275 throw new RuntimeException(
276 "ForceOverwrite cannot be used when conditions are being used.");
277 }
278 messageDigest = null;
279 if (provider != null) {
280 try {
281 messageDigest = MessageDigest.getInstance(algorithm, provider);
282 } catch (NoSuchAlgorithmException noalgo) {
283 throw new RuntimeException(noalgo);
284 } catch (NoSuchProviderException noprovider) {
285 throw new RuntimeException(noprovider);
286 }
287 } else {
288 try {
289 messageDigest = MessageDigest.getInstance(algorithm);
290 } catch (NoSuchAlgorithmException noalgo) {
291 throw new RuntimeException(noalgo);
292 }
293 }
294 if (messageDigest == null) {
295 throw new RuntimeException("Unable to create Message Digest");
296 }
297 if (fileext == null) {
298 fileext = "." + algorithm;
299 } else if (fileext.trim().length() == 0) {
300 throw new RuntimeException("File extension when specified must not be an empty string");
301 }
302 try {
303 if (file != null) {
304 if (totalproperty != null || todir != null) {
305 relativeFilePaths.put(
306 file, file.getName().replace(File.separatorChar, '/'));
307 }
308 addToIncludeFileMap(file);
309 }
310 return generateChecksums();
311 } finally {
312 fileext = savedFileExt;
313 includeFileMap.clear();
314 }
315 }
316
317
318
319
320
321 private void addToIncludeFileMap(File file) throws RuntimeException {
322 if (file.exists()) {
323 if (property == null) {
324 File checksumFile = getChecksumFile(file);
325 if (forceOverwrite || isCondition
326 || (file.lastModified() > checksumFile.lastModified())) {
327 includeFileMap.put(file, checksumFile);
328 } else {
329
330 if (totalproperty != null) {
331
332 String checksum = readChecksum(checksumFile);
333 byte[] digest = decodeHex(checksum.toCharArray());
334 allDigests.put(file, digest);
335 }
336 }
337 } else {
338 includeFileMap.put(file, property);
339 }
340 } else {
341 String message = "Could not find file "
342 + file.getAbsolutePath()
343 + " to generate checksum for.";
344
345 throw new RuntimeException(message);
346 }
347 }
348
349 private File getChecksumFile(File file) {
350 File directory;
351 if (todir != null) {
352
353 String path = (String) relativeFilePaths.get(file);
354 if (path == null) {
355
356 throw new RuntimeException(
357 "Internal error: "
358 + "relativeFilePaths could not match file"
359 + file + "\n"
360 + "please file a bug report on this");
361 }
362 directory = new File(todir, path).getParentFile();
363
364 directory.mkdirs();
365 } else {
366
367
368 directory = file.getParentFile();
369 }
370
371 return new File(directory, file.getName() + fileext);
372 }
373
374
375
376
377 private boolean generateChecksums() {
378 boolean checksumMatches = true;
379 FileInputStream fis = null;
380 FileOutputStream fos = null;
381 byte[] buf = new byte[readBufferSize];
382 try {
383 for (Enumeration e = includeFileMap.keys(); e.hasMoreElements();) {
384 messageDigest.reset();
385 File src = (File) e.nextElement();
386 if (!isCondition) {
387
388 }
389 fis = new FileInputStream(src);
390 DigestInputStream dis = new DigestInputStream(fis,
391 messageDigest);
392 while (dis.read(buf, 0, readBufferSize) != -1) {
393
394 }
395 dis.close();
396 fis.close();
397 fis = null;
398 byte[] fileDigest = messageDigest.digest();
399 if (totalproperty != null) {
400 allDigests.put(src, fileDigest);
401 }
402 String checksum = createDigestString(fileDigest);
403
404 Object destination = includeFileMap.get(src);
405 if (destination instanceof java.lang.String) {
406 String prop = (String) destination;
407 if (isCondition) {
408 checksumMatches
409 = checksumMatches && checksum.equals(property);
410 } else {
411
412 }
413 } else if (destination instanceof java.io.File) {
414 if (isCondition) {
415 File existingFile = (File) destination;
416 if (existingFile.exists()) {
417 try {
418 String suppliedChecksum =
419 readChecksum(existingFile);
420 checksumMatches = checksumMatches
421 && checksum.equals(suppliedChecksum);
422 } catch (Exception be) {
423
424 checksumMatches = false;
425 }
426 } else {
427 checksumMatches = false;
428 }
429 } else {
430 File dest = (File) destination;
431 fos = new FileOutputStream(dest);
432 fos.write(format.format(new Object[]{
433 checksum,
434 src.getName(),
435 }).getBytes());
436 fos.write(System.getProperty("line.separator").getBytes());
437 fos.close();
438 fos = null;
439 }
440 }
441 }
442 if (totalproperty != null) {
443
444
445 Set keys = allDigests.keySet();
446 Object[] keyArray = keys.toArray();
447
448 Arrays.sort(keyArray);
449
450 messageDigest.reset();
451 for (int i = 0; i < keyArray.length; i++) {
452 File src = (File) keyArray[i];
453
454
455 byte[] digest = (byte[]) allDigests.get(src);
456 messageDigest.update(digest);
457
458
459 String fileName = (String) relativeFilePaths.get(src);
460 messageDigest.update(fileName.getBytes());
461 }
462
463
464 }
465 } catch (Exception e) {
466 throw new RuntimeException(e);
467 } finally {
468 close(fis);
469 close(fos);
470 }
471 return checksumMatches;
472 }
473
474
475
476
477
478
479 public static void close(Writer device) {
480 if (device != null) {
481 try {
482 device.close();
483 } catch (IOException ioex) {
484
485 }
486 }
487 }
488
489
490
491
492
493
494
495 public static void close(Reader device) {
496 if (device != null) {
497 try {
498 device.close();
499 } catch (IOException ioex) {
500
501 }
502 }
503 }
504
505 public static void close(OutputStream device) {
506 if (device != null) {
507 try {
508 device.close();
509 } catch (IOException ioex) {
510
511 }
512 }
513 }
514
515
516
517
518
519
520
521 public static void close(InputStream device) {
522 if (device != null) {
523 try {
524 device.close();
525 } catch (IOException ioex) {
526
527 }
528 }
529 }
530
531 private String createDigestString(byte[] fileDigest) {
532 StringBuffer checksumSb = new StringBuffer();
533 for (int i = 0; i < fileDigest.length; i++) {
534 String hexStr = Integer.toHexString(0x00ff & fileDigest[i]);
535 if (hexStr.length() < 2) {
536 checksumSb.append("0");
537 }
538 checksumSb.append(hexStr);
539 }
540 return checksumSb.toString();
541 }
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556 public static byte[] decodeHex(char[] data) {
557 int l = data.length;
558
559 if ((l & 0x01) != 0) {
560 throw new RuntimeException("odd number of characters.");
561 }
562
563 byte[] out = new byte[l >> 1];
564
565
566 for (int i = 0, j = 0; j < l; i++) {
567 int f = Character.digit(data[j++], 16) << 4;
568 f = f | Character.digit(data[j++], 16);
569 out[i] = (byte) (f & 0xFF);
570 }
571
572 return out;
573 }
574
575
576
577
578
579
580 private String readChecksum(File f) {
581 BufferedReader diskChecksumReader = null;
582 try {
583 diskChecksumReader = new BufferedReader(new FileReader(f));
584 Object[] result = format.parse(diskChecksumReader.readLine());
585 if (result == null || result.length == 0 || result[0] == null) {
586 throw new RuntimeException("failed to find a checksum");
587 }
588 return (String) result[0];
589 } catch (IOException e) {
590 throw new RuntimeException("Couldn't read checksum file " + f, e);
591 } catch (ParseException e) {
592 throw new RuntimeException("Couldn't read checksum file " + f, e);
593 } finally {
594 close(diskChecksumReader);
595 }
596 }
597
598 }