Source Code Cross Referenced for Codecs.java in  » Net » SkunkDAV » HTTPClient » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Net » SkunkDAV » HTTPClient 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * @(#)Codecs.java					0.3-2 18/06/1999
0003:         *
0004:         *  This file is part of the HTTPClient package
0005:         *  Copyright (C) 1996-1999  Ronald Tschalär
0006:         *
0007:         *  This library is free software; you can redistribute it and/or
0008:         *  modify it under the terms of the GNU Lesser General Public
0009:         *  License as published by the Free Software Foundation; either
0010:         *  version 2 of the License, or (at your option) any later version.
0011:         *
0012:         *  This library is distributed in the hope that it will be useful,
0013:         *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0014:         *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015:         *  Lesser General Public License for more details.
0016:         *
0017:         *  You should have received a copy of the GNU Lesser General Public
0018:         *  License along with this library; if not, write to the Free
0019:         *  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
0020:         *  MA 02111-1307, USA
0021:         *
0022:         *  For questions, suggestions, bug-reports, enhancement-requests etc.
0023:         *  I may be contacted at:
0024:         *
0025:         *  ronald@innovation.ch
0026:         *
0027:         */
0028:
0029:        package HTTPClient;
0030:
0031:        import java.util.BitSet;
0032:        import java.util.Vector;
0033:        import java.util.StringTokenizer;
0034:        import java.io.IOException;
0035:        import java.io.InputStream;
0036:        import java.io.DataInputStream;
0037:        import java.io.File;
0038:        import java.io.FileInputStream;
0039:        import java.io.FileOutputStream;
0040:        import java.io.BufferedReader;
0041:
0042:        /**
0043:         * This class collects various encoders and decoders.
0044:         *
0045:         * @version	0.3-2  18/06/1999
0046:         * @author	Ronald Tschalär
0047:         */
0048:
0049:        public class Codecs {
0050:            private static BitSet BoundChar;
0051:            private static BitSet EBCDICUnsafeChar;
0052:            private static byte[] Base64EncMap, Base64DecMap;
0053:            private static char[] UUEncMap;
0054:            private static byte[] UUDecMap;
0055:
0056:            private final static String ContDisp = "\r\nContent-Disposition: form-data; name=\"";
0057:            private final static String FileName = "\"; filename=\"";
0058:            private final static String Boundary = "\r\n-----ieoau._._+2_8_GoodLuck8.3-dskdfJwSJKlrWLr0234324jfLdsjfdAuaoei-----";
0059:
0060:            // Class Initializer
0061:
0062:            static {
0063:                // rfc-2046 & rfc-2045: (bcharsnospace & token)
0064:                // used for multipart codings
0065:                BoundChar = new BitSet(256);
0066:                for (int ch = '0'; ch <= '9'; ch++)
0067:                    BoundChar.set(ch);
0068:                for (int ch = 'A'; ch <= 'Z'; ch++)
0069:                    BoundChar.set(ch);
0070:                for (int ch = 'a'; ch <= 'z'; ch++)
0071:                    BoundChar.set(ch);
0072:                BoundChar.set('+');
0073:                BoundChar.set('_');
0074:                BoundChar.set('-');
0075:                BoundChar.set('.');
0076:
0077:                // EBCDIC unsafe characters to be quoted in quoted-printable
0078:                // See first NOTE in section 6.7 of rfc-2045
0079:                EBCDICUnsafeChar = new BitSet(256);
0080:                EBCDICUnsafeChar.set('!');
0081:                EBCDICUnsafeChar.set('"');
0082:                EBCDICUnsafeChar.set('#');
0083:                EBCDICUnsafeChar.set('$');
0084:                EBCDICUnsafeChar.set('@');
0085:                EBCDICUnsafeChar.set('[');
0086:                EBCDICUnsafeChar.set('\\');
0087:                EBCDICUnsafeChar.set(']');
0088:                EBCDICUnsafeChar.set('^');
0089:                EBCDICUnsafeChar.set('`');
0090:                EBCDICUnsafeChar.set('{');
0091:                EBCDICUnsafeChar.set('|');
0092:                EBCDICUnsafeChar.set('}');
0093:                EBCDICUnsafeChar.set('~');
0094:
0095:                // rfc-2045: Base64 Alphabet
0096:                byte[] map = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D',
0097:                        (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H',
0098:                        (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
0099:                        (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
0100:                        (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
0101:                        (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X',
0102:                        (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b',
0103:                        (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
0104:                        (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
0105:                        (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n',
0106:                        (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r',
0107:                        (byte) 's', (byte) 't', (byte) 'u', (byte) 'v',
0108:                        (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z',
0109:                        (byte) '0', (byte) '1', (byte) '2', (byte) '3',
0110:                        (byte) '4', (byte) '5', (byte) '6', (byte) '7',
0111:                        (byte) '8', (byte) '9', (byte) '+', (byte) '/' };
0112:                Base64EncMap = map;
0113:                Base64DecMap = new byte[128];
0114:                for (int idx = 0; idx < Base64EncMap.length; idx++)
0115:                    Base64DecMap[Base64EncMap[idx]] = (byte) idx;
0116:
0117:                // uuencode'ing maps
0118:                UUEncMap = new char[64];
0119:                for (int idx = 0; idx < UUEncMap.length; idx++)
0120:                    UUEncMap[idx] = (char) (idx + 0x20);
0121:                UUDecMap = new byte[128];
0122:                for (int idx = 0; idx < UUEncMap.length; idx++)
0123:                    UUDecMap[UUEncMap[idx]] = (byte) idx;
0124:            }
0125:
0126:            // Constructors
0127:
0128:            /**
0129:             * This class isn't meant to be instantiated.
0130:             */
0131:            private Codecs() {
0132:            }
0133:
0134:            // Methods
0135:
0136:            /**
0137:             * This method encodes the given string using the base64-encoding
0138:             * specified in RFC-2045 (Section 6.8). It's used for example in the
0139:             * "Basic" authorization scheme.
0140:             *
0141:             * @param  str the string
0142:             * @return the base64-encoded <var>str</var>
0143:             */
0144:            public final static String base64Encode(String str) {
0145:                if (str == null)
0146:                    return null;
0147:
0148:                byte data[] = new byte[str.length()];
0149:                str.getBytes(0, str.length(), data, 0);
0150:
0151:                return new String(base64Encode(data), 0);
0152:            }
0153:
0154:            /**
0155:             * This method encodes the given byte[] using the base64-encoding
0156:             * specified in RFC-2045 (Section 6.8).
0157:             *
0158:             * @param  data the data
0159:             * @return the base64-encoded <var>data</var>
0160:             */
0161:            public final static byte[] base64Encode(byte[] data) {
0162:                if (data == null)
0163:                    return null;
0164:
0165:                int sidx, didx;
0166:                byte dest[] = new byte[((data.length + 2) / 3) * 4];
0167:
0168:                // 3-byte to 4-byte conversion + 0-63 to ascii printable conversion
0169:                for (sidx = 0, didx = 0; sidx < data.length - 2; sidx += 3) {
0170:                    dest[didx++] = Base64EncMap[(data[sidx] >>> 2) & 077];
0171:                    dest[didx++] = Base64EncMap[(data[sidx + 1] >>> 4) & 017
0172:                            | (data[sidx] << 4) & 077];
0173:                    dest[didx++] = Base64EncMap[(data[sidx + 2] >>> 6) & 003
0174:                            | (data[sidx + 1] << 2) & 077];
0175:                    dest[didx++] = Base64EncMap[data[sidx + 2] & 077];
0176:                }
0177:                if (sidx < data.length) {
0178:                    dest[didx++] = Base64EncMap[(data[sidx] >>> 2) & 077];
0179:                    if (sidx < data.length - 1) {
0180:                        dest[didx++] = Base64EncMap[(data[sidx + 1] >>> 4)
0181:                                & 017 | (data[sidx] << 4) & 077];
0182:                        dest[didx++] = Base64EncMap[(data[sidx + 1] << 2) & 077];
0183:                    } else
0184:                        dest[didx++] = Base64EncMap[(data[sidx] << 4) & 077];
0185:                }
0186:
0187:                // add padding
0188:                for (; didx < dest.length; didx++)
0189:                    dest[didx] = (byte) '=';
0190:
0191:                return dest;
0192:            }
0193:
0194:            /**
0195:             * This method decodes the given string using the base64-encoding
0196:             * specified in RFC-2045 (Section 6.8).
0197:             *
0198:             * @param  str the base64-encoded string.
0199:             * @return the decoded <var>str</var>.
0200:             */
0201:            public final static String base64Decode(String str) {
0202:                if (str == null)
0203:                    return null;
0204:
0205:                byte data[] = new byte[str.length()];
0206:                str.getBytes(0, str.length(), data, 0);
0207:
0208:                return new String(base64Decode(data), 0);
0209:            }
0210:
0211:            /**
0212:             * This method decodes the given byte[] using the base64-encoding
0213:             * specified in RFC-2045 (Section 6.8).
0214:             *
0215:             * @param  data the base64-encoded data.
0216:             * @return the decoded <var>data</var>.
0217:             */
0218:            public final static byte[] base64Decode(byte[] data) {
0219:                if (data == null)
0220:                    return null;
0221:
0222:                int tail = data.length;
0223:                while (data[tail - 1] == '=')
0224:                    tail--;
0225:
0226:                byte dest[] = new byte[tail - data.length / 4];
0227:
0228:                // ascii printable to 0-63 conversion
0229:                for (int idx = 0; idx < data.length; idx++)
0230:                    data[idx] = Base64DecMap[data[idx]];
0231:
0232:                // 4-byte to 3-byte conversion
0233:                int sidx, didx;
0234:                for (sidx = 0, didx = 0; didx < dest.length - 2; sidx += 4, didx += 3) {
0235:                    dest[didx] = (byte) (((data[sidx] << 2) & 255) | ((data[sidx + 1] >>> 4) & 003));
0236:                    dest[didx + 1] = (byte) (((data[sidx + 1] << 4) & 255) | ((data[sidx + 2] >>> 2) & 017));
0237:                    dest[didx + 2] = (byte) (((data[sidx + 2] << 6) & 255) | (data[sidx + 3] & 077));
0238:                }
0239:                if (didx < dest.length)
0240:                    dest[didx] = (byte) (((data[sidx] << 2) & 255) | ((data[sidx + 1] >>> 4) & 003));
0241:                if (++didx < dest.length)
0242:                    dest[didx] = (byte) (((data[sidx + 1] << 4) & 255) | ((data[sidx + 2] >>> 2) & 017));
0243:
0244:                return dest;
0245:            }
0246:
0247:            /**
0248:             * This method encodes the given byte[] using the unix uuencode
0249:             * encding. The output is split into lines starting with the encoded
0250:             * number of encoded octets in the line and ending with a newline.
0251:             * No line is longer than 45 octets (60 characters), not including
0252:             * length and newline.
0253:             *
0254:             * <P><em>Note:</em> just the raw data is encoded; no 'begin' and 'end'
0255:             * lines are added as is done by the unix <code>uuencode</code> utility.
0256:             *
0257:             * @param  data the data
0258:             * @return the uuencoded <var>data</var>
0259:             */
0260:            public final static char[] uuencode(byte[] data) {
0261:                if (data == null)
0262:                    return null;
0263:                if (data.length == 0)
0264:                    return new char[0];
0265:
0266:                int line_len = 45; // line length, in octets
0267:
0268:                int sidx, didx;
0269:                char nl[] = System.getProperty("line.separator", "\n")
0270:                        .toCharArray(), dest[] = new char[(data.length + 2) / 3
0271:                        * 4 + ((data.length + line_len - 1) / line_len)
0272:                        * (nl.length + 1)];
0273:
0274:                // split into lines, adding line-length and line terminator
0275:                for (sidx = 0, didx = 0; sidx + line_len < data.length;) {
0276:                    // line length
0277:                    dest[didx++] = UUEncMap[line_len];
0278:
0279:                    // 3-byte to 4-byte conversion + 0-63 to ascii printable conversion
0280:                    for (int end = sidx + line_len; sidx < end; sidx += 3) {
0281:                        dest[didx++] = UUEncMap[(data[sidx] >>> 2) & 077];
0282:                        dest[didx++] = UUEncMap[(data[sidx + 1] >>> 4) & 017
0283:                                | (data[sidx] << 4) & 077];
0284:                        dest[didx++] = UUEncMap[(data[sidx + 2] >>> 6) & 003
0285:                                | (data[sidx + 1] << 2) & 077];
0286:                        dest[didx++] = UUEncMap[data[sidx + 2] & 077];
0287:                    }
0288:
0289:                    // line terminator
0290:                    for (int idx = 0; idx < nl.length; idx++)
0291:                        dest[didx++] = nl[idx];
0292:                }
0293:
0294:                // last line
0295:
0296:                // line length
0297:                dest[didx++] = UUEncMap[data.length - sidx];
0298:
0299:                // 3-byte to 4-byte conversion + 0-63 to ascii printable conversion
0300:                for (; sidx + 2 < data.length; sidx += 3) {
0301:                    dest[didx++] = UUEncMap[(data[sidx] >>> 2) & 077];
0302:                    dest[didx++] = UUEncMap[(data[sidx + 1] >>> 4) & 017
0303:                            | (data[sidx] << 4) & 077];
0304:                    dest[didx++] = UUEncMap[(data[sidx + 2] >>> 6) & 003
0305:                            | (data[sidx + 1] << 2) & 077];
0306:                    dest[didx++] = UUEncMap[data[sidx + 2] & 077];
0307:                }
0308:
0309:                if (sidx < data.length - 1) {
0310:                    dest[didx++] = UUEncMap[(data[sidx] >>> 2) & 077];
0311:                    dest[didx++] = UUEncMap[(data[sidx + 1] >>> 4) & 017
0312:                            | (data[sidx] << 4) & 077];
0313:                    dest[didx++] = UUEncMap[(data[sidx + 1] << 2) & 077];
0314:                    dest[didx++] = UUEncMap[0];
0315:                } else if (sidx < data.length) {
0316:                    dest[didx++] = UUEncMap[(data[sidx] >>> 2) & 077];
0317:                    dest[didx++] = UUEncMap[(data[sidx] << 4) & 077];
0318:                    dest[didx++] = UUEncMap[0];
0319:                    dest[didx++] = UUEncMap[0];
0320:                }
0321:
0322:                // line terminator
0323:                for (int idx = 0; idx < nl.length; idx++)
0324:                    dest[didx++] = nl[idx];
0325:
0326:                // sanity check
0327:                if (didx != dest.length)
0328:                    throw new Error("Calculated " + dest.length
0329:                            + " chars but wrote " + didx + " chars!");
0330:
0331:                return dest;
0332:            }
0333:
0334:            /**
0335:             * TBD! How to return file name and mode?
0336:             *
0337:             * @param rdr the reader from which to read and decode the data
0338:             * @exception ParseException if either the "begin" or "end" line are not
0339:             *                           found, or the "begin" is incorrect
0340:             * @exception IOException if the <var>rdr</var> throws an IOException
0341:             */
0342:            private final static byte[] uudecode(BufferedReader rdr)
0343:                    throws ParseException, IOException {
0344:                String line, file_name;
0345:                int file_mode;
0346:
0347:                // search for beginning
0348:
0349:                while ((line = rdr.readLine()) != null
0350:                        && !line.startsWith("begin "))
0351:                    ;
0352:                if (line == null)
0353:                    throw new ParseException("'begin' line not found");
0354:
0355:                // parse 'begin' line
0356:
0357:                StringTokenizer tok = new StringTokenizer(line);
0358:                tok.nextToken(); // throw away 'begin'
0359:                try // extract mode
0360:                {
0361:                    file_mode = Integer.parseInt(tok.nextToken(), 8);
0362:                } catch (Exception e) {
0363:                    throw new ParseException("Invalid mode on line: " + line);
0364:                }
0365:                try // extract name
0366:                {
0367:                    file_name = tok.nextToken();
0368:                } catch (java.util.NoSuchElementException e) {
0369:                    throw new ParseException("No file name found on line: "
0370:                            + line);
0371:                }
0372:
0373:                // read and parse body
0374:
0375:                byte[] body = new byte[1000];
0376:                int off = 0;
0377:
0378:                while ((line = rdr.readLine()) != null && !line.equals("end")) {
0379:                    byte[] tmp = uudecode(line.toCharArray());
0380:                    if (off + tmp.length > body.length)
0381:                        body = Util.resizeArray(body, off + 1000);
0382:                    System.arraycopy(tmp, 0, body, off, tmp.length);
0383:                    off += tmp.length;
0384:                }
0385:
0386:                if (line == null)
0387:                    throw new ParseException("'end' line not found");
0388:
0389:                return Util.resizeArray(body, off);
0390:            }
0391:
0392:            /**
0393:             * This method decodes the given uuencoded char[].
0394:             *
0395:             * <P><em>Note:</em> just the actual data is decoded; any 'begin' and
0396:             * 'end' lines such as those generated by the unix <code>uuencode</code>
0397:             * utility must not be included.
0398:             *
0399:             * @param  data the uuencode-encoded data.
0400:             * @return the decoded <var>data</var>.
0401:             */
0402:            public final static byte[] uudecode(char[] data) {
0403:                if (data == null)
0404:                    return null;
0405:
0406:                int sidx, didx;
0407:                byte dest[] = new byte[data.length / 4 * 3];
0408:
0409:                for (sidx = 0, didx = 0; sidx < data.length;) {
0410:                    // get line length (in number of encoded octets)
0411:                    int len = UUDecMap[data[sidx++]];
0412:
0413:                    // ascii printable to 0-63 and 4-byte to 3-byte conversion
0414:                    int end = didx + len;
0415:                    for (; didx < end - 2; sidx += 4) {
0416:                        byte A = UUDecMap[data[sidx]], B = UUDecMap[data[sidx + 1]], C = UUDecMap[data[sidx + 2]], D = UUDecMap[data[sidx + 3]];
0417:                        dest[didx++] = (byte) (((A << 2) & 255) | ((B >>> 4) & 003));
0418:                        dest[didx++] = (byte) (((B << 4) & 255) | ((C >>> 2) & 017));
0419:                        dest[didx++] = (byte) (((C << 6) & 255) | (D & 077));
0420:                    }
0421:
0422:                    if (didx < end) {
0423:                        byte A = UUDecMap[data[sidx]], B = UUDecMap[data[sidx + 1]];
0424:                        dest[didx++] = (byte) (((A << 2) & 255) | ((B >>> 4) & 003));
0425:                    }
0426:                    if (didx < end) {
0427:                        byte B = UUDecMap[data[sidx + 1]], C = UUDecMap[data[sidx + 2]];
0428:                        dest[didx++] = (byte) (((B << 4) & 255) | ((C >>> 2) & 017));
0429:                    }
0430:
0431:                    // skip padding
0432:                    while (sidx < data.length && data[sidx] != '\n'
0433:                            && data[sidx] != '\r')
0434:                        sidx++;
0435:
0436:                    // skip end of line
0437:                    while (sidx < data.length
0438:                            && (data[sidx] == '\n' || data[sidx] == '\r'))
0439:                        sidx++;
0440:                }
0441:
0442:                return Util.resizeArray(dest, didx);
0443:            }
0444:
0445:            /**
0446:             * This method does a quoted-printable encoding of the given string
0447:             * according to RFC-2045 (Section 6.7). <em>Note:</em> this assumes
0448:             * 8-bit characters.
0449:             *
0450:             * @param  str the string
0451:             * @return the quoted-printable encoded string
0452:             */
0453:            public final static String quotedPrintableEncode(String str) {
0454:                if (str == null)
0455:                    return null;
0456:
0457:                char map[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
0458:                        '9', 'A', 'B', 'C', 'D', 'E', 'F' }, nl[] = System
0459:                        .getProperty("line.separator", "\n").toCharArray(), res[] = new char[(int) (str
0460:                        .length() * 1.5)], src[] = str.toCharArray();
0461:                char ch;
0462:                int cnt = 0, didx = 1, last = 0, slen = str.length();
0463:
0464:                for (int sidx = 0; sidx < slen; sidx++) {
0465:                    ch = src[sidx];
0466:
0467:                    if (ch == nl[0] && match(src, sidx, nl)) // Rule #4
0468:                    {
0469:                        if (res[didx - 1] == ' ') // Rule #3
0470:                        {
0471:                            res[didx - 1] = '=';
0472:                            res[didx++] = '2';
0473:                            res[didx++] = '0';
0474:                        } else if (res[didx - 1] == '\t') // Rule #3
0475:                        {
0476:                            res[didx - 1] = '=';
0477:                            res[didx++] = '0';
0478:                            res[didx++] = '9';
0479:                        }
0480:
0481:                        res[didx++] = '\r';
0482:                        res[didx++] = '\n';
0483:                        sidx += nl.length - 1;
0484:                        cnt = didx;
0485:                    } else if (ch > 126 || (ch < 32 && ch != '\t') || ch == '='
0486:                            || EBCDICUnsafeChar.get((int) ch)) { // Rule #1, #2
0487:                        res[didx++] = '=';
0488:                        res[didx++] = map[(ch & 0xf0) >>> 4];
0489:                        res[didx++] = map[ch & 0x0f];
0490:                    } else // Rule #1
0491:                    {
0492:                        res[didx++] = ch;
0493:                    }
0494:
0495:                    if (didx > cnt + 70) // Rule #5
0496:                    {
0497:                        res[didx++] = '=';
0498:                        res[didx++] = '\r';
0499:                        res[didx++] = '\n';
0500:                        cnt = didx;
0501:                    }
0502:
0503:                    if (didx > res.length - 5)
0504:                        res = Util.resizeArray(res, res.length + 500);
0505:                }
0506:
0507:                return String.valueOf(res, 1, didx - 1);
0508:            }
0509:
0510:            private final static boolean match(char[] str, int start, char[] arr) {
0511:                if (str.length < start + arr.length)
0512:                    return false;
0513:
0514:                for (int idx = 1; idx < arr.length; idx++)
0515:                    if (str[start + idx] != arr[idx])
0516:                        return false;
0517:                return true;
0518:            }
0519:
0520:            /**
0521:             * This method does a quoted-printable decoding of the given string
0522:             * according to RFC-2045 (Section 6.7). <em>Note:</em> this method
0523:             * expects the whole message in one chunk, not line by line.
0524:             *
0525:             * @param  str the message
0526:             * @return the decoded message
0527:             * @exception ParseException If a '=' is not followed by a valid
0528:             *                           2-digit hex number or '\r\n'.
0529:             */
0530:            public final static String quotedPrintableDecode(String str)
0531:                    throws ParseException {
0532:                if (str == null)
0533:                    return null;
0534:
0535:                char res[] = new char[(int) (str.length() * 1.1)], src[] = str
0536:                        .toCharArray(), nl[] = System.getProperty(
0537:                        "line.separator", "\n").toCharArray();
0538:                int last = 0, didx = 0, slen = str.length();
0539:
0540:                for (int sidx = 0; sidx < slen;) {
0541:                    char ch = src[sidx++];
0542:
0543:                    if (ch == '=') {
0544:                        if (sidx >= slen - 1)
0545:                            throw new ParseException(
0546:                                    "Premature end of input detected");
0547:
0548:                        if (src[sidx] == '\n' || src[sidx] == '\r') { // Rule #5
0549:                            sidx++;
0550:
0551:                            if (src[sidx - 1] == '\r' && src[sidx] == '\n')
0552:                                sidx++;
0553:                        } else // Rule #1
0554:                        {
0555:                            char repl;
0556:                            int hi = Character.digit(src[sidx], 16), lo = Character
0557:                                    .digit(src[sidx + 1], 16);
0558:
0559:                            if ((hi | lo) < 0)
0560:                                throw new ParseException(new String(src,
0561:                                        sidx - 1, 3)
0562:                                        + " is an invalid code");
0563:                            else {
0564:                                repl = (char) (hi << 4 | lo);
0565:                                sidx += 2;
0566:                            }
0567:
0568:                            res[didx++] = repl;
0569:                        }
0570:                        last = didx;
0571:                    } else if (ch == '\n' || ch == '\r') // Rule #4
0572:                    {
0573:                        if (ch == '\r' && sidx < slen && src[sidx] == '\n')
0574:                            sidx++;
0575:                        for (int idx = 0; idx < nl.length; idx++)
0576:                            res[last++] = nl[idx];
0577:                        didx = last;
0578:                    } else // Rule #1, #2
0579:                    {
0580:                        res[didx++] = ch;
0581:                        if (ch != ' ' && ch != '\t') // Rule #3
0582:                            last = didx;
0583:                    }
0584:
0585:                    if (didx > res.length - nl.length - 2)
0586:                        res = Util.resizeArray(res, res.length + 500);
0587:                }
0588:
0589:                return new String(res, 0, didx);
0590:            }
0591:
0592:            /**
0593:             * This method urlencodes the given string. This method is here for
0594:             * symmetry reasons and just calls java.net.URLEncoder.encode().
0595:             *
0596:             * @param  str the string
0597:             * @return the url-encoded string
0598:             */
0599:            public final static String URLEncode(String str) {
0600:                if (str == null)
0601:                    return null;
0602:
0603:                return java.net.URLEncoder.encode(str);
0604:            }
0605:
0606:            /**
0607:             * This method decodes the given urlencoded string.
0608:             *
0609:             * @param  str the url-encoded string
0610:             * @return the decoded string
0611:             * @exception ParseException If a '%' is not followed by a valid
0612:             *                           2-digit hex number.
0613:             */
0614:            public final static String URLDecode(String str)
0615:                    throws ParseException {
0616:                if (str == null)
0617:                    return null;
0618:
0619:                char[] res = new char[str.length()];
0620:                int didx = 0;
0621:
0622:                for (int sidx = 0; sidx < str.length(); sidx++) {
0623:                    char ch = str.charAt(sidx);
0624:                    if (ch == '+')
0625:                        res[didx++] = ' ';
0626:                    else if (ch == '%') {
0627:                        try {
0628:                            res[didx++] = (char) Integer.parseInt(str
0629:                                    .substring(sidx + 1, sidx + 3), 16);
0630:                            sidx += 2;
0631:                        } catch (NumberFormatException e) {
0632:                            throw new ParseException(str.substring(sidx,
0633:                                    sidx + 3)
0634:                                    + " is an invalid code");
0635:                        }
0636:                    } else
0637:                        res[didx++] = ch;
0638:                }
0639:
0640:                return String.valueOf(res, 0, didx);
0641:            }
0642:
0643:            /**
0644:             * This method decodes a multipart/form-data encoded string.
0645:             *
0646:             * @param     data        the form-data to decode.
0647:             * @param     cont_type   the content type header (must contain the
0648:             *			      boundary string).
0649:             * @param     dir         the directory to create the files in.
0650:             * @return                an array of name/value pairs, one for each part;
0651:             *                        the name is the 'name' attribute given in the
0652:             *                        Content-Disposition header; the value is either
0653:             *                        the name of the file if a filename attribute was
0654:             *                        found, or the contents of the part.
0655:             * @exception IOException If any file operation fails.
0656:             * @exception ParseException If an error during parsing occurs.
0657:             * @see #mpFormDataDecode(byte[], java.lang.String, java.lang.String, HTTPClient.FilenameMangler)
0658:             */
0659:            public final static NVPair[] mpFormDataDecode(byte[] data,
0660:                    String cont_type, String dir) throws IOException,
0661:                    ParseException {
0662:                return mpFormDataDecode(data, cont_type, dir, null);
0663:            }
0664:
0665:            /**
0666:             * This method decodes a multipart/form-data encoded string. The boundary
0667:             * is parsed from the <var>cont_type</var> parameter, which must be of the
0668:             * form 'multipart/form-data; boundary=...'. Any encoded files are created
0669:             * in the directory specified by <var>dir</var> using the encoded filename.
0670:             *
0671:             * <P><em>Note:</em> Does not handle nested encodings (yet).
0672:             *
0673:             * <P>Examples: If you're receiving a multipart/form-data encoded response
0674:             * from a server you could use something like:
0675:             * <PRE>
0676:             *     NVPair[] opts = Codecs.mpFormDataDecode(resp.getData(),
0677:             *                                  resp.getHeader("Content-type"), ".");
0678:             * </PRE>
0679:             * If you're using this in a Servlet to decode the body of a request from
0680:             * a client you could use something like:
0681:             * <PRE>
0682:             *     byte[] body = new byte[req.getContentLength()];
0683:             *     new DataInputStream(req.getInputStream()).readFully(body);
0684:             *     NVPair[] opts = Codecs.mpFormDataDecode(body, req.getContentType(),
0685:             *                                             ".");
0686:             * </PRE>
0687:             * Assuming the data received looked something like:
0688:             * <PRE>
0689:             * -----------------------------114975832116442893661388290519
0690:             * Content-Disposition: form-data; name="option"
0691:             *                                                         &nbsp;
0692:             * doit
0693:             * -----------------------------114975832116442893661388290519
0694:             * Content-Disposition: form-data; name="comment"; filename="comment.txt"
0695:             *                                                         &nbsp;
0696:             * Gnus and Gnats are not Gnomes.
0697:             * -----------------------------114975832116442893661388290519--
0698:             * </PRE>
0699:             * you would get one file called <VAR>comment.txt</VAR> in the current
0700:             * directory, and opts would contain two elements: {"option", "doit"}
0701:             * and {"comment", "comment.txt"}
0702:             *
0703:             * @param     data        the form-data to decode.
0704:             * @param     cont_type   the content type header (must contain the
0705:             *			      boundary string).
0706:             * @param     dir         the directory to create the files in.
0707:             * @param     mangler     the filename mangler, or null if no mangling is
0708:             *                        to be done. This is invoked just before each
0709:             *                        file is created and written, thereby allowing
0710:             *                        you to control the names of the files.
0711:             * @return                an array of name/value pairs, one for each part;
0712:             *                        the name is the 'name' attribute given in the
0713:             *                        Content-Disposition header; the value is either
0714:             *                        the name of the file if a filename attribute was
0715:             *                        found, or the contents of the part.
0716:             * @exception IOException If any file operation fails.
0717:             * @exception ParseException If an error during parsing occurs.
0718:             */
0719:            public final static NVPair[] mpFormDataDecode(byte[] data,
0720:                    String cont_type, String dir, FilenameMangler mangler)
0721:                    throws IOException, ParseException {
0722:                // Find and extract boundary string
0723:
0724:                String bndstr = Util.getParameter("boundary", cont_type);
0725:                if (bndstr == null)
0726:                    throw new ParseException(
0727:                            "'boundary' parameter not found in Content-type: "
0728:                                    + cont_type);
0729:
0730:                byte[] srtbndry = new byte[bndstr.length() + 4], boundary = new byte[bndstr
0731:                        .length() + 6], endbndry = new byte[bndstr.length() + 6];
0732:
0733:                ("--" + bndstr + "\r\n").getBytes(0, srtbndry.length, srtbndry,
0734:                        0);
0735:                ("\r\n--" + bndstr + "\r\n").getBytes(0, boundary.length,
0736:                        boundary, 0);
0737:                ("\r\n--" + bndstr + "--").getBytes(0, endbndry.length,
0738:                        endbndry, 0);
0739:
0740:                // setup search routines
0741:
0742:                int[] bs = Util.compile_search(srtbndry), bc = Util
0743:                        .compile_search(boundary), be = Util
0744:                        .compile_search(endbndry);
0745:
0746:                // let's start parsing the actual data
0747:
0748:                int start = Util.findStr(srtbndry, bs, data, 0, data.length);
0749:                if (start == -1) // didn't even find the start
0750:                    throw new ParseException("Starting boundary not found: "
0751:                            + new String(srtbndry, 0));
0752:                start += srtbndry.length;
0753:
0754:                NVPair[] res = new NVPair[10];
0755:                boolean done = false;
0756:                int idx;
0757:
0758:                for (idx = 0; !done; idx++) {
0759:                    // find end of this part
0760:
0761:                    int end = Util.findStr(boundary, bc, data, start,
0762:                            data.length);
0763:                    if (end == -1) // must be the last part
0764:                    {
0765:                        end = Util.findStr(endbndry, be, data, start,
0766:                                data.length);
0767:                        if (end == -1)
0768:                            throw new ParseException(
0769:                                    "Ending boundary not found: "
0770:                                            + new String(endbndry, 0));
0771:                        done = true;
0772:                    }
0773:
0774:                    // parse header(s)
0775:
0776:                    String hdr, name = null, value, filename = null, cont_disp = null;
0777:
0778:                    while (true) {
0779:                        int next = findEOL(data, start) + 2;
0780:                        if (next - 2 <= start)
0781:                            break; // empty line -> end of headers
0782:                        hdr = new String(data, 0, start, next - 2 - start);
0783:                        start = next;
0784:
0785:                        // handle line continuation
0786:                        byte ch;
0787:                        while (next < data.length - 1
0788:                                && ((ch = data[next]) == ' ' || ch == '\t')) {
0789:                            next = findEOL(data, start) + 2;
0790:                            hdr += new String(data, 0, start, next - 2 - start);
0791:                            start = next;
0792:                        }
0793:
0794:                        if (!hdr.regionMatches(true, 0, "Content-Disposition",
0795:                                0, 19))
0796:                            continue;
0797:                        Vector pcd = Util.parseHeader(hdr.substring(hdr
0798:                                .indexOf(':') + 1));
0799:                        HttpHeaderElement elem = Util.getElement(pcd,
0800:                                "form-data");
0801:
0802:                        if (elem == null)
0803:                            throw new ParseException(
0804:                                    "Expected 'Content-Disposition: form-data' in line: "
0805:                                            + hdr);
0806:
0807:                        NVPair[] params = elem.getParams();
0808:                        name = filename = null;
0809:                        for (int pidx = 0; pidx < params.length; pidx++) {
0810:                            if (params[pidx].getName().equalsIgnoreCase("name"))
0811:                                name = params[pidx].getValue();
0812:                            if (params[pidx].getName().equalsIgnoreCase(
0813:                                    "filename"))
0814:                                filename = params[pidx].getValue();
0815:                        }
0816:                        if (name == null)
0817:                            throw new ParseException(
0818:                                    "'name' parameter not found in header: "
0819:                                            + hdr);
0820:
0821:                        cont_disp = hdr;
0822:                    }
0823:
0824:                    start += 2;
0825:                    if (start > end)
0826:                        throw new ParseException(
0827:                                "End of header not found at offset " + end);
0828:
0829:                    if (cont_disp == null)
0830:                        throw new ParseException(
0831:                                "Missing 'Content-Disposition' header at offset "
0832:                                        + start);
0833:
0834:                    // handle data for this part
0835:
0836:                    if (filename != null) // It's a file
0837:                    {
0838:                        if (mangler != null)
0839:                            filename = mangler.mangleFilename(filename, name);
0840:                        if (filename != null) {
0841:                            File file = new File(dir, filename);
0842:                            FileOutputStream out = new FileOutputStream(file);
0843:
0844:                            out.write(data, start, end - start);
0845:                            out.close();
0846:                        }
0847:
0848:                        value = filename;
0849:                    } else // It's simple data
0850:                    {
0851:                        value = new String(data, 0, start, end - start);
0852:                    }
0853:
0854:                    if (idx >= res.length)
0855:                        res = Util.resizeArray(res, idx + 10);
0856:                    res[idx] = new NVPair(name, value);
0857:
0858:                    start = end + boundary.length;
0859:                }
0860:
0861:                return Util.resizeArray(res, idx);
0862:            }
0863:
0864:            /**
0865:             * Searches for the next CRLF in an array.
0866:             *
0867:             * @param  arr the byte array to search.
0868:             * @param  off the offset at which to start the search.
0869:             * @return the position of the CR or (arr.length-2) if not found
0870:             */
0871:            private final static int findEOL(byte[] arr, int off) {
0872:                while (off < arr.length - 1
0873:                        && !(arr[off++] == '\r' && arr[off] == '\n'))
0874:                    ;
0875:                return off - 1;
0876:            }
0877:
0878:            /**
0879:             * This method encodes name/value pairs and files into a byte array
0880:             * using the multipart/form-data encoding.
0881:             *
0882:             * @param     opts        the simple form-data to encode (may be null);
0883:             *                        for each NVPair the name refers to the 'name'
0884:             *                        attribute to be used in the header of the part,
0885:             *                        and the value is contents of the part.
0886:             * @param     files       the files to encode (may be null); for each
0887:             *                        NVPair the name refers to the 'name' attribute
0888:             *                        to be used in the header of the part, and the
0889:             *                        value is the actual filename (the file will be
0890:             *                        read and it's contents put in the body of that
0891:             *                        part).
0892:             * @param     cont_type   this returns a new NVPair in the 0'th element
0893:             *                        which contains
0894:             *			      name = "Content-Type",
0895:             *			      value = "multipart/form-data; boundary=..."
0896:             *                        (the reason this parameter is an array is
0897:             *                        because a) that's the only way to simulate
0898:             *                        pass-by-reference and b) you need an array for
0899:             *                        the headers parameter to the Post() or Put()
0900:             *                        anyway).
0901:             * @return                an encoded byte array containing all the opts
0902:             *			      and files.
0903:             * @exception IOException If any file operation fails.
0904:             * @see #mpFormDataEncode(HTTPClient.NVPair[], HTTPClient.NVPair[], HTTPClient.NVPair[], HTTPClient.FilenameMangler)
0905:             */
0906:            public final static byte[] mpFormDataEncode(NVPair[] opts,
0907:                    NVPair[] files, NVPair[] cont_type) throws IOException {
0908:                return mpFormDataEncode(opts, files, cont_type, null);
0909:            }
0910:
0911:            private static NVPair[] dummy = new NVPair[0];
0912:
0913:            /**
0914:             * This method encodes name/value pairs and files into a byte array
0915:             * using the multipart/form-data encoding. The boundary is returned
0916:             * as part of <var>cont_type</var>.
0917:             * <BR>Example:
0918:             * <PRE>
0919:             *     NVPair[] opts = { new NVPair("option", "doit") };
0920:             *     NVPair[] file = { new NVPair("comment", "comment.txt") };
0921:             *     NVPair[] hdrs = new NVPair[1];
0922:             *     byte[]   data = Codecs.mpFormDataEncode(opts, file, hdrs);
0923:             *     con.Post("/cgi-bin/handle-it", data, hdrs);
0924:             * </PRE>
0925:             * <VAR>data</VAR> will look something like the following:
0926:             * <PRE>
0927:             * -----------------------------114975832116442893661388290519
0928:             * Content-Disposition: form-data; name="option"
0929:             *                                                         &nbsp;
0930:             * doit
0931:             * -----------------------------114975832116442893661388290519
0932:             * Content-Disposition: form-data; name="comment"; filename="comment.txt"
0933:             *                                                         &nbsp;
0934:             * Gnus and Gnats are not Gnomes.
0935:             * -----------------------------114975832116442893661388290519--
0936:             * </PRE>
0937:             * where the "Gnus and Gnats ..." is the contents of the file
0938:             * <VAR>comment.txt</VAR> in the current directory.
0939:             *
0940:             * @param     opts        the simple form-data to encode (may be null);
0941:             *                        for each NVPair the name refers to the 'name'
0942:             *                        attribute to be used in the header of the part,
0943:             *                        and the value is contents of the part.
0944:             * @param     files       the files to encode (may be null); for each
0945:             *                        NVPair the name refers to the 'name' attribute
0946:             *                        to be used in the header of the part, and the
0947:             *                        value is the actual filename (the file will be
0948:             *                        read and it's contents put in the body of that
0949:             *                        part).
0950:             * @param     cont_type   this returns a new NVPair in the 0'th element
0951:             *                        which contains
0952:             *			      name = "Content-Type",
0953:             *			      value = "multipart/form-data; boundary=..."
0954:             *                        (the reason this parameter is an array is
0955:             *                        because a) that's the only way to simulate
0956:             *                        pass-by-reference and b) you need an array for
0957:             *                        the headers parameter to the Post() or Put()
0958:             *                        anyway).
0959:             * @param     mangler     the filename mangler, or null if no mangling is
0960:             *                        to be done. This allows you to change the name
0961:             *                        used in the <var>filename</var> attribute of the
0962:             *                        Content-Disposition header. Note: the mangler
0963:             *                        will be invoked twice for each filename.
0964:             * @return                an encoded byte array containing all the opts
0965:             *			      and files.
0966:             * @exception IOException If any file operation fails.
0967:             */
0968:            public final static byte[] mpFormDataEncode(NVPair[] opts,
0969:                    NVPair[] files, NVPair[] cont_type, FilenameMangler mangler)
0970:                    throws IOException {
0971:                int len = 0, hdr_len = 2 + 2 + 70 + 2 + 39 + 2 + 2;
0972:                //       \r\n --  bnd  \r\n C..  \r\n \r\n
0973:                byte[] boundary = new byte[74], cont_disp = new byte[40], filename = new byte[13];
0974:
0975:                ContDisp.getBytes(0, ContDisp.length(), cont_disp, 0);
0976:                FileName.getBytes(0, FileName.length(), filename, 0);
0977:                Boundary.getBytes(0, Boundary.length(), boundary, 0);
0978:
0979:                if (opts == null)
0980:                    opts = dummy;
0981:                if (files == null)
0982:                    files = dummy;
0983:
0984:                // Calculate the length of the data
0985:
0986:                for (int idx = 0; idx < opts.length; idx++)
0987:                    len += hdr_len + opts[idx].getName().length()
0988:                            + opts[idx].getValue().length();
0989:
0990:                for (int idx = 0; idx < files.length; idx++) {
0991:                    File file = new File(files[idx].getValue());
0992:                    String fname = file.getName();
0993:                    if (mangler != null)
0994:                        fname = mangler.mangleFilename(fname, files[idx]
0995:                                .getName());
0996:                    if (fname != null) {
0997:                        len += hdr_len + files[idx].getName().length() + 13;
0998:                        len += fname.length() + file.length();
0999:                    }
1000:                }
1001:
1002:                len -= 2; // first CR LF is not written
1003:                len += 2 + 2 + 70 + 2 + 2; // \r\n -- bnd -- \r\n
1004:
1005:                // Now fill array
1006:
1007:                byte[] res = new byte[len];
1008:                int pos = 0;
1009:
1010:                NewBound: for (int new_c = 0x30303030; new_c != 0x7A7A7A7A; new_c++) {
1011:                    pos = 0;
1012:
1013:                    // modify boundary in hopes that it will be unique
1014:                    while (!BoundChar.get(new_c & 0xff))
1015:                        new_c += 0x00000001;
1016:                    while (!BoundChar.get(new_c >> 8 & 0xff))
1017:                        new_c += 0x00000100;
1018:                    while (!BoundChar.get(new_c >> 16 & 0xff))
1019:                        new_c += 0x00010000;
1020:                    while (!BoundChar.get(new_c >> 24 & 0xff))
1021:                        new_c += 0x01000000;
1022:                    boundary[40] = (byte) (new_c & 0xff);
1023:                    boundary[42] = (byte) (new_c >> 8 & 0xff);
1024:                    boundary[44] = (byte) (new_c >> 16 & 0xff);
1025:                    boundary[46] = (byte) (new_c >> 24 & 0xff);
1026:
1027:                    int off = 2;
1028:                    int[] bnd_cmp = Util.compile_search(boundary);
1029:
1030:                    for (int idx = 0; idx < opts.length; idx++) {
1031:                        System.arraycopy(boundary, off, res, pos,
1032:                                boundary.length - off);
1033:                        pos += boundary.length - off;
1034:                        off = 0;
1035:                        System.arraycopy(cont_disp, 0, res, pos,
1036:                                cont_disp.length);
1037:                        pos += cont_disp.length;
1038:
1039:                        int nlen = opts[idx].getName().length();
1040:                        opts[idx].getName().getBytes(0, nlen, res, pos);
1041:                        if (nlen >= boundary.length
1042:                                && Util.findStr(boundary, bnd_cmp, res, pos,
1043:                                        pos + nlen) != -1)
1044:                            continue NewBound;
1045:                        pos += nlen;
1046:
1047:                        res[pos++] = (byte) '"';
1048:                        res[pos++] = (byte) '\r';
1049:                        res[pos++] = (byte) '\n';
1050:                        res[pos++] = (byte) '\r';
1051:                        res[pos++] = (byte) '\n';
1052:
1053:                        int vlen = opts[idx].getValue().length();
1054:                        opts[idx].getValue().getBytes(0, vlen, res, pos);
1055:                        if (vlen >= boundary.length
1056:                                && Util.findStr(boundary, bnd_cmp, res, pos,
1057:                                        pos + vlen) != -1)
1058:                            continue NewBound;
1059:                        pos += vlen;
1060:                    }
1061:
1062:                    for (int idx = 0; idx < files.length; idx++) {
1063:                        File file = new File(files[idx].getValue());
1064:                        String fname = file.getName();
1065:                        if (mangler != null)
1066:                            fname = mangler.mangleFilename(fname, files[idx]
1067:                                    .getName());
1068:                        if (fname == null)
1069:                            continue;
1070:
1071:                        System.arraycopy(boundary, off, res, pos,
1072:                                boundary.length - off);
1073:                        pos += boundary.length - off;
1074:                        off = 0;
1075:                        System.arraycopy(cont_disp, 0, res, pos,
1076:                                cont_disp.length);
1077:                        pos += cont_disp.length;
1078:
1079:                        int nlen = files[idx].getName().length();
1080:                        files[idx].getName().getBytes(0, nlen, res, pos);
1081:                        if (nlen >= boundary.length
1082:                                && Util.findStr(boundary, bnd_cmp, res, pos,
1083:                                        pos + nlen) != -1)
1084:                            continue NewBound;
1085:                        pos += nlen;
1086:
1087:                        System
1088:                                .arraycopy(filename, 0, res, pos,
1089:                                        filename.length);
1090:                        pos += filename.length;
1091:
1092:                        nlen = fname.length();
1093:                        fname.getBytes(0, nlen, res, pos);
1094:                        if (nlen >= boundary.length
1095:                                && Util.findStr(boundary, bnd_cmp, res, pos,
1096:                                        pos + nlen) != -1)
1097:                            continue NewBound;
1098:                        pos += nlen;
1099:
1100:                        res[pos++] = (byte) '"';
1101:                        res[pos++] = (byte) '\r';
1102:                        res[pos++] = (byte) '\n';
1103:                        res[pos++] = (byte) '\r';
1104:                        res[pos++] = (byte) '\n';
1105:
1106:                        nlen = (int) file.length();
1107:                        int opos = pos;
1108:                        FileInputStream fin = new FileInputStream(file);
1109:                        while (nlen > 0) {
1110:                            int got = fin.read(res, pos, nlen);
1111:                            nlen -= got;
1112:                            pos += got;
1113:                        }
1114:                        if (Util.findStr(boundary, bnd_cmp, res, opos, pos) != -1)
1115:                            continue NewBound;
1116:                    }
1117:
1118:                    break NewBound;
1119:                }
1120:
1121:                System.arraycopy(boundary, 0, res, pos, boundary.length);
1122:                pos += boundary.length;
1123:                res[pos++] = (byte) '-';
1124:                res[pos++] = (byte) '-';
1125:                res[pos++] = (byte) '\r';
1126:                res[pos++] = (byte) '\n';
1127:
1128:                if (pos != len)
1129:                    throw new Error("Calculated " + len + " bytes but wrote "
1130:                            + pos + " bytes!");
1131:
1132:                /* the boundary parameter should be quoted (rfc-2046, section 5.1.1)
1133:                 * but too many script authors are not capable of reading specs...
1134:                 * So, I give up and don't quote it.
1135:                 */
1136:                cont_type[0] = new NVPair("Content-Type",
1137:                        "multipart/form-data; boundary="
1138:                                + new String(boundary, 0, 4, 70));
1139:
1140:                return res;
1141:            }
1142:
1143:            /**
1144:             * Turns an array of name/value pairs into the string
1145:             * "name1=value1&name2=value2&name3=value3". The names and values are
1146:             * first urlencoded. This is the form in which form-data is passed to
1147:             * a cgi script.
1148:             *
1149:             * @param pairs the array of name/value pairs
1150:             * @return a string containg the encoded name/value pairs
1151:             */
1152:            public final static String nv2query(NVPair pairs[]) {
1153:                if (pairs == null)
1154:                    return null;
1155:
1156:                int idx;
1157:                StringBuffer qbuf = new StringBuffer();
1158:
1159:                for (idx = 0; idx < pairs.length; idx++) {
1160:                    qbuf.append(URLEncode(pairs[idx].getName()) + "="
1161:                            + URLEncode(pairs[idx].getValue()) + "&");
1162:                }
1163:
1164:                if (idx > 0)
1165:                    qbuf.setLength(qbuf.length() - 1); // remove trailing '&'
1166:
1167:                return qbuf.toString();
1168:            }
1169:
1170:            /**
1171:             * Turns a string of the form "name1=value1&name2=value2&name3=value3"
1172:             * into an array of name/value pairs. The names and values are
1173:             * urldecoded. The query string is in the form in which form-data is
1174:             * received in a cgi script.
1175:             *
1176:             * @param query the query string containing the encoded name/value pairs
1177:             * @return an array of NVPairs
1178:             * @exception ParseException If the '=' is missing in any field, or if
1179:             *				 the urldecoding of the name or value fails
1180:             */
1181:            public final static NVPair[] query2nv(String query)
1182:                    throws ParseException {
1183:                if (query == null)
1184:                    return null;
1185:
1186:                int idx = -1, cnt = 1;
1187:                while ((idx = query.indexOf('&', idx + 1)) != -1)
1188:                    cnt++;
1189:                NVPair[] pairs = new NVPair[cnt];
1190:
1191:                for (idx = 0, cnt = 0; cnt < pairs.length; cnt++) {
1192:                    int eq = query.indexOf('=', idx);
1193:                    int end = query.indexOf('&', idx);
1194:
1195:                    if (end == -1)
1196:                        end = query.length();
1197:
1198:                    if (eq == -1 || eq >= end)
1199:                        throw new ParseException("'=' missing in "
1200:                                + query.substring(idx, end));
1201:
1202:                    pairs[cnt] = new NVPair(
1203:                            URLDecode(query.substring(idx, eq)),
1204:                            URLDecode(query.substring(eq + 1, end)));
1205:
1206:                    idx = end + 1;
1207:                }
1208:
1209:                return pairs;
1210:            }
1211:
1212:            /**
1213:             * Encodes data used the chunked encoding. <var>last</var> signales if
1214:             * this is the last chunk, in which case the appropriate footer is
1215:             * generated.
1216:             *
1217:             * @param data  the data to be encoded; may be null.
1218:             * @param ftrs  optional headers to include in the footer (ignored if
1219:             *              not last); may be null.
1220:             * @param last  whether this is the last chunk.
1221:             * @return an array of bytes containing the chunk
1222:             */
1223:            public final static byte[] chunkedEncode(byte[] data,
1224:                    NVPair[] ftrs, boolean last) {
1225:                return chunkedEncode(data, 0, data == null ? 0 : data.length,
1226:                        ftrs, last);
1227:            }
1228:
1229:            /**
1230:             * Encodes data used the chunked encoding. <var>last</var> signales if
1231:             * this is the last chunk, in which case the appropriate footer is
1232:             * generated.
1233:             *
1234:             * @param data  the data to be encoded; may be null.
1235:             * @param off   an offset into the <var>data</var>
1236:             * @param len   the number of bytes to take from <var>data</var>
1237:             * @param ftrs  optional headers to include in the footer (ignored if
1238:             *              not last); may be null.
1239:             * @param last  whether this is the last chunk.
1240:             * @return an array of bytes containing the chunk
1241:             */
1242:            public final static byte[] chunkedEncode(byte[] data, int off,
1243:                    int len, NVPair[] ftrs, boolean last) {
1244:                if (data == null) {
1245:                    data = new byte[0];
1246:                    len = 0;
1247:                }
1248:                if (last && ftrs == null)
1249:                    ftrs = new NVPair[0];
1250:
1251:                // get length of data as hex-string
1252:                String hex_len = Integer.toString(len, 16);
1253:
1254:                // calculate length of chunk
1255:
1256:                int res_len = 0;
1257:                if (len > 0) // len CRLF data CRLF
1258:                    res_len += hex_len.length() + 2 + len + 2;
1259:
1260:                if (last) {
1261:                    res_len += 1 + 2; // 0 CRLF
1262:                    for (int idx = 0; idx < ftrs.length; idx++)
1263:                        res_len += ftrs[idx].getName().length() + 2 + // name ": "
1264:                                ftrs[idx].getValue().length() + 2; // value CRLF
1265:                    res_len += 2; // CRLF
1266:                }
1267:
1268:                // allocate result
1269:
1270:                byte[] res = new byte[res_len];
1271:                int r_off = 0;
1272:
1273:                // fill result
1274:
1275:                if (len > 0) {
1276:                    hex_len.getBytes(0, hex_len.length(), res, r_off);
1277:                    r_off += hex_len.length();
1278:                    res[r_off++] = (byte) '\r';
1279:                    res[r_off++] = (byte) '\n';
1280:
1281:                    System.arraycopy(data, off, res, r_off, len);
1282:                    r_off += len;
1283:                    res[r_off++] = (byte) '\r';
1284:                    res[r_off++] = (byte) '\n';
1285:                }
1286:
1287:                if (last) {
1288:                    res[r_off++] = (byte) '0';
1289:                    res[r_off++] = (byte) '\r';
1290:                    res[r_off++] = (byte) '\n';
1291:
1292:                    for (int idx = 0; idx < ftrs.length; idx++) {
1293:                        ftrs[idx].getName().getBytes(0,
1294:                                ftrs[idx].getName().length(), res, r_off);
1295:                        r_off += ftrs[idx].getName().length();
1296:
1297:                        res[r_off++] = (byte) ':';
1298:                        res[r_off++] = (byte) ' ';
1299:
1300:                        ftrs[idx].getValue().getBytes(0,
1301:                                ftrs[idx].getValue().length(), res, r_off);
1302:                        r_off += ftrs[idx].getValue().length();
1303:
1304:                        res[r_off++] = (byte) '\r';
1305:                        res[r_off++] = (byte) '\n';
1306:                    }
1307:
1308:                    res[r_off++] = (byte) '\r';
1309:                    res[r_off++] = (byte) '\n';
1310:                }
1311:
1312:                if (r_off != res.length)
1313:                    throw new Error("Calculated " + res.length
1314:                            + " bytes but wrote " + r_off + " bytes!");
1315:
1316:                return res;
1317:            }
1318:
1319:            /**
1320:             * Decodes chunked data. The chunks are read from an InputStream, which
1321:             * is assumed to be correctly positioned. Use 'xxx instanceof byte[]'
1322:             * and 'xxx instanceof NVPair[]' to determine if this was data or the
1323:             * last chunk.
1324:             *
1325:             * @param  input  the stream from which to read the next chunk.
1326:             * @return If this was a data chunk then it returns a byte[]; else
1327:             *         it's the footer and it returns a NVPair[] containing the
1328:             *         footers.
1329:             * @exception ParseException If any exception during parsing occured.
1330:             * @exception IOException    If any exception during reading occured.
1331:             */
1332:            public final static Object chunkedDecode(InputStream input)
1333:                    throws ParseException, IOException {
1334:                int len = getChunkLength(input);
1335:
1336:                if (len > 0) // it's a chunk
1337:                {
1338:                    byte[] res = new byte[len];
1339:
1340:                    int off = 0;
1341:                    while (len != -1 && off < res.length) {
1342:                        len = input.read(res, off, res.length - off);
1343:                        off += len;
1344:                    }
1345:
1346:                    if (len == -1)
1347:                        throw new ParseException(
1348:                                "Premature EOF while reading chunk;"
1349:                                        + "Expected: " + res.length
1350:                                        + " Bytes, " + "Received: " + (off + 1)
1351:                                        + " Bytes");
1352:
1353:                    input.read(); // CR
1354:                    input.read(); // LF
1355:
1356:                    return res;
1357:                } else // it's the end
1358:                {
1359:                    NVPair[] res = new NVPair[0];
1360:
1361:                    DataInputStream datain = new DataInputStream(input);
1362:                    String line;
1363:
1364:                    // read and parse footer
1365:                    while ((line = datain.readLine()) != null
1366:                            && line.length() > 0) {
1367:                        int colon = line.indexOf(':');
1368:                        if (colon == -1)
1369:                            throw new ParseException(
1370:                                    "Error in Footer format: no "
1371:                                            + "':' found in '" + line + "'");
1372:                        res = Util.resizeArray(res, res.length + 1);
1373:                        res[res.length - 1] = new NVPair(line.substring(0,
1374:                                colon).trim(), line.substring(colon + 1).trim());
1375:                    }
1376:
1377:                    return res;
1378:                }
1379:
1380:            }
1381:
1382:            /**
1383:             * Gets the length of the chunk.
1384:             *
1385:             * @param  input  the stream from which to read the next chunk.
1386:             * @return  the length of chunk to follow (w/o trailing CR LF).
1387:             * @exception ParseException If any exception during parsing occured.
1388:             * @exception IOException    If any exception during reading occured.
1389:             */
1390:            final static int getChunkLength(InputStream input)
1391:                    throws ParseException, IOException {
1392:                byte[] hex_len = new byte[8]; // if they send more than 2GB chunks...
1393:                int off = 0, ch;
1394:
1395:                // read chunk length
1396:
1397:                while ((ch = input.read()) != '\r' && ch != '\n' && ch != ';'
1398:                        && off < hex_len.length)
1399:                    hex_len[off++] = (byte) ch;
1400:
1401:                if (ch == ';') // chunk-ext (ignore it)
1402:                    while ((ch = input.read()) != '\r' && ch != '\n')
1403:                        ;
1404:
1405:                if (ch != '\n' && (ch != '\r' || input.read() != '\n'))
1406:                    throw new ParseException("Didn't find valid chunk length: "
1407:                            + new String(hex_len, 0, 0, off));
1408:
1409:                // parse chunk length
1410:
1411:                int len;
1412:                try {
1413:                    len = Integer.parseInt(new String(hex_len, 0, 0, off)
1414:                            .trim(), 16);
1415:                } catch (NumberFormatException nfe) {
1416:                    throw new ParseException("Didn't find valid chunk length: "
1417:                            + new String(hex_len, 0, 0, off));
1418:                }
1419:
1420:                return len;
1421:            }
1422:
1423:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.