diff --git a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClient.java b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClient.java index bd4a0266fc..1ae6508ed0 100644 --- a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClient.java +++ b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClient.java @@ -45,13 +45,13 @@ import org.jboss.netty.logging.InternalLogger; import org.jboss.netty.logging.InternalLoggerFactory; public class HttpUploadClient { - - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(HttpUploadClient.class); - + + private static final InternalLogger logger = InternalLoggerFactory + .getInstance(HttpUploadClient.class); + private final String baseUri; private final String filePath; - + public HttpUploadClient(String baseUri, String filePath) { this.baseUri = baseUri; this.filePath = filePath; @@ -75,8 +75,10 @@ public class HttpUploadClient { logger.error("Invalid URI syntax" + e.getCause()); return; } - String scheme = uriSimple.getScheme() == null? "http" : uriSimple.getScheme(); - String host = uriSimple.getHost() == null? "localhost" : uriSimple.getHost(); + String scheme = uriSimple.getScheme() == null ? "http" : uriSimple + .getScheme(); + String host = uriSimple.getHost() == null ? "localhost" : uriSimple + .getHost(); int port = uriSimple.getPort(); if (port == -1) { if (scheme.equalsIgnoreCase("http")) { @@ -86,7 +88,8 @@ public class HttpUploadClient { } } - if (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https")) { + if (!scheme.equalsIgnoreCase("http") + && !scheme.equalsIgnoreCase("https")) { logger.error("Only HTTP(S) is supported."); return; } @@ -101,7 +104,7 @@ public class HttpUploadClient { return; } File file = new File(filePath); - if (! file.canRead()) { + if (!file.canRead()) { logger.error("A correct path is needed"); return; } @@ -109,36 +112,41 @@ public class HttpUploadClient { // Configure the client. ClientBootstrap bootstrap = new ClientBootstrap( new NioClientSocketChannelFactory( - Executors.newCachedThreadPool(), + Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // Set up the event pipeline factory. bootstrap.setPipelineFactory(new HttpUploadClientPipelineFactory(ssl)); - // setup the factory: here using a mixed memory/disk based on size threshold + // setup the factory: here using a mixed memory/disk based on size + // threshold HttpDataFactory factory = new DefaultHttpDataFactory( DefaultHttpDataFactory.MINSIZE); // Disk if size exceed MINSIZE - DiskFileUpload.deleteOnExitTemporaryFile = true; // should delete file on exit (in normal exit) + DiskFileUpload.deleteOnExitTemporaryFile = true; // should delete file + // on exit (in normal + // exit) DiskFileUpload.baseDirectory = null; // system temp directory - DiskAttribute.deleteOnExitTemporaryFile = true; // should delete file on exit (in normal exit) + DiskAttribute.deleteOnExitTemporaryFile = true; // should delete file on + // exit (in normal exit) DiskAttribute.baseDirectory = null; // system temp directory // Simple Get form: no factory used (not usable) - List> headers = - formget(bootstrap, host, port, get, uriSimple); + List> headers = formget(bootstrap, host, port, + get, uriSimple); if (headers == null) { factory.cleanAllHttpDatas(); return; } // Simple Post form: factory used for big attributes - List bodylist = - formpost(bootstrap, host, port, uriSimple, file, factory, headers); + List bodylist = formpost(bootstrap, host, port, + uriSimple, file, factory, headers); if (bodylist == null) { factory.cleanAllHttpDatas(); return; } // Multipart Post form: factory used - formpostmultipart(bootstrap, host, port, uriFile, factory, headers, bodylist); + formpostmultipart(bootstrap, host, port, uriFile, factory, headers, + bodylist); // Shut down executor threads to exit. bootstrap.releaseExternalResources(); @@ -147,16 +155,19 @@ public class HttpUploadClient { } /** - * Standard usage of HTTP API in Netty without file Upload (get is not able to achieve File upload - * due to limitation on request size). + * Standard usage of HTTP API in Netty without file Upload (get is not able + * to achieve File upload due to limitation on request size). + * * @return the list of headers that will be used in every example after - **/ - private static List> formget(ClientBootstrap bootstrap, String host, int port, String get, + **/ + private static List> formget( + ClientBootstrap bootstrap, String host, int port, String get, URI uriSimple) { // XXX /formget // No use of HttpPostRequestEncoder since not a POST // Start the connection attempt. - ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); + ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, + port)); // Wait until the connection attempt succeeds or fails. Channel channel = future.awaitUninterruptibly().getChannel(); if (!future.isSuccess()) { @@ -173,7 +184,8 @@ public class HttpUploadClient { encoder.addParam("secondinfo", "secondvalue ���&"); // not the big one since it is not compatible with GET size // encoder.addParam("thirdinfo", textArea); - encoder.addParam("thirdinfo", "third value\r\ntest second line\r\n\r\nnew line\r\n"); + encoder.addParam("thirdinfo", + "third value\r\ntest second line\r\n\r\nnew line\r\n"); encoder.addParam("Send", "Send"); URI uriGet; @@ -185,20 +197,23 @@ public class HttpUploadClient { return null; } - HttpRequest request = new DefaultHttpRequest( - HttpVersion.HTTP_1_1, HttpMethod.GET, uriGet.toASCIIString()); + HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, + HttpMethod.GET, uriGet.toASCIIString()); request.setHeader(HttpHeaders.Names.HOST, host); - request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); - request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP + "," + - HttpHeaders.Values.DEFLATE); + request.setHeader(HttpHeaders.Names.CONNECTION, + HttpHeaders.Values.CLOSE); + request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, + HttpHeaders.Values.GZIP + "," + HttpHeaders.Values.DEFLATE); - request.setHeader(HttpHeaders.Names.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.7"); + request.setHeader(HttpHeaders.Names.ACCEPT_CHARSET, + "ISO-8859-1,utf-8;q=0.7,*;q=0.7"); request.setHeader(HttpHeaders.Names.ACCEPT_LANGUAGE, "fr"); request.setHeader(HttpHeaders.Names.REFERER, uriSimple.toString()); - request.setHeader(HttpHeaders.Names.USER_AGENT, "Netty Simple Http Client side"); + request.setHeader(HttpHeaders.Names.USER_AGENT, + "Netty Simple Http Client side"); request.setHeader(HttpHeaders.Names.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); - //connection will not close but needed + // connection will not close but needed // request.setHeader("Connection","keep-alive"); // request.setHeader("Keep-Alive","300"); @@ -218,17 +233,19 @@ public class HttpUploadClient { } /** - * Standard post without multipart but already support on Factory (memory management) - * - * @return the list of HttpData object (attribute and file) to be reused on next post + * Standard post without multipart but already support on Factory (memory + * management) + * + * @return the list of HttpData object (attribute and file) to be reused on + * next post */ private static List formpost(ClientBootstrap bootstrap, - String host, int port, - URI uriSimple, File file, HttpDataFactory factory, - List> headers) { + String host, int port, URI uriSimple, File file, + HttpDataFactory factory, List> headers) { // XXX /formpost // Start the connection attempt. - ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); + ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, + port)); // Wait until the connection attempt succeeds or fails. Channel channel = future.awaitUninterruptibly().getChannel(); if (!future.isSuccess()) { @@ -238,14 +255,14 @@ public class HttpUploadClient { } // Prepare the HTTP request. - HttpRequest request = new DefaultHttpRequest( - HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString()); + HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, + HttpMethod.POST, uriSimple.toASCIIString()); // Use the PostBody encoder HttpPostRequestEncoder bodyRequestEncoder = null; try { - bodyRequestEncoder = new HttpPostRequestEncoder(factory, - request, false); // false => not multipart + bodyRequestEncoder = new HttpPostRequestEncoder(factory, request, + false); // false => not multipart } catch (NullPointerException e) { // should not be since args are not null e.printStackTrace(); @@ -254,7 +271,8 @@ public class HttpUploadClient { e.printStackTrace(); } - // it is legal to add directly header or cookie into the request until finalize + // it is legal to add directly header or cookie into the request until + // finalize for (Entry entry : headers) { request.setHeader(entry.getKey(), entry.getValue()); } @@ -263,9 +281,11 @@ public class HttpUploadClient { try { bodyRequestEncoder.addBodyAttribute("getform", "POST"); bodyRequestEncoder.addBodyAttribute("info", "first value"); - bodyRequestEncoder.addBodyAttribute("secondinfo", "secondvalue ���&"); + bodyRequestEncoder.addBodyAttribute("secondinfo", + "secondvalue ���&"); bodyRequestEncoder.addBodyAttribute("thirdinfo", textArea); - bodyRequestEncoder.addBodyFileUpload("myfile", file, "application/x-zip-compressed", false); + bodyRequestEncoder.addBodyFileUpload("myfile", file, + "application/x-zip-compressed", false); bodyRequestEncoder.addBodyAttribute("Send", "Send"); } catch (NullPointerException e) { // should not be since not null args @@ -282,23 +302,29 @@ public class HttpUploadClient { // if an encoding error occurs e.printStackTrace(); } - // Create the bodylist to be reused on the last version with Multipart support - List bodylist = bodyRequestEncoder.getBodyListAttributes(); + // Create the bodylist to be reused on the last version with Multipart + // support + List bodylist = bodyRequestEncoder + .getBodyListAttributes(); // send request channel.write(request); // test if request was chunked and if so, finish the write - if (bodyRequestEncoder.isChunked()) { // could do either request.isChunked() + if (bodyRequestEncoder.isChunked()) { // could do either + // request.isChunked() // either do it through ChunkedWriteHandler channel.write(bodyRequestEncoder).awaitUninterruptibly(); } - // Do not clear here since we will reuse the InterfaceHttpData on the next request - // for the example (limit action on client side). Take this as a broadcast of the same + // Do not clear here since we will reuse the InterfaceHttpData on the + // next request + // for the example (limit action on client side). Take this as a + // broadcast of the same // request on both Post actions. // - // On standard program, it is clearly recommended to clean all files after each request + // On standard program, it is clearly recommended to clean all files + // after each request // bodyRequestEncoder.cleanFiles(); // Wait for the server to close the connection. @@ -309,12 +335,14 @@ public class HttpUploadClient { /** * Multipart example */ - private static void formpostmultipart(ClientBootstrap bootstrap, String host, int port, - URI uriFile, HttpDataFactory factory, - List> headers, List bodylist) { + private static void formpostmultipart(ClientBootstrap bootstrap, + String host, int port, URI uriFile, HttpDataFactory factory, + List> headers, + List bodylist) { // XXX /formpostmultipart // Start the connection attempt. - ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); + ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, + port)); // Wait until the connection attempt succeeds or fails. Channel channel = future.awaitUninterruptibly().getChannel(); if (!future.isSuccess()) { @@ -324,14 +352,14 @@ public class HttpUploadClient { } // Prepare the HTTP request. - HttpRequest request = new DefaultHttpRequest( - HttpVersion.HTTP_1_1, HttpMethod.POST, uriFile.toASCIIString()); + HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, + HttpMethod.POST, uriFile.toASCIIString()); // Use the PostBody encoder HttpPostRequestEncoder bodyRequestEncoder = null; try { - bodyRequestEncoder = new HttpPostRequestEncoder(factory, - request, true); // true => multipart + bodyRequestEncoder = new HttpPostRequestEncoder(factory, request, + true); // true => multipart } catch (NullPointerException e) { // should not be since no null args e.printStackTrace(); @@ -340,7 +368,8 @@ public class HttpUploadClient { e.printStackTrace(); } - // it is legal to add directly header or cookie into the request until finalize + // it is legal to add directly header or cookie into the request until + // finalize for (Entry entry : headers) { request.setHeader(entry.getKey(), entry.getValue()); } @@ -352,7 +381,8 @@ public class HttpUploadClient { // should not be since previously created e1.printStackTrace(); } catch (ErrorDataEncoderException e1) { - // again should not be since previously encoded (except if an error occurs previously) + // again should not be since previously encoded (except if an error + // occurs previously) e1.printStackTrace(); } @@ -381,9 +411,8 @@ public class HttpUploadClient { public static void main(String[] args) { if (args.length != 2) { - logger.error( - "Usage: " + HttpUploadClient.class.getSimpleName() + - " baseURI filePath"); + logger.error("Usage: " + HttpUploadClient.class.getSimpleName() + + " baseURI filePath"); return; } @@ -394,592 +423,591 @@ public class HttpUploadClient { } // use to simulate a big TEXTAREA field in a form - private static final String textArea = - "lkjlkjlKJLKJLKJLKJLJlkj lklkj\r\n\r\nLKJJJJJJJJKKKKKKKKKKKKKKK ����&\r\n\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n"; + private static final String textArea = "lkjlkjlKJLKJLKJLKJLJlkj lklkj\r\n\r\nLKJJJJJJJJKKKKKKKKKKKKKKK ����&\r\n\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + + "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n"; } diff --git a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClientHandler.java b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClientHandler.java index 4d7f38f523..4a8e3ce351 100644 --- a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClientHandler.java +++ b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClientHandler.java @@ -27,14 +27,15 @@ import org.jboss.netty.logging.InternalLoggerFactory; import org.jboss.netty.util.CharsetUtil; public class HttpUploadClientHandler extends SimpleChannelUpstreamHandler { - - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(HttpUploadClientHandler.class); + + private static final InternalLogger logger = InternalLoggerFactory + .getInstance(HttpUploadClientHandler.class); private volatile boolean readingChunks; @Override - public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) + throws Exception { if (!readingChunks) { HttpResponse response = (HttpResponse) e.getMessage(); @@ -42,8 +43,8 @@ public class HttpUploadClientHandler extends SimpleChannelUpstreamHandler { logger.info("VERSION: " + response.getProtocolVersion()); if (!response.getHeaderNames().isEmpty()) { - for (String name: response.getHeaderNames()) { - for (String value: response.getHeaders(name)) { + for (String name : response.getHeaderNames()) { + for (String value : response.getHeaders(name)) { logger.info("HEADER: " + name + " = " + value); } } diff --git a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClientPipelineFactory.java b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClientPipelineFactory.java index 01899bff96..8a2c1ab3d7 100644 --- a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClientPipelineFactory.java +++ b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClientPipelineFactory.java @@ -41,8 +41,8 @@ public class HttpUploadClientPipelineFactory implements ChannelPipelineFactory { // Enable HTTPS if necessary. if (ssl) { - SSLEngine engine = - SecureChatSslContextFactory.getClientContext().createSSLEngine(); + SSLEngine engine = SecureChatSslContextFactory.getClientContext() + .createSSLEngine(); engine.setUseClientMode(true); pipeline.addLast("ssl", new SslHandler(engine)); @@ -50,12 +50,13 @@ public class HttpUploadClientPipelineFactory implements ChannelPipelineFactory { pipeline.addLast("codec", new HttpClientCodec()); - // Remove the following line if you don't want automatic content decompression. + // Remove the following line if you don't want automatic content + // decompression. pipeline.addLast("inflater", new HttpContentDecompressor()); // to be used since huge file transfer pipeline.addLast("chunkedWriter", new ChunkedWriteHandler()); - + pipeline.addLast("handler", new HttpUploadClientHandler()); return pipeline; } diff --git a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadServer.java b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadServer.java index 01e35d9d12..e24528f214 100644 --- a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadServer.java +++ b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadServer.java @@ -23,33 +23,33 @@ import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; public class HttpUploadServer { - private final int port; + private final int port; - public HttpUploadServer(int port) { - this.port = port; - } + public HttpUploadServer(int port) { + this.port = port; + } - public void run() { - // Configure the server. - ServerBootstrap bootstrap = new ServerBootstrap( - new NioServerSocketChannelFactory( - Executors.newCachedThreadPool(), - Executors.newCachedThreadPool())); + public void run() { + // Configure the server. + ServerBootstrap bootstrap = new ServerBootstrap( + new NioServerSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool())); - // Set up the event pipeline factory. - bootstrap.setPipelineFactory(new HttpUploadServerPipelineFactory()); + // Set up the event pipeline factory. + bootstrap.setPipelineFactory(new HttpUploadServerPipelineFactory()); - // Bind and start to accept incoming connections. - bootstrap.bind(new InetSocketAddress(port)); - } + // Bind and start to accept incoming connections. + bootstrap.bind(new InetSocketAddress(port)); + } - public static void main(String[] args) { - int port; - if (args.length > 0) { - port = Integer.parseInt(args[0]); - } else { - port = 8080; - } - new HttpUploadServer(port).run(); - } + public static void main(String[] args) { + int port; + if (args.length > 0) { + port = Integer.parseInt(args[0]); + } else { + port = 8080; + } + new HttpUploadServer(port).run(); + } } diff --git a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadServerHandler.java b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadServerHandler.java index d94ecff338..423d087782 100644 --- a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadServerHandler.java +++ b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadServerHandler.java @@ -63,9 +63,9 @@ import org.jboss.netty.logging.InternalLoggerFactory; import org.jboss.netty.util.CharsetUtil; public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { - - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(HttpUploadServerHandler.class); + + private static final InternalLogger logger = InternalLoggerFactory + .getInstance(HttpUploadServerHandler.class); private volatile HttpRequest request; @@ -96,7 +96,8 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { } @Override - public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) + throws Exception { if (!readingChunks) { // clean previous FileUpload if Any if (decoder != null) { @@ -115,18 +116,18 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { responseContent.append("WELCOME TO THE WILD WILD WEB SERVER\r\n"); responseContent.append("===================================\r\n"); - responseContent.append("VERSION: " + - request.getProtocolVersion().getText() + "\r\n"); + responseContent.append("VERSION: " + + request.getProtocolVersion().getText() + "\r\n"); - responseContent.append("REQUEST_URI: " + request.getUri() + - "\r\n\r\n"); + responseContent.append("REQUEST_URI: " + request.getUri() + + "\r\n\r\n"); responseContent.append("\r\n\r\n"); // new method List> headers = request.getHeaders(); - for (Entry entry: headers) { - responseContent.append("HEADER: " + entry.getKey() + "=" + - entry.getValue() + "\r\n"); + for (Entry entry : headers) { + responseContent.append("HEADER: " + entry.getKey() + "=" + + entry.getValue() + "\r\n"); } responseContent.append("\r\n\r\n"); @@ -139,19 +140,19 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { CookieDecoder decoder = new CookieDecoder(); cookies = decoder.decode(value); } - for (Cookie cookie: cookies) { + for (Cookie cookie : cookies) { responseContent.append("COOKIE: " + cookie.toString() + "\r\n"); } responseContent.append("\r\n\r\n"); - QueryStringDecoder decoderQuery = new QueryStringDecoder(request - .getUri()); + QueryStringDecoder decoderQuery = new QueryStringDecoder( + request.getUri()); Map> uriAttributes = decoderQuery .getParameters(); - for (String key: uriAttributes.keySet()) { - for (String valuen: uriAttributes.get(key)) { - responseContent.append("URI: " + key + "=" + valuen + - "\r\n"); + for (String key : uriAttributes.keySet()) { + for (String valuen : uriAttributes.get(key)) { + responseContent.append("URI: " + key + "=" + valuen + + "\r\n"); } } responseContent.append("\r\n\r\n"); @@ -174,10 +175,10 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { return; } - responseContent.append("Is Chunked: " + request.isChunked() + - "\r\n"); - responseContent.append("IsMultipart: " + decoder.isMultipart() + - "\r\n"); + responseContent.append("Is Chunked: " + request.isChunked() + + "\r\n"); + responseContent.append("IsMultipart: " + decoder.isMultipart() + + "\r\n"); if (request.isChunked()) { // Chunk version responseContent.append("Chunks: "); @@ -202,7 +203,8 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { return; } responseContent.append("o"); - // example of reading chunk by chunk (minimize memory usage due to Factory) + // example of reading chunk by chunk (minimize memory usage due to + // Factory) readHttpDataChunkByChunk(e.getChannel()); // example of reading only if at the end if (chunk.isLast()) { @@ -215,7 +217,7 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { /** * Example of reading all InterfaceHttpData from finished transfer - * + * * @param channel */ private void readHttpDataAllReceive(Channel channel) { @@ -230,7 +232,7 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { Channels.close(channel); return; } - for (InterfaceHttpData data: datas) { + for (InterfaceHttpData data : datas) { writeHttpData(data); } responseContent.append("\r\n\r\nEND OF CONTENT AT FINAL END\r\n"); @@ -239,7 +241,7 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { /** * Example of reading request by chunk and getting values from chunk to * chunk - * + * * @param channel */ private void readHttpDataChunkByChunk(Channel channel) { @@ -267,25 +269,25 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { } catch (IOException e1) { // Error while reading data from File, only print name and error e1.printStackTrace(); - responseContent.append("\r\nBODY Attribute: " + - attribute.getHttpDataType().name() + ": " + - attribute.getName() + " Error while reading value: " + - e1.getMessage() + "\r\n"); + responseContent.append("\r\nBODY Attribute: " + + attribute.getHttpDataType().name() + ": " + + attribute.getName() + " Error while reading value: " + + e1.getMessage() + "\r\n"); return; } if (value.length() > 100) { - responseContent.append("\r\nBODY Attribute: " + - attribute.getHttpDataType().name() + ": " + - attribute.getName() + " data too long\r\n"); + responseContent.append("\r\nBODY Attribute: " + + attribute.getHttpDataType().name() + ": " + + attribute.getName() + " data too long\r\n"); } else { - responseContent.append("\r\nBODY Attribute: " + - attribute.getHttpDataType().name() + ": " + - attribute.toString() + "\r\n"); + responseContent.append("\r\nBODY Attribute: " + + attribute.getHttpDataType().name() + ": " + + attribute.toString() + "\r\n"); } } else { - responseContent.append("\r\nBODY FileUpload: " + - data.getHttpDataType().name() + ": " + data.toString() + - "\r\n"); + responseContent.append("\r\nBODY FileUpload: " + + data.getHttpDataType().name() + ": " + data.toString() + + "\r\n"); if (data.getHttpDataType() == HttpDataType.FileUpload) { FileUpload fileUpload = (FileUpload) data; if (fileUpload.isCompleted()) { @@ -303,8 +305,8 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { responseContent.append("\r\n"); } else { responseContent - .append("\tFile too long to be printed out:" + - fileUpload.length() + "\r\n"); + .append("\tFile too long to be printed out:" + + fileUpload.length() + "\r\n"); } // fileUpload.isInMemory();// tells if the file is in Memory // or on File @@ -322,15 +324,15 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { private void writeResponse(Channel channel) { // Convert the response content to a ChannelBuffer. - ChannelBuffer buf = ChannelBuffers.copiedBuffer(responseContent - .toString(), CharsetUtil.UTF_8); + ChannelBuffer buf = ChannelBuffers.copiedBuffer( + responseContent.toString(), CharsetUtil.UTF_8); responseContent.setLength(0); // Decide whether to close the connection or not. boolean close = HttpHeaders.Values.CLOSE.equalsIgnoreCase(request - .getHeader(HttpHeaders.Names.CONNECTION)) || - request.getProtocolVersion().equals(HttpVersion.HTTP_1_0) && - !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request + .getHeader(HttpHeaders.Names.CONNECTION)) + || request.getProtocolVersion().equals(HttpVersion.HTTP_1_0) + && !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request .getHeader(HttpHeaders.Names.CONNECTION)); // Build the response object. @@ -343,8 +345,8 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { if (!close) { // There's no need to add 'Content-Length' header // if this is the last response. - response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String - .valueOf(buf.readableBytes())); + response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, + String.valueOf(buf.readableBytes())); } Set cookies; @@ -358,11 +360,11 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { if (!cookies.isEmpty()) { // Reset the cookies if necessary. CookieEncoder cookieEncoder = new CookieEncoder(true); - for (Cookie cookie: cookies) { + for (Cookie cookie : cookies) { cookieEncoder.addCookie(cookie); } - response.addHeader(HttpHeaders.Names.SET_COOKIE, cookieEncoder - .encode()); + response.addHeader(HttpHeaders.Names.SET_COOKIE, + cookieEncoder.encode()); } // Write the response. ChannelFuture future = channel.write(response); @@ -468,16 +470,16 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { responseContent.append(""); responseContent.append(""); - ChannelBuffer buf = ChannelBuffers.copiedBuffer(responseContent - .toString(), CharsetUtil.UTF_8); + ChannelBuffer buf = ChannelBuffers.copiedBuffer( + responseContent.toString(), CharsetUtil.UTF_8); // Build the response object. HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); response.setContent(buf); response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/html; charset=UTF-8"); - response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buf - .readableBytes())); + response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, + String.valueOf(buf.readableBytes())); // Write the response. e.getChannel().write(response); } diff --git a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadServerPipelineFactory.java b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadServerPipelineFactory.java index 42b1dc3a39..cb2cdd0cd6 100644 --- a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadServerPipelineFactory.java +++ b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadServerPipelineFactory.java @@ -30,15 +30,17 @@ public class HttpUploadServerPipelineFactory implements ChannelPipelineFactory { ChannelPipeline pipeline = pipeline(); // Uncomment the following line if you want HTTPS - //SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine(); - //engine.setUseClientMode(false); - //pipeline.addLast("ssl", new SslHandler(engine)); + // SSLEngine engine = + // SecureChatSslContextFactory.getServerContext().createSSLEngine(); + // engine.setUseClientMode(false); + // pipeline.addLast("ssl", new SslHandler(engine)); pipeline.addLast("decoder", new HttpRequestDecoder()); pipeline.addLast("encoder", new HttpResponseEncoder()); - // Remove the following line if you don't want automatic content compression. + // Remove the following line if you don't want automatic content + // compression. pipeline.addLast("deflater", new HttpContentCompressor()); pipeline.addLast("handler", new HttpUploadServerHandler()); diff --git a/src/main/java/org/jboss/netty/handler/codec/http/AbstractDiskHttpData.java b/src/main/java/org/jboss/netty/handler/codec/http/AbstractDiskHttpData.java index 6040cb95d9..4d54c741d7 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/AbstractDiskHttpData.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/AbstractDiskHttpData.java @@ -344,4 +344,4 @@ public abstract class AbstractDiskHttpData extends AbstractHttpData { return file; } -} \ No newline at end of file +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/AbstractHttpData.java b/src/main/java/org/jboss/netty/handler/codec/http/AbstractHttpData.java index b218b98236..4e3e043feb 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/AbstractHttpData.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/AbstractHttpData.java @@ -94,4 +94,4 @@ public abstract class AbstractHttpData implements HttpData { public long length() { return size; } -} \ No newline at end of file +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/AbstractMemoryHttpData.java b/src/main/java/org/jboss/netty/handler/codec/http/AbstractMemoryHttpData.java index fe7d3c1d86..46f7e5e08b 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/AbstractMemoryHttpData.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/AbstractMemoryHttpData.java @@ -223,4 +223,4 @@ public abstract class AbstractMemoryHttpData extends AbstractHttpData { public File getFile() throws IOException { throw new IOException("Not represented by a file"); } -} \ No newline at end of file +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/Attribute.java b/src/main/java/org/jboss/netty/handler/codec/http/Attribute.java index 57cbc68ccf..ab6a5b9327 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/Attribute.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/Attribute.java @@ -31,4 +31,4 @@ public interface Attribute extends HttpData { * @param value */ void setValue(String value) throws IOException; -} \ No newline at end of file +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/DefaultHttpDataFactory.java b/src/main/java/org/jboss/netty/handler/codec/http/DefaultHttpDataFactory.java index 9376b9ecff..50d352f617 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/DefaultHttpDataFactory.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/DefaultHttpDataFactory.java @@ -21,8 +21,6 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; -import org.jboss.netty.handler.codec.http.HttpRequest; - /** * Default factory giving Attribute and FileUpload according to constructor * @@ -187,4 +185,4 @@ public class DefaultHttpDataFactory implements HttpDataFactory { requestFileDeleteMap.remove(request); } } -} \ No newline at end of file +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/DiskAttribute.java b/src/main/java/org/jboss/netty/handler/codec/http/DiskAttribute.java index 45d30ed244..8ae34fd092 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/DiskAttribute.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/DiskAttribute.java @@ -144,4 +144,4 @@ public class DiskAttribute extends AbstractDiskHttpData implements Attribute { protected String getPrefix() { return prefix; } -} \ No newline at end of file +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/DiskFileUpload.java b/src/main/java/org/jboss/netty/handler/codec/http/DiskFileUpload.java index 52c7f452a3..2141325e9c 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/DiskFileUpload.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/DiskFileUpload.java @@ -18,8 +18,6 @@ package org.jboss.netty.handler.codec.http; import java.io.File; import java.nio.charset.Charset; -import org.jboss.netty.handler.codec.http.HttpHeaders; - /** * Disk FileUpload implementation that stores file into real files */ @@ -159,4 +157,4 @@ public class DiskFileUpload extends AbstractDiskHttpData implements FileUpload { protected String getPrefix() { return prefix; } -} \ No newline at end of file +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/HttpHeaders.java b/src/main/java/org/jboss/netty/handler/codec/http/HttpHeaders.java index b642fbe735..55417c1bcb 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/HttpHeaders.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/HttpHeaders.java @@ -30,1003 +30,1003 @@ import java.util.TreeSet; */ public class HttpHeaders { - /** - * Standard HTTP header names. - * - * @apiviz.stereotype static - */ - public static final class Names { - /** - * {@code "Accept"} - */ - public static final String ACCEPT = "Accept"; - /** - * {@code "Accept-Charset"} - */ - public static final String ACCEPT_CHARSET = "Accept-Charset"; - /** - * {@code "Accept-Encoding"} - */ - public static final String ACCEPT_ENCODING = "Accept-Encoding"; - /** - * {@code "Accept-Language"} - */ - public static final String ACCEPT_LANGUAGE = "Accept-Language"; - /** - * {@code "Accept-Ranges"} - */ - public static final String ACCEPT_RANGES = "Accept-Ranges"; - /** - * {@code "Accept-Patch"} - */ - public static final String ACCEPT_PATCH = "Accept-Patch"; - /** - * {@code "Age"} - */ - public static final String AGE = "Age"; - /** - * {@code "Allow"} - */ - public static final String ALLOW = "Allow"; - /** - * {@code "Authorization"} - */ - public static final String AUTHORIZATION = "Authorization"; - /** - * {@code "Cache-Control"} - */ - public static final String CACHE_CONTROL = "Cache-Control"; - /** - * {@code "Connection"} - */ - public static final String CONNECTION = "Connection"; - /** - * {@code "Content-Base"} - */ - public static final String CONTENT_BASE = "Content-Base"; - /** - * {@code "Content-Encoding"} - */ - public static final String CONTENT_ENCODING = "Content-Encoding"; - /** - * {@code "Content-Language"} - */ - public static final String CONTENT_LANGUAGE = "Content-Language"; - /** - * {@code "Content-Length"} - */ - public static final String CONTENT_LENGTH = "Content-Length"; - /** - * {@code "Content-Location"} - */ - public static final String CONTENT_LOCATION = "Content-Location"; - /** - * {@code "Content-Transfer-Encoding"} - */ - public static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding"; - /** - * {@code "Content-MD5"} - */ - public static final String CONTENT_MD5 = "Content-MD5"; - /** - * {@code "Content-Range"} - */ - public static final String CONTENT_RANGE = "Content-Range"; - /** - * {@code "Content-Type"} - */ - public static final String CONTENT_TYPE = "Content-Type"; - /** - * {@code "Cookie"} - */ - public static final String COOKIE = "Cookie"; - /** - * {@code "Date"} - */ - public static final String DATE = "Date"; - /** - * {@code "ETag"} - */ - public static final String ETAG = "ETag"; - /** - * {@code "Expect"} - */ - public static final String EXPECT = "Expect"; - /** - * {@code "Expires"} - */ - public static final String EXPIRES = "Expires"; - /** - * {@code "From"} - */ - public static final String FROM = "From"; - /** - * {@code "Host"} - */ - public static final String HOST = "Host"; - /** - * {@code "If-Match"} - */ - public static final String IF_MATCH = "If-Match"; - /** - * {@code "If-Modified-Since"} - */ - public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; - /** - * {@code "If-None-Match"} - */ - public static final String IF_NONE_MATCH = "If-None-Match"; - /** - * {@code "If-Range"} - */ - public static final String IF_RANGE = "If-Range"; - /** - * {@code "If-Unmodified-Since"} - */ - public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; - /** - * {@code "Last-Modified"} - */ - public static final String LAST_MODIFIED = "Last-Modified"; - /** - * {@code "Location"} - */ - public static final String LOCATION = "Location"; - /** - * {@code "Max-Forwards"} - */ - public static final String MAX_FORWARDS = "Max-Forwards"; - /** - * {@code "Origin"} - */ - public static final String ORIGIN = "Origin"; - /** - * {@code "Pragma"} - */ - public static final String PRAGMA = "Pragma"; - /** - * {@code "Proxy-Authenticate"} - */ - public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; - /** - * {@code "Proxy-Authorization"} - */ - public static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; - /** - * {@code "Range"} - */ - public static final String RANGE = "Range"; - /** - * {@code "Referer"} - */ - public static final String REFERER = "Referer"; - /** - * {@code "Retry-After"} - */ - public static final String RETRY_AFTER = "Retry-After"; - /** - * {@code "Sec-WebSocket-Key1"} - */ - public static final String SEC_WEBSOCKET_KEY1 = "Sec-WebSocket-Key1"; - /** - * {@code "Sec-WebSocket-Key2"} - */ - public static final String SEC_WEBSOCKET_KEY2 = "Sec-WebSocket-Key2"; - /** - * {@code "Sec-WebSocket-Location"} - */ - public static final String SEC_WEBSOCKET_LOCATION = "Sec-WebSocket-Location"; - /** - * {@code "Sec-WebSocket-Origin"} - */ - public static final String SEC_WEBSOCKET_ORIGIN = "Sec-WebSocket-Origin"; - /** - * {@code "Sec-WebSocket-Protocol"} - */ - public static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol"; - /** - * {@code "Sec-WebSocket-Version"} - */ - public static final String SEC_WEBSOCKET_VERSION = "Sec-WebSocket-Version"; - /** - * {@code "Sec-WebSocket-Key"} - */ - public static final String SEC_WEBSOCKET_KEY = "Sec-WebSocket-Key"; - /** - * {@code "Sec-WebSocket-Accept"} - */ - public static final String SEC_WEBSOCKET_ACCEPT = "Sec-WebSocket-Accept"; - /** - * {@code "Server"} - */ - public static final String SERVER = "Server"; - /** - * {@code "Set-Cookie"} - */ - public static final String SET_COOKIE = "Set-Cookie"; - /** - * {@code "Set-Cookie2"} - */ - public static final String SET_COOKIE2 = "Set-Cookie2"; - /** - * {@code "TE"} - */ - public static final String TE = "TE"; - /** - * {@code "Trailer"} - */ - public static final String TRAILER = "Trailer"; - /** - * {@code "Transfer-Encoding"} - */ - public static final String TRANSFER_ENCODING = "Transfer-Encoding"; - /** - * {@code "Upgrade"} - */ - public static final String UPGRADE = "Upgrade"; - /** - * {@code "User-Agent"} - */ - public static final String USER_AGENT = "User-Agent"; - /** - * {@code "Vary"} - */ - public static final String VARY = "Vary"; - /** - * {@code "Via"} - */ - public static final String VIA = "Via"; - /** - * {@code "Warning"} - */ - public static final String WARNING = "Warning"; - /** - * {@code "WebSocket-Location"} - */ - public static final String WEBSOCKET_LOCATION = "WebSocket-Location"; - /** - * {@code "WebSocket-Origin"} - */ - public static final String WEBSOCKET_ORIGIN = "WebSocket-Origin"; - /** - * {@code "WebSocket-Protocol"} - */ - public static final String WEBSOCKET_PROTOCOL = "WebSocket-Protocol"; - /** - * {@code "WWW-Authenticate"} - */ - public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; - - private Names() { - super(); - } - } - - /** - * Standard HTTP header values. - * - * @apiviz.stereotype static - */ - public static final class Values { - /** - * {@code "application/x-www-form-urlencoded"} - */ - public static final String APPLICATION_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded"; - /** - * {@code "base64"} - */ - public static final String BASE64 = "base64"; - /** - * {@code "binary"} - */ - public static final String BINARY = "binary"; - /** - * {@code "boundary"} - */ - static final String BOUNDARY = "boundary"; - /** - * {@code "bytes"} - */ - public static final String BYTES = "bytes"; - /** - * {@code "charset"} - */ - public static final String CHARSET = "charset"; - /** - * {@code "chunked"} - */ - public static final String CHUNKED = "chunked"; - /** - * {@code "close"} - */ - public static final String CLOSE = "close"; - /** - * {@code "compress"} - */ - public static final String COMPRESS = "compress"; - /** - * {@code "100-continue"} - */ - public static final String CONTINUE = "100-continue"; - /** - * {@code "deflate"} - */ - public static final String DEFLATE = "deflate"; - /** - * {@code "gzip"} - */ - public static final String GZIP = "gzip"; - /** - * {@code "identity"} - */ - public static final String IDENTITY = "identity"; - /** - * {@code "keep-alive"} - */ - public static final String KEEP_ALIVE = "keep-alive"; - /** - * {@code "max-age"} - */ - public static final String MAX_AGE = "max-age"; - /** - * {@code "max-stale"} - */ - public static final String MAX_STALE = "max-stale"; - /** - * {@code "min-fresh"} - */ - public static final String MIN_FRESH = "min-fresh"; - /** - * {@code "multipart/form-data"} - */ - static final String MULTIPART_FORM_DATA = "multipart/form-data"; - /** - * {@code "must-revalidate"} - */ - public static final String MUST_REVALIDATE = "must-revalidate"; - /** - * {@code "no-cache"} - */ - public static final String NO_CACHE = "no-cache"; - /** - * {@code "no-store"} - */ - public static final String NO_STORE = "no-store"; - /** - * {@code "no-transform"} - */ - public static final String NO_TRANSFORM = "no-transform"; - /** - * {@code "none"} - */ - public static final String NONE = "none"; - /** - * {@code "only-if-cached"} - */ - public static final String ONLY_IF_CACHED = "only-if-cached"; - /** - * {@code "private"} - */ - public static final String PRIVATE = "private"; - /** - * {@code "proxy-revalidate"} - */ - public static final String PROXY_REVALIDATE = "proxy-revalidate"; - /** - * {@code "public"} - */ - public static final String PUBLIC = "public"; - /** - * {@code "quoted-printable"} - */ - public static final String QUOTED_PRINTABLE = "quoted-printable"; - /** - * {@code "s-maxage"} - */ - public static final String S_MAXAGE = "s-maxage"; - /** - * {@code "trailers"} - */ - public static final String TRAILERS = "trailers"; - /** - * {@code "Upgrade"} - */ - public static final String UPGRADE = "Upgrade"; - /** - * {@code "WebSocket"} - */ - public static final String WEBSOCKET = "WebSocket"; - - private Values() { - super(); - } - } - - /** - * Returns {@code true} if and only if the connection can remain open and - * thus 'kept alive'. This methods respects the value of the - * {@code "Connection"} header first and then the return value of - * {@link HttpVersion#isKeepAliveDefault()}. - */ - public static boolean isKeepAlive(HttpMessage message) { - String connection = message.getHeader(Names.CONNECTION); - if (Values.CLOSE.equalsIgnoreCase(connection)) { - return false; - } - - if (message.getProtocolVersion().isKeepAliveDefault()) { - return !Values.CLOSE.equalsIgnoreCase(connection); - } else { - return Values.KEEP_ALIVE.equalsIgnoreCase(connection); - } - } - - /** - * Sets the value of the {@code "Connection"} header depending on the - * protocol version of the specified message. This method sets or removes - * the {@code "Connection"} header depending on what the default keep alive - * mode of the message's protocol version is, as specified by - * {@link HttpVersion#isKeepAliveDefault()}. - *
    - *
  • If the connection is kept alive by default: - *
      - *
    • set to {@code "close"} if {@code keepAlive} is {@code false}.
    • - *
    • remove otherwise.
    • - *
    - *
  • - *
  • If the connection is closed by default: - *
      - *
    • set to {@code "keep-alive"} if {@code keepAlive} is {@code true}.
    • - *
    • remove otherwise.
    • - *
    - *
  • - *
- */ - public static void setKeepAlive(HttpMessage message, boolean keepAlive) { - if (message.getProtocolVersion().isKeepAliveDefault()) { - if (keepAlive) { - message.removeHeader(Names.CONNECTION); - } else { - message.setHeader(Names.CONNECTION, Values.CLOSE); - } - } else { - if (keepAlive) { - message.setHeader(Names.CONNECTION, Values.KEEP_ALIVE); - } else { - message.removeHeader(Names.CONNECTION); - } - } - } - - /** - * Returns the header value with the specified header name. If there are - * more than one header value for the specified header name, the first value - * is returned. - * - * @return the header value or {@code null} if there is no such header - */ - public static String getHeader(HttpMessage message, String name) { - return message.getHeader(name); - } - - /** - * Returns the header value with the specified header name. If there are - * more than one header value for the specified header name, the first value - * is returned. - * - * @return the header value or the {@code defaultValue} if there is no such - * header - */ - public static String getHeader(HttpMessage message, String name, - String defaultValue) { - String value = message.getHeader(name); - if (value == null) { - return defaultValue; - } - return value; - } - - /** - * Sets a new header with the specified name and value. If there is an - * existing header with the same name, the existing header is removed. - */ - public static void setHeader(HttpMessage message, String name, Object value) { - message.setHeader(name, value); - } - - /** - * Sets a new header with the specified name and values. If there is an - * existing header with the same name, the existing header is removed. - */ - public static void setHeader(HttpMessage message, String name, - Iterable values) { - message.setHeader(name, values); - } - - /** - * Adds a new header with the specified name and value. - */ - public static void addHeader(HttpMessage message, String name, Object value) { - message.addHeader(name, value); - } - - /** - * Returns the integer header value with the specified header name. If there - * are more than one header value for the specified header name, the first - * value is returned. - * - * @return the header value - * @throws NumberFormatException - * if there is no such header or the header value is not a - * number - */ - public static int getIntHeader(HttpMessage message, String name) { - String value = getHeader(message, name); - if (value == null) { - throw new NumberFormatException("null"); - } - return Integer.parseInt(value); - } - - /** - * Returns the integer header value with the specified header name. If there - * are more than one header value for the specified header name, the first - * value is returned. - * - * @return the header value or the {@code defaultValue} if there is no such - * header or the header value is not a number - */ - public static int getIntHeader(HttpMessage message, String name, - int defaultValue) { - String value = getHeader(message, name); - if (value == null) { - return defaultValue; - } - - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * Sets a new integer header with the specified name and value. If there is - * an existing header with the same name, the existing header is removed. - */ - public static void setIntHeader(HttpMessage message, String name, int value) { - message.setHeader(name, value); - } - - /** - * Sets a new integer header with the specified name and values. If there is - * an existing header with the same name, the existing header is removed. - */ - public static void setIntHeader(HttpMessage message, String name, - Iterable values) { - message.setHeader(name, values); - } - - /** - * Adds a new integer header with the specified name and value. - */ - public static void addIntHeader(HttpMessage message, String name, int value) { - message.addHeader(name, value); - } - - /** - * Returns the length of the content. Please note that this value is not - * retrieved from {@link HttpMessage#getContent()} but from the - * {@code "Content-Length"} header, and thus they are independent from each - * other. - * - * @return the content length or {@code 0} if this message does not have the - * {@code "Content-Length"} header - */ - public static long getContentLength(HttpMessage message) { - return getContentLength(message, 0L); - } - - /** - * Returns the length of the content. Please note that this value is not - * retrieved from {@link HttpMessage#getContent()} but from the - * {@code "Content-Length"} header, and thus they are independent from each - * other. - * - * @return the content length or {@code defaultValue} if this message does - * not have the {@code "Content-Length"} header - */ - public static long getContentLength(HttpMessage message, long defaultValue) { - String contentLength = message.getHeader(Names.CONTENT_LENGTH); - if (contentLength != null) { - return Long.parseLong(contentLength); - } - - // WebSockset messages have constant content-lengths. - if (message instanceof HttpRequest) { - HttpRequest req = (HttpRequest) message; - if (HttpMethod.GET.equals(req.getMethod()) - && req.containsHeader(Names.SEC_WEBSOCKET_KEY1) - && req.containsHeader(Names.SEC_WEBSOCKET_KEY2)) { - return 8; - } - } else if (message instanceof HttpResponse) { - HttpResponse res = (HttpResponse) message; - if (res.getStatus().getCode() == 101 - && res.containsHeader(Names.SEC_WEBSOCKET_ORIGIN) - && res.containsHeader(Names.SEC_WEBSOCKET_LOCATION)) { - return 16; - } - } - - return defaultValue; - } - - /** - * Sets the {@code "Content-Length"} header. - */ - public static void setContentLength(HttpMessage message, long length) { - message.setHeader(Names.CONTENT_LENGTH, length); - } - - /** - * Returns the value of the {@code "Host"} header. - */ - public static String getHost(HttpMessage message) { - return message.getHeader(Names.HOST); - } - - /** - * Returns the value of the {@code "Host"} header. If there is no such - * header, the {@code defaultValue} is returned. - */ - public static String getHost(HttpMessage message, String defaultValue) { - return getHeader(message, Names.HOST, defaultValue); - } - - /** - * Sets the {@code "Host"} header. - */ - public static void setHost(HttpMessage message, String value) { - message.setHeader(Names.HOST, value); - } - - /** - * Returns {@code true} if and only if the specified message contains the - * {@code "Expect: 100-continue"} header. - */ - public static boolean is100ContinueExpected(HttpMessage message) { - // Expect: 100-continue is for requests only. - if (!(message instanceof HttpRequest)) { - return false; - } - - // It works only on HTTP/1.1 or later. - if (message.getProtocolVersion().compareTo(HttpVersion.HTTP_1_1) < 0) { - return false; - } - - // In most cases, there will be one or zero 'Expect' header. - String value = message.getHeader(Names.EXPECT); - if (value == null) { - return false; - } - if (Values.CONTINUE.equalsIgnoreCase(value)) { - return true; - } - - // Multiple 'Expect' headers. Search through them. - for (String v : message.getHeaders(Names.EXPECT)) { - if (Values.CONTINUE.equalsIgnoreCase(v)) { - return true; - } - } - return false; - } - - /** - * Sets the {@code "Expect: 100-continue"} header to the specified message. - * If there is any existing {@code "Expect"} header, they are replaced with - * the new one. - */ - public static void set100ContinueExpected(HttpMessage message) { - set100ContinueExpected(message, true); - } - - /** - * Sets or removes the {@code "Expect: 100-continue"} header to / from the - * specified message. If the specified {@code value} is {@code true}, the - * {@code "Expect: 100-continue"} header is set and all other previous - * {@code "Expect"} headers are removed. Otherwise, all {@code "Expect"} - * headers are removed completely. - */ - public static void set100ContinueExpected(HttpMessage message, boolean set) { - if (set) { - message.setHeader(Names.EXPECT, Values.CONTINUE); - } else { - message.removeHeader(Names.EXPECT); - } - } - - private static final int BUCKET_SIZE = 17; - - private static int hash(String name) { - int h = 0; - for (int i = name.length() - 1; i >= 0; i--) { - char c = name.charAt(i); - if (c >= 'A' && c <= 'Z') { - c += 32; - } - h = 31 * h + c; - } - - if (h > 0) { - return h; - } else if (h == Integer.MIN_VALUE) { - return Integer.MAX_VALUE; - } else { - return -h; - } - } - - private static boolean eq(String name1, String name2) { - int nameLen = name1.length(); - if (nameLen != name2.length()) { - return false; - } - - for (int i = nameLen - 1; i >= 0; i--) { - char c1 = name1.charAt(i); - char c2 = name2.charAt(i); - if (c1 != c2) { - if (c1 >= 'A' && c1 <= 'Z') { - c1 += 32; - } - if (c2 >= 'A' && c2 <= 'Z') { - c2 += 32; - } - if (c1 != c2) { - return false; - } - } - } - return true; - } - - private static int index(int hash) { - return hash % BUCKET_SIZE; - } - - private final Entry[] entries = new Entry[BUCKET_SIZE]; - private final Entry head = new Entry(-1, null, null); - - HttpHeaders() { - head.before = head.after = head; - } - - void validateHeaderName(String name) { - HttpCodecUtil.validateHeaderName(name); - } - - void addHeader(final String name, final Object value) { - validateHeaderName(name); - String strVal = toString(value); - HttpCodecUtil.validateHeaderValue(strVal); - int h = hash(name); - int i = index(h); - addHeader0(h, i, name, strVal); - } - - private void addHeader0(int h, int i, final String name, final String value) { - // Update the hash table. - Entry e = entries[i]; - Entry newEntry; - entries[i] = newEntry = new Entry(h, name, value); - newEntry.next = e; - - // Update the linked list. - newEntry.addBefore(head); - } - - void removeHeader(final String name) { - if (name == null) { - throw new NullPointerException("name"); - } - int h = hash(name); - int i = index(h); - removeHeader0(h, i, name); - } - - private void removeHeader0(int h, int i, String name) { - Entry e = entries[i]; - if (e == null) { - return; - } - - for (;;) { - if (e.hash == h && eq(name, e.key)) { - e.remove(); - Entry next = e.next; - if (next != null) { - entries[i] = next; - e = next; - } else { - entries[i] = null; - return; - } - } else { - break; - } - } - - for (;;) { - Entry next = e.next; - if (next == null) { - break; - } - if (next.hash == h && eq(name, next.key)) { - e.next = next.next; - next.remove(); - } else { - e = next; - } - } - } - - void setHeader(final String name, final Object value) { - validateHeaderName(name); - String strVal = toString(value); - HttpCodecUtil.validateHeaderValue(strVal); - int h = hash(name); - int i = index(h); - removeHeader0(h, i, name); - addHeader0(h, i, name, strVal); - } - - void setHeader(final String name, final Iterable values) { - if (values == null) { - throw new NullPointerException("values"); - } - - validateHeaderName(name); - - int h = hash(name); - int i = index(h); - - removeHeader0(h, i, name); - for (Object v : values) { - if (v == null) { - break; - } - String strVal = toString(v); - HttpCodecUtil.validateHeaderValue(strVal); - addHeader0(h, i, name, strVal); - } - } - - void clearHeaders() { - for (int i = 0; i < entries.length; i++) { - entries[i] = null; - } - head.before = head.after = head; - } - - String getHeader(final String name) { - if (name == null) { - throw new NullPointerException("name"); - } - - int h = hash(name); - int i = index(h); - Entry e = entries[i]; - while (e != null) { - if (e.hash == h && eq(name, e.key)) { - return e.value; - } - - e = e.next; - } - return null; - } - - List getHeaders(final String name) { - if (name == null) { - throw new NullPointerException("name"); - } - - LinkedList values = new LinkedList(); - - int h = hash(name); - int i = index(h); - Entry e = entries[i]; - while (e != null) { - if (e.hash == h && eq(name, e.key)) { - values.addFirst(e.value); - } - e = e.next; - } - return values; - } - - List> getHeaders() { - List> all = new LinkedList>(); - - Entry e = head.after; - while (e != head) { - all.add(e); - e = e.after; - } - return all; - } - - boolean containsHeader(String name) { - return getHeader(name) != null; - } - - Set getHeaderNames() { - Set names = new TreeSet(CaseIgnoringComparator.INSTANCE); - - Entry e = head.after; - while (e != head) { - names.add(e.key); - e = e.after; - } - return names; - } - - private static String toString(Object value) { - if (value == null) { - return null; - } - return value.toString(); - } - - private static final class Entry implements Map.Entry { - final int hash; - final String key; - String value; - Entry next; - Entry before, after; - - Entry(int hash, String key, String value) { - this.hash = hash; - this.key = key; - this.value = value; - } - - void remove() { - before.after = after; - after.before = before; - } - - void addBefore(Entry e) { - after = e; - before = e.before; - before.after = this; - after.before = this; - } - - public String getKey() { - return key; - } - - public String getValue() { - return value; - } - - public String setValue(String value) { - if (value == null) { - throw new NullPointerException("value"); - } - HttpCodecUtil.validateHeaderValue(value); - String oldValue = this.value; - this.value = value; - return oldValue; - } - - @Override - public String toString() { - return key + "=" + value; - } - } + /** + * Standard HTTP header names. + * + * @apiviz.stereotype static + */ + public static final class Names { + /** + * {@code "Accept"} + */ + public static final String ACCEPT = "Accept"; + /** + * {@code "Accept-Charset"} + */ + public static final String ACCEPT_CHARSET = "Accept-Charset"; + /** + * {@code "Accept-Encoding"} + */ + public static final String ACCEPT_ENCODING = "Accept-Encoding"; + /** + * {@code "Accept-Language"} + */ + public static final String ACCEPT_LANGUAGE = "Accept-Language"; + /** + * {@code "Accept-Ranges"} + */ + public static final String ACCEPT_RANGES = "Accept-Ranges"; + /** + * {@code "Accept-Patch"} + */ + public static final String ACCEPT_PATCH = "Accept-Patch"; + /** + * {@code "Age"} + */ + public static final String AGE = "Age"; + /** + * {@code "Allow"} + */ + public static final String ALLOW = "Allow"; + /** + * {@code "Authorization"} + */ + public static final String AUTHORIZATION = "Authorization"; + /** + * {@code "Cache-Control"} + */ + public static final String CACHE_CONTROL = "Cache-Control"; + /** + * {@code "Connection"} + */ + public static final String CONNECTION = "Connection"; + /** + * {@code "Content-Base"} + */ + public static final String CONTENT_BASE = "Content-Base"; + /** + * {@code "Content-Encoding"} + */ + public static final String CONTENT_ENCODING = "Content-Encoding"; + /** + * {@code "Content-Language"} + */ + public static final String CONTENT_LANGUAGE = "Content-Language"; + /** + * {@code "Content-Length"} + */ + public static final String CONTENT_LENGTH = "Content-Length"; + /** + * {@code "Content-Location"} + */ + public static final String CONTENT_LOCATION = "Content-Location"; + /** + * {@code "Content-Transfer-Encoding"} + */ + public static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding"; + /** + * {@code "Content-MD5"} + */ + public static final String CONTENT_MD5 = "Content-MD5"; + /** + * {@code "Content-Range"} + */ + public static final String CONTENT_RANGE = "Content-Range"; + /** + * {@code "Content-Type"} + */ + public static final String CONTENT_TYPE = "Content-Type"; + /** + * {@code "Cookie"} + */ + public static final String COOKIE = "Cookie"; + /** + * {@code "Date"} + */ + public static final String DATE = "Date"; + /** + * {@code "ETag"} + */ + public static final String ETAG = "ETag"; + /** + * {@code "Expect"} + */ + public static final String EXPECT = "Expect"; + /** + * {@code "Expires"} + */ + public static final String EXPIRES = "Expires"; + /** + * {@code "From"} + */ + public static final String FROM = "From"; + /** + * {@code "Host"} + */ + public static final String HOST = "Host"; + /** + * {@code "If-Match"} + */ + public static final String IF_MATCH = "If-Match"; + /** + * {@code "If-Modified-Since"} + */ + public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; + /** + * {@code "If-None-Match"} + */ + public static final String IF_NONE_MATCH = "If-None-Match"; + /** + * {@code "If-Range"} + */ + public static final String IF_RANGE = "If-Range"; + /** + * {@code "If-Unmodified-Since"} + */ + public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; + /** + * {@code "Last-Modified"} + */ + public static final String LAST_MODIFIED = "Last-Modified"; + /** + * {@code "Location"} + */ + public static final String LOCATION = "Location"; + /** + * {@code "Max-Forwards"} + */ + public static final String MAX_FORWARDS = "Max-Forwards"; + /** + * {@code "Origin"} + */ + public static final String ORIGIN = "Origin"; + /** + * {@code "Pragma"} + */ + public static final String PRAGMA = "Pragma"; + /** + * {@code "Proxy-Authenticate"} + */ + public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; + /** + * {@code "Proxy-Authorization"} + */ + public static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; + /** + * {@code "Range"} + */ + public static final String RANGE = "Range"; + /** + * {@code "Referer"} + */ + public static final String REFERER = "Referer"; + /** + * {@code "Retry-After"} + */ + public static final String RETRY_AFTER = "Retry-After"; + /** + * {@code "Sec-WebSocket-Key1"} + */ + public static final String SEC_WEBSOCKET_KEY1 = "Sec-WebSocket-Key1"; + /** + * {@code "Sec-WebSocket-Key2"} + */ + public static final String SEC_WEBSOCKET_KEY2 = "Sec-WebSocket-Key2"; + /** + * {@code "Sec-WebSocket-Location"} + */ + public static final String SEC_WEBSOCKET_LOCATION = "Sec-WebSocket-Location"; + /** + * {@code "Sec-WebSocket-Origin"} + */ + public static final String SEC_WEBSOCKET_ORIGIN = "Sec-WebSocket-Origin"; + /** + * {@code "Sec-WebSocket-Protocol"} + */ + public static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol"; + /** + * {@code "Sec-WebSocket-Version"} + */ + public static final String SEC_WEBSOCKET_VERSION = "Sec-WebSocket-Version"; + /** + * {@code "Sec-WebSocket-Key"} + */ + public static final String SEC_WEBSOCKET_KEY = "Sec-WebSocket-Key"; + /** + * {@code "Sec-WebSocket-Accept"} + */ + public static final String SEC_WEBSOCKET_ACCEPT = "Sec-WebSocket-Accept"; + /** + * {@code "Server"} + */ + public static final String SERVER = "Server"; + /** + * {@code "Set-Cookie"} + */ + public static final String SET_COOKIE = "Set-Cookie"; + /** + * {@code "Set-Cookie2"} + */ + public static final String SET_COOKIE2 = "Set-Cookie2"; + /** + * {@code "TE"} + */ + public static final String TE = "TE"; + /** + * {@code "Trailer"} + */ + public static final String TRAILER = "Trailer"; + /** + * {@code "Transfer-Encoding"} + */ + public static final String TRANSFER_ENCODING = "Transfer-Encoding"; + /** + * {@code "Upgrade"} + */ + public static final String UPGRADE = "Upgrade"; + /** + * {@code "User-Agent"} + */ + public static final String USER_AGENT = "User-Agent"; + /** + * {@code "Vary"} + */ + public static final String VARY = "Vary"; + /** + * {@code "Via"} + */ + public static final String VIA = "Via"; + /** + * {@code "Warning"} + */ + public static final String WARNING = "Warning"; + /** + * {@code "WebSocket-Location"} + */ + public static final String WEBSOCKET_LOCATION = "WebSocket-Location"; + /** + * {@code "WebSocket-Origin"} + */ + public static final String WEBSOCKET_ORIGIN = "WebSocket-Origin"; + /** + * {@code "WebSocket-Protocol"} + */ + public static final String WEBSOCKET_PROTOCOL = "WebSocket-Protocol"; + /** + * {@code "WWW-Authenticate"} + */ + public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; + + private Names() { + super(); + } + } + + /** + * Standard HTTP header values. + * + * @apiviz.stereotype static + */ + public static final class Values { + /** + * {@code "application/x-www-form-urlencoded"} + */ + public static final String APPLICATION_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded"; + /** + * {@code "base64"} + */ + public static final String BASE64 = "base64"; + /** + * {@code "binary"} + */ + public static final String BINARY = "binary"; + /** + * {@code "boundary"} + */ + static final String BOUNDARY = "boundary"; + /** + * {@code "bytes"} + */ + public static final String BYTES = "bytes"; + /** + * {@code "charset"} + */ + public static final String CHARSET = "charset"; + /** + * {@code "chunked"} + */ + public static final String CHUNKED = "chunked"; + /** + * {@code "close"} + */ + public static final String CLOSE = "close"; + /** + * {@code "compress"} + */ + public static final String COMPRESS = "compress"; + /** + * {@code "100-continue"} + */ + public static final String CONTINUE = "100-continue"; + /** + * {@code "deflate"} + */ + public static final String DEFLATE = "deflate"; + /** + * {@code "gzip"} + */ + public static final String GZIP = "gzip"; + /** + * {@code "identity"} + */ + public static final String IDENTITY = "identity"; + /** + * {@code "keep-alive"} + */ + public static final String KEEP_ALIVE = "keep-alive"; + /** + * {@code "max-age"} + */ + public static final String MAX_AGE = "max-age"; + /** + * {@code "max-stale"} + */ + public static final String MAX_STALE = "max-stale"; + /** + * {@code "min-fresh"} + */ + public static final String MIN_FRESH = "min-fresh"; + /** + * {@code "multipart/form-data"} + */ + static final String MULTIPART_FORM_DATA = "multipart/form-data"; + /** + * {@code "must-revalidate"} + */ + public static final String MUST_REVALIDATE = "must-revalidate"; + /** + * {@code "no-cache"} + */ + public static final String NO_CACHE = "no-cache"; + /** + * {@code "no-store"} + */ + public static final String NO_STORE = "no-store"; + /** + * {@code "no-transform"} + */ + public static final String NO_TRANSFORM = "no-transform"; + /** + * {@code "none"} + */ + public static final String NONE = "none"; + /** + * {@code "only-if-cached"} + */ + public static final String ONLY_IF_CACHED = "only-if-cached"; + /** + * {@code "private"} + */ + public static final String PRIVATE = "private"; + /** + * {@code "proxy-revalidate"} + */ + public static final String PROXY_REVALIDATE = "proxy-revalidate"; + /** + * {@code "public"} + */ + public static final String PUBLIC = "public"; + /** + * {@code "quoted-printable"} + */ + public static final String QUOTED_PRINTABLE = "quoted-printable"; + /** + * {@code "s-maxage"} + */ + public static final String S_MAXAGE = "s-maxage"; + /** + * {@code "trailers"} + */ + public static final String TRAILERS = "trailers"; + /** + * {@code "Upgrade"} + */ + public static final String UPGRADE = "Upgrade"; + /** + * {@code "WebSocket"} + */ + public static final String WEBSOCKET = "WebSocket"; + + private Values() { + super(); + } + } + + /** + * Returns {@code true} if and only if the connection can remain open and + * thus 'kept alive'. This methods respects the value of the + * {@code "Connection"} header first and then the return value of + * {@link HttpVersion#isKeepAliveDefault()}. + */ + public static boolean isKeepAlive(HttpMessage message) { + String connection = message.getHeader(Names.CONNECTION); + if (Values.CLOSE.equalsIgnoreCase(connection)) { + return false; + } + + if (message.getProtocolVersion().isKeepAliveDefault()) { + return !Values.CLOSE.equalsIgnoreCase(connection); + } else { + return Values.KEEP_ALIVE.equalsIgnoreCase(connection); + } + } + + /** + * Sets the value of the {@code "Connection"} header depending on the + * protocol version of the specified message. This method sets or removes + * the {@code "Connection"} header depending on what the default keep alive + * mode of the message's protocol version is, as specified by + * {@link HttpVersion#isKeepAliveDefault()}. + *
    + *
  • If the connection is kept alive by default: + *
      + *
    • set to {@code "close"} if {@code keepAlive} is {@code false}.
    • + *
    • remove otherwise.
    • + *
    + *
  • + *
  • If the connection is closed by default: + *
      + *
    • set to {@code "keep-alive"} if {@code keepAlive} is {@code true}.
    • + *
    • remove otherwise.
    • + *
    + *
  • + *
+ */ + public static void setKeepAlive(HttpMessage message, boolean keepAlive) { + if (message.getProtocolVersion().isKeepAliveDefault()) { + if (keepAlive) { + message.removeHeader(Names.CONNECTION); + } else { + message.setHeader(Names.CONNECTION, Values.CLOSE); + } + } else { + if (keepAlive) { + message.setHeader(Names.CONNECTION, Values.KEEP_ALIVE); + } else { + message.removeHeader(Names.CONNECTION); + } + } + } + + /** + * Returns the header value with the specified header name. If there are + * more than one header value for the specified header name, the first value + * is returned. + * + * @return the header value or {@code null} if there is no such header + */ + public static String getHeader(HttpMessage message, String name) { + return message.getHeader(name); + } + + /** + * Returns the header value with the specified header name. If there are + * more than one header value for the specified header name, the first value + * is returned. + * + * @return the header value or the {@code defaultValue} if there is no such + * header + */ + public static String getHeader(HttpMessage message, String name, + String defaultValue) { + String value = message.getHeader(name); + if (value == null) { + return defaultValue; + } + return value; + } + + /** + * Sets a new header with the specified name and value. If there is an + * existing header with the same name, the existing header is removed. + */ + public static void setHeader(HttpMessage message, String name, Object value) { + message.setHeader(name, value); + } + + /** + * Sets a new header with the specified name and values. If there is an + * existing header with the same name, the existing header is removed. + */ + public static void setHeader(HttpMessage message, String name, + Iterable values) { + message.setHeader(name, values); + } + + /** + * Adds a new header with the specified name and value. + */ + public static void addHeader(HttpMessage message, String name, Object value) { + message.addHeader(name, value); + } + + /** + * Returns the integer header value with the specified header name. If there + * are more than one header value for the specified header name, the first + * value is returned. + * + * @return the header value + * @throws NumberFormatException + * if there is no such header or the header value is not a + * number + */ + public static int getIntHeader(HttpMessage message, String name) { + String value = getHeader(message, name); + if (value == null) { + throw new NumberFormatException("null"); + } + return Integer.parseInt(value); + } + + /** + * Returns the integer header value with the specified header name. If there + * are more than one header value for the specified header name, the first + * value is returned. + * + * @return the header value or the {@code defaultValue} if there is no such + * header or the header value is not a number + */ + public static int getIntHeader(HttpMessage message, String name, + int defaultValue) { + String value = getHeader(message, name); + if (value == null) { + return defaultValue; + } + + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * Sets a new integer header with the specified name and value. If there is + * an existing header with the same name, the existing header is removed. + */ + public static void setIntHeader(HttpMessage message, String name, int value) { + message.setHeader(name, value); + } + + /** + * Sets a new integer header with the specified name and values. If there is + * an existing header with the same name, the existing header is removed. + */ + public static void setIntHeader(HttpMessage message, String name, + Iterable values) { + message.setHeader(name, values); + } + + /** + * Adds a new integer header with the specified name and value. + */ + public static void addIntHeader(HttpMessage message, String name, int value) { + message.addHeader(name, value); + } + + /** + * Returns the length of the content. Please note that this value is not + * retrieved from {@link HttpMessage#getContent()} but from the + * {@code "Content-Length"} header, and thus they are independent from each + * other. + * + * @return the content length or {@code 0} if this message does not have the + * {@code "Content-Length"} header + */ + public static long getContentLength(HttpMessage message) { + return getContentLength(message, 0L); + } + + /** + * Returns the length of the content. Please note that this value is not + * retrieved from {@link HttpMessage#getContent()} but from the + * {@code "Content-Length"} header, and thus they are independent from each + * other. + * + * @return the content length or {@code defaultValue} if this message does + * not have the {@code "Content-Length"} header + */ + public static long getContentLength(HttpMessage message, long defaultValue) { + String contentLength = message.getHeader(Names.CONTENT_LENGTH); + if (contentLength != null) { + return Long.parseLong(contentLength); + } + + // WebSockset messages have constant content-lengths. + if (message instanceof HttpRequest) { + HttpRequest req = (HttpRequest) message; + if (HttpMethod.GET.equals(req.getMethod()) + && req.containsHeader(Names.SEC_WEBSOCKET_KEY1) + && req.containsHeader(Names.SEC_WEBSOCKET_KEY2)) { + return 8; + } + } else if (message instanceof HttpResponse) { + HttpResponse res = (HttpResponse) message; + if (res.getStatus().getCode() == 101 + && res.containsHeader(Names.SEC_WEBSOCKET_ORIGIN) + && res.containsHeader(Names.SEC_WEBSOCKET_LOCATION)) { + return 16; + } + } + + return defaultValue; + } + + /** + * Sets the {@code "Content-Length"} header. + */ + public static void setContentLength(HttpMessage message, long length) { + message.setHeader(Names.CONTENT_LENGTH, length); + } + + /** + * Returns the value of the {@code "Host"} header. + */ + public static String getHost(HttpMessage message) { + return message.getHeader(Names.HOST); + } + + /** + * Returns the value of the {@code "Host"} header. If there is no such + * header, the {@code defaultValue} is returned. + */ + public static String getHost(HttpMessage message, String defaultValue) { + return getHeader(message, Names.HOST, defaultValue); + } + + /** + * Sets the {@code "Host"} header. + */ + public static void setHost(HttpMessage message, String value) { + message.setHeader(Names.HOST, value); + } + + /** + * Returns {@code true} if and only if the specified message contains the + * {@code "Expect: 100-continue"} header. + */ + public static boolean is100ContinueExpected(HttpMessage message) { + // Expect: 100-continue is for requests only. + if (!(message instanceof HttpRequest)) { + return false; + } + + // It works only on HTTP/1.1 or later. + if (message.getProtocolVersion().compareTo(HttpVersion.HTTP_1_1) < 0) { + return false; + } + + // In most cases, there will be one or zero 'Expect' header. + String value = message.getHeader(Names.EXPECT); + if (value == null) { + return false; + } + if (Values.CONTINUE.equalsIgnoreCase(value)) { + return true; + } + + // Multiple 'Expect' headers. Search through them. + for (String v : message.getHeaders(Names.EXPECT)) { + if (Values.CONTINUE.equalsIgnoreCase(v)) { + return true; + } + } + return false; + } + + /** + * Sets the {@code "Expect: 100-continue"} header to the specified message. + * If there is any existing {@code "Expect"} header, they are replaced with + * the new one. + */ + public static void set100ContinueExpected(HttpMessage message) { + set100ContinueExpected(message, true); + } + + /** + * Sets or removes the {@code "Expect: 100-continue"} header to / from the + * specified message. If the specified {@code value} is {@code true}, the + * {@code "Expect: 100-continue"} header is set and all other previous + * {@code "Expect"} headers are removed. Otherwise, all {@code "Expect"} + * headers are removed completely. + */ + public static void set100ContinueExpected(HttpMessage message, boolean set) { + if (set) { + message.setHeader(Names.EXPECT, Values.CONTINUE); + } else { + message.removeHeader(Names.EXPECT); + } + } + + private static final int BUCKET_SIZE = 17; + + private static int hash(String name) { + int h = 0; + for (int i = name.length() - 1; i >= 0; i--) { + char c = name.charAt(i); + if (c >= 'A' && c <= 'Z') { + c += 32; + } + h = 31 * h + c; + } + + if (h > 0) { + return h; + } else if (h == Integer.MIN_VALUE) { + return Integer.MAX_VALUE; + } else { + return -h; + } + } + + private static boolean eq(String name1, String name2) { + int nameLen = name1.length(); + if (nameLen != name2.length()) { + return false; + } + + for (int i = nameLen - 1; i >= 0; i--) { + char c1 = name1.charAt(i); + char c2 = name2.charAt(i); + if (c1 != c2) { + if (c1 >= 'A' && c1 <= 'Z') { + c1 += 32; + } + if (c2 >= 'A' && c2 <= 'Z') { + c2 += 32; + } + if (c1 != c2) { + return false; + } + } + } + return true; + } + + private static int index(int hash) { + return hash % BUCKET_SIZE; + } + + private final Entry[] entries = new Entry[BUCKET_SIZE]; + private final Entry head = new Entry(-1, null, null); + + HttpHeaders() { + head.before = head.after = head; + } + + void validateHeaderName(String name) { + HttpCodecUtil.validateHeaderName(name); + } + + void addHeader(final String name, final Object value) { + validateHeaderName(name); + String strVal = toString(value); + HttpCodecUtil.validateHeaderValue(strVal); + int h = hash(name); + int i = index(h); + addHeader0(h, i, name, strVal); + } + + private void addHeader0(int h, int i, final String name, final String value) { + // Update the hash table. + Entry e = entries[i]; + Entry newEntry; + entries[i] = newEntry = new Entry(h, name, value); + newEntry.next = e; + + // Update the linked list. + newEntry.addBefore(head); + } + + void removeHeader(final String name) { + if (name == null) { + throw new NullPointerException("name"); + } + int h = hash(name); + int i = index(h); + removeHeader0(h, i, name); + } + + private void removeHeader0(int h, int i, String name) { + Entry e = entries[i]; + if (e == null) { + return; + } + + for (;;) { + if (e.hash == h && eq(name, e.key)) { + e.remove(); + Entry next = e.next; + if (next != null) { + entries[i] = next; + e = next; + } else { + entries[i] = null; + return; + } + } else { + break; + } + } + + for (;;) { + Entry next = e.next; + if (next == null) { + break; + } + if (next.hash == h && eq(name, next.key)) { + e.next = next.next; + next.remove(); + } else { + e = next; + } + } + } + + void setHeader(final String name, final Object value) { + validateHeaderName(name); + String strVal = toString(value); + HttpCodecUtil.validateHeaderValue(strVal); + int h = hash(name); + int i = index(h); + removeHeader0(h, i, name); + addHeader0(h, i, name, strVal); + } + + void setHeader(final String name, final Iterable values) { + if (values == null) { + throw new NullPointerException("values"); + } + + validateHeaderName(name); + + int h = hash(name); + int i = index(h); + + removeHeader0(h, i, name); + for (Object v : values) { + if (v == null) { + break; + } + String strVal = toString(v); + HttpCodecUtil.validateHeaderValue(strVal); + addHeader0(h, i, name, strVal); + } + } + + void clearHeaders() { + for (int i = 0; i < entries.length; i++) { + entries[i] = null; + } + head.before = head.after = head; + } + + String getHeader(final String name) { + if (name == null) { + throw new NullPointerException("name"); + } + + int h = hash(name); + int i = index(h); + Entry e = entries[i]; + while (e != null) { + if (e.hash == h && eq(name, e.key)) { + return e.value; + } + + e = e.next; + } + return null; + } + + List getHeaders(final String name) { + if (name == null) { + throw new NullPointerException("name"); + } + + LinkedList values = new LinkedList(); + + int h = hash(name); + int i = index(h); + Entry e = entries[i]; + while (e != null) { + if (e.hash == h && eq(name, e.key)) { + values.addFirst(e.value); + } + e = e.next; + } + return values; + } + + List> getHeaders() { + List> all = new LinkedList>(); + + Entry e = head.after; + while (e != head) { + all.add(e); + e = e.after; + } + return all; + } + + boolean containsHeader(String name) { + return getHeader(name) != null; + } + + Set getHeaderNames() { + Set names = new TreeSet(CaseIgnoringComparator.INSTANCE); + + Entry e = head.after; + while (e != head) { + names.add(e.key); + e = e.after; + } + return names; + } + + private static String toString(Object value) { + if (value == null) { + return null; + } + return value.toString(); + } + + private static final class Entry implements Map.Entry { + final int hash; + final String key; + String value; + Entry next; + Entry before, after; + + Entry(int hash, String key, String value) { + this.hash = hash; + this.key = key; + this.value = value; + } + + void remove() { + before.after = after; + after.before = before; + } + + void addBefore(Entry e) { + after = e; + before = e.before; + before.after = this; + after.before = this; + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + public String setValue(String value) { + if (value == null) { + throw new NullPointerException("value"); + } + HttpCodecUtil.validateHeaderValue(value); + String oldValue = this.value; + this.value = value; + return oldValue; + } + + @Override + public String toString() { + return key + "=" + value; + } + } } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/HttpPostBodyUtil.java b/src/main/java/org/jboss/netty/handler/codec/http/HttpPostBodyUtil.java index 60af88d490..6168763924 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/HttpPostBodyUtil.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/HttpPostBodyUtil.java @@ -178,4 +178,4 @@ final class HttpPostBodyUtil { return result; } -} \ No newline at end of file +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/HttpPostRequestDecoder.java b/src/main/java/org/jboss/netty/handler/codec/http/HttpPostRequestDecoder.java index 95276662f4..8f1ea9e1df 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/HttpPostRequestDecoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/HttpPostRequestDecoder.java @@ -26,1545 +26,1474 @@ import java.util.TreeMap; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; -import org.jboss.netty.handler.codec.http.HttpChunk; -import org.jboss.netty.handler.codec.http.HttpHeaders; -import org.jboss.netty.handler.codec.http.HttpMethod; import org.jboss.netty.handler.codec.http.HttpPostBodyUtil.TransferEncodingMechanism; -import org.jboss.netty.handler.codec.http.HttpRequest; - /** * This decoder will decode Body and can handle POST BODY. */ public class HttpPostRequestDecoder { - - static final String MULTIPART_FORM_DATA = "multipart/form-data"; - static final String BOUNDARY = "boundary"; - - /** - * Factory used to create InterfaceHttpData - */ - private final HttpDataFactory factory; + /** + * Factory used to create InterfaceHttpData + */ + private final HttpDataFactory factory; - /** - * Request to decode - */ - private final HttpRequest request; + /** + * Request to decode + */ + private final HttpRequest request; - /** - * Default charset to use - */ - private final Charset charset; + /** + * Default charset to use + */ + private final Charset charset; - /** - * Does request have a body to decode - */ - private boolean bodyToDecode; + /** + * Does request have a body to decode + */ + private boolean bodyToDecode; - /** - * Does the last chunk already received - */ - private boolean isLastChunk; + /** + * Does the last chunk already received + */ + private boolean isLastChunk; - /** - * HttpDatas from Body - */ - private final List bodyListHttpData = new ArrayList(); + /** + * HttpDatas from Body + */ + private final List bodyListHttpData = new ArrayList(); - /** - * HttpDatas as Map from Body - */ - private final Map> bodyMapHttpData = new TreeMap>( - CaseIgnoringComparator.INSTANCE); + /** + * HttpDatas as Map from Body + */ + private final Map> bodyMapHttpData = new TreeMap>( + CaseIgnoringComparator.INSTANCE); - /** - * The current channelBuffer - */ - private ChannelBuffer undecodedChunk; + /** + * The current channelBuffer + */ + private ChannelBuffer undecodedChunk; - /** - * Does this request is a Multipart request - */ - private boolean isMultipart; + /** + * Does this request is a Multipart request + */ + private boolean isMultipart; - /** - * Body HttpDatas current position - */ - private int bodyListHttpDataRank; + /** + * Body HttpDatas current position + */ + private int bodyListHttpDataRank; - /** - * If multipart, this is the boundary for the flobal multipart - */ - private String multipartDataBoundary; + /** + * If multipart, this is the boundary for the flobal multipart + */ + private String multipartDataBoundary; - /** - * If multipart, there could be internal multiparts (mixed) to the global - * multipart. Only one level is allowed. - */ - private String multipartMixedBoundary; + /** + * If multipart, there could be internal multiparts (mixed) to the global multipart. + * Only one level is allowed. + */ + private String multipartMixedBoundary; - /** - * Current status - */ - private MultiPartStatus currentStatus = MultiPartStatus.NOTSTARTED; + /** + * Current status + */ + private MultiPartStatus currentStatus = MultiPartStatus.NOTSTARTED; - /** - * Used in Multipart - */ - private Map currentFieldAttributes; + /** + * Used in Multipart + */ + private Map currentFieldAttributes; - /** - * The current FileUpload that is currently in decode process - */ - private FileUpload currentFileUpload; + /** + * The current FileUpload that is currently in decode process + */ + private FileUpload currentFileUpload; - /** - * The current Attribute that is currently in decode process - */ - private Attribute currentAttribute; + /** + * The current Attribute that is currently in decode process + */ + private Attribute currentAttribute; - /** - * - * @param request - * the request to decode - * @throws NullPointerException - * for request - * @throws IncompatibleDataDecoderException - * if the request has no body to decode - * @throws ErrorDataDecoderException - * if the default charset was wrong when decoding or other - * errors - */ - public HttpPostRequestDecoder(HttpRequest request) - throws ErrorDataDecoderException, IncompatibleDataDecoderException { - this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), - request, HttpCodecUtil.DEFAULT_CHARSET); - } + /** + * + * @param request the request to decode + * @throws NullPointerException for request + * @throws IncompatibleDataDecoderException if the request has no body to decode + * @throws ErrorDataDecoderException if the default charset was wrong when decoding or other errors + */ + public HttpPostRequestDecoder(HttpRequest request) + throws ErrorDataDecoderException, IncompatibleDataDecoderException { + this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), + request, HttpCodecUtil.DEFAULT_CHARSET); + } - /** - * - * @param factory - * the factory used to create InterfaceHttpData - * @param request - * the request to decode - * @throws NullPointerException - * for request or factory - * @throws IncompatibleDataDecoderException - * if the request has no body to decode - * @throws ErrorDataDecoderException - * if the default charset was wrong when decoding or other - * errors - */ - public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request) - throws ErrorDataDecoderException, IncompatibleDataDecoderException { - this(factory, request, HttpCodecUtil.DEFAULT_CHARSET); - } + /** + * + * @param factory the factory used to create InterfaceHttpData + * @param request the request to decode + * @throws NullPointerException for request or factory + * @throws IncompatibleDataDecoderException if the request has no body to decode + * @throws ErrorDataDecoderException if the default charset was wrong when decoding or other errors + */ + public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request) + throws ErrorDataDecoderException, IncompatibleDataDecoderException { + this(factory, request, HttpCodecUtil.DEFAULT_CHARSET); + } - /** - * - * @param factory - * the factory used to create InterfaceHttpData - * @param request - * the request to decode - * @param charset - * the charset to use as default - * @throws NullPointerException - * for request or charset or factory - * @throws IncompatibleDataDecoderException - * if the request has no body to decode - * @throws ErrorDataDecoderException - * if the default charset was wrong when decoding or other - * errors - */ - public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request, - Charset charset) throws ErrorDataDecoderException, - IncompatibleDataDecoderException { - if (factory == null) { - throw new NullPointerException("factory"); - } - if (request == null) { - throw new NullPointerException("request"); - } - if (charset == null) { - throw new NullPointerException("charset"); - } - this.request = request; - HttpMethod method = request.getMethod(); - if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT) - || method.equals(HttpMethod.PATCH)) { - bodyToDecode = true; - } - this.charset = charset; - this.factory = factory; - // Fill default values - if (this.request.containsHeader(HttpHeaders.Names.CONTENT_TYPE)) { - checkMultipart(this.request - .getHeader(HttpHeaders.Names.CONTENT_TYPE)); - } else { - isMultipart = false; - } - if (!bodyToDecode) { - throw new IncompatibleDataDecoderException("No Body to decode"); - } - if (!this.request.isChunked()) { - undecodedChunk = this.request.getContent(); - isLastChunk = true; - parseBody(); - } - } + /** + * + * @param factory the factory used to create InterfaceHttpData + * @param request the request to decode + * @param charset the charset to use as default + * @throws NullPointerException for request or charset or factory + * @throws IncompatibleDataDecoderException if the request has no body to decode + * @throws ErrorDataDecoderException if the default charset was wrong when decoding or other errors + */ + public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request, + Charset charset) throws ErrorDataDecoderException, + IncompatibleDataDecoderException { + if (factory == null) { + throw new NullPointerException("factory"); + } + if (request == null) { + throw new NullPointerException("request"); + } + if (charset == null) { + throw new NullPointerException("charset"); + } + this.request = request; + HttpMethod method = request.getMethod(); + if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PATCH)) { + bodyToDecode = true; + } + this.charset = charset; + this.factory = factory; + // Fill default values + if (this.request.containsHeader(HttpHeaders.Names.CONTENT_TYPE)) { + checkMultipart(this.request.getHeader(HttpHeaders.Names.CONTENT_TYPE)); + } else { + isMultipart = false; + } + if (!bodyToDecode) { + throw new IncompatibleDataDecoderException("No Body to decode"); + } + if (!this.request.isChunked()) { + undecodedChunk = this.request.getContent(); + isLastChunk = true; + parseBody(); + } + } - /** - * states follow NOTSTARTED PREAMBLE ( (HEADERDELIMITER DISPOSITION (FIELD | - * FILEUPLOAD))* (HEADERDELIMITER DISPOSITION MIXEDPREAMBLE (MIXEDDELIMITER - * MIXEDDISPOSITION MIXEDFILEUPLOAD)+ MIXEDCLOSEDELIMITER)* CLOSEDELIMITER)+ - * EPILOGUE - * - * First status is: NOSTARTED - * - * Content-type: multipart/form-data, boundary=AaB03x => PREAMBLE in Header - * - * --AaB03x => HEADERDELIMITER content-disposition: form-data; name="field1" - * => DISPOSITION - * - * Joe Blow => FIELD --AaB03x => HEADERDELIMITER content-disposition: - * form-data; name="pics" => DISPOSITION Content-type: multipart/mixed, - * boundary=BbC04y - * - * --BbC04y => MIXEDDELIMITER Content-disposition: attachment; - * filename="file1.txt" => MIXEDDISPOSITION Content-Type: text/plain - * - * ... contents of file1.txt ... => MIXEDFILEUPLOAD --BbC04y => - * MIXEDDELIMITER Content-disposition: file; filename="file2.gif" => - * MIXEDDISPOSITION Content-type: image/gif Content-Transfer-Encoding: - * binary - * - * ...contents of file2.gif... => MIXEDFILEUPLOAD --BbC04y-- => - * MIXEDCLOSEDELIMITER --AaB03x-- => CLOSEDELIMITER - * - * Once CLOSEDELIMITER is found, last status is EPILOGUE - */ - private enum MultiPartStatus { - NOTSTARTED, PREAMBLE, HEADERDELIMITER, DISPOSITION, FIELD, FILEUPLOAD, MIXEDPREAMBLE, MIXEDDELIMITER, MIXEDDISPOSITION, MIXEDFILEUPLOAD, MIXEDCLOSEDELIMITER, CLOSEDELIMITER, PREEPILOGUE, EPILOGUE - } + /** + * states follow + * NOTSTARTED PREAMBLE ( + * (HEADERDELIMITER DISPOSITION (FIELD | FILEUPLOAD))* + * (HEADERDELIMITER DISPOSITION MIXEDPREAMBLE + * (MIXEDDELIMITER MIXEDDISPOSITION MIXEDFILEUPLOAD)+ + * MIXEDCLOSEDELIMITER)* + * CLOSEDELIMITER)+ EPILOGUE + * + * First status is: NOSTARTED - /** - * Check from the request ContentType if this request is a Multipart - * request. - * - * @param contentType - * @throws ErrorDataDecoderException - */ - private void checkMultipart(String contentType) - throws ErrorDataDecoderException { - // Check if Post using "multipart/form-data; boundary=--89421926422648" - String[] headerContentType = splitHeaderContentType(contentType); - if (headerContentType[0].toLowerCase().startsWith( - MULTIPART_FORM_DATA) - && headerContentType[1].toLowerCase().startsWith( - BOUNDARY)) { - String[] boundary = headerContentType[1].split("="); - if (boundary.length != 2) { - throw new ErrorDataDecoderException("Needs a boundary value"); - } - multipartDataBoundary = "--" + boundary[1]; - isMultipart = true; - currentStatus = MultiPartStatus.HEADERDELIMITER; - } else { - isMultipart = false; - } - } + Content-type: multipart/form-data, boundary=AaB03x => PREAMBLE in Header - /** - * True if this request is a Multipart request - * - * @return True if this request is a Multipart request - */ - public boolean isMultipart() { - return isMultipart; - } + --AaB03x => HEADERDELIMITER + content-disposition: form-data; name="field1" => DISPOSITION - /** - * This method returns a List of all HttpDatas from body.
- * - * If chunked, all chunks must have been offered using offer() method. If - * not, NotEnoughDataDecoderException will be raised. - * - * @return the list of HttpDatas from Body part for POST method - * @throws NotEnoughDataDecoderException - * Need more chunks - */ - public List getBodyHttpDatas() - throws NotEnoughDataDecoderException { - if (!isLastChunk) { - throw new NotEnoughDataDecoderException(); - } - return bodyListHttpData; - } + Joe Blow => FIELD + --AaB03x => HEADERDELIMITER + content-disposition: form-data; name="pics" => DISPOSITION + Content-type: multipart/mixed, boundary=BbC04y - /** - * This method returns a List of all HttpDatas with the given name from - * body.
- * - * If chunked, all chunks must have been offered using offer() method. If - * not, NotEnoughDataDecoderException will be raised. - * - * @param name - * @return All Body HttpDatas with the given name (ignore case) - * @throws NotEnoughDataDecoderException - * need more chunks - */ - public List getBodyHttpDatas(String name) - throws NotEnoughDataDecoderException { - if (!isLastChunk) { - throw new NotEnoughDataDecoderException(); - } - return bodyMapHttpData.get(name); - } + --BbC04y => MIXEDDELIMITER + Content-disposition: attachment; filename="file1.txt" => MIXEDDISPOSITION + Content-Type: text/plain - /** - * This method returns the first InterfaceHttpData with the given name from - * body.
- * - * If chunked, all chunks must have been offered using offer() method. If - * not, NotEnoughDataDecoderException will be raised. - * - * @param name - * @return The first Body InterfaceHttpData with the given name (ignore - * case) - * @throws NotEnoughDataDecoderException - * need more chunks - */ - public InterfaceHttpData getBodyHttpData(String name) - throws NotEnoughDataDecoderException { - if (!isLastChunk) { - throw new NotEnoughDataDecoderException(); - } - List list = bodyMapHttpData.get(name); - if (list != null) { - return list.get(0); - } - return null; - } + ... contents of file1.txt ... => MIXEDFILEUPLOAD + --BbC04y => MIXEDDELIMITER + Content-disposition: file; filename="file2.gif" => MIXEDDISPOSITION + Content-type: image/gif + Content-Transfer-Encoding: binary - /** - * Initialized the internals from a new chunk - * - * @param chunk - * the new received chunk - * @throws ErrorDataDecoderException - * if there is a problem with the charset decoding or other - * errors - */ - public void offer(HttpChunk chunk) throws ErrorDataDecoderException { - ChannelBuffer chunked = chunk.getContent(); - if (undecodedChunk == null) { - undecodedChunk = chunked; - } else { - // undecodedChunk = ChannelBuffers.wrappedBuffer(undecodedChunk, - // chunk.getContent()); - // less memory usage - undecodedChunk = ChannelBuffers.wrappedBuffer(undecodedChunk, - chunked); - } - if (chunk.isLast()) { - isLastChunk = true; - } - parseBody(); - } + ...contents of file2.gif... => MIXEDFILEUPLOAD + --BbC04y-- => MIXEDCLOSEDELIMITER + --AaB03x-- => CLOSEDELIMITER - /** - * True if at current status, there is an available decoded - * InterfaceHttpData from the Body. - * - * This method works for chunked and not chunked request. - * - * @return True if at current status, there is a decoded InterfaceHttpData - * @throws EndOfDataDecoderException - * No more data will be available - */ - public boolean hasNext() throws EndOfDataDecoderException { - if (currentStatus == MultiPartStatus.EPILOGUE) { - // OK except if end of list - if (bodyListHttpDataRank >= bodyListHttpData.size()) { - throw new EndOfDataDecoderException(); - } - } - return bodyListHttpData.size() > 0 - && bodyListHttpDataRank < bodyListHttpData.size(); - } + Once CLOSEDELIMITER is found, last status is EPILOGUE + */ + private enum MultiPartStatus { + NOTSTARTED, + PREAMBLE, + HEADERDELIMITER, + DISPOSITION, + FIELD, + FILEUPLOAD, + MIXEDPREAMBLE, + MIXEDDELIMITER, + MIXEDDISPOSITION, + MIXEDFILEUPLOAD, + MIXEDCLOSEDELIMITER, + CLOSEDELIMITER, + PREEPILOGUE, + EPILOGUE + } - /** - * Returns the next available InterfaceHttpData or null if, at the time it - * is called, there is no more available InterfaceHttpData. A subsequent - * call to offer(httpChunk) could enable more data. - * - * @return the next available InterfaceHttpData or null if none - * @throws EndOfDataDecoderException - * No more data will be available - */ - public InterfaceHttpData next() throws EndOfDataDecoderException { - if (hasNext()) { - return bodyListHttpData.get(bodyListHttpDataRank++); - } - return null; - } + /** + * Check from the request ContentType if this request is a Multipart request. + * @param contentType + * @throws ErrorDataDecoderException + */ + private void checkMultipart(String contentType) + throws ErrorDataDecoderException { + // Check if Post using "multipart/form-data; boundary=--89421926422648" + String[] headerContentType = splitHeaderContentType(contentType); + if (headerContentType[0].toLowerCase().startsWith( + HttpHeaders.Values.MULTIPART_FORM_DATA) && + headerContentType[1].toLowerCase().startsWith( + HttpHeaders.Values.BOUNDARY)) { + String[] boundary = headerContentType[1].split("="); + if (boundary.length != 2) { + throw new ErrorDataDecoderException("Needs a boundary value"); + } + multipartDataBoundary = "--" + boundary[1]; + isMultipart = true; + currentStatus = MultiPartStatus.HEADERDELIMITER; + } else { + isMultipart = false; + } + } - /** - * This method will parse as much as possible data and fill the list and map - * - * @throws ErrorDataDecoderException - * if there is a problem with the charset decoding or other - * errors - */ - private void parseBody() throws ErrorDataDecoderException { - if (currentStatus == MultiPartStatus.PREEPILOGUE - || currentStatus == MultiPartStatus.EPILOGUE) { - if (isLastChunk) { - currentStatus = MultiPartStatus.EPILOGUE; - } - return; - } - if (isMultipart) { - parseBodyMultipart(); - } else { - parseBodyAttributes(); - } - } + /** + * True if this request is a Multipart request + * @return True if this request is a Multipart request + */ + public boolean isMultipart() { + return isMultipart; + } - /** - * Utility function to add a new decoded data - * - * @param data - */ - private void addHttpData(InterfaceHttpData data) { - if (data == null) { - return; - } - List datas = bodyMapHttpData.get(data.getName()); - if (datas == null) { - datas = new ArrayList(1); - bodyMapHttpData.put(data.getName(), datas); - } - datas.add(data); - bodyListHttpData.add(data); - } + /** + * This method returns a List of all HttpDatas from body.
+ * + * If chunked, all chunks must have been offered using offer() method. + * If not, NotEnoughDataDecoderException will be raised. + * + * @return the list of HttpDatas from Body part for POST method + * @throws NotEnoughDataDecoderException Need more chunks + */ + public List getBodyHttpDatas() + throws NotEnoughDataDecoderException { + if (!isLastChunk) { + throw new NotEnoughDataDecoderException(); + } + return bodyListHttpData; + } - /** - * This method fill the map and list with as much Attribute as possible from - * Body in not Multipart mode. - * - * @throws ErrorDataDecoderException - * if there is a problem with the charset decoding or other - * errors - */ - private void parseBodyAttributes() throws ErrorDataDecoderException { - int firstpos = undecodedChunk.readerIndex(); - int currentpos = firstpos; - int equalpos = firstpos; - int ampersandpos = firstpos; - if (currentStatus == MultiPartStatus.NOTSTARTED) { - currentStatus = MultiPartStatus.DISPOSITION; - } - boolean contRead = true; - try { - while (undecodedChunk.readable() && contRead) { - char read = (char) undecodedChunk.readUnsignedByte(); - currentpos++; - switch (currentStatus) { - case DISPOSITION:// search '=' - if (read == '=') { - currentStatus = MultiPartStatus.FIELD; - equalpos = currentpos - 1; - String key = decodeAttribute( - undecodedChunk.toString(firstpos, equalpos - - firstpos, charset), charset); - currentAttribute = factory - .createAttribute(request, key); - firstpos = currentpos; - } else if (read == '&') { // special empty FIELD - currentStatus = MultiPartStatus.DISPOSITION; - ampersandpos = currentpos - 1; - String key = decodeAttribute( - undecodedChunk.toString(firstpos, ampersandpos - - firstpos, charset), charset); - currentAttribute = factory - .createAttribute(request, key); - currentAttribute.setValue(""); // empty - addHttpData(currentAttribute); - currentAttribute = null; - firstpos = currentpos; - contRead = true; - } - break; - case FIELD:// search '&' or end of line - if (read == '&') { - currentStatus = MultiPartStatus.DISPOSITION; - ampersandpos = currentpos - 1; - setFinalBuffer(undecodedChunk.slice(firstpos, - ampersandpos - firstpos)); - firstpos = currentpos; - contRead = true; - } else if (read == HttpCodecUtil.CR) { - if (undecodedChunk.readable()) { - read = (char) undecodedChunk.readUnsignedByte(); - currentpos++; - if (read == HttpCodecUtil.LF) { - currentStatus = MultiPartStatus.PREEPILOGUE; - ampersandpos = currentpos - 2; - setFinalBuffer(undecodedChunk.slice(firstpos, - ampersandpos - firstpos)); - firstpos = currentpos; - contRead = false; - } else { - // Error - contRead = false; - throw new ErrorDataDecoderException( - "Bad end of line"); - } - } else { - currentpos--; - } - } else if (read == HttpCodecUtil.LF) { - currentStatus = MultiPartStatus.PREEPILOGUE; - ampersandpos = currentpos - 1; - setFinalBuffer(undecodedChunk.slice(firstpos, - ampersandpos - firstpos)); - firstpos = currentpos; - contRead = false; - } - break; - default: - // just stop - contRead = false; - } - } - if (isLastChunk && currentAttribute != null) { - // special case - ampersandpos = currentpos; - if (ampersandpos > firstpos) { - setFinalBuffer(undecodedChunk.slice(firstpos, ampersandpos - - firstpos)); - } else if (!currentAttribute.isCompleted()) { - setFinalBuffer(ChannelBuffers.EMPTY_BUFFER); - } - firstpos = currentpos; - currentStatus = MultiPartStatus.EPILOGUE; - return; - } - if (contRead && currentAttribute != null) { - // reset index except if to continue in case of FIELD status - if (currentStatus == MultiPartStatus.FIELD) { - currentAttribute.addContent( - undecodedChunk.slice(firstpos, currentpos - - firstpos), false); - firstpos = currentpos; - } - undecodedChunk.readerIndex(firstpos); - } else { - // end of line so keep index - } - } catch (ErrorDataDecoderException e) { - // error while decoding - undecodedChunk.readerIndex(firstpos); - throw e; - } catch (IOException e) { - // error while decoding - undecodedChunk.readerIndex(firstpos); - throw new ErrorDataDecoderException(e); - } - } + /** + * This method returns a List of all HttpDatas with the given name from body.
+ * + * If chunked, all chunks must have been offered using offer() method. + * If not, NotEnoughDataDecoderException will be raised. - private void setFinalBuffer(ChannelBuffer buffer) - throws ErrorDataDecoderException, IOException { - currentAttribute.addContent(buffer, true); - String value = decodeAttribute(currentAttribute.getChannelBuffer() - .toString(charset), charset); - currentAttribute.setValue(value); - addHttpData(currentAttribute); - currentAttribute = null; - } + * @param name + * @return All Body HttpDatas with the given name (ignore case) + * @throws NotEnoughDataDecoderException need more chunks + */ + public List getBodyHttpDatas(String name) + throws NotEnoughDataDecoderException { + if (!isLastChunk) { + throw new NotEnoughDataDecoderException(); + } + return bodyMapHttpData.get(name); + } - /** - * Decode component - * - * @param s - * @param charset - * @return the decoded component - * @throws ErrorDataDecoderException - */ - private static String decodeAttribute(String s, Charset charset) - throws ErrorDataDecoderException { - if (s == null) { - return ""; - } - try { - return URLDecoder.decode(s, charset.name()); - } catch (UnsupportedEncodingException e) { - throw new ErrorDataDecoderException(charset.toString(), e); - } - } + /** + * This method returns the first InterfaceHttpData with the given name from body.
+ * + * If chunked, all chunks must have been offered using offer() method. + * If not, NotEnoughDataDecoderException will be raised. + * + * @param name + * @return The first Body InterfaceHttpData with the given name (ignore case) + * @throws NotEnoughDataDecoderException need more chunks + */ + public InterfaceHttpData getBodyHttpData(String name) + throws NotEnoughDataDecoderException { + if (!isLastChunk) { + throw new NotEnoughDataDecoderException(); + } + List list = bodyMapHttpData.get(name); + if (list != null) { + return list.get(0); + } + return null; + } - /** - * Parse the Body for multipart - * - * @throws ErrorDataDecoderException - * if there is a problem with the charset decoding or other - * errors - */ - private void parseBodyMultipart() throws ErrorDataDecoderException { - if (undecodedChunk == null || undecodedChunk.readableBytes() == 0) { - // nothing to decode - return; - } - InterfaceHttpData data = decodeMultipart(currentStatus); - while (data != null) { - addHttpData(data); - if (currentStatus == MultiPartStatus.PREEPILOGUE - || currentStatus == MultiPartStatus.EPILOGUE) { - break; - } - data = decodeMultipart(currentStatus); - } - } + /** + * Initialized the internals from a new chunk + * @param chunk the new received chunk + * @throws ErrorDataDecoderException if there is a problem with the charset decoding or + * other errors + */ + public void offer(HttpChunk chunk) throws ErrorDataDecoderException { + ChannelBuffer chunked = chunk.getContent(); + if (undecodedChunk == null) { + undecodedChunk = chunked; + } else { + //undecodedChunk = ChannelBuffers.wrappedBuffer(undecodedChunk, chunk.getContent()); + // less memory usage + undecodedChunk = ChannelBuffers.wrappedBuffer( + undecodedChunk, chunked); + } + if (chunk.isLast()) { + isLastChunk = true; + } + parseBody(); + } - /** - * Decode a multipart request by pieces
- *
- * NOTSTARTED PREAMBLE (
- * (HEADERDELIMITER DISPOSITION (FIELD | FILEUPLOAD))*
- * (HEADERDELIMITER DISPOSITION MIXEDPREAMBLE
- * (MIXEDDELIMITER MIXEDDISPOSITION MIXEDFILEUPLOAD)+
- * MIXEDCLOSEDELIMITER)*
- * CLOSEDELIMITER)+ EPILOGUE
- * - * Inspired from HttpMessageDecoder - * - * @param state - * @return the next decoded InterfaceHttpData or null if none until now. - * @throws ErrorDataDecoderException - * if an error occurs - */ - private InterfaceHttpData decodeMultipart(MultiPartStatus state) - throws ErrorDataDecoderException { - switch (state) { - case NOTSTARTED: - throw new ErrorDataDecoderException( - "Should not be called with the current status"); - case PREAMBLE: - // Content-type: multipart/form-data, boundary=AaB03x - throw new ErrorDataDecoderException( - "Should not be called with the current status"); - case HEADERDELIMITER: { - // --AaB03x or --AaB03x-- - return findMultipartDelimiter(multipartDataBoundary, - MultiPartStatus.DISPOSITION, MultiPartStatus.PREEPILOGUE); - } - case DISPOSITION: { - // content-disposition: form-data; name="field1" - // content-disposition: form-data; name="pics"; filename="file1.txt" - // and other immediate values like - // Content-type: image/gif - // Content-Type: text/plain - // Content-Type: text/plain; charset=ISO-8859-1 - // Content-Transfer-Encoding: binary - // The following line implies a change of mode (mixed mode) - // Content-type: multipart/mixed, boundary=BbC04y - return findMultipartDisposition(); - } - case FIELD: { - // Now get value according to Content-Type and Charset - Charset localCharset = null; - Attribute charsetAttribute = currentFieldAttributes - .get(HttpHeaders.Values.CHARSET); - if (charsetAttribute != null) { - try { - localCharset = Charset.forName(charsetAttribute.getValue()); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - } - Attribute nameAttribute = currentFieldAttributes - .get(HttpPostBodyUtil.NAME); - if (currentAttribute == null) { - try { - currentAttribute = factory.createAttribute(request, - nameAttribute.getValue()); - } catch (NullPointerException e) { - throw new ErrorDataDecoderException(e); - } catch (IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - if (localCharset != null) { - currentAttribute.setCharset(localCharset); - } - } - // load data - try { - loadFieldMultipart(multipartDataBoundary); - } catch (NotEnoughDataDecoderException e) { - return null; - } - Attribute finalAttribute = currentAttribute; - currentAttribute = null; - currentFieldAttributes = null; - // ready to load the next one - currentStatus = MultiPartStatus.HEADERDELIMITER; - return finalAttribute; - } - case FILEUPLOAD: { - // eventually restart from existing FileUpload - return getFileUpload(multipartDataBoundary); - } - case MIXEDDELIMITER: { - // --AaB03x or --AaB03x-- - // Note that currentFieldAttributes exists - return findMultipartDelimiter(multipartMixedBoundary, - MultiPartStatus.MIXEDDISPOSITION, - MultiPartStatus.HEADERDELIMITER); - } - case MIXEDDISPOSITION: { - return findMultipartDisposition(); - } - case MIXEDFILEUPLOAD: { - // eventually restart from existing FileUpload - return getFileUpload(multipartMixedBoundary); - } - case PREEPILOGUE: - return null; - case EPILOGUE: - return null; - default: - throw new ErrorDataDecoderException("Shouldn't reach here."); - } - } + /** + * True if at current status, there is an available decoded InterfaceHttpData from the Body. + * + * This method works for chunked and not chunked request. + * + * @return True if at current status, there is a decoded InterfaceHttpData + * @throws EndOfDataDecoderException No more data will be available + */ + public boolean hasNext() throws EndOfDataDecoderException { + if (currentStatus == MultiPartStatus.EPILOGUE) { + // OK except if end of list + if (bodyListHttpDataRank >= bodyListHttpData.size()) { + throw new EndOfDataDecoderException(); + } + } + return bodyListHttpData.size() > 0 && + bodyListHttpDataRank < bodyListHttpData.size(); + } - /** - * Find the next Multipart Delimiter - * - * @param delimiter - * delimiter to find - * @param dispositionStatus - * the next status if the delimiter is a start - * @param closeDelimiterStatus - * the next status if the delimiter is a close delimiter - * @return the next InterfaceHttpData if any - * @throws ErrorDataDecoderException - */ - private InterfaceHttpData findMultipartDelimiter(String delimiter, - MultiPartStatus dispositionStatus, - MultiPartStatus closeDelimiterStatus) - throws ErrorDataDecoderException { - // --AaB03x or --AaB03x-- - int readerIndex = undecodedChunk.readerIndex(); - HttpPostBodyUtil.skipControlCharacters(undecodedChunk); - skipOneLine(); - String newline; - try { - newline = readLine(); - } catch (NotEnoughDataDecoderException e) { - undecodedChunk.readerIndex(readerIndex); - return null; - } - if (newline.equals(delimiter)) { - currentStatus = dispositionStatus; - return decodeMultipart(dispositionStatus); - } else if (newline.equals(delimiter + "--")) { - // CLOSEDELIMITER or MIXED CLOSEDELIMITER found - currentStatus = closeDelimiterStatus; - if (currentStatus == MultiPartStatus.HEADERDELIMITER) { - // MIXEDCLOSEDELIMITER - // end of the Mixed part - currentFieldAttributes = null; - return decodeMultipart(MultiPartStatus.HEADERDELIMITER); - } - return null; - } - undecodedChunk.readerIndex(readerIndex); - throw new ErrorDataDecoderException("No Multipart delimiter found"); - } + /** + * Returns the next available InterfaceHttpData or null if, at the time it is called, there is no more + * available InterfaceHttpData. A subsequent call to offer(httpChunk) could enable more data. + * + * @return the next available InterfaceHttpData or null if none + * @throws EndOfDataDecoderException No more data will be available + */ + public InterfaceHttpData next() throws EndOfDataDecoderException { + if (hasNext()) { + return bodyListHttpData.get(bodyListHttpDataRank++); + } + return null; + } - /** - * Find the next Disposition - * - * @return the next InterfaceHttpData if any - * @throws ErrorDataDecoderException - */ - private InterfaceHttpData findMultipartDisposition() - throws ErrorDataDecoderException { - int readerIndex = undecodedChunk.readerIndex(); - if (currentStatus == MultiPartStatus.DISPOSITION) { - currentFieldAttributes = new TreeMap( - CaseIgnoringComparator.INSTANCE); - } - // read many lines until empty line with newline found! Store all data - while (!skipOneLine()) { - HttpPostBodyUtil.skipControlCharacters(undecodedChunk); - String newline; - try { - newline = readLine(); - } catch (NotEnoughDataDecoderException e) { - undecodedChunk.readerIndex(readerIndex); - return null; - } - String[] contents = splitMultipartHeader(newline); - if (contents[0] - .equalsIgnoreCase(HttpPostBodyUtil.CONTENT_DISPOSITION)) { - boolean checkSecondArg = false; - if (currentStatus == MultiPartStatus.DISPOSITION) { - checkSecondArg = contents[1] - .equalsIgnoreCase(HttpPostBodyUtil.FORM_DATA); - } else { - checkSecondArg = contents[1] - .equalsIgnoreCase(HttpPostBodyUtil.ATTACHMENT) - || contents[1] - .equalsIgnoreCase(HttpPostBodyUtil.FILE); - } - if (checkSecondArg) { - // read next values and store them in the map as Attribute - for (int i = 2; i < contents.length; i++) { - String[] values = contents[i].split("="); - Attribute attribute; - try { - attribute = factory.createAttribute( - request, - values[0].trim(), - decodeAttribute(cleanString(values[1]), - charset)); - } catch (NullPointerException e) { - throw new ErrorDataDecoderException(e); - } catch (IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } - currentFieldAttributes.put(attribute.getName(), - attribute); - } - } - } else if (contents[0] - .equalsIgnoreCase(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING)) { - Attribute attribute; - try { - attribute = factory.createAttribute(request, - HttpHeaders.Names.CONTENT_TRANSFER_ENCODING, - cleanString(contents[1])); - } catch (NullPointerException e) { - throw new ErrorDataDecoderException(e); - } catch (IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } - currentFieldAttributes.put( - HttpHeaders.Names.CONTENT_TRANSFER_ENCODING, attribute); - } else if (contents[0] - .equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH)) { - Attribute attribute; - try { - attribute = factory.createAttribute(request, - HttpHeaders.Names.CONTENT_LENGTH, - cleanString(contents[1])); - } catch (NullPointerException e) { - throw new ErrorDataDecoderException(e); - } catch (IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } - currentFieldAttributes.put(HttpHeaders.Names.CONTENT_LENGTH, - attribute); - } else if (contents[0] - .equalsIgnoreCase(HttpHeaders.Names.CONTENT_TYPE)) { - // Take care of possible "multipart/mixed" - if (contents[1] - .equalsIgnoreCase(HttpPostBodyUtil.MULTIPART_MIXED)) { - if (currentStatus == MultiPartStatus.DISPOSITION) { - String[] values = contents[2].split("="); - multipartMixedBoundary = "--" + values[1]; - currentStatus = MultiPartStatus.MIXEDDELIMITER; - return decodeMultipart(MultiPartStatus.MIXEDDELIMITER); - } else { - throw new ErrorDataDecoderException( - "Mixed Multipart found in a previous Mixed Multipart"); - } - } else { - for (int i = 1; i < contents.length; i++) { - if (contents[i].toLowerCase().startsWith( - HttpHeaders.Values.CHARSET)) { - String[] values = contents[i].split("="); - Attribute attribute; - try { - attribute = factory.createAttribute(request, - HttpHeaders.Values.CHARSET, - cleanString(values[1])); - } catch (NullPointerException e) { - throw new ErrorDataDecoderException(e); - } catch (IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } - currentFieldAttributes.put( - HttpHeaders.Values.CHARSET, attribute); - } else { - Attribute attribute; - try { - attribute = factory.createAttribute( - request, - contents[0].trim(), - decodeAttribute( - cleanString(contents[i]), - charset)); - } catch (NullPointerException e) { - throw new ErrorDataDecoderException(e); - } catch (IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } - currentFieldAttributes.put(attribute.getName(), - attribute); - } - } - } - } else { - throw new ErrorDataDecoderException("Unknown Params: " - + newline); - } - } - // Is it a FileUpload - Attribute filenameAttribute = currentFieldAttributes - .get(HttpPostBodyUtil.FILENAME); - if (currentStatus == MultiPartStatus.DISPOSITION) { - if (filenameAttribute != null) { - // FileUpload - currentStatus = MultiPartStatus.FILEUPLOAD; - // do not change the buffer position - return decodeMultipart(MultiPartStatus.FILEUPLOAD); - } else { - // Field - currentStatus = MultiPartStatus.FIELD; - // do not change the buffer position - return decodeMultipart(MultiPartStatus.FIELD); - } - } else { - if (filenameAttribute != null) { - // FileUpload - currentStatus = MultiPartStatus.MIXEDFILEUPLOAD; - // do not change the buffer position - return decodeMultipart(MultiPartStatus.MIXEDFILEUPLOAD); - } else { - // Field is not supported in MIXED mode - throw new ErrorDataDecoderException("Filename not found"); - } - } - } + /** + * This method will parse as much as possible data and fill the list and map + * @throws ErrorDataDecoderException if there is a problem with the charset decoding or + * other errors + */ + private void parseBody() throws ErrorDataDecoderException { + if (currentStatus == MultiPartStatus.PREEPILOGUE || + currentStatus == MultiPartStatus.EPILOGUE) { + if (isLastChunk) { + currentStatus = MultiPartStatus.EPILOGUE; + } + return; + } + if (isMultipart) { + parseBodyMultipart(); + } else { + parseBodyAttributes(); + } + } - /** - * Get the FileUpload (new one or current one) - * - * @param delimiter - * the delimiter to use - * @return the InterfaceHttpData if any - * @throws ErrorDataDecoderException - */ - private InterfaceHttpData getFileUpload(String delimiter) - throws ErrorDataDecoderException { - // eventually restart from existing FileUpload - // Now get value according to Content-Type and Charset - Attribute encoding = currentFieldAttributes - .get(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING); - Charset localCharset = charset; - // Default - TransferEncodingMechanism mechanism = TransferEncodingMechanism.BIT7; - if (encoding != null) { - String code; - try { - code = encoding.getValue().toLowerCase(); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - if (code.equals(HttpPostBodyUtil.TransferEncodingMechanism.BIT7.value)) { - localCharset = HttpPostBodyUtil.US_ASCII; - } else if (code - .equals(HttpPostBodyUtil.TransferEncodingMechanism.BIT8.value)) { - localCharset = HttpPostBodyUtil.ISO_8859_1; - mechanism = TransferEncodingMechanism.BIT8; - } else if (code - .equals(HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value)) { - // no real charset, so let the default - mechanism = TransferEncodingMechanism.BINARY; - } else { - throw new ErrorDataDecoderException( - "TransferEncoding Unknown: " + code); - } - } - Attribute charsetAttribute = currentFieldAttributes - .get(HttpHeaders.Values.CHARSET); - if (charsetAttribute != null) { - try { - localCharset = Charset.forName(charsetAttribute.getValue()); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - } - if (currentFileUpload == null) { - Attribute filenameAttribute = currentFieldAttributes - .get(HttpPostBodyUtil.FILENAME); - Attribute nameAttribute = currentFieldAttributes - .get(HttpPostBodyUtil.NAME); - Attribute contentTypeAttribute = currentFieldAttributes - .get(HttpHeaders.Names.CONTENT_TYPE); - if (contentTypeAttribute == null) { - throw new ErrorDataDecoderException( - "Content-Type is absent but required"); - } - Attribute lengthAttribute = currentFieldAttributes - .get(HttpHeaders.Names.CONTENT_LENGTH); - long size; - try { - size = lengthAttribute != null ? Long.parseLong(lengthAttribute - .getValue()) : 0L; - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } catch (NumberFormatException e) { - size = 0; - } - try { - currentFileUpload = factory.createFileUpload(request, - nameAttribute.getValue(), filenameAttribute.getValue(), - contentTypeAttribute.getValue(), mechanism.value, - localCharset, size); - } catch (NullPointerException e) { - throw new ErrorDataDecoderException(e); - } catch (IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - } - // load data as much as possible - try { - readFileUploadByteMultipart(delimiter); - } catch (NotEnoughDataDecoderException e) { - // do not change the buffer position - // since some can be already saved into FileUpload - // So do not change the currentStatus - return null; - } - if (currentFileUpload.isCompleted()) { - // ready to load the next one - if (currentStatus == MultiPartStatus.FILEUPLOAD) { - currentStatus = MultiPartStatus.HEADERDELIMITER; - currentFieldAttributes = null; - } else { - currentStatus = MultiPartStatus.MIXEDDELIMITER; - cleanMixedAttributes(); - } - FileUpload fileUpload = currentFileUpload; - currentFileUpload = null; - return fileUpload; - } - // do not change the buffer position - // since some can be already saved into FileUpload - // So do not change the currentStatus - return null; - } + /** + * Utility function to add a new decoded data + * @param data + */ + private void addHttpData(InterfaceHttpData data) { + if (data == null) { + return; + } + List datas = bodyMapHttpData.get(data.getName()); + if (datas == null) { + datas = new ArrayList(1); + bodyMapHttpData.put(data.getName(), datas); + } + datas.add(data); + bodyListHttpData.add(data); + } - /** - * Clean all HttpDatas (on Disk) for the current request. - */ - public void cleanFiles() { - factory.cleanRequestHttpDatas(request); - } + /** + * This method fill the map and list with as much Attribute as possible from Body in + * not Multipart mode. + * + * @throws ErrorDataDecoderException if there is a problem with the charset decoding or + * other errors + */ + private void parseBodyAttributes() throws ErrorDataDecoderException { + int firstpos = undecodedChunk.readerIndex(); + int currentpos = firstpos; + int equalpos = firstpos; + int ampersandpos = firstpos; + if (currentStatus == MultiPartStatus.NOTSTARTED) { + currentStatus = MultiPartStatus.DISPOSITION; + } + boolean contRead = true; + try { + while (undecodedChunk.readable() && contRead) { + char read = (char) undecodedChunk.readUnsignedByte(); + currentpos++; + switch (currentStatus) { + case DISPOSITION:// search '=' + if (read == '=') { + currentStatus = MultiPartStatus.FIELD; + equalpos = currentpos - 1; + String key = decodeAttribute( + undecodedChunk.toString(firstpos, equalpos - firstpos, charset), + charset); + currentAttribute = factory.createAttribute(request, key); + firstpos = currentpos; + } else if (read == '&') { // special empty FIELD + currentStatus = MultiPartStatus.DISPOSITION; + ampersandpos = currentpos - 1; + String key = decodeAttribute(undecodedChunk.toString(firstpos, ampersandpos - firstpos, charset), charset); + currentAttribute = factory.createAttribute(request, key); + currentAttribute.setValue(""); // empty + addHttpData(currentAttribute); + currentAttribute = null; + firstpos = currentpos; + contRead = true; + } + break; + case FIELD:// search '&' or end of line + if (read == '&') { + currentStatus = MultiPartStatus.DISPOSITION; + ampersandpos = currentpos - 1; + setFinalBuffer(undecodedChunk.slice(firstpos, ampersandpos - firstpos)); + firstpos = currentpos; + contRead = true; + } else if (read == HttpCodecUtil.CR) { + if (undecodedChunk.readable()) { + read = (char) undecodedChunk.readUnsignedByte(); + currentpos++; + if (read == HttpCodecUtil.LF) { + currentStatus = MultiPartStatus.PREEPILOGUE; + ampersandpos = currentpos - 2; + setFinalBuffer( + undecodedChunk.slice(firstpos, ampersandpos - firstpos)); + firstpos = currentpos; + contRead = false; + } else { + // Error + contRead = false; + throw new ErrorDataDecoderException("Bad end of line"); + } + } else { + currentpos--; + } + } else if (read == HttpCodecUtil.LF) { + currentStatus = MultiPartStatus.PREEPILOGUE; + ampersandpos = currentpos - 1; + setFinalBuffer( + undecodedChunk.slice(firstpos, ampersandpos - firstpos)); + firstpos = currentpos; + contRead = false; + } + break; + default: + // just stop + contRead = false; + } + } + if (isLastChunk && currentAttribute != null) { + // special case + ampersandpos = currentpos; + if (ampersandpos > firstpos) { + setFinalBuffer( + undecodedChunk.slice(firstpos, ampersandpos - firstpos)); + } else if (! currentAttribute.isCompleted()) { + setFinalBuffer(ChannelBuffers.EMPTY_BUFFER); + } + firstpos = currentpos; + currentStatus = MultiPartStatus.EPILOGUE; + return; + } + if (contRead && currentAttribute != null) { + // reset index except if to continue in case of FIELD status + if (currentStatus == MultiPartStatus.FIELD) { + currentAttribute.addContent( + undecodedChunk.slice(firstpos, currentpos - firstpos), + false); + firstpos = currentpos; + } + undecodedChunk.readerIndex(firstpos); + } else { + // end of line so keep index + } + } catch (ErrorDataDecoderException e) { + // error while decoding + undecodedChunk.readerIndex(firstpos); + throw e; + } catch (IOException e) { + // error while decoding + undecodedChunk.readerIndex(firstpos); + throw new ErrorDataDecoderException(e); + } + } - /** - * Remove the given FileUpload from the list of FileUploads to clean - */ - public void removeHttpDataFromClean(InterfaceHttpData data) { - factory.removeHttpDataFromClean(request, data); - } + private void setFinalBuffer(ChannelBuffer buffer) throws ErrorDataDecoderException, IOException { + currentAttribute.addContent(buffer, true); + String value = decodeAttribute( + currentAttribute.getChannelBuffer().toString(charset), + charset); + currentAttribute.setValue(value); + addHttpData(currentAttribute); + currentAttribute = null; + } - /** - * Remove all Attributes that should be cleaned between two FileUpload in - * Mixed mode - */ - private void cleanMixedAttributes() { - currentFieldAttributes.remove(HttpHeaders.Values.CHARSET); - currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_LENGTH); - currentFieldAttributes - .remove(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING); - currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_TYPE); - currentFieldAttributes.remove(HttpPostBodyUtil.FILENAME); - } + /** + * Decode component + * @param s + * @param charset + * @return the decoded component + * @throws ErrorDataDecoderException + */ + private static String decodeAttribute(String s, Charset charset) + throws ErrorDataDecoderException { + if (s == null) { + return ""; + } + try { + return URLDecoder.decode(s, charset.name()); + } catch (UnsupportedEncodingException e) { + throw new ErrorDataDecoderException(charset.toString(), e); + } + } - /** - * Read one line up to the CRLF or LF - * - * @return the String from one line - * @throws NotEnoughDataDecoderException - * Need more chunks and reset the readerInder to the previous - * value - */ - private String readLine() throws NotEnoughDataDecoderException { - int readerIndex = undecodedChunk.readerIndex(); - try { - StringBuilder sb = new StringBuilder(64); - while (undecodedChunk.readable()) { - byte nextByte = undecodedChunk.readByte(); - if (nextByte == HttpCodecUtil.CR) { - nextByte = undecodedChunk.readByte(); - if (nextByte == HttpCodecUtil.LF) { - return sb.toString(); - } - } else if (nextByte == HttpCodecUtil.LF) { - return sb.toString(); - } else { - sb.append((char) nextByte); - } - } - } catch (IndexOutOfBoundsException e) { - undecodedChunk.readerIndex(readerIndex); - throw new NotEnoughDataDecoderException(e); - } - undecodedChunk.readerIndex(readerIndex); - throw new NotEnoughDataDecoderException(); - } + /** + * Parse the Body for multipart + * + * @throws ErrorDataDecoderException if there is a problem with the charset decoding or other errors + */ + private void parseBodyMultipart() throws ErrorDataDecoderException { + if (undecodedChunk == null || undecodedChunk.readableBytes() == 0) { + // nothing to decode + return; + } + InterfaceHttpData data = decodeMultipart(currentStatus); + while (data != null) { + addHttpData(data); + if (currentStatus == MultiPartStatus.PREEPILOGUE || + currentStatus == MultiPartStatus.EPILOGUE) { + break; + } + data = decodeMultipart(currentStatus); + } + } - /** - * Read a FileUpload data as Byte (Binary) and add the bytes directly to the - * FileUpload. If the delimiter is found, the FileUpload is completed. - * - * @param delimiter - * @throws NotEnoughDataDecoderException - * Need more chunks but do not reset the readerInder since some - * values will be already added to the FileOutput - * @throws ErrorDataDecoderException - * write IO error occurs with the FileUpload - */ - private void readFileUploadByteMultipart(String delimiter) - throws NotEnoughDataDecoderException, ErrorDataDecoderException { - int readerIndex = undecodedChunk.readerIndex(); - // found the decoder limit - boolean newLine = true; - int index = 0; - int lastPosition = undecodedChunk.readerIndex(); - boolean found = false; - while (undecodedChunk.readable()) { - byte nextByte = undecodedChunk.readByte(); - if (newLine) { - // Check the delimiter - if (nextByte == delimiter.codePointAt(index)) { - index++; - if (delimiter.length() == index) { - found = true; - break; - } - continue; - } else { - newLine = false; - index = 0; - // continue until end of line - if (nextByte == HttpCodecUtil.CR) { - if (undecodedChunk.readable()) { - nextByte = undecodedChunk.readByte(); - if (nextByte == HttpCodecUtil.LF) { - newLine = true; - index = 0; - lastPosition = undecodedChunk.readerIndex() - 2; - } - } - } else if (nextByte == HttpCodecUtil.LF) { - newLine = true; - index = 0; - lastPosition = undecodedChunk.readerIndex() - 1; - } else { - // save last valid position - lastPosition = undecodedChunk.readerIndex(); - } - } - } else { - // continue until end of line - if (nextByte == HttpCodecUtil.CR) { - if (undecodedChunk.readable()) { - nextByte = undecodedChunk.readByte(); - if (nextByte == HttpCodecUtil.LF) { - newLine = true; - index = 0; - lastPosition = undecodedChunk.readerIndex() - 2; - } - } - } else if (nextByte == HttpCodecUtil.LF) { - newLine = true; - index = 0; - lastPosition = undecodedChunk.readerIndex() - 1; - } else { - // save last valid position - lastPosition = undecodedChunk.readerIndex(); - } - } - } - ChannelBuffer buffer = undecodedChunk.slice(readerIndex, lastPosition - - readerIndex); - if (found) { - // found so lastPosition is correct and final - try { - currentFileUpload.addContent(buffer, true); - // just before the CRLF and delimiter - undecodedChunk.readerIndex(lastPosition); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - } else { - // possibly the delimiter is partially found but still the last - // position is OK - try { - currentFileUpload.addContent(buffer, false); - // last valid char (not CR, not LF, not beginning of delimiter) - undecodedChunk.readerIndex(lastPosition); - throw new NotEnoughDataDecoderException(); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - } - } + /** + * Decode a multipart request by pieces
+ *
+ * NOTSTARTED PREAMBLE (
+ * (HEADERDELIMITER DISPOSITION (FIELD | FILEUPLOAD))*
+ * (HEADERDELIMITER DISPOSITION MIXEDPREAMBLE
+ * (MIXEDDELIMITER MIXEDDISPOSITION MIXEDFILEUPLOAD)+
+ * MIXEDCLOSEDELIMITER)*
+ * CLOSEDELIMITER)+ EPILOGUE
+ * + * Inspired from HttpMessageDecoder + * + * @param state + * @return the next decoded InterfaceHttpData or null if none until now. + * @throws ErrorDataDecoderException if an error occurs + */ + private InterfaceHttpData decodeMultipart(MultiPartStatus state) + throws ErrorDataDecoderException { + switch (state) { + case NOTSTARTED: + throw new ErrorDataDecoderException( + "Should not be called with the current status"); + case PREAMBLE: + // Content-type: multipart/form-data, boundary=AaB03x + throw new ErrorDataDecoderException( + "Should not be called with the current status"); + case HEADERDELIMITER: { + // --AaB03x or --AaB03x-- + return findMultipartDelimiter(multipartDataBoundary, + MultiPartStatus.DISPOSITION, MultiPartStatus.PREEPILOGUE); + } + case DISPOSITION: { + // content-disposition: form-data; name="field1" + // content-disposition: form-data; name="pics"; filename="file1.txt" + // and other immediate values like + // Content-type: image/gif + // Content-Type: text/plain + // Content-Type: text/plain; charset=ISO-8859-1 + // Content-Transfer-Encoding: binary + // The following line implies a change of mode (mixed mode) + // Content-type: multipart/mixed, boundary=BbC04y + return findMultipartDisposition(); + } + case FIELD: { + // Now get value according to Content-Type and Charset + Charset localCharset = null; + Attribute charsetAttribute = currentFieldAttributes + .get(HttpHeaders.Values.CHARSET); + if (charsetAttribute != null) { + try { + localCharset = Charset.forName(charsetAttribute.getValue()); + } catch (IOException e) { + throw new ErrorDataDecoderException(e); + } + } + Attribute nameAttribute = currentFieldAttributes + .get(HttpPostBodyUtil.NAME); + if (currentAttribute == null) { + try { + currentAttribute = factory.createAttribute(request, nameAttribute + .getValue()); + } catch (NullPointerException e) { + throw new ErrorDataDecoderException(e); + } catch (IllegalArgumentException e) { + throw new ErrorDataDecoderException(e); + } catch (IOException e) { + throw new ErrorDataDecoderException(e); + } + if (localCharset != null) { + currentAttribute.setCharset(localCharset); + } + } + // load data + try { + loadFieldMultipart(multipartDataBoundary); + } catch (NotEnoughDataDecoderException e) { + return null; + } + Attribute finalAttribute = currentAttribute; + currentAttribute = null; + currentFieldAttributes = null; + // ready to load the next one + currentStatus = MultiPartStatus.HEADERDELIMITER; + return finalAttribute; + } + case FILEUPLOAD: { + // eventually restart from existing FileUpload + return getFileUpload(multipartDataBoundary); + } + case MIXEDDELIMITER: { + // --AaB03x or --AaB03x-- + // Note that currentFieldAttributes exists + return findMultipartDelimiter(multipartMixedBoundary, + MultiPartStatus.MIXEDDISPOSITION, + MultiPartStatus.HEADERDELIMITER); + } + case MIXEDDISPOSITION: { + return findMultipartDisposition(); + } + case MIXEDFILEUPLOAD: { + // eventually restart from existing FileUpload + return getFileUpload(multipartMixedBoundary); + } + case PREEPILOGUE: + return null; + case EPILOGUE: + return null; + default: + throw new ErrorDataDecoderException("Shouldn't reach here."); + } + } - /** - * Load the field value from a Multipart request - * - * @throws NotEnoughDataDecoderException - * Need more chunks - * @throws ErrorDataDecoderException - */ - private void loadFieldMultipart(String delimiter) - throws NotEnoughDataDecoderException, ErrorDataDecoderException { - int readerIndex = undecodedChunk.readerIndex(); - try { - // found the decoder limit - boolean newLine = true; - int index = 0; - int lastPosition = undecodedChunk.readerIndex(); - boolean found = false; - while (undecodedChunk.readable()) { - byte nextByte = undecodedChunk.readByte(); - if (newLine) { - // Check the delimiter - if (nextByte == delimiter.codePointAt(index)) { - index++; - if (delimiter.length() == index) { - found = true; - break; - } - continue; - } else { - newLine = false; - index = 0; - // continue until end of line - if (nextByte == HttpCodecUtil.CR) { - if (undecodedChunk.readable()) { - nextByte = undecodedChunk.readByte(); - if (nextByte == HttpCodecUtil.LF) { - newLine = true; - index = 0; - lastPosition = undecodedChunk.readerIndex() - 2; - } - } - } else if (nextByte == HttpCodecUtil.LF) { - newLine = true; - index = 0; - lastPosition = undecodedChunk.readerIndex() - 1; - } else { - lastPosition = undecodedChunk.readerIndex(); - } - } - } else { - // continue until end of line - if (nextByte == HttpCodecUtil.CR) { - if (undecodedChunk.readable()) { - nextByte = undecodedChunk.readByte(); - if (nextByte == HttpCodecUtil.LF) { - newLine = true; - index = 0; - lastPosition = undecodedChunk.readerIndex() - 2; - } - } - } else if (nextByte == HttpCodecUtil.LF) { - newLine = true; - index = 0; - lastPosition = undecodedChunk.readerIndex() - 1; - } else { - lastPosition = undecodedChunk.readerIndex(); - } - } - } - if (found) { - // found so lastPosition is correct - // but position is just after the delimiter (either close - // delimiter or simple one) - // so go back of delimiter size - try { - currentAttribute.addContent( - undecodedChunk.slice(readerIndex, lastPosition - - readerIndex), true); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - undecodedChunk.readerIndex(lastPosition); - } else { - try { - currentAttribute.addContent( - undecodedChunk.slice(readerIndex, lastPosition - - readerIndex), false); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - undecodedChunk.readerIndex(lastPosition); - throw new NotEnoughDataDecoderException(); - } - } catch (IndexOutOfBoundsException e) { - undecodedChunk.readerIndex(readerIndex); - throw new NotEnoughDataDecoderException(e); - } - } + /** + * Find the next Multipart Delimiter + * @param delimiter delimiter to find + * @param dispositionStatus the next status if the delimiter is a start + * @param closeDelimiterStatus the next status if the delimiter is a close delimiter + * @return the next InterfaceHttpData if any + * @throws ErrorDataDecoderException + */ + private InterfaceHttpData findMultipartDelimiter(String delimiter, + MultiPartStatus dispositionStatus, + MultiPartStatus closeDelimiterStatus) + throws ErrorDataDecoderException { + // --AaB03x or --AaB03x-- + int readerIndex = undecodedChunk.readerIndex(); + HttpPostBodyUtil.skipControlCharacters(undecodedChunk); + skipOneLine(); + String newline; + try { + newline = readLine(); + } catch (NotEnoughDataDecoderException e) { + undecodedChunk.readerIndex(readerIndex); + return null; + } + if (newline.equals(delimiter)) { + currentStatus = dispositionStatus; + return decodeMultipart(dispositionStatus); + } else if (newline.equals(delimiter + "--")) { + // CLOSEDELIMITER or MIXED CLOSEDELIMITER found + currentStatus = closeDelimiterStatus; + if (currentStatus == MultiPartStatus.HEADERDELIMITER) { + // MIXEDCLOSEDELIMITER + // end of the Mixed part + currentFieldAttributes = null; + return decodeMultipart(MultiPartStatus.HEADERDELIMITER); + } + return null; + } + undecodedChunk.readerIndex(readerIndex); + throw new ErrorDataDecoderException("No Multipart delimiter found"); + } - /** - * Clean the String from any unallowed character - * - * @return the cleaned String - */ - private String cleanString(String field) { - StringBuilder sb = new StringBuilder(field.length()); - int i = 0; - for (i = 0; i < field.length(); i++) { - char nextChar = field.charAt(i); - if (nextChar == HttpCodecUtil.COLON) { - sb.append(HttpCodecUtil.SP); - } else if (nextChar == HttpCodecUtil.COMMA) { - sb.append(HttpCodecUtil.SP); - } else if (nextChar == HttpCodecUtil.EQUALS) { - sb.append(HttpCodecUtil.SP); - } else if (nextChar == HttpCodecUtil.SEMICOLON) { - sb.append(HttpCodecUtil.SP); - } else if (nextChar == HttpCodecUtil.HT) { - sb.append(HttpCodecUtil.SP); - } else if (nextChar == HttpCodecUtil.DOUBLE_QUOTE) { - // nothing added, just removes it - } else { - sb.append(nextChar); - } - } - return sb.toString().trim(); - } + /** + * Find the next Disposition + * @return the next InterfaceHttpData if any + * @throws ErrorDataDecoderException + */ + private InterfaceHttpData findMultipartDisposition() + throws ErrorDataDecoderException { + int readerIndex = undecodedChunk.readerIndex(); + if (currentStatus == MultiPartStatus.DISPOSITION) { + currentFieldAttributes = new TreeMap( + CaseIgnoringComparator.INSTANCE); + } + // read many lines until empty line with newline found! Store all data + while (!skipOneLine()) { + HttpPostBodyUtil.skipControlCharacters(undecodedChunk); + String newline; + try { + newline = readLine(); + } catch (NotEnoughDataDecoderException e) { + undecodedChunk.readerIndex(readerIndex); + return null; + } + String[] contents = splitMultipartHeader(newline); + if (contents[0].equalsIgnoreCase(HttpPostBodyUtil.CONTENT_DISPOSITION)) { + boolean checkSecondArg = false; + if (currentStatus == MultiPartStatus.DISPOSITION) { + checkSecondArg = contents[1] + .equalsIgnoreCase(HttpPostBodyUtil.FORM_DATA); + } else { + checkSecondArg = contents[1] + .equalsIgnoreCase(HttpPostBodyUtil.ATTACHMENT) || + contents[1] + .equalsIgnoreCase(HttpPostBodyUtil.FILE); + } + if (checkSecondArg) { + // read next values and store them in the map as Attribute + for (int i = 2; i < contents.length; i ++) { + String[] values = contents[i].split("="); + Attribute attribute; + try { + attribute = factory.createAttribute(request, values[0].trim(), + decodeAttribute(cleanString(values[1]), charset)); + } catch (NullPointerException e) { + throw new ErrorDataDecoderException(e); + } catch (IllegalArgumentException e) { + throw new ErrorDataDecoderException(e); + } + currentFieldAttributes.put(attribute.getName(), + attribute); + } + } + } else if (contents[0] + .equalsIgnoreCase(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING)) { + Attribute attribute; + try { + attribute = factory.createAttribute(request, + HttpHeaders.Names.CONTENT_TRANSFER_ENCODING, + cleanString(contents[1])); + } catch (NullPointerException e) { + throw new ErrorDataDecoderException(e); + } catch (IllegalArgumentException e) { + throw new ErrorDataDecoderException(e); + } + currentFieldAttributes.put( + HttpHeaders.Names.CONTENT_TRANSFER_ENCODING, attribute); + } else if (contents[0] + .equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH)) { + Attribute attribute; + try { + attribute = factory.createAttribute(request, + HttpHeaders.Names.CONTENT_LENGTH, + cleanString(contents[1])); + } catch (NullPointerException e) { + throw new ErrorDataDecoderException(e); + } catch (IllegalArgumentException e) { + throw new ErrorDataDecoderException(e); + } + currentFieldAttributes.put(HttpHeaders.Names.CONTENT_LENGTH, + attribute); + } else if (contents[0].equalsIgnoreCase(HttpHeaders.Names.CONTENT_TYPE)) { + // Take care of possible "multipart/mixed" + if (contents[1].equalsIgnoreCase(HttpPostBodyUtil.MULTIPART_MIXED)) { + if (currentStatus == MultiPartStatus.DISPOSITION) { + String[] values = contents[2].split("="); + multipartMixedBoundary = "--" + values[1]; + currentStatus = MultiPartStatus.MIXEDDELIMITER; + return decodeMultipart(MultiPartStatus.MIXEDDELIMITER); + } else { + throw new ErrorDataDecoderException( + "Mixed Multipart found in a previous Mixed Multipart"); + } + } else { + for (int i = 1; i < contents.length; i ++) { + if (contents[i].toLowerCase().startsWith( + HttpHeaders.Values.CHARSET)) { + String[] values = contents[i].split("="); + Attribute attribute; + try { + attribute = factory.createAttribute(request, + HttpHeaders.Values.CHARSET, + cleanString(values[1])); + } catch (NullPointerException e) { + throw new ErrorDataDecoderException(e); + } catch (IllegalArgumentException e) { + throw new ErrorDataDecoderException(e); + } + currentFieldAttributes.put(HttpHeaders.Values.CHARSET, + attribute); + } else { + Attribute attribute; + try { + attribute = factory.createAttribute(request, + contents[0].trim(), + decodeAttribute(cleanString(contents[i]), charset)); + } catch (NullPointerException e) { + throw new ErrorDataDecoderException(e); + } catch (IllegalArgumentException e) { + throw new ErrorDataDecoderException(e); + } + currentFieldAttributes.put(attribute.getName(), + attribute); + } + } + } + } else { + throw new ErrorDataDecoderException("Unknown Params: " + + newline); + } + } + // Is it a FileUpload + Attribute filenameAttribute = currentFieldAttributes + .get(HttpPostBodyUtil.FILENAME); + if (currentStatus == MultiPartStatus.DISPOSITION) { + if (filenameAttribute != null) { + // FileUpload + currentStatus = MultiPartStatus.FILEUPLOAD; + // do not change the buffer position + return decodeMultipart(MultiPartStatus.FILEUPLOAD); + } else { + // Field + currentStatus = MultiPartStatus.FIELD; + // do not change the buffer position + return decodeMultipart(MultiPartStatus.FIELD); + } + } else { + if (filenameAttribute != null) { + // FileUpload + currentStatus = MultiPartStatus.MIXEDFILEUPLOAD; + // do not change the buffer position + return decodeMultipart(MultiPartStatus.MIXEDFILEUPLOAD); + } else { + // Field is not supported in MIXED mode + throw new ErrorDataDecoderException("Filename not found"); + } + } + } - /** - * Skip one empty line - * - * @return True if one empty line was skipped - */ - private boolean skipOneLine() { - if (!undecodedChunk.readable()) { - return false; - } - byte nextByte = undecodedChunk.readByte(); - if (nextByte == HttpCodecUtil.CR) { - if (!undecodedChunk.readable()) { - undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1); - return false; - } - nextByte = undecodedChunk.readByte(); - if (nextByte == HttpCodecUtil.LF) { - return true; - } - undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 2); - return false; - } else if (nextByte == HttpCodecUtil.LF) { - return true; - } - undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1); - return false; - } + /** + * Get the FileUpload (new one or current one) + * @param delimiter the delimiter to use + * @return the InterfaceHttpData if any + * @throws ErrorDataDecoderException + */ + private InterfaceHttpData getFileUpload(String delimiter) + throws ErrorDataDecoderException { + // eventually restart from existing FileUpload + // Now get value according to Content-Type and Charset + Attribute encoding = currentFieldAttributes + .get(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING); + Charset localCharset = charset; + // Default + TransferEncodingMechanism mechanism = TransferEncodingMechanism.BIT7; + if (encoding != null) { + String code; + try { + code = encoding.getValue().toLowerCase(); + } catch (IOException e) { + throw new ErrorDataDecoderException(e); + } + if (code.equals(HttpPostBodyUtil.TransferEncodingMechanism.BIT7.value)) { + localCharset = HttpPostBodyUtil.US_ASCII; + } else if (code.equals(HttpPostBodyUtil.TransferEncodingMechanism.BIT8.value)) { + localCharset = HttpPostBodyUtil.ISO_8859_1; + mechanism = TransferEncodingMechanism.BIT8; + } else if (code + .equals(HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value)) { + // no real charset, so let the default + mechanism = TransferEncodingMechanism.BINARY; + } else { + throw new ErrorDataDecoderException( + "TransferEncoding Unknown: " + code); + } + } + Attribute charsetAttribute = currentFieldAttributes + .get(HttpHeaders.Values.CHARSET); + if (charsetAttribute != null) { + try { + localCharset = Charset.forName(charsetAttribute.getValue()); + } catch (IOException e) { + throw new ErrorDataDecoderException(e); + } + } + if (currentFileUpload == null) { + Attribute filenameAttribute = currentFieldAttributes + .get(HttpPostBodyUtil.FILENAME); + Attribute nameAttribute = currentFieldAttributes + .get(HttpPostBodyUtil.NAME); + Attribute contentTypeAttribute = currentFieldAttributes + .get(HttpHeaders.Names.CONTENT_TYPE); + if (contentTypeAttribute == null) { + throw new ErrorDataDecoderException( + "Content-Type is absent but required"); + } + Attribute lengthAttribute = currentFieldAttributes + .get(HttpHeaders.Names.CONTENT_LENGTH); + long size; + try { + size = lengthAttribute != null? Long.parseLong(lengthAttribute + .getValue()) : 0L; + } catch (IOException e) { + throw new ErrorDataDecoderException(e); + } catch (NumberFormatException e) { + size = 0; + } + try { + currentFileUpload = factory.createFileUpload( + request, + nameAttribute.getValue(), filenameAttribute.getValue(), + contentTypeAttribute.getValue(), mechanism.value, + localCharset, size); + } catch (NullPointerException e) { + throw new ErrorDataDecoderException(e); + } catch (IllegalArgumentException e) { + throw new ErrorDataDecoderException(e); + } catch (IOException e) { + throw new ErrorDataDecoderException(e); + } + } + // load data as much as possible + try { + readFileUploadByteMultipart(delimiter); + } catch (NotEnoughDataDecoderException e) { + // do not change the buffer position + // since some can be already saved into FileUpload + // So do not change the currentStatus + return null; + } + if (currentFileUpload.isCompleted()) { + // ready to load the next one + if (currentStatus == MultiPartStatus.FILEUPLOAD) { + currentStatus = MultiPartStatus.HEADERDELIMITER; + currentFieldAttributes = null; + } else { + currentStatus = MultiPartStatus.MIXEDDELIMITER; + cleanMixedAttributes(); + } + FileUpload fileUpload = currentFileUpload; + currentFileUpload = null; + return fileUpload; + } + // do not change the buffer position + // since some can be already saved into FileUpload + // So do not change the currentStatus + return null; + } - /** - * Split the very first line (Content-Type value) in 2 Strings - * - * @param sb - * @return the array of 2 Strings - */ - private String[] splitHeaderContentType(String sb) { - int size = sb.length(); - int aStart; - int aEnd; - int bStart; - int bEnd; - aStart = HttpPostBodyUtil.findNonWhitespace(sb, 0); - aEnd = HttpPostBodyUtil.findWhitespace(sb, aStart); - if (aEnd >= size) { - return new String[] { sb, "" }; - } - if (sb.charAt(aEnd) == ';') { - aEnd--; - } - bStart = HttpPostBodyUtil.findNonWhitespace(sb, aEnd); - bEnd = HttpPostBodyUtil.findEndOfString(sb); - return new String[] { sb.substring(aStart, aEnd), - sb.substring(bStart, bEnd) }; - } - - /** - * Split one header in Multipart - * - * @param sb - * @return an array of String where rank 0 is the name of the header, - * follows by several values that were separated by ';' or ',' - */ - private String[] splitMultipartHeader(String sb) { - ArrayList headers = new ArrayList(1); - int nameStart; - int nameEnd; - int colonEnd; - int valueStart; - int valueEnd; - nameStart = HttpPostBodyUtil.findNonWhitespace(sb, 0); - for (nameEnd = nameStart; nameEnd < sb.length(); nameEnd++) { - char ch = sb.charAt(nameEnd); - if (ch == ':' || Character.isWhitespace(ch)) { - break; - } - } - for (colonEnd = nameEnd; colonEnd < sb.length(); colonEnd++) { - if (sb.charAt(colonEnd) == ':') { - colonEnd++; - break; - } - } - valueStart = HttpPostBodyUtil.findNonWhitespace(sb, colonEnd); - valueEnd = HttpPostBodyUtil.findEndOfString(sb); - headers.add(sb.substring(nameStart, nameEnd)); - String svalue = sb.substring(valueStart, valueEnd); - String[] values = null; - if (svalue.indexOf(";") >= 0) { - values = svalue.split(";"); - } else { - values = svalue.split(","); - } - for (String value : values) { - headers.add(value.trim()); - } - String[] array = new String[headers.size()]; - for (int i = 0; i < headers.size(); i++) { - array[i] = headers.get(i); - } - return array; - } - - /** - * Exception when try reading data from request in chunked format, and not - * enough data are available (need more chunks) - */ - public static class NotEnoughDataDecoderException extends Exception { - /** + /** + * Clean all HttpDatas (on Disk) for the current request. */ - private static final long serialVersionUID = -7846841864603865638L; + public void cleanFiles() { + factory.cleanRequestHttpDatas(request); + } - /** + /** + * Remove the given FileUpload from the list of FileUploads to clean + */ + public void removeHttpDataFromClean(InterfaceHttpData data) { + factory.removeHttpDataFromClean(request, data); + } + + /** + * Remove all Attributes that should be cleaned between two FileUpload in Mixed mode + */ + private void cleanMixedAttributes() { + currentFieldAttributes.remove(HttpHeaders.Values.CHARSET); + currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_LENGTH); + currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING); + currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_TYPE); + currentFieldAttributes.remove(HttpPostBodyUtil.FILENAME); + } + + /** + * Read one line up to the CRLF or LF + * @return the String from one line + * @throws NotEnoughDataDecoderException Need more chunks and + * reset the readerInder to the previous value + */ + private String readLine() throws NotEnoughDataDecoderException { + int readerIndex = undecodedChunk.readerIndex(); + try { + StringBuilder sb = new StringBuilder(64); + while (undecodedChunk.readable()) { + byte nextByte = undecodedChunk.readByte(); + if (nextByte == HttpCodecUtil.CR) { + nextByte = undecodedChunk.readByte(); + if (nextByte == HttpCodecUtil.LF) { + return sb.toString(); + } + } else if (nextByte == HttpCodecUtil.LF) { + return sb.toString(); + } else { + sb.append((char) nextByte); + } + } + } catch (IndexOutOfBoundsException e) { + undecodedChunk.readerIndex(readerIndex); + throw new NotEnoughDataDecoderException(e); + } + undecodedChunk.readerIndex(readerIndex); + throw new NotEnoughDataDecoderException(); + } + + /** + * Read a FileUpload data as Byte (Binary) and add the bytes directly to the + * FileUpload. If the delimiter is found, the FileUpload is completed. + * @param delimiter + * @throws NotEnoughDataDecoderException Need more chunks but + * do not reset the readerInder since some values will be already added to the FileOutput + * @throws ErrorDataDecoderException write IO error occurs with the FileUpload + */ + private void readFileUploadByteMultipart(String delimiter) + throws NotEnoughDataDecoderException, ErrorDataDecoderException { + int readerIndex = undecodedChunk.readerIndex(); + // found the decoder limit + boolean newLine = true; + int index = 0; + int lastPosition = undecodedChunk.readerIndex(); + boolean found = false; + while (undecodedChunk.readable()) { + byte nextByte = undecodedChunk.readByte(); + if (newLine) { + // Check the delimiter + if (nextByte == delimiter.codePointAt(index)) { + index ++; + if (delimiter.length() == index) { + found = true; + break; + } + continue; + } else { + newLine = false; + index = 0; + // continue until end of line + if (nextByte == HttpCodecUtil.CR) { + if (undecodedChunk.readable()) { + nextByte = undecodedChunk.readByte(); + if (nextByte == HttpCodecUtil.LF) { + newLine = true; + index = 0; + lastPosition = undecodedChunk.readerIndex() - 2; + } + } + } else if (nextByte == HttpCodecUtil.LF) { + newLine = true; + index = 0; + lastPosition = undecodedChunk.readerIndex() - 1; + } else { + // save last valid position + lastPosition = undecodedChunk.readerIndex(); + } + } + } else { + // continue until end of line + if (nextByte == HttpCodecUtil.CR) { + if (undecodedChunk.readable()) { + nextByte = undecodedChunk.readByte(); + if (nextByte == HttpCodecUtil.LF) { + newLine = true; + index = 0; + lastPosition = undecodedChunk.readerIndex() - 2; + } + } + } else if (nextByte == HttpCodecUtil.LF) { + newLine = true; + index = 0; + lastPosition = undecodedChunk.readerIndex() - 1; + } else { + // save last valid position + lastPosition = undecodedChunk.readerIndex(); + } + } + } + ChannelBuffer buffer = undecodedChunk.slice(readerIndex, lastPosition - + readerIndex); + if (found) { + // found so lastPosition is correct and final + try { + currentFileUpload.addContent(buffer, true); + // just before the CRLF and delimiter + undecodedChunk.readerIndex(lastPosition); + } catch (IOException e) { + throw new ErrorDataDecoderException(e); + } + } else { + // possibly the delimiter is partially found but still the last position is OK + try { + currentFileUpload.addContent(buffer, false); + // last valid char (not CR, not LF, not beginning of delimiter) + undecodedChunk.readerIndex(lastPosition); + throw new NotEnoughDataDecoderException(); + } catch (IOException e) { + throw new ErrorDataDecoderException(e); + } + } + } + + /** + * Load the field value from a Multipart request + * @throws NotEnoughDataDecoderException Need more chunks + * @throws ErrorDataDecoderException + */ + private void loadFieldMultipart(String delimiter) + throws NotEnoughDataDecoderException, ErrorDataDecoderException { + int readerIndex = undecodedChunk.readerIndex(); + try { + // found the decoder limit + boolean newLine = true; + int index = 0; + int lastPosition = undecodedChunk.readerIndex(); + boolean found = false; + while (undecodedChunk.readable()) { + byte nextByte = undecodedChunk.readByte(); + if (newLine) { + // Check the delimiter + if (nextByte == delimiter.codePointAt(index)) { + index ++; + if (delimiter.length() == index) { + found = true; + break; + } + continue; + } else { + newLine = false; + index = 0; + // continue until end of line + if (nextByte == HttpCodecUtil.CR) { + if (undecodedChunk.readable()) { + nextByte = undecodedChunk.readByte(); + if (nextByte == HttpCodecUtil.LF) { + newLine = true; + index = 0; + lastPosition = undecodedChunk.readerIndex() - 2; + } + } + } else if (nextByte == HttpCodecUtil.LF) { + newLine = true; + index = 0; + lastPosition = undecodedChunk.readerIndex() - 1; + } else { + lastPosition = undecodedChunk.readerIndex(); + } + } + } else { + // continue until end of line + if (nextByte == HttpCodecUtil.CR) { + if (undecodedChunk.readable()) { + nextByte = undecodedChunk.readByte(); + if (nextByte == HttpCodecUtil.LF) { + newLine = true; + index = 0; + lastPosition = undecodedChunk.readerIndex() - 2; + } + } + } else if (nextByte == HttpCodecUtil.LF) { + newLine = true; + index = 0; + lastPosition = undecodedChunk.readerIndex() - 1; + } else { + lastPosition = undecodedChunk.readerIndex(); + } + } + } + if (found) { + // found so lastPosition is correct + // but position is just after the delimiter (either close delimiter or simple one) + // so go back of delimiter size + try { + currentAttribute.addContent( + undecodedChunk.slice(readerIndex, lastPosition - readerIndex), + true); + } catch (IOException e) { + throw new ErrorDataDecoderException(e); + } + undecodedChunk.readerIndex(lastPosition); + } else { + try { + currentAttribute.addContent( + undecodedChunk.slice(readerIndex, lastPosition - readerIndex), + false); + } catch (IOException e) { + throw new ErrorDataDecoderException(e); + } + undecodedChunk.readerIndex(lastPosition); + throw new NotEnoughDataDecoderException(); + } + } catch (IndexOutOfBoundsException e) { + undecodedChunk.readerIndex(readerIndex); + throw new NotEnoughDataDecoderException(e); + } + } + + /** + * Clean the String from any unallowed character + * @return the cleaned String + */ + private String cleanString(String field) { + StringBuilder sb = new StringBuilder(field.length()); + int i = 0; + for (i = 0; i < field.length(); i ++) { + char nextChar = field.charAt(i); + if (nextChar == HttpCodecUtil.COLON) { + sb.append(HttpCodecUtil.SP); + } else if (nextChar == HttpCodecUtil.COMMA) { + sb.append(HttpCodecUtil.SP); + } else if (nextChar == HttpCodecUtil.EQUALS) { + sb.append(HttpCodecUtil.SP); + } else if (nextChar == HttpCodecUtil.SEMICOLON) { + sb.append(HttpCodecUtil.SP); + } else if (nextChar == HttpCodecUtil.HT) { + sb.append(HttpCodecUtil.SP); + } else if (nextChar == HttpCodecUtil.DOUBLE_QUOTE) { + // nothing added, just removes it + } else { + sb.append(nextChar); + } + } + return sb.toString().trim(); + } + + /** + * Skip one empty line + * @return True if one empty line was skipped + */ + private boolean skipOneLine() { + if (!undecodedChunk.readable()) { + return false; + } + byte nextByte = undecodedChunk.readByte(); + if (nextByte == HttpCodecUtil.CR) { + if (!undecodedChunk.readable()) { + undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1); + return false; + } + nextByte = undecodedChunk.readByte(); + if (nextByte == HttpCodecUtil.LF) { + return true; + } + undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 2); + return false; + } else if (nextByte == HttpCodecUtil.LF) { + return true; + } + undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1); + return false; + } + + /** + * Split the very first line (Content-Type value) in 2 Strings + * @param sb + * @return the array of 2 Strings + */ + private String[] splitHeaderContentType(String sb) { + int size = sb.length(); + int aStart; + int aEnd; + int bStart; + int bEnd; + aStart = HttpPostBodyUtil.findNonWhitespace(sb, 0); + aEnd = HttpPostBodyUtil.findWhitespace(sb, aStart); + if (aEnd >= size) { + return new String[] { sb, "" }; + } + if (sb.charAt(aEnd) == ';') { + aEnd --; + } + bStart = HttpPostBodyUtil.findNonWhitespace(sb, aEnd); + bEnd = HttpPostBodyUtil.findEndOfString(sb); + return new String[] { sb.substring(aStart, aEnd), + sb.substring(bStart, bEnd) }; + } + + /** + * Split one header in Multipart + * @param sb + * @return an array of String where rank 0 is the name of the header, follows by several + * values that were separated by ';' or ',' + */ + private String[] splitMultipartHeader(String sb) { + ArrayList headers = new ArrayList(1); + int nameStart; + int nameEnd; + int colonEnd; + int valueStart; + int valueEnd; + nameStart = HttpPostBodyUtil.findNonWhitespace(sb, 0); + for (nameEnd = nameStart; nameEnd < sb.length(); nameEnd ++) { + char ch = sb.charAt(nameEnd); + if (ch == ':' || Character.isWhitespace(ch)) { + break; + } + } + for (colonEnd = nameEnd; colonEnd < sb.length(); colonEnd ++) { + if (sb.charAt(colonEnd) == ':') { + colonEnd ++; + break; + } + } + valueStart = HttpPostBodyUtil.findNonWhitespace(sb, colonEnd); + valueEnd = HttpPostBodyUtil.findEndOfString(sb); + headers.add(sb.substring(nameStart, nameEnd)); + String svalue = sb.substring(valueStart, valueEnd); + String[] values = null; + if (svalue.indexOf(";") >= 0) { + values = svalue.split(";"); + } else { + values = svalue.split(","); + } + for (String value: values) { + headers.add(value.trim()); + } + String[] array = new String[headers.size()]; + for (int i = 0; i < headers.size(); i ++) { + array[i] = headers.get(i); + } + return array; + } + + /** + * Exception when try reading data from request in chunked format, and not enough + * data are available (need more chunks) + */ + public static class NotEnoughDataDecoderException extends Exception { + /** */ - public NotEnoughDataDecoderException() { - } + private static final long serialVersionUID = -7846841864603865638L; - /** - * @param arg0 - */ - public NotEnoughDataDecoderException(String arg0) { - super(arg0); - } - - /** - * @param arg0 - */ - public NotEnoughDataDecoderException(Throwable arg0) { - super(arg0); - } - - /** - * @param arg0 - * @param arg1 - */ - public NotEnoughDataDecoderException(String arg0, Throwable arg1) { - super(arg0, arg1); - } - } - - /** - * Exception when the body is fully decoded, even if there is still data - */ - public static class EndOfDataDecoderException extends Exception { - /** + /** */ - private static final long serialVersionUID = 1336267941020800769L; + public NotEnoughDataDecoderException() { + } - } + /** + * @param arg0 + */ + public NotEnoughDataDecoderException(String arg0) { + super(arg0); + } - /** - * Exception when an error occurs while decoding - */ - public static class ErrorDataDecoderException extends Exception { - /** + /** + * @param arg0 + */ + public NotEnoughDataDecoderException(Throwable arg0) { + super(arg0); + } + + /** + * @param arg0 + * @param arg1 + */ + public NotEnoughDataDecoderException(String arg0, Throwable arg1) { + super(arg0, arg1); + } + } + + /** + * Exception when the body is fully decoded, even if there is still data + */ + public static class EndOfDataDecoderException extends Exception { + /** */ - private static final long serialVersionUID = 5020247425493164465L; + private static final long serialVersionUID = 1336267941020800769L; - /** + } + + /** + * Exception when an error occurs while decoding + */ + public static class ErrorDataDecoderException extends Exception { + /** */ - public ErrorDataDecoderException() { - } + private static final long serialVersionUID = 5020247425493164465L; - /** - * @param arg0 - */ - public ErrorDataDecoderException(String arg0) { - super(arg0); - } - - /** - * @param arg0 - */ - public ErrorDataDecoderException(Throwable arg0) { - super(arg0); - } - - /** - * @param arg0 - * @param arg1 - */ - public ErrorDataDecoderException(String arg0, Throwable arg1) { - super(arg0, arg1); - } - } - - /** - * Exception when an unappropriated method was called on a request - */ - public static class IncompatibleDataDecoderException extends Exception { - /** + /** */ - private static final long serialVersionUID = -953268047926250267L; + public ErrorDataDecoderException() { + } - /** + /** + * @param arg0 + */ + public ErrorDataDecoderException(String arg0) { + super(arg0); + } + + /** + * @param arg0 + */ + public ErrorDataDecoderException(Throwable arg0) { + super(arg0); + } + + /** + * @param arg0 + * @param arg1 + */ + public ErrorDataDecoderException(String arg0, Throwable arg1) { + super(arg0, arg1); + } + } + + /** + * Exception when an unappropriated method was called on a request + */ + public static class IncompatibleDataDecoderException extends Exception { + /** */ - public IncompatibleDataDecoderException() { - } + private static final long serialVersionUID = -953268047926250267L; - /** - * @param arg0 - */ - public IncompatibleDataDecoderException(String arg0) { - super(arg0); - } + /** + */ + public IncompatibleDataDecoderException() { + } - /** - * @param arg0 - */ - public IncompatibleDataDecoderException(Throwable arg0) { - super(arg0); - } + /** + * @param arg0 + */ + public IncompatibleDataDecoderException(String arg0) { + super(arg0); + } - /** - * @param arg0 - * @param arg1 - */ - public IncompatibleDataDecoderException(String arg0, Throwable arg1) { - super(arg0, arg1); - } - } + /** + * @param arg0 + */ + public IncompatibleDataDecoderException(Throwable arg0) { + super(arg0); + } + + /** + * @param arg0 + * @param arg1 + */ + public IncompatibleDataDecoderException(String arg0, Throwable arg1) { + super(arg0, arg1); + } + } } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/HttpPostRequestEncoder.java b/src/main/java/org/jboss/netty/handler/codec/http/HttpPostRequestEncoder.java index cc8d337d2b..e7e3b87b55 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/HttpPostRequestEncoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/HttpPostRequestEncoder.java @@ -33,1039 +33,953 @@ import org.jboss.netty.handler.stream.ChunkedInput; * This encoder will help to encode Request for a FORM as POST. */ public class HttpPostRequestEncoder implements ChunkedInput { - /** - * Factory used to create InterfaceHttpData - */ - private final HttpDataFactory factory; + /** + * Factory used to create InterfaceHttpData + */ + private final HttpDataFactory factory; - /** - * Request to encode - */ - private final HttpRequest request; + /** + * Request to encode + */ + private final HttpRequest request; - /** - * Default charset to use - */ - private final Charset charset; + /** + * Default charset to use + */ + private final Charset charset; - /** - * Chunked false by default - */ - private boolean isChunked; + /** + * Chunked false by default + */ + private boolean isChunked; - /** - * InterfaceHttpData for Body (without encoding) - */ - private List bodyListDatas; - /** - * The final Multipart List of InterfaceHttpData including encoding - */ - private List multipartHttpDatas; + /** + * InterfaceHttpData for Body (without encoding) + */ + private List bodyListDatas; + /** + * The final Multipart List of InterfaceHttpData including encoding + */ + private List multipartHttpDatas; - /** - * Does this request is a Multipart request - */ - private final boolean isMultipart; + /** + * Does this request is a Multipart request + */ + private final boolean isMultipart; - /** - * If multipart, this is the boundary for the flobal multipart - */ - private String multipartDataBoundary; + /** + * If multipart, this is the boundary for the flobal multipart + */ + private String multipartDataBoundary; - /** - * If multipart, there could be internal multiparts (mixed) to the global - * multipart. Only one level is allowed. - */ - private String multipartMixedBoundary; - /** - * To check if the header has been finalized - */ - private boolean headerFinalized; + /** + * If multipart, there could be internal multiparts (mixed) to the global multipart. + * Only one level is allowed. + */ + private String multipartMixedBoundary; + /** + * To check if the header has been finalized + */ + private boolean headerFinalized; - /** - * - * @param request - * the request to encode - * @param multipart - * True if the FORM is a ENCTYPE="multipart/form-data" - * @throws NullPointerException - * for request - * @throws ErrorDataEncoderException - * if the request is not a POST - */ - public HttpPostRequestEncoder(HttpRequest request, boolean multipart) - throws ErrorDataEncoderException { - this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), - request, multipart, HttpCodecUtil.DEFAULT_CHARSET); - } + /** + * + * @param request the request to encode + * @param multipart True if the FORM is a ENCTYPE="multipart/form-data" + * @throws NullPointerException for request + * @throws ErrorDataEncoderException if the request is not a POST + */ + public HttpPostRequestEncoder(HttpRequest request, boolean multipart) + throws ErrorDataEncoderException { + this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), + request, multipart, HttpCodecUtil.DEFAULT_CHARSET); + } - /** - * - * @param factory - * the factory used to create InterfaceHttpData - * @param request - * the request to encode - * @param multipart - * True if the FORM is a ENCTYPE="multipart/form-data" - * @throws NullPointerException - * for request and factory - * @throws ErrorDataEncoderException - * if the request is not a POST - */ - public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, - boolean multipart) throws ErrorDataEncoderException { - this(factory, request, multipart, HttpCodecUtil.DEFAULT_CHARSET); - } + /** + * + * @param factory the factory used to create InterfaceHttpData + * @param request the request to encode + * @param multipart True if the FORM is a ENCTYPE="multipart/form-data" + * @throws NullPointerException for request and factory + * @throws ErrorDataEncoderException if the request is not a POST + */ + public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, boolean multipart) + throws ErrorDataEncoderException { + this(factory, request, multipart, HttpCodecUtil.DEFAULT_CHARSET); + } - /** - * - * @param factory - * the factory used to create InterfaceHttpData - * @param request - * the request to encode - * @param multipart - * True if the FORM is a ENCTYPE="multipart/form-data" - * @param charset - * the charset to use as default - * @throws NullPointerException - * for request or charset or factory - * @throws ErrorDataEncoderException - * if the request is not a POST - */ - public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, - boolean multipart, Charset charset) - throws ErrorDataEncoderException { - if (factory == null) { - throw new NullPointerException("factory"); - } - if (request == null) { - throw new NullPointerException("request"); - } - if (charset == null) { - throw new NullPointerException("charset"); - } - if (request.getMethod() != HttpMethod.POST) { - throw new ErrorDataEncoderException( - "Cannot create a Encoder if not a POST"); - } - this.request = request; - this.charset = charset; - this.factory = factory; - // Fill default values - bodyListDatas = new ArrayList(); - // default mode - isLastChunk = false; - isLastChunkSent = false; - isMultipart = multipart; - multipartHttpDatas = new ArrayList(); - if (isMultipart) { - initDataMultipart(); - } - } + /** + * + * @param factory the factory used to create InterfaceHttpData + * @param request the request to encode + * @param multipart True if the FORM is a ENCTYPE="multipart/form-data" + * @param charset the charset to use as default + * @throws NullPointerException for request or charset or factory + * @throws ErrorDataEncoderException if the request is not a POST + */ + public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, + boolean multipart, Charset charset) throws ErrorDataEncoderException { + if (factory == null) { + throw new NullPointerException("factory"); + } + if (request == null) { + throw new NullPointerException("request"); + } + if (charset == null) { + throw new NullPointerException("charset"); + } + if (request.getMethod() != HttpMethod.POST) { + throw new ErrorDataEncoderException("Cannot create a Encoder if not a POST"); + } + this.request = request; + this.charset = charset; + this.factory = factory; + // Fill default values + bodyListDatas = new ArrayList(); + // default mode + isLastChunk = false; + isLastChunkSent = false; + isMultipart = multipart; + multipartHttpDatas = new ArrayList(); + if (isMultipart) { + initDataMultipart(); + } + } - /** - * Clean all HttpDatas (on Disk) for the current request. - */ - public void cleanFiles() { - factory.cleanRequestHttpDatas(request); - } - - /** - * Does the last non empty chunk already encoded so that next chunk will be - * empty (last chunk) - */ - private boolean isLastChunk; - /** - * Last chunk already sent - */ - private boolean isLastChunkSent; - /** - * The current FileUpload that is currently in encode process - */ - private FileUpload currentFileUpload; - /** - * While adding a FileUpload, is the multipart currently in Mixed Mode - */ - private boolean duringMixedMode; - - /** - * Global Body size - */ - private long globalBodySize; - - /** - * True if this request is a Multipart request - * - * @return True if this request is a Multipart request - */ - public boolean isMultipart() { - return isMultipart; - } - - /** - * Init the delimiter for Global Part (Data). - */ - private void initDataMultipart() { - multipartDataBoundary = getNewMultipartDelimiter(); - } - - /** - * Init the delimiter for Mixed Part (Mixed). - */ - private void initMixedMultipart() { - multipartMixedBoundary = getNewMultipartDelimiter(); - } - - /** - * - * @return a newly generated Delimiter (either for DATA or MIXED) - */ - private String getNewMultipartDelimiter() { - // construct a generated delimiter - Random random = new Random(); - return Long.toHexString(random.nextLong()).toLowerCase(); - } - - /** - * This method returns a List of all InterfaceHttpData from body part.
- * - * @return the list of InterfaceHttpData from Body part - */ - public List getBodyListAttributes() { - return bodyListDatas; - } - - /** - * Set the Body HttpDatas list - * - * @param datas - * @throws NullPointerException - * for datas - * @throws ErrorDataEncoderException - * if the encoding is in error or if the finalize were already - * done - */ - public void setBodyHttpDatas(List datas) - throws ErrorDataEncoderException { - if (datas == null) { - throw new NullPointerException("datas"); - } - globalBodySize = 0; - bodyListDatas.clear(); - currentFileUpload = null; - duringMixedMode = false; - multipartHttpDatas.clear(); - for (InterfaceHttpData data : datas) { - addBodyHttpData(data); - } - } - - /** - * Add a simple attribute in the body as Name=Value - * - * @param name - * name of the parameter - * @param value - * the value of the parameter - * @throws NullPointerException - * for name - * @throws ErrorDataEncoderException - * if the encoding is in error or if the finalize were already - * done - */ - public void addBodyAttribute(String name, String value) - throws ErrorDataEncoderException { - if (name == null) { - throw new NullPointerException("name"); - } - String svalue = value; - if (value == null) { - svalue = ""; - } - Attribute data = factory.createAttribute(request, name, svalue); - addBodyHttpData(data); - } - - /** - * Add a file as a FileUpload - * - * @param name - * the name of the parameter - * @param file - * the file to be uploaded (if not Multipart mode, only the - * filename will be included) - * @param contentType - * the associated contentType for the File - * @param isText - * True if this file should be transmitted in Text format (else - * binary) - * @throws NullPointerException - * for name and file - * @throws ErrorDataEncoderException - * if the encoding is in error or if the finalize were already - * done - */ - public void addBodyFileUpload(String name, File file, String contentType, - boolean isText) throws ErrorDataEncoderException { - if (name == null) { - throw new NullPointerException("name"); - } - if (file == null) { - throw new NullPointerException("file"); - } - String scontentType = contentType; - String contentTransferEncoding = null; - if (contentType == null) { - if (isText) { - scontentType = HttpPostBodyUtil.DEFAULT_TEXT_CONTENT_TYPE; - } else { - scontentType = HttpPostBodyUtil.DEFAULT_BINARY_CONTENT_TYPE; - } - } - if (!isText) { - contentTransferEncoding = HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value; - } - FileUpload fileUpload = factory.createFileUpload(request, name, - file.getName(), scontentType, contentTransferEncoding, null, - file.length()); - try { - fileUpload.setContent(file); - } catch (IOException e) { - throw new ErrorDataEncoderException(e); - } - addBodyHttpData(fileUpload); - } - - /** - * Add a series of Files associated with one File parameter (implied Mixed - * mode in Multipart) - * - * @param name - * the name of the parameter - * @param file - * the array of files - * @param contentType - * the array of content Types associated with each file - * @param isText - * the array of isText attribute (False meaning binary mode) for - * each file - * @throws NullPointerException - * also throws if array have different sizes - * @throws ErrorDataEncoderException - * if the encoding is in error or if the finalize were already - * done - */ - public void addBodyFileUploads(String name, File[] file, - String[] contentType, boolean[] isText) - throws ErrorDataEncoderException { - if (file.length != contentType.length && file.length != isText.length) { - throw new NullPointerException("Different array length"); - } - for (int i = 0; i < file.length; i++) { - addBodyFileUpload(name, file[i], contentType[i], isText[i]); - } - } - - /** - * Add the InterfaceHttpData to the Body list - * - * @param data - * @throws NullPointerException - * for data - * @throws ErrorDataEncoderException - * if the encoding is in error or if the finalize were already - * done - */ - public void addBodyHttpData(InterfaceHttpData data) - throws ErrorDataEncoderException { - if (headerFinalized) { - throw new ErrorDataEncoderException( - "Cannot add value once finalized"); - } - if (data == null) { - throw new NullPointerException("data"); - } - bodyListDatas.add(data); - if (!isMultipart) { - if (data instanceof Attribute) { - Attribute attribute = (Attribute) data; - try { - // name=value& with encoded name and attribute - String key = encodeAttribute(attribute.getName(), charset); - String value = encodeAttribute(attribute.getValue(), - charset); - Attribute newattribute = factory.createAttribute(request, - key, value); - multipartHttpDatas.add(newattribute); - globalBodySize += newattribute.getName().length() + 1 - + newattribute.length() + 1; - } catch (IOException e) { - throw new ErrorDataEncoderException(e); - } - } else if (data instanceof FileUpload) { - // since not Multipart, only name=filename => Attribute - FileUpload fileUpload = (FileUpload) data; - // name=filename& with encoded name and filename - String key = encodeAttribute(fileUpload.getName(), charset); - String value = encodeAttribute(fileUpload.getFilename(), - charset); - Attribute newattribute = factory.createAttribute(request, key, - value); - multipartHttpDatas.add(newattribute); - globalBodySize += newattribute.getName().length() + 1 - + newattribute.length() + 1; - } - return; - } - /* - * Logic: if not Attribute: add Data to body list if (duringMixedMode) - * add endmixedmultipart delimiter currentFileUpload = null - * duringMixedMode = false; add multipart delimiter, multipart body - * header and Data to multipart list reset currentFileUpload, - * duringMixedMode if FileUpload: take care of multiple file for one - * field => mixed mode if (duringMixeMode) if (currentFileUpload.name == - * data.name) add mixedmultipart delimiter, mixedmultipart body header - * and Data to multipart list else add endmixedmultipart delimiter, - * multipart body header and Data to multipart list currentFileUpload = - * data duringMixedMode = false; else if (currentFileUpload.name == - * data.name) change multipart body header of previous file into - * multipart list to mixedmultipart start, mixedmultipart body header - * add mixedmultipart delimiter, mixedmultipart body header and Data to - * multipart list duringMixedMode = true else add multipart delimiter, - * multipart body header and Data to multipart list currentFileUpload = - * data duringMixedMode = false; Do not add last delimiter! Could be: if - * duringmixedmode: endmixedmultipart + endmultipart else only - * endmultipart - */ - if (data instanceof Attribute) { - if (duringMixedMode) { - InternalAttribute internal = new InternalAttribute(); - internal.addValue("\r\n--" + multipartMixedBoundary + "--"); - multipartHttpDatas.add(internal); - multipartMixedBoundary = null; - currentFileUpload = null; - duringMixedMode = false; - } - InternalAttribute internal = new InternalAttribute(); - if (multipartHttpDatas.size() > 0) { - // previously a data field so CRLF - internal.addValue("\r\n"); - } - internal.addValue("--" + multipartDataBoundary + "\r\n"); - // content-disposition: form-data; name="field1" - Attribute attribute = (Attribute) data; - internal.addValue(HttpPostBodyUtil.CONTENT_DISPOSITION + ": " - + HttpPostBodyUtil.FORM_DATA + "; " + HttpPostBodyUtil.NAME - + "=\"" + encodeAttribute(attribute.getName(), charset) - + "\"\r\n"); - Charset localcharset = attribute.getCharset(); - if (localcharset != null) { - // Content-Type: charset=charset - internal.addValue(HttpHeaders.Names.CONTENT_TYPE + ": " - + HttpHeaders.Values.CHARSET + "=" + localcharset - + "\r\n"); - } - // CRLF between body header and data - internal.addValue("\r\n"); - multipartHttpDatas.add(internal); - multipartHttpDatas.add(data); - globalBodySize += attribute.length() + internal.size(); - } else if (data instanceof FileUpload) { - FileUpload fileUpload = (FileUpload) data; - InternalAttribute internal = new InternalAttribute(); - if (multipartHttpDatas.size() > 0) { - // previously a data field so CRLF - internal.addValue("\r\n"); - } - boolean localMixed = false; - if (duringMixedMode) { - if (currentFileUpload != null - && currentFileUpload.getName().equals( - fileUpload.getName())) { - // continue a mixed mode - - localMixed = true; - } else { - // end a mixed mode - - // add endmixedmultipart delimiter, multipart body header - // and - // Data to multipart list - internal.addValue("--" + multipartMixedBoundary + "--"); - multipartHttpDatas.add(internal); - multipartMixedBoundary = null; - // start a new one (could be replaced if mixed start again - // from here - internal = new InternalAttribute(); - internal.addValue("\r\n"); - localMixed = false; - // new currentFileUpload and no more in Mixed mode - currentFileUpload = fileUpload; - duringMixedMode = false; - } - } else { - if (currentFileUpload != null - && currentFileUpload.getName().equals( - fileUpload.getName())) { - // create a new mixed mode (from previous file) - - // change multipart body header of previous file into - // multipart list to - // mixedmultipart start, mixedmultipart body header - - // change Internal (size()-2 position in multipartHttpDatas) - // from (line starting with *) - // --AaB03x - // * Content-Disposition: form-data; name="files"; - // filename="file1.txt" - // Content-Type: text/plain - // to (lines starting with *) - // --AaB03x - // * Content-Disposition: form-data; name="files" - // * Content-Type: multipart/mixed; boundary=BbC04y - // * - // * --BbC04y - // * Content-Disposition: file; filename="file1.txt" - // Content-Type: text/plain - initMixedMultipart(); - InternalAttribute pastAttribute = (InternalAttribute) multipartHttpDatas - .get(multipartHttpDatas.size() - 2); - // remove past size - globalBodySize -= pastAttribute.size(); - String replacement = HttpPostBodyUtil.CONTENT_DISPOSITION - + ": " + HttpPostBodyUtil.FORM_DATA + "; " - + HttpPostBodyUtil.NAME + "=\"" - + encodeAttribute(fileUpload.getName(), charset) - + "\"\r\n"; - replacement += HttpHeaders.Names.CONTENT_TYPE + ": " - + HttpPostBodyUtil.MULTIPART_MIXED + "; " - + HttpHeaders.Values.BOUNDARY + "=" - + multipartMixedBoundary + "\r\n\r\n"; - replacement += "--" + multipartMixedBoundary + "\r\n"; - replacement += HttpPostBodyUtil.CONTENT_DISPOSITION - + ": " - + HttpPostBodyUtil.FILE - + "; " - + HttpPostBodyUtil.FILENAME - + "=\"" - + encodeAttribute(fileUpload.getFilename(), charset) - + "\"\r\n"; - pastAttribute.setValue(replacement, 1); - // update past size - globalBodySize += pastAttribute.size(); - - // now continue - // add mixedmultipart delimiter, mixedmultipart body header - // and - // Data to multipart list - localMixed = true; - duringMixedMode = true; - } else { - // a simple new multipart - // add multipart delimiter, multipart body header and Data - // to multipart list - localMixed = false; - currentFileUpload = fileUpload; - duringMixedMode = false; - } - } - - if (localMixed) { - // add mixedmultipart delimiter, mixedmultipart body header and - // Data to multipart list - internal.addValue("--" + multipartMixedBoundary + "\r\n"); - // Content-Disposition: file; filename="file1.txt" - internal.addValue(HttpPostBodyUtil.CONTENT_DISPOSITION + ": " - + HttpPostBodyUtil.FILE + "; " - + HttpPostBodyUtil.FILENAME + "=\"" - + encodeAttribute(fileUpload.getFilename(), charset) - + "\"\r\n"); - - } else { - internal.addValue("--" + multipartDataBoundary + "\r\n"); - // Content-Disposition: form-data; name="files"; - // filename="file1.txt" - internal.addValue(HttpPostBodyUtil.CONTENT_DISPOSITION + ": " - + HttpPostBodyUtil.FORM_DATA + "; " - + HttpPostBodyUtil.NAME + "=\"" - + encodeAttribute(fileUpload.getName(), charset) - + "\"; " + HttpPostBodyUtil.FILENAME + "=\"" - + encodeAttribute(fileUpload.getFilename(), charset) - + "\"\r\n"); - } - // Content-Type: image/gif - // Content-Type: text/plain; charset=ISO-8859-1 - // Content-Transfer-Encoding: binary - internal.addValue(HttpHeaders.Names.CONTENT_TYPE + ": " - + fileUpload.getContentType()); - String contentTransferEncoding = fileUpload - .getContentTransferEncoding(); - if (contentTransferEncoding != null - && contentTransferEncoding - .equals(HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value)) { - internal.addValue("\r\n" - + HttpHeaders.Names.CONTENT_TRANSFER_ENCODING - + ": " - + HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value - + "\r\n\r\n"); - } else if (fileUpload.getCharset() != null) { - internal.addValue("; " + HttpHeaders.Values.CHARSET + "=" - + fileUpload.getCharset() + "\r\n\r\n"); - } else { - internal.addValue("\r\n\r\n"); - } - multipartHttpDatas.add(internal); - multipartHttpDatas.add(data); - globalBodySize += fileUpload.length() + internal.size(); - } - } - - /** - * Iterator to be used when encoding will be called chunk after chunk - */ - private ListIterator iterator; - - /** - * Finalize the request by preparing the Header in the request and returns - * the request ready to be sent.
- * Once finalized, no data must be added.
- * If the request does not need chunk (isChunked() == false), this request - * is the only object to send to the remote server. - * - * @return the request object (chunked or not according to size of body) - * @throws ErrorDataEncoderException - * if the encoding is in error or if the finalize were already - * done - */ - public HttpRequest finalizeRequest() throws ErrorDataEncoderException { - // Finalize the multipartHttpDatas - if (!headerFinalized) { - if (isMultipart) { - InternalAttribute internal = new InternalAttribute(); - if (duringMixedMode) { - internal.addValue("\r\n--" + multipartMixedBoundary + "--"); - } - internal.addValue("\r\n--" + multipartDataBoundary + "--\r\n"); - multipartHttpDatas.add(internal); - multipartMixedBoundary = null; - currentFileUpload = null; - duringMixedMode = false; - globalBodySize += internal.size(); - } - headerFinalized = true; - } else { - throw new ErrorDataEncoderException("Header already encoded"); - } - List contentTypes = request - .getHeaders(HttpHeaders.Names.CONTENT_TYPE); - List transferEncoding = request - .getHeaders(HttpHeaders.Names.TRANSFER_ENCODING); - if (contentTypes != null) { - request.removeHeader(HttpHeaders.Names.CONTENT_TYPE); - for (String contentType : contentTypes) { - // "multipart/form-data; boundary=--89421926422648" - if (contentType.toLowerCase().startsWith( - HttpHeaders.Values.MULTIPART_FORM_DATA)) { - // ignore - } else if (contentType.toLowerCase().startsWith( - HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED)) { - // ignore - } else { - request.addHeader(HttpHeaders.Names.CONTENT_TYPE, - contentType); - } - } - } - if (isMultipart) { - String value = HttpHeaders.Values.MULTIPART_FORM_DATA + "; " - + HttpHeaders.Values.BOUNDARY + "=" + multipartDataBoundary; - request.addHeader(HttpHeaders.Names.CONTENT_TYPE, value); - } else { - // Not multipart - request.addHeader(HttpHeaders.Names.CONTENT_TYPE, - HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED); - } - // Now consider size for chunk or not - long realSize = globalBodySize; - if (isMultipart) { - iterator = multipartHttpDatas.listIterator(); - } else { - realSize -= 1; // last '&' removed - iterator = multipartHttpDatas.listIterator(); - } - request.setHeader(HttpHeaders.Names.CONTENT_LENGTH, - String.valueOf(realSize)); - if (realSize > HttpPostBodyUtil.chunkSize) { - isChunked = true; - if (transferEncoding != null) { - request.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING); - for (String v : transferEncoding) { - if (v.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) { - // ignore - } else { - request.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, - v); - } - } - } - request.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, - HttpHeaders.Values.CHUNKED); - request.setContent(ChannelBuffers.EMPTY_BUFFER); - } else { - // get the only one body and set it to the request - HttpChunk chunk = nextChunk(); - request.setContent(chunk.getContent()); - } - return request; - } - - /** - * @return True if the request is by Chunk - */ - public boolean isChunked() { - return isChunked; - } - - /** - * Encode one attribute - * - * @param s - * @param charset - * @return the encoded attribute - * @throws ErrorDataEncoderException - * if the encoding is in error - */ - private static String encodeAttribute(String s, Charset charset) - throws ErrorDataEncoderException { - if (s == null) { - return ""; - } - try { - return URLEncoder.encode(s, charset.name()); - } catch (UnsupportedEncodingException e) { - throw new ErrorDataEncoderException(charset.name(), e); - } - } - - /** - * The ChannelBuffer currently used by the encoder - */ - private ChannelBuffer currentBuffer; - /** - * The current InterfaceHttpData to encode (used if more chunks are - * available) - */ - private InterfaceHttpData currentData; - /** - * If not multipart, does the currentBuffer stands for the Key or for the - * Value - */ - private boolean isKey = true; - - /** - * - * @return the next ChannelBuffer to send as a HttpChunk and modifying - * currentBuffer accordingly - */ - private ChannelBuffer fillChannelBuffer() { - int length = currentBuffer.readableBytes(); - if (length > HttpPostBodyUtil.chunkSize) { - ChannelBuffer slice = currentBuffer.slice( - currentBuffer.readerIndex(), HttpPostBodyUtil.chunkSize); - currentBuffer.skipBytes(HttpPostBodyUtil.chunkSize); - return slice; - } else { - // to continue - ChannelBuffer slice = currentBuffer; - currentBuffer = null; - return slice; - } - } - - /** - * From the current context (currentBuffer and currentData), returns the - * next HttpChunk (if possible) trying to get sizeleft bytes more into the - * currentBuffer. This is the Multipart version. - * - * @param sizeleft - * the number of bytes to try to get from currentData - * @return the next HttpChunk or null if not enough bytes were found - * @throws ErrorDataEncoderException - * if the encoding is in error - */ - private HttpChunk encodeNextChunkMultipart(int sizeleft) - throws ErrorDataEncoderException { - if (currentData == null) { - return null; - } - ChannelBuffer buffer; - if (currentData instanceof InternalAttribute) { - String internal = ((InternalAttribute) currentData).toString(); - byte[] bytes; - try { - bytes = internal.getBytes("ASCII"); - } catch (UnsupportedEncodingException e) { - throw new ErrorDataEncoderException(e); - } - buffer = ChannelBuffers.wrappedBuffer(bytes); - currentData = null; - } else { - if (currentData instanceof Attribute) { - try { - buffer = ((Attribute) currentData).getChunk(sizeleft); - } catch (IOException e) { - throw new ErrorDataEncoderException(e); - } - } else { - try { - buffer = ((FileUpload) currentData).getChunk(sizeleft); - } catch (IOException e) { - throw new ErrorDataEncoderException(e); - } - } - if (buffer.capacity() == 0) { - // end for current InterfaceHttpData, need more data - currentData = null; - return null; - } - } - if (currentBuffer == null) { - currentBuffer = buffer; - } else { - currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer, buffer); - } - if (currentBuffer.readableBytes() < HttpPostBodyUtil.chunkSize) { - currentData = null; - return null; - } - buffer = fillChannelBuffer(); - return new DefaultHttpChunk(buffer); - } - - /** - * From the current context (currentBuffer and currentData), returns the - * next HttpChunk (if possible) trying to get sizeleft bytes more into the - * currentBuffer. This is the UrlEncoded version. - * - * @param sizeleft - * the number of bytes to try to get from currentData - * @return the next HttpChunk or null if not enough bytes were found - * @throws ErrorDataEncoderException - * if the encoding is in error - */ - private HttpChunk encodeNextChunkUrlEncoded(int sizeleft) - throws ErrorDataEncoderException { - if (currentData == null) { - return null; - } - int size = sizeleft; - ChannelBuffer buffer; - if (isKey) { - // get name - String key = currentData.getName(); - buffer = ChannelBuffers.wrappedBuffer(key.getBytes()); - isKey = false; - if (currentBuffer == null) { - currentBuffer = ChannelBuffers.wrappedBuffer(buffer, - ChannelBuffers.wrappedBuffer("=".getBytes())); - // continue - size -= buffer.readableBytes() + 1; - } else { - currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer, - buffer, ChannelBuffers.wrappedBuffer("=".getBytes())); - // continue - size -= buffer.readableBytes() + 1; - } - if (currentBuffer.readableBytes() >= HttpPostBodyUtil.chunkSize) { - buffer = fillChannelBuffer(); - return new DefaultHttpChunk(buffer); - } - } - try { - buffer = ((Attribute) currentData).getChunk(size); - } catch (IOException e) { - throw new ErrorDataEncoderException(e); - } - ChannelBuffer delimiter = null; - if (buffer.readableBytes() < size) { - // delimiter - isKey = true; - delimiter = iterator.hasNext() ? ChannelBuffers.wrappedBuffer("&" - .getBytes()) : null; - } - if (buffer.capacity() == 0) { - // end for current InterfaceHttpData, need potentially more data - currentData = null; - if (currentBuffer == null) { - currentBuffer = delimiter; - } else { - if (delimiter != null) { - currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer, - delimiter); - } - } - if (currentBuffer.readableBytes() >= HttpPostBodyUtil.chunkSize) { - buffer = fillChannelBuffer(); - return new DefaultHttpChunk(buffer); - } - return null; - } - if (currentBuffer == null) { - if (delimiter != null) { - currentBuffer = ChannelBuffers.wrappedBuffer(buffer, delimiter); - } else { - currentBuffer = buffer; - } - } else { - if (delimiter != null) { - currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer, - buffer, delimiter); - } else { - currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer, - buffer); - } - } - if (currentBuffer.readableBytes() < HttpPostBodyUtil.chunkSize) { - // end for current InterfaceHttpData, need more data - currentData = null; - isKey = true; - return null; - } - buffer = fillChannelBuffer(); - // size = 0 - return new DefaultHttpChunk(buffer); - } - - @Override - public void close() throws Exception { - // NO since the user can want to reuse (broadcast for instance) - // cleanFiles(); - } - - /** - * Returns the next available HttpChunk. The caller is responsible to test - * if this chunk is the last one (isLast()), in order to stop calling this - * method. - * - * @return the next available HttpChunk - * @throws ErrorDataEncoderException - * if the encoding is in error - */ - @Override - public HttpChunk nextChunk() throws ErrorDataEncoderException { - if (isLastChunk) { - isLastChunkSent = true; - return new DefaultHttpChunk(ChannelBuffers.EMPTY_BUFFER); - } - ChannelBuffer buffer = null; - int size = HttpPostBodyUtil.chunkSize; - // first test if previous buffer is not empty - if (currentBuffer != null) { - size -= currentBuffer.readableBytes(); - } - if (size <= 0) { - // NextChunk from buffer - buffer = fillChannelBuffer(); - return new DefaultHttpChunk(buffer); - } - // size > 0 - if (currentData != null) { - // continue to read data - if (isMultipart) { - HttpChunk chunk = encodeNextChunkMultipart(size); - if (chunk != null) { - return chunk; - } - } else { - HttpChunk chunk = encodeNextChunkUrlEncoded(size); - if (chunk != null) { - // NextChunk Url from currentData - return chunk; - } - } - size = HttpPostBodyUtil.chunkSize - currentBuffer.readableBytes(); - } - if (!iterator.hasNext()) { - isLastChunk = true; - // NextChunk as last non empty from buffer - buffer = currentBuffer; - currentBuffer = null; - return new DefaultHttpChunk(buffer); - } - while (size > 0 && iterator.hasNext()) { - currentData = iterator.next(); - HttpChunk chunk; - if (isMultipart) { - chunk = encodeNextChunkMultipart(size); - } else { - chunk = encodeNextChunkUrlEncoded(size); - } - if (chunk == null) { - // not enough - size = HttpPostBodyUtil.chunkSize - - currentBuffer.readableBytes(); - continue; - } - // NextChunk from data - return chunk; - } - // end since no more data - isLastChunk = true; - if (currentBuffer == null) { - isLastChunkSent = true; - // LastChunk with no more data - return new DefaultHttpChunk(ChannelBuffers.EMPTY_BUFFER); - } - // Previous LastChunk with no more data - buffer = currentBuffer; - currentBuffer = null; - return new DefaultHttpChunk(buffer); - } - - @Override - public boolean isEndOfInput() throws Exception { - return isLastChunkSent; - } - - @Override - public boolean hasNextChunk() throws Exception { - return !isLastChunkSent; - } - - /** - * Exception when an error occurs while encoding - */ - public static class ErrorDataEncoderException extends Exception { - /** + /** + * Clean all HttpDatas (on Disk) for the current request. */ - private static final long serialVersionUID = 5020247425493164465L; + public void cleanFiles() { + factory.cleanRequestHttpDatas(request); + } - /** + /** + * Does the last non empty chunk already encoded so that next chunk will be empty (last chunk) + */ + private boolean isLastChunk; + /** + * Last chunk already sent + */ + private boolean isLastChunkSent; + /** + * The current FileUpload that is currently in encode process + */ + private FileUpload currentFileUpload; + /** + * While adding a FileUpload, is the multipart currently in Mixed Mode + */ + private boolean duringMixedMode; + + /** + * Global Body size + */ + private long globalBodySize; + + /** + * True if this request is a Multipart request + * @return True if this request is a Multipart request + */ + public boolean isMultipart() { + return isMultipart; + } + + /** + * Init the delimiter for Global Part (Data). + */ + private void initDataMultipart() { + multipartDataBoundary = getNewMultipartDelimiter(); + } + + /** + * Init the delimiter for Mixed Part (Mixed). + */ + private void initMixedMultipart() { + multipartMixedBoundary = getNewMultipartDelimiter(); + } + + /** + * + * @return a newly generated Delimiter (either for DATA or MIXED) + */ + private String getNewMultipartDelimiter() { + // construct a generated delimiter + Random random = new Random(); + return Long.toHexString(random.nextLong()).toLowerCase(); + } + + /** + * This method returns a List of all InterfaceHttpData from body part.
+ + * @return the list of InterfaceHttpData from Body part + */ + public List getBodyListAttributes() { + return bodyListDatas; + } + + /** + * Set the Body HttpDatas list + * @param datas + * @throws NullPointerException for datas + * @throws ErrorDataEncoderException if the encoding is in error or if the finalize were already done + */ + public void setBodyHttpDatas(List datas) + throws ErrorDataEncoderException { + if (datas == null) { + throw new NullPointerException("datas"); + } + globalBodySize = 0; + bodyListDatas.clear(); + currentFileUpload = null; + duringMixedMode = false; + multipartHttpDatas.clear(); + for (InterfaceHttpData data: datas) { + addBodyHttpData(data); + } + } + + /** + * Add a simple attribute in the body as Name=Value + * @param name name of the parameter + * @param value the value of the parameter + * @throws NullPointerException for name + * @throws ErrorDataEncoderException if the encoding is in error or if the finalize were already done + */ + public void addBodyAttribute(String name, String value) + throws ErrorDataEncoderException { + if (name == null) { + throw new NullPointerException("name"); + } + String svalue = value; + if (value == null) { + svalue = ""; + } + Attribute data = factory.createAttribute(request, name, svalue); + addBodyHttpData(data); + } + + /** + * Add a file as a FileUpload + * @param name the name of the parameter + * @param file the file to be uploaded (if not Multipart mode, only the filename will be included) + * @param contentType the associated contentType for the File + * @param isText True if this file should be transmitted in Text format (else binary) + * @throws NullPointerException for name and file + * @throws ErrorDataEncoderException if the encoding is in error or if the finalize were already done + */ + public void addBodyFileUpload(String name, File file, String contentType, boolean isText) + throws ErrorDataEncoderException { + if (name == null) { + throw new NullPointerException("name"); + } + if (file == null) { + throw new NullPointerException("file"); + } + String scontentType = contentType; + String contentTransferEncoding = null; + if (contentType == null) { + if (isText) { + scontentType = HttpPostBodyUtil.DEFAULT_TEXT_CONTENT_TYPE; + } else { + scontentType = HttpPostBodyUtil.DEFAULT_BINARY_CONTENT_TYPE; + } + } + if (!isText) { + contentTransferEncoding = HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value; + } + FileUpload fileUpload = factory.createFileUpload(request, name, file.getName(), + scontentType, contentTransferEncoding, null, file.length()); + try { + fileUpload.setContent(file); + } catch (IOException e) { + throw new ErrorDataEncoderException(e); + } + addBodyHttpData(fileUpload); + } + + /** + * Add a series of Files associated with one File parameter (implied Mixed mode in Multipart) + * @param name the name of the parameter + * @param file the array of files + * @param contentType the array of content Types associated with each file + * @param isText the array of isText attribute (False meaning binary mode) for each file + * @throws NullPointerException also throws if array have different sizes + * @throws ErrorDataEncoderException if the encoding is in error or if the finalize were already done + */ + public void addBodyFileUploads(String name, File[] file, String[] contentType, boolean[] isText) + throws ErrorDataEncoderException { + if (file.length != contentType.length && file.length != isText.length) { + throw new NullPointerException("Different array length"); + } + for (int i = 0; i < file.length; i++) { + addBodyFileUpload(name, file[i], contentType[i], isText[i]); + } + } + + /** + * Add the InterfaceHttpData to the Body list + * @param data + * @throws NullPointerException for data + * @throws ErrorDataEncoderException if the encoding is in error or if the finalize were already done + */ + public void addBodyHttpData(InterfaceHttpData data) + throws ErrorDataEncoderException { + if (headerFinalized) { + throw new ErrorDataEncoderException("Cannot add value once finalized"); + } + if (data == null) { + throw new NullPointerException("data"); + } + bodyListDatas.add(data); + if (! isMultipart) { + if (data instanceof Attribute) { + Attribute attribute = (Attribute) data; + try { + // name=value& with encoded name and attribute + String key = encodeAttribute(attribute.getName(), charset); + String value = encodeAttribute(attribute.getValue(), charset); + Attribute newattribute = factory.createAttribute(request, key, value); + multipartHttpDatas.add(newattribute); + globalBodySize += newattribute.getName().length() + 1 + + newattribute.length() + 1; + } catch (IOException e) { + throw new ErrorDataEncoderException(e); + } + } else if (data instanceof FileUpload) { + // since not Multipart, only name=filename => Attribute + FileUpload fileUpload = (FileUpload) data; + // name=filename& with encoded name and filename + String key = encodeAttribute(fileUpload.getName(), charset); + String value = encodeAttribute(fileUpload.getFilename(), charset); + Attribute newattribute = factory.createAttribute(request, key, value); + multipartHttpDatas.add(newattribute); + globalBodySize += newattribute.getName().length() + 1 + + newattribute.length() + 1; + } + return; + } + /* + * Logic: + * if not Attribute: + * add Data to body list + * if (duringMixedMode) + * add endmixedmultipart delimiter + * currentFileUpload = null + * duringMixedMode = false; + * add multipart delimiter, multipart body header and Data to multipart list + * reset currentFileUpload, duringMixedMode + * if FileUpload: take care of multiple file for one field => mixed mode + * if (duringMixeMode) + * if (currentFileUpload.name == data.name) + * add mixedmultipart delimiter, mixedmultipart body header and Data to multipart list + * else + * add endmixedmultipart delimiter, multipart body header and Data to multipart list + * currentFileUpload = data + * duringMixedMode = false; + * else + * if (currentFileUpload.name == data.name) + * change multipart body header of previous file into multipart list to + * mixedmultipart start, mixedmultipart body header + * add mixedmultipart delimiter, mixedmultipart body header and Data to multipart list + * duringMixedMode = true + * else + * add multipart delimiter, multipart body header and Data to multipart list + * currentFileUpload = data + * duringMixedMode = false; + * Do not add last delimiter! Could be: + * if duringmixedmode: endmixedmultipart + endmultipart + * else only endmultipart + */ + if (data instanceof Attribute) { + if (duringMixedMode) { + InternalAttribute internal = new InternalAttribute(); + internal.addValue("\r\n--" + multipartMixedBoundary + "--"); + multipartHttpDatas.add(internal); + multipartMixedBoundary = null; + currentFileUpload = null; + duringMixedMode = false; + } + InternalAttribute internal = new InternalAttribute(); + if (multipartHttpDatas.size() > 0) { + // previously a data field so CRLF + internal.addValue("\r\n"); + } + internal.addValue("--" + multipartDataBoundary + "\r\n"); + // content-disposition: form-data; name="field1" + Attribute attribute = (Attribute) data; + internal.addValue(HttpPostBodyUtil.CONTENT_DISPOSITION + ": " + + HttpPostBodyUtil.FORM_DATA + "; " + + HttpPostBodyUtil.NAME + "=\"" + + encodeAttribute(attribute.getName(), charset) + "\"\r\n"); + Charset localcharset = attribute.getCharset(); + if (localcharset != null) { + // Content-Type: charset=charset + internal.addValue(HttpHeaders.Names.CONTENT_TYPE + ": " + + HttpHeaders.Values.CHARSET + "=" + localcharset + "\r\n"); + } + // CRLF between body header and data + internal.addValue("\r\n"); + multipartHttpDatas.add(internal); + multipartHttpDatas.add(data); + globalBodySize += attribute.length() + internal.size(); + } else if (data instanceof FileUpload) { + FileUpload fileUpload = (FileUpload) data; + InternalAttribute internal = new InternalAttribute(); + if (multipartHttpDatas.size() > 0) { + // previously a data field so CRLF + internal.addValue("\r\n"); + } + boolean localMixed = false; + if (duringMixedMode) { + if (currentFileUpload != null && + currentFileUpload.getName().equals(fileUpload.getName())) { + // continue a mixed mode + + localMixed = true; + } else { + // end a mixed mode + + // add endmixedmultipart delimiter, multipart body header and + // Data to multipart list + internal.addValue("--" + multipartMixedBoundary + "--"); + multipartHttpDatas.add(internal); + multipartMixedBoundary = null; + // start a new one (could be replaced if mixed start again from here + internal = new InternalAttribute(); + internal.addValue("\r\n"); + localMixed = false; + // new currentFileUpload and no more in Mixed mode + currentFileUpload = fileUpload; + duringMixedMode = false; + } + } else { + if (currentFileUpload != null && + currentFileUpload.getName().equals(fileUpload.getName())) { + // create a new mixed mode (from previous file) + + // change multipart body header of previous file into multipart list to + // mixedmultipart start, mixedmultipart body header + + // change Internal (size()-2 position in multipartHttpDatas) + // from (line starting with *) + // --AaB03x + // * Content-Disposition: form-data; name="files"; filename="file1.txt" + // Content-Type: text/plain + // to (lines starting with *) + // --AaB03x + // * Content-Disposition: form-data; name="files" + // * Content-Type: multipart/mixed; boundary=BbC04y + // * + // * --BbC04y + // * Content-Disposition: file; filename="file1.txt" + // Content-Type: text/plain + initMixedMultipart(); + InternalAttribute pastAttribute = + (InternalAttribute) multipartHttpDatas.get(multipartHttpDatas.size() - 2); + // remove past size + globalBodySize -= pastAttribute.size(); + String replacement = HttpPostBodyUtil.CONTENT_DISPOSITION + ": " + + HttpPostBodyUtil.FORM_DATA + "; " + HttpPostBodyUtil.NAME + "=\"" + + encodeAttribute(fileUpload.getName(), charset) + "\"\r\n"; + replacement += HttpHeaders.Names.CONTENT_TYPE + ": " + + HttpPostBodyUtil.MULTIPART_MIXED + "; " + HttpHeaders.Values.BOUNDARY + + "=" + multipartMixedBoundary + "\r\n\r\n"; + replacement += "--" + multipartMixedBoundary + "\r\n"; + replacement += HttpPostBodyUtil.CONTENT_DISPOSITION + ": " + + HttpPostBodyUtil.FILE + "; " + HttpPostBodyUtil.FILENAME + "=\"" + + encodeAttribute(fileUpload.getFilename(), charset) + + "\"\r\n"; + pastAttribute.setValue(replacement, 1); + // update past size + globalBodySize += pastAttribute.size(); + + // now continue + // add mixedmultipart delimiter, mixedmultipart body header and + // Data to multipart list + localMixed = true; + duringMixedMode = true; + } else { + // a simple new multipart + //add multipart delimiter, multipart body header and Data to multipart list + localMixed = false; + currentFileUpload = fileUpload; + duringMixedMode = false; + } + } + + if (localMixed) { + // add mixedmultipart delimiter, mixedmultipart body header and + // Data to multipart list + internal.addValue("--" + multipartMixedBoundary + "\r\n"); + // Content-Disposition: file; filename="file1.txt" + internal.addValue(HttpPostBodyUtil.CONTENT_DISPOSITION + ": " + + HttpPostBodyUtil.FILE + "; " + HttpPostBodyUtil.FILENAME + "=\"" + + encodeAttribute(fileUpload.getFilename(), charset) + + "\"\r\n"); + + } else { + internal.addValue("--" + multipartDataBoundary + "\r\n"); + // Content-Disposition: form-data; name="files"; filename="file1.txt" + internal.addValue(HttpPostBodyUtil.CONTENT_DISPOSITION + ": " + + HttpPostBodyUtil.FORM_DATA + "; " + HttpPostBodyUtil.NAME + "=\"" + + encodeAttribute(fileUpload.getName(), charset) + "\"; " + + HttpPostBodyUtil.FILENAME + "=\"" + + encodeAttribute(fileUpload.getFilename(), charset) + + "\"\r\n"); + } + // Content-Type: image/gif + // Content-Type: text/plain; charset=ISO-8859-1 + // Content-Transfer-Encoding: binary + internal.addValue(HttpHeaders.Names.CONTENT_TYPE + ": " + + fileUpload.getContentType()); + String contentTransferEncoding = fileUpload.getContentTransferEncoding(); + if (contentTransferEncoding != null && + contentTransferEncoding.equals( + HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value)) { + internal.addValue("\r\n" + HttpHeaders.Names.CONTENT_TRANSFER_ENCODING + + ": " + HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value + + "\r\n\r\n"); + } else if (fileUpload.getCharset() != null) { + internal.addValue("; " + HttpHeaders.Values.CHARSET + "=" + + fileUpload.getCharset() + "\r\n\r\n"); + } else { + internal.addValue("\r\n\r\n"); + } + multipartHttpDatas.add(internal); + multipartHttpDatas.add(data); + globalBodySize += fileUpload.length() + internal.size(); + } + } + + /** + * Iterator to be used when encoding will be called chunk after chunk + */ + private ListIterator iterator; + + /** + * Finalize the request by preparing the Header in the request and + * returns the request ready to be sent.
+ * Once finalized, no data must be added.
+ * If the request does not need chunk (isChunked() == false), + * this request is the only object to send to + * the remote server. + * + * @return the request object (chunked or not according to size of body) + * @throws ErrorDataEncoderException if the encoding is in error or if the finalize were already done + */ + public HttpRequest finalizeRequest() throws ErrorDataEncoderException { + // Finalize the multipartHttpDatas + if (! headerFinalized) { + if (isMultipart) { + InternalAttribute internal = new InternalAttribute(); + if (duringMixedMode) { + internal.addValue("\r\n--" + multipartMixedBoundary + "--"); + } + internal.addValue("\r\n--" + multipartDataBoundary + "--\r\n"); + multipartHttpDatas.add(internal); + multipartMixedBoundary = null; + currentFileUpload = null; + duringMixedMode = false; + globalBodySize += internal.size(); + } + headerFinalized = true; + } else { + throw new ErrorDataEncoderException("Header already encoded"); + } + List contentTypes = request.getHeaders(HttpHeaders.Names.CONTENT_TYPE); + List transferEncoding = + request.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING); + if (contentTypes != null) { + request.removeHeader(HttpHeaders.Names.CONTENT_TYPE); + for (String contentType: contentTypes) { + // "multipart/form-data; boundary=--89421926422648" + if (contentType.toLowerCase().startsWith( + HttpHeaders.Values.MULTIPART_FORM_DATA)) { + // ignore + } else if (contentType.toLowerCase().startsWith( + HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED)) { + // ignore + } else { + request.addHeader(HttpHeaders.Names.CONTENT_TYPE, contentType); + } + } + } + if (isMultipart) { + String value = HttpHeaders.Values.MULTIPART_FORM_DATA + "; " + + HttpHeaders.Values.BOUNDARY + "=" + multipartDataBoundary; + request.addHeader(HttpHeaders.Names.CONTENT_TYPE, value); + } else { + // Not multipart + request.addHeader(HttpHeaders.Names.CONTENT_TYPE, + HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED); + } + // Now consider size for chunk or not + long realSize = globalBodySize; + if (isMultipart) { + iterator = multipartHttpDatas.listIterator(); + } else { + realSize -= 1; // last '&' removed + iterator = multipartHttpDatas.listIterator(); + } + request.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String + .valueOf(realSize)); + if (realSize > HttpPostBodyUtil.chunkSize) { + isChunked = true; + if (transferEncoding != null) { + request.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING); + for (String v: transferEncoding) { + if (v.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) { + // ignore + } else { + request.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, v); + } + } + } + request.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, + HttpHeaders.Values.CHUNKED); + request.setContent(ChannelBuffers.EMPTY_BUFFER); + } else { + // get the only one body and set it to the request + HttpChunk chunk = nextChunk(); + request.setContent(chunk.getContent()); + } + return request; + } + + /** + * @return True if the request is by Chunk + */ + public boolean isChunked() { + return isChunked; + } + + /** + * Encode one attribute + * @param s + * @param charset + * @return the encoded attribute + * @throws ErrorDataEncoderException if the encoding is in error + */ + private static String encodeAttribute(String s, Charset charset) + throws ErrorDataEncoderException { + if (s == null) { + return ""; + } + try { + return URLEncoder.encode(s, charset.name()); + } catch (UnsupportedEncodingException e) { + throw new ErrorDataEncoderException(charset.name(), e); + } + } + + /** + * The ChannelBuffer currently used by the encoder + */ + private ChannelBuffer currentBuffer; + /** + * The current InterfaceHttpData to encode (used if more chunks are available) + */ + private InterfaceHttpData currentData; + /** + * If not multipart, does the currentBuffer stands for the Key or for the Value + */ + private boolean isKey = true; + + /** + * + * @return the next ChannelBuffer to send as a HttpChunk and modifying currentBuffer + * accordingly + */ + private ChannelBuffer fillChannelBuffer() { + int length = currentBuffer.readableBytes(); + if (length > HttpPostBodyUtil.chunkSize) { + ChannelBuffer slice = + currentBuffer.slice(currentBuffer.readerIndex(), HttpPostBodyUtil.chunkSize); + currentBuffer.skipBytes(HttpPostBodyUtil.chunkSize); + return slice; + } else { + // to continue + ChannelBuffer slice = currentBuffer; + currentBuffer = null; + return slice; + } + } + + /** + * From the current context (currentBuffer and currentData), returns the next HttpChunk + * (if possible) trying to get sizeleft bytes more into the currentBuffer. + * This is the Multipart version. + * + * @param sizeleft the number of bytes to try to get from currentData + * @return the next HttpChunk or null if not enough bytes were found + * @throws ErrorDataEncoderException if the encoding is in error + */ + private HttpChunk encodeNextChunkMultipart(int sizeleft) throws ErrorDataEncoderException { + if (currentData == null) { + return null; + } + ChannelBuffer buffer; + if (currentData instanceof InternalAttribute) { + String internal = ((InternalAttribute) currentData).toString(); + byte[] bytes; + try { + bytes = internal.getBytes("ASCII"); + } catch (UnsupportedEncodingException e) { + throw new ErrorDataEncoderException(e); + } + buffer = ChannelBuffers.wrappedBuffer(bytes); + currentData = null; + } else { + if (currentData instanceof Attribute) { + try { + buffer = ((Attribute) currentData).getChunk(sizeleft); + } catch (IOException e) { + throw new ErrorDataEncoderException(e); + } + } else { + try { + buffer = ((FileUpload) currentData).getChunk(sizeleft); + } catch (IOException e) { + throw new ErrorDataEncoderException(e); + } + } + if (buffer.capacity() == 0) { + // end for current InterfaceHttpData, need more data + currentData = null; + return null; + } + } + if (currentBuffer == null) { + currentBuffer = buffer; + } else { + currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer, + buffer); + } + if (currentBuffer.readableBytes() < HttpPostBodyUtil.chunkSize) { + currentData = null; + return null; + } + buffer = fillChannelBuffer(); + return new DefaultHttpChunk(buffer); + } + + /** + * From the current context (currentBuffer and currentData), returns the next HttpChunk + * (if possible) trying to get sizeleft bytes more into the currentBuffer. + * This is the UrlEncoded version. + * + * @param sizeleft the number of bytes to try to get from currentData + * @return the next HttpChunk or null if not enough bytes were found + * @throws ErrorDataEncoderException if the encoding is in error + */ + private HttpChunk encodeNextChunkUrlEncoded(int sizeleft) throws ErrorDataEncoderException { + if (currentData == null) { + return null; + } + int size = sizeleft; + ChannelBuffer buffer; + if (isKey) { + // get name + String key = currentData.getName(); + buffer = ChannelBuffers.wrappedBuffer(key.getBytes()); + isKey = false; + if (currentBuffer == null) { + currentBuffer = ChannelBuffers.wrappedBuffer( + buffer, ChannelBuffers.wrappedBuffer("=".getBytes())); + //continue + size -= buffer.readableBytes() + 1; + } else { + currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer, + buffer, ChannelBuffers.wrappedBuffer("=".getBytes())); + //continue + size -= buffer.readableBytes() + 1; + } + if (currentBuffer.readableBytes() >= HttpPostBodyUtil.chunkSize) { + buffer = fillChannelBuffer(); + return new DefaultHttpChunk(buffer); + } + } + try { + buffer = ((Attribute) currentData).getChunk(size); + } catch (IOException e) { + throw new ErrorDataEncoderException(e); + } + ChannelBuffer delimiter = null; + if (buffer.readableBytes() < size) { + // delimiter + isKey = true; + delimiter = iterator.hasNext() ? + ChannelBuffers.wrappedBuffer("&".getBytes()) : + null; + } + if (buffer.capacity() == 0) { + // end for current InterfaceHttpData, need potentially more data + currentData = null; + if (currentBuffer == null) { + currentBuffer = delimiter; + } else { + if (delimiter != null) { + currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer, + delimiter); + } + } + if (currentBuffer.readableBytes() >= HttpPostBodyUtil.chunkSize) { + buffer = fillChannelBuffer(); + return new DefaultHttpChunk(buffer); + } + return null; + } + if (currentBuffer == null) { + if (delimiter != null) { + currentBuffer = ChannelBuffers.wrappedBuffer(buffer, + delimiter); + } else { + currentBuffer = buffer; + } + } else { + if (delimiter != null) { + currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer, + buffer, delimiter); + } else { + currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer, + buffer); + } + } + if (currentBuffer.readableBytes() < HttpPostBodyUtil.chunkSize) { + // end for current InterfaceHttpData, need more data + currentData = null; + isKey = true; + return null; + } + buffer = fillChannelBuffer(); + // size = 0 + return new DefaultHttpChunk(buffer); + } + + @Override + public void close() throws Exception { + //NO since the user can want to reuse (broadcast for instance) cleanFiles(); + } + + /** + * Returns the next available HttpChunk. The caller is responsible to test if this chunk is the + * last one (isLast()), in order to stop calling this method. + * + * @return the next available HttpChunk + * @throws ErrorDataEncoderException if the encoding is in error + */ + @Override + public HttpChunk nextChunk() throws ErrorDataEncoderException { + if (isLastChunk) { + isLastChunkSent = true; + return new DefaultHttpChunk(ChannelBuffers.EMPTY_BUFFER); + } + ChannelBuffer buffer = null; + int size = HttpPostBodyUtil.chunkSize; + // first test if previous buffer is not empty + if (currentBuffer != null) { + size -= currentBuffer.readableBytes(); + } + if (size <= 0) { + //NextChunk from buffer + buffer = fillChannelBuffer(); + return new DefaultHttpChunk(buffer); + } + // size > 0 + if (currentData != null) { + // continue to read data + if (isMultipart) { + HttpChunk chunk = encodeNextChunkMultipart(size); + if (chunk != null) { + return chunk; + } + } else { + HttpChunk chunk = encodeNextChunkUrlEncoded(size); + if (chunk != null) { + //NextChunk Url from currentData + return chunk; + } + } + size = HttpPostBodyUtil.chunkSize - currentBuffer.readableBytes(); + } + if (! iterator.hasNext()) { + isLastChunk = true; + //NextChunk as last non empty from buffer + buffer = currentBuffer; + currentBuffer = null; + return new DefaultHttpChunk(buffer); + } + while (size > 0 && iterator.hasNext()) { + currentData = iterator.next(); + HttpChunk chunk; + if (isMultipart) { + chunk = encodeNextChunkMultipart(size); + } else { + chunk = encodeNextChunkUrlEncoded(size); + } + if (chunk == null) { + // not enough + size = HttpPostBodyUtil.chunkSize - currentBuffer.readableBytes(); + continue; + } + //NextChunk from data + return chunk; + } + // end since no more data + isLastChunk = true; + if (currentBuffer == null) { + isLastChunkSent = true; + //LastChunk with no more data + return new DefaultHttpChunk(ChannelBuffers.EMPTY_BUFFER); + } + //Previous LastChunk with no more data + buffer = currentBuffer; + currentBuffer = null; + return new DefaultHttpChunk(buffer); + } + + @Override + public boolean isEndOfInput() throws Exception { + return isLastChunkSent; + } + + + public boolean hasNextChunk() throws Exception { + return !isLastChunkSent; + } + + /** + * Exception when an error occurs while encoding + */ + public static class ErrorDataEncoderException extends Exception { + /** */ - public ErrorDataEncoderException() { - } + private static final long serialVersionUID = 5020247425493164465L; - /** - * @param arg0 - */ - public ErrorDataEncoderException(String arg0) { - super(arg0); - } + /** + */ + public ErrorDataEncoderException() { + } - /** - * @param arg0 - */ - public ErrorDataEncoderException(Throwable arg0) { - super(arg0); - } + /** + * @param arg0 + */ + public ErrorDataEncoderException(String arg0) { + super(arg0); + } - /** - * @param arg0 - * @param arg1 - */ - public ErrorDataEncoderException(String arg0, Throwable arg1) { - super(arg0, arg1); - } - } + /** + * @param arg0 + */ + public ErrorDataEncoderException(Throwable arg0) { + super(arg0); + } + /** + * @param arg0 + * @param arg1 + */ + public ErrorDataEncoderException(String arg0, Throwable arg1) { + super(arg0, arg1); + } + } } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/InterfaceHttpData.java b/src/main/java/org/jboss/netty/handler/codec/http/InterfaceHttpData.java index 90d3292e4c..53a55129ef 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/InterfaceHttpData.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/InterfaceHttpData.java @@ -33,4 +33,4 @@ public interface InterfaceHttpData extends Comparable { * @return The HttpDataType */ HttpDataType getHttpDataType(); -} \ No newline at end of file +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/MemoryAttribute.java b/src/main/java/org/jboss/netty/handler/codec/http/MemoryAttribute.java index e6061a70e3..b69b81c605 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/MemoryAttribute.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/MemoryAttribute.java @@ -105,4 +105,4 @@ public class MemoryAttribute extends AbstractMemoryHttpData implements Attribute return getName() + "=" + getValue(); } -} \ No newline at end of file +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/MemoryFileUpload.java b/src/main/java/org/jboss/netty/handler/codec/http/MemoryFileUpload.java index 7b9c11305e..04b3970bd7 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/MemoryFileUpload.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/MemoryFileUpload.java @@ -17,8 +17,6 @@ package org.jboss.netty.handler.codec.http; import java.nio.charset.Charset; -import org.jboss.netty.handler.codec.http.HttpHeaders; - /** * Default FileUpload implementation that stores file into memory.

* @@ -125,4 +123,4 @@ public class MemoryFileUpload extends AbstractMemoryHttpData implements FileUplo "Completed: " + isCompleted() + "\r\nIsInMemory: " + isInMemory(); } -} \ No newline at end of file +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/MixedAttribute.java b/src/main/java/org/jboss/netty/handler/codec/http/MixedAttribute.java index d0c5b15b35..44675e53fe 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/MixedAttribute.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/MixedAttribute.java @@ -199,4 +199,4 @@ public class MixedAttribute implements Attribute { return attribute.getFile(); } -} \ No newline at end of file +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/MixedFileUpload.java b/src/main/java/org/jboss/netty/handler/codec/http/MixedFileUpload.java index 1099e59733..377a451434 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/MixedFileUpload.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/MixedFileUpload.java @@ -224,4 +224,4 @@ public class MixedFileUpload implements FileUpload { return fileUpload.getFile(); } -} \ No newline at end of file +}