1:
37:
38:
39: package ;
40:
41: import ;
42:
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55:
56:
63: public abstract class JarUtils
64: {
65:
66:
67:
68:
69: public static final String META_INF = "META-INF/";
70: public static final String DSA_SUFFIX = ".DSA";
71: public static final String SF_SUFFIX = ".SF";
72: public static final String NAME = "Name";
73:
74:
77: public static final String MANIFEST_VERSION = "Manifest-Version";
78:
79:
83: public static final String SIGNATURE_VERSION = "Signature-Version";
84:
85:
86: public static final byte[] CRLF = new byte[] { 0x0D, 0x0A };
87: private static final String DEFAULT_MF_VERSION = "1.0";
88: private static final String DEFAULT_SF_VERSION = "1.0";
89: private static final Name CREATED_BY = new Name("Created-By");
90: private static final String CREATOR = SystemProperties.getProperty("java.version")
91: + " ("
92: + SystemProperties.getProperty("java.vendor")
93: + ")";
94:
95:
96:
97:
98:
99: public static void
100: readMFManifest(Attributes attr, Map entries, InputStream in)
101: throws IOException
102: {
103: BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
104: readMainSection(attr, br);
105: readIndividualSections(entries, br);
106: }
107:
108: public static void
109: readSFManifest(Attributes attr, Map entries, InputStream in)
110: throws IOException
111: {
112: BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
113: String version_header = Name.SIGNATURE_VERSION.toString();
114: try
115: {
116: String version = expectHeader(version_header, br);
117: attr.putValue(SIGNATURE_VERSION, version);
118:
119:
120:
121:
122: }
123: catch (IOException ioe)
124: {
125: throw new JarException("Signature file MUST start with a "
126: + version_header + ": " + ioe.getMessage());
127: }
128: read_attributes(attr, br);
129:
130:
131: String s = br.readLine();
132: while (s != null && s.length() > 0)
133: {
134: Attributes eAttr = readSectionName(s, br, entries);
135: read_attributes(eAttr, br);
136: s = br.readLine();
137: }
138: }
139:
140: private static void readMainSection(Attributes attr, BufferedReader br)
141: throws IOException
142: {
143:
144: read_attributes(attr, br);
145:
146:
147:
148: if (attr.getValue(Name.MANIFEST_VERSION) == null)
149: attr.putValue(MANIFEST_VERSION, "0.0");
150: }
151:
152: private static void readIndividualSections(Map entries, BufferedReader br)
153: throws IOException
154: {
155: String s = br.readLine();
156: while (s != null && (! s.equals("")))
157: {
158: Attributes attr = readSectionName(s, br, entries);
159: read_attributes(attr, br);
160: s = br.readLine();
161: }
162: }
163:
164:
169: private static void readVersionInfo(Attributes attr, BufferedReader br)
170: throws IOException
171: {
172: String version_header = Name.MANIFEST_VERSION.toString();
173: try
174: {
175: String value = expectHeader(version_header, br);
176: attr.putValue(MANIFEST_VERSION, value);
177: }
178: catch (IOException ioe)
179: {
180: throw new JarException("Manifest should start with a " + version_header
181: + ": " + ioe.getMessage());
182: }
183: }
184:
185: private static String expectHeader(String header, BufferedReader br)
186: throws IOException
187: {
188: String s = br.readLine();
189: if (s == null)
190: throw new JarException("unexpected end of file");
191:
192: return expectHeader(header, br, s);
193: }
194:
195: private static void read_attributes(Attributes attr, BufferedReader br)
196: throws IOException
197: {
198: String s = br.readLine();
199: while (s != null && (! s.equals("")))
200: {
201: readAttribute(attr, s, br);
202: s = br.readLine();
203: }
204: }
205:
206: private static void
207: readAttribute(Attributes attr, String s, BufferedReader br) throws IOException
208: {
209: try
210: {
211: int colon = s.indexOf(": ");
212: String name = s.substring(0, colon);
213: String value_start = s.substring(colon + 2);
214: String value = readHeaderValue(value_start, br);
215: attr.putValue(name, value);
216: }
217: catch (IndexOutOfBoundsException iobe)
218: {
219: throw new JarException("Manifest contains a bad header: " + s);
220: }
221: }
222:
223: private static String readHeaderValue(String s, BufferedReader br)
224: throws IOException
225: {
226: boolean try_next = true;
227: while (try_next)
228: {
229:
230: br.mark(1);
231: if (br.read() == ' ')
232: s += br.readLine();
233: else
234: {
235: br.reset();
236: try_next = false;
237: }
238: }
239: return s;
240: }
241:
242: private static Attributes
243: readSectionName(String s, BufferedReader br, Map entries) throws JarException
244: {
245: try
246: {
247: String name = expectHeader(NAME, br, s);
248: Attributes attr = new Attributes();
249: entries.put(name, attr);
250: return attr;
251: }
252: catch (IOException ioe)
253: {
254: throw new JarException("Section should start with a Name header: "
255: + ioe.getMessage());
256: }
257: }
258:
259: private static String expectHeader(String header, BufferedReader br, String s)
260: throws IOException
261: {
262: try
263: {
264: String name = s.substring(0, header.length() + 1);
265: if (name.equalsIgnoreCase(header + ":"))
266: {
267: String value_start = s.substring(header.length() + 2);
268: return readHeaderValue(value_start, br);
269: }
270: }
271: catch (IndexOutOfBoundsException ignored)
272: {
273: }
274:
275: throw new JarException("unexpected '" + s + "'");
276: }
277:
278:
279:
280: public static void
281: writeMFManifest(Attributes attr, Map entries, OutputStream stream)
282: throws IOException
283: {
284: BufferedOutputStream out = stream instanceof BufferedOutputStream
285: ? (BufferedOutputStream) stream
286: : new BufferedOutputStream(stream, 4096);
287: writeVersionInfo(attr, out);
288: Iterator i;
289: Map.Entry e;
290: for (i = attr.entrySet().iterator(); i.hasNext();)
291: {
292: e = (Map.Entry) i.next();
293:
294: if (! Name.MANIFEST_VERSION.equals(e.getKey()))
295: writeAttributeEntry(e, out);
296: }
297: out.write(CRLF);
298:
299: Iterator j;
300: for (i = entries.entrySet().iterator(); i.hasNext();)
301: {
302: e = (Map.Entry) i.next();
303: writeHeader(NAME, e.getKey().toString(), out);
304: Attributes eAttr = (Attributes) e.getValue();
305: for (j = eAttr.entrySet().iterator(); j.hasNext();)
306: {
307: Map.Entry e2 = (Map.Entry) j.next();
308: writeAttributeEntry(e2, out);
309: }
310: out.write(CRLF);
311: }
312:
313: out.flush();
314: }
315:
316: public static void
317: writeSFManifest(Attributes attr, Map entries, OutputStream stream)
318: throws IOException
319: {
320: BufferedOutputStream out = stream instanceof BufferedOutputStream
321: ? (BufferedOutputStream) stream
322: : new BufferedOutputStream(stream, 4096);
323: writeHeader(Name.SIGNATURE_VERSION.toString(), DEFAULT_SF_VERSION, out);
324: writeHeader(CREATED_BY.toString(), CREATOR, out);
325: Iterator i;
326: Map.Entry e;
327: for (i = attr.entrySet().iterator(); i.hasNext();)
328: {
329: e = (Map.Entry) i.next();
330: Name name = (Name) e.getKey();
331: if (Name.SIGNATURE_VERSION.equals(name) || CREATED_BY.equals(name))
332: continue;
333:
334: writeHeader(name.toString(), (String) e.getValue(), out);
335: }
336: out.write(CRLF);
337:
338: Iterator j;
339: for (i = entries.entrySet().iterator(); i.hasNext();)
340: {
341: e = (Map.Entry) i.next();
342: writeHeader(NAME, e.getKey().toString(), out);
343: Attributes eAttr = (Attributes) e.getValue();
344: for (j = eAttr.entrySet().iterator(); j.hasNext();)
345: {
346: Map.Entry e2 = (Map.Entry) j.next();
347: writeHeader(e2.getKey().toString(), (String) e2.getValue(), out);
348: }
349: out.write(CRLF);
350: }
351:
352: out.flush();
353: }
354:
355: private static void writeVersionInfo(Attributes attr, OutputStream out)
356: throws IOException
357: {
358:
359: String version = attr.getValue(Name.MANIFEST_VERSION);
360: if (version == null)
361: version = DEFAULT_MF_VERSION;
362:
363: writeHeader(Name.MANIFEST_VERSION.toString(), version, out);
364: }
365:
366: private static void writeAttributeEntry(Map.Entry entry, OutputStream out)
367: throws IOException
368: {
369: String name = entry.getKey().toString();
370: String value = entry.getValue().toString();
371: if (name.equalsIgnoreCase(NAME))
372: throw new JarException("Attributes cannot be called 'Name'");
373:
374: if (name.startsWith("From"))
375: throw new JarException("Header cannot start with the four letters 'From'"
376: + name);
377:
378: writeHeader(name, value, out);
379: }
380:
381:
404: private static void writeHeader(String name, String value, OutputStream out)
405: throws IOException
406: {
407: String target = name + ": ";
408: byte[] b = target.getBytes("UTF-8");
409: if (b.length > 72)
410: throw new IOException("Attribute's name already longer than 70 bytes");
411:
412: if (b.length == 72)
413: {
414: out.write(b);
415: out.write(CRLF);
416: target = " " + value;
417: }
418: else
419: target = target + value;
420:
421: int n;
422: while (true)
423: {
424: b = target.getBytes("UTF-8");
425: if (b.length < 73)
426: {
427: out.write(b);
428: break;
429: }
430:
431:
432: n = 72;
433: while (true)
434: {
435: b = target.substring(0, n).getBytes("UTF-8");
436: if (b.length < 73)
437: break;
438:
439: n--;
440: if (n < 1)
441: throw new IOException("Header is unbreakable and longer than 72 bytes");
442: }
443:
444: out.write(b);
445: out.write(CRLF);
446: target = " " + target.substring(n);
447: }
448:
449: out.write(CRLF);
450: }
451: }