/public/javascripts/jQuery-validationEngine-2.6.1/test/NanoHTTPD.java

https://bitbucket.org/hamidrezas/melobit · Java · 759 lines · 515 code · 71 blank · 173 comment · 85 complexity · 699f7b305e671fd63a03034af8f4cd40 MD5 · raw file

  1. import java.io.BufferedReader;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.IOException;
  6. import java.io.InputStream;
  7. import java.io.InputStreamReader;
  8. import java.io.OutputStream;
  9. import java.io.PrintWriter;
  10. import java.net.ServerSocket;
  11. import java.net.Socket;
  12. import java.net.URLEncoder;
  13. import java.util.Date;
  14. import java.util.Enumeration;
  15. import java.util.Hashtable;
  16. import java.util.Locale;
  17. import java.util.Properties;
  18. import java.util.StringTokenizer;
  19. import java.util.TimeZone;
  20. /**
  21. * A simple, tiny, nicely embeddable HTTP 1.0 server in Java
  22. *
  23. * <p> NanoHTTPD version 1.14,
  24. * Copyright &copy; 2001,2005-2010 Jarno Elonen (elonen@iki.fi, http://iki.fi/elonen/)
  25. *
  26. * <p><b>Features + limitations: </b><ul>
  27. *
  28. * <li> Only one Java file </li>
  29. * <li> Java 1.1 compatible </li>
  30. * <li> Released as open source, Modified BSD licence </li>
  31. * <li> No fixed config files, logging, authorization etc. (Implement yourself if you need them.) </li>
  32. * <li> Supports parameter parsing of GET and POST methods </li>
  33. * <li> Supports both dynamic content and file serving </li>
  34. * <li> Never caches anything </li>
  35. * <li> Doesn't limit bandwidth, request time or simultaneous connections </li>
  36. * <li> Default code serves files and shows all HTTP parameters and headers</li>
  37. * <li> File server supports directory listing, index.html and index.htm </li>
  38. * <li> File server does the 301 redirection trick for directories without '/'</li>
  39. * <li> File server supports simple skipping for files (continue download) </li>
  40. * <li> File server uses current directory as a web root </li>
  41. * <li> File server serves also very long files without memory overhead </li>
  42. * <li> Contains a built-in list of most common mime types </li>
  43. * <li> All header names are converted lowercase so they don't vary between browsers/clients </li>
  44. *
  45. * </ul>
  46. *
  47. * <p><b>Ways to use: </b><ul>
  48. *
  49. * <li> Run as a standalone app, serves files from current directory and shows requests</li>
  50. * <li> Subclass serve() and embed to your own program </li>
  51. * <li> Call serveFile() from serve() with your own base directory </li>
  52. *
  53. * </ul>
  54. *
  55. * See the end of the source file for distribution license
  56. * (Modified BSD licence)
  57. */
  58. public class NanoHTTPD
  59. {
  60. // ==================================================
  61. // API parts
  62. // ==================================================
  63. /**
  64. * Override this to customize the server.<p>
  65. *
  66. * (By default, this delegates to serveFile() and allows directory listing.)
  67. *
  68. * @parm uri Percent-decoded URI without parameters, for example "/index.cgi"
  69. * @parm method "GET", "POST" etc.
  70. * @parm parms Parsed, percent decoded parameters from URI and, in case of POST, data.
  71. * @parm header Header entries, percent decoded
  72. * @return HTTP response, see class Response for details
  73. */
  74. public Response serve( String uri, String method, Properties header, Properties parms )
  75. {
  76. System.out.println( method + " '" + uri + "' " );
  77. Enumeration e = header.propertyNames();
  78. while ( e.hasMoreElements())
  79. {
  80. String value = (String)e.nextElement();
  81. // orefalo: way to much logging
  82. // System.out.println( " HDR: '" + value + "' = '" +
  83. // header.getProperty( value ) + "'" );
  84. }
  85. e = parms.propertyNames();
  86. while ( e.hasMoreElements())
  87. {
  88. String value = (String)e.nextElement();
  89. System.out.println( " PRM: '" + value + "' = '" +
  90. parms.getProperty( value ) + "'" );
  91. }
  92. return serveFile( uri, header, new File("."), true );
  93. }
  94. /**
  95. * HTTP response.
  96. * Return one of these from serve().
  97. */
  98. public class Response
  99. {
  100. /**
  101. * Default constructor: response = HTTP_OK, data = mime = 'null'
  102. */
  103. public Response()
  104. {
  105. this.status = HTTP_OK;
  106. }
  107. /**
  108. * Basic constructor.
  109. */
  110. public Response( String status, String mimeType, InputStream data )
  111. {
  112. this.status = status;
  113. this.mimeType = mimeType;
  114. this.data = data;
  115. }
  116. /**
  117. * Convenience method that makes an InputStream out of
  118. * given text.
  119. */
  120. public Response( String status, String mimeType, String txt )
  121. {
  122. this.status = status;
  123. this.mimeType = mimeType;
  124. this.data = new ByteArrayInputStream( txt.getBytes());
  125. }
  126. /**
  127. * Adds given line to the header.
  128. */
  129. public void addHeader( String name, String value )
  130. {
  131. header.put( name, value );
  132. }
  133. /**
  134. * HTTP status code after processing, e.g. "200 OK", HTTP_OK
  135. */
  136. public String status;
  137. /**
  138. * MIME type of content, e.g. "text/html"
  139. */
  140. public String mimeType;
  141. /**
  142. * Data of the response, may be null.
  143. */
  144. public InputStream data;
  145. /**
  146. * Headers for the HTTP response. Use addHeader()
  147. * to add lines.
  148. */
  149. public Properties header = new Properties();
  150. }
  151. /**
  152. * Some HTTP response status codes
  153. */
  154. public static final String
  155. HTTP_OK = "200 OK",
  156. HTTP_REDIRECT = "301 Moved Permanently",
  157. HTTP_FORBIDDEN = "403 Forbidden",
  158. HTTP_NOTFOUND = "404 Not Found",
  159. HTTP_BADREQUEST = "400 Bad Request",
  160. HTTP_INTERNALERROR = "500 Internal Server Error",
  161. HTTP_NOTIMPLEMENTED = "501 Not Implemented";
  162. /**
  163. * Common mime types for dynamic content
  164. */
  165. public static final String
  166. MIME_PLAINTEXT = "text/plain",
  167. MIME_HTML = "text/html",
  168. MIME_DEFAULT_BINARY = "application/octet-stream";
  169. // ==================================================
  170. // Socket & server code
  171. // ==================================================
  172. /**
  173. * Starts a HTTP server to given port.<p>
  174. * Throws an IOException if the socket is already in use
  175. */
  176. public NanoHTTPD( int port ) throws IOException
  177. {
  178. myTcpPort = port;
  179. myServerSocket = new ServerSocket( myTcpPort );
  180. myThread = new Thread( new Runnable()
  181. {
  182. public void run()
  183. {
  184. try
  185. {
  186. while( true )
  187. new HTTPSession( myServerSocket.accept());
  188. }
  189. catch ( IOException ioe )
  190. {}
  191. }
  192. });
  193. myThread.setDaemon( true );
  194. myThread.start();
  195. }
  196. /**
  197. * Stops the server.
  198. */
  199. public void stop()
  200. {
  201. try
  202. {
  203. myServerSocket.close();
  204. myThread.join();
  205. }
  206. catch ( IOException ioe ) {}
  207. catch ( InterruptedException e ) {}
  208. }
  209. /**
  210. * Starts as a standalone file server and waits for Enter.
  211. */
  212. public static void main( String[] args )
  213. {
  214. System.out.println( "NanoHTTPD 1.14 (C) 2001,2005-2010 Jarno Elonen\n" +
  215. "(Command line options: [port] [--licence])\n" );
  216. // Show licence if requested
  217. int lopt = -1;
  218. for ( int i=0; i<args.length; ++i )
  219. if ( args[i].toLowerCase().endsWith( "licence" ))
  220. {
  221. lopt = i;
  222. System.out.println( LICENCE + "\n" );
  223. }
  224. // Change port if requested
  225. int port = 80;
  226. if ( args.length > 0 && lopt != 0 )
  227. port = Integer.parseInt( args[0] );
  228. if ( args.length > 1 &&
  229. args[1].toLowerCase().endsWith( "licence" ))
  230. System.out.println( LICENCE + "\n" );
  231. NanoHTTPD nh = null;
  232. try
  233. {
  234. nh = new NanoHTTPD( port );
  235. }
  236. catch( IOException ioe )
  237. {
  238. System.err.println( "Couldn't start server:\n" + ioe );
  239. System.exit( -1 );
  240. }
  241. nh.myFileDir = new File("");
  242. System.out.println( "Now serving files in port " + port + " from \"" +
  243. new File("").getAbsolutePath() + "\"" );
  244. System.out.println( "Hit Enter to stop.\n" );
  245. try { System.in.read(); } catch( Throwable t ) {};
  246. }
  247. /**
  248. * Handles one session, i.e. parses the HTTP request
  249. * and returns the response.
  250. */
  251. private class HTTPSession implements Runnable
  252. {
  253. public HTTPSession( Socket s )
  254. {
  255. mySocket = s;
  256. Thread t = new Thread( this );
  257. t.setDaemon( true );
  258. t.start();
  259. }
  260. public void run()
  261. {
  262. try
  263. {
  264. InputStream is = mySocket.getInputStream();
  265. if ( is == null) return;
  266. BufferedReader in = new BufferedReader( new InputStreamReader( is ));
  267. // Read the request line
  268. String inLine = in.readLine();
  269. if (inLine == null) return;
  270. StringTokenizer st = new StringTokenizer( inLine );
  271. if ( !st.hasMoreTokens())
  272. sendError( HTTP_BADREQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html" );
  273. String method = st.nextToken();
  274. if ( !st.hasMoreTokens())
  275. sendError( HTTP_BADREQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html" );
  276. String uri = st.nextToken();
  277. // Decode parameters from the URI
  278. Properties parms = new Properties();
  279. int qmi = uri.indexOf( '?' );
  280. if ( qmi >= 0 )
  281. {
  282. decodeParms( uri.substring( qmi+1 ), parms );
  283. uri = decodePercent( uri.substring( 0, qmi ));
  284. }
  285. else uri = decodePercent(uri);
  286. // If there's another token, it's protocol version,
  287. // followed by HTTP headers. Ignore version but parse headers.
  288. // NOTE: this now forces header names uppercase since they are
  289. // case insensitive and vary by client.
  290. Properties header = new Properties();
  291. if ( st.hasMoreTokens())
  292. {
  293. String line = in.readLine();
  294. while ( line.trim().length() > 0 )
  295. {
  296. int p = line.indexOf( ':' );
  297. header.put( line.substring(0,p).trim().toLowerCase(), line.substring(p+1).trim());
  298. line = in.readLine();
  299. }
  300. }
  301. // If the method is POST, there may be parameters
  302. // in data section, too, read it:
  303. if ( method.equalsIgnoreCase( "POST" ))
  304. {
  305. long size = 0x7FFFFFFFFFFFFFFFl;
  306. String contentLength = header.getProperty("content-length");
  307. if (contentLength != null)
  308. {
  309. try { size = Integer.parseInt(contentLength); }
  310. catch (NumberFormatException ex) {}
  311. }
  312. String postLine = "";
  313. char buf[] = new char[512];
  314. int read = in.read(buf);
  315. while ( read >= 0 && size > 0 && !postLine.endsWith("\r\n") )
  316. {
  317. size -= read;
  318. postLine += String.valueOf(buf, 0, read);
  319. if ( size > 0 )
  320. read = in.read(buf);
  321. }
  322. postLine = postLine.trim();
  323. decodeParms( postLine, parms );
  324. }
  325. // Ok, now do the serve()
  326. Response r = serve( uri, method, header, parms );
  327. if ( r == null )
  328. sendError( HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: Serve() returned a null response." );
  329. else
  330. sendResponse( r.status, r.mimeType, r.header, r.data );
  331. in.close();
  332. }
  333. catch ( IOException ioe )
  334. {
  335. try
  336. {
  337. sendError( HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
  338. }
  339. catch ( Throwable t ) {}
  340. }
  341. catch ( InterruptedException ie )
  342. {
  343. // Thrown by sendError, ignore and exit the thread.
  344. }
  345. }
  346. /**
  347. * Decodes the percent encoding scheme. <br/>
  348. * For example: "an+example%20string" -> "an example string"
  349. */
  350. private String decodePercent( String str ) throws InterruptedException
  351. {
  352. try
  353. {
  354. StringBuffer sb = new StringBuffer();
  355. for( int i=0; i<str.length(); i++ )
  356. {
  357. char c = str.charAt( i );
  358. switch ( c )
  359. {
  360. case '+':
  361. sb.append( ' ' );
  362. break;
  363. case '%':
  364. sb.append((char)Integer.parseInt( str.substring(i+1,i+3), 16 ));
  365. i += 2;
  366. break;
  367. default:
  368. sb.append( c );
  369. break;
  370. }
  371. }
  372. return new String( sb.toString().getBytes());
  373. }
  374. catch( Exception e )
  375. {
  376. sendError( HTTP_BADREQUEST, "BAD REQUEST: Bad percent-encoding." );
  377. return null;
  378. }
  379. }
  380. /**
  381. * Decodes parameters in percent-encoded URI-format
  382. * ( e.g. "name=Jack%20Daniels&pass=Single%20Malt" ) and
  383. * adds them to given Properties. NOTE: this doesn't support multiple
  384. * identical keys due to the simplicity of Properties -- if you need multiples,
  385. * you might want to replace the Properties with a Hastable of Vectors or such.
  386. */
  387. private void decodeParms( String parms, Properties p )
  388. throws InterruptedException
  389. {
  390. if ( parms == null )
  391. return;
  392. StringTokenizer st = new StringTokenizer( parms, "&" );
  393. while ( st.hasMoreTokens())
  394. {
  395. String e = st.nextToken();
  396. int sep = e.indexOf( '=' );
  397. if ( sep >= 0 )
  398. p.put( decodePercent( e.substring( 0, sep )).trim(),
  399. decodePercent( e.substring( sep+1 )));
  400. }
  401. }
  402. /**
  403. * Returns an error message as a HTTP response and
  404. * throws InterruptedException to stop furhter request processing.
  405. */
  406. private void sendError( String status, String msg ) throws InterruptedException
  407. {
  408. sendResponse( status, MIME_PLAINTEXT, null, new ByteArrayInputStream( msg.getBytes()));
  409. throw new InterruptedException();
  410. }
  411. /**
  412. * Sends given response to the socket.
  413. */
  414. private void sendResponse( String status, String mime, Properties header, InputStream data )
  415. {
  416. try
  417. {
  418. if ( status == null )
  419. throw new Error( "sendResponse(): Status can't be null." );
  420. OutputStream out = mySocket.getOutputStream();
  421. PrintWriter pw = new PrintWriter( out );
  422. pw.print("HTTP/1.0 " + status + " \r\n");
  423. if ( mime != null )
  424. pw.print("Content-Type: " + mime + "\r\n");
  425. if ( header == null || header.getProperty( "Date" ) == null )
  426. pw.print( "Date: " + gmtFrmt.format( new Date()) + "\r\n");
  427. if ( header != null )
  428. {
  429. Enumeration e = header.keys();
  430. while ( e.hasMoreElements())
  431. {
  432. String key = (String)e.nextElement();
  433. String value = header.getProperty( key );
  434. pw.print( key + ": " + value + "\r\n");
  435. }
  436. }
  437. pw.print("\r\n");
  438. pw.flush();
  439. if ( data != null )
  440. {
  441. byte[] buff = new byte[2048];
  442. while (true)
  443. {
  444. int read = data.read( buff, 0, 2048 );
  445. if (read <= 0)
  446. break;
  447. out.write( buff, 0, read );
  448. }
  449. }
  450. out.flush();
  451. out.close();
  452. if ( data != null )
  453. data.close();
  454. }
  455. catch( IOException ioe )
  456. {
  457. // Couldn't write? No can do.
  458. try { mySocket.close(); } catch( Throwable t ) {}
  459. }
  460. }
  461. private Socket mySocket;
  462. };
  463. /**
  464. * URL-encodes everything between "/"-characters.
  465. * Encodes spaces as '%20' instead of '+'.
  466. */
  467. private String encodeUri( String uri )
  468. {
  469. String newUri = "";
  470. StringTokenizer st = new StringTokenizer( uri, "/ ", true );
  471. while ( st.hasMoreTokens())
  472. {
  473. String tok = st.nextToken();
  474. if ( tok.equals( "/" ))
  475. newUri += "/";
  476. else if ( tok.equals( " " ))
  477. newUri += "%20";
  478. else
  479. {
  480. newUri += URLEncoder.encode( tok );
  481. // For Java 1.4 you'll want to use this instead:
  482. // try { newUri += URLEncoder.encode( tok, "UTF-8" ); } catch ( UnsupportedEncodingException uee )
  483. }
  484. }
  485. return newUri;
  486. }
  487. private int myTcpPort;
  488. private final ServerSocket myServerSocket;
  489. private Thread myThread;
  490. File myFileDir;
  491. // ==================================================
  492. // File server code
  493. // ==================================================
  494. /**
  495. * Serves file from homeDir and its' subdirectories (only).
  496. * Uses only URI, ignores all headers and HTTP parameters.
  497. */
  498. public Response serveFile( String uri, Properties header, File homeDir,
  499. boolean allowDirectoryListing )
  500. {
  501. // Make sure we won't die of an exception later
  502. if ( !homeDir.isDirectory())
  503. return new Response( HTTP_INTERNALERROR, MIME_PLAINTEXT,
  504. "INTERNAL ERRROR: serveFile(): given homeDir is not a directory." );
  505. // Remove URL arguments
  506. uri = uri.trim().replace( File.separatorChar, '/' );
  507. if ( uri.indexOf( '?' ) >= 0 )
  508. uri = uri.substring(0, uri.indexOf( '?' ));
  509. // Prohibit getting out of current directory
  510. if ( uri.startsWith( ".." ) || uri.endsWith( ".." ) || uri.indexOf( "../" ) >= 0 )
  511. return new Response( HTTP_FORBIDDEN, MIME_PLAINTEXT,
  512. "FORBIDDEN: Won't serve ../ for security reasons." );
  513. File f = new File( homeDir, uri );
  514. if ( !f.exists())
  515. return new Response( HTTP_NOTFOUND, MIME_PLAINTEXT,
  516. "Error 404, file not found." );
  517. // List the directory, if necessary
  518. if ( f.isDirectory())
  519. {
  520. // Browsers get confused without '/' after the
  521. // directory, send a redirect.
  522. if ( !uri.endsWith( "/" ))
  523. {
  524. uri += "/";
  525. Response r = new Response( HTTP_REDIRECT, MIME_HTML,
  526. "<html><body>Redirected: <a href=\"" + uri + "\">" +
  527. uri + "</a></body></html>");
  528. r.addHeader( "Location", uri );
  529. return r;
  530. }
  531. // First try index.html and index.htm
  532. if ( new File( f, "index.html" ).exists())
  533. f = new File( homeDir, uri + "/index.html" );
  534. else if ( new File( f, "index.htm" ).exists())
  535. f = new File( homeDir, uri + "/index.htm" );
  536. // No index file, list the directory
  537. else if ( allowDirectoryListing )
  538. {
  539. String[] files = f.list();
  540. String msg = "<html><body><h1>Directory " + uri + "</h1><br/>";
  541. if ( uri.length() > 1 )
  542. {
  543. String u = uri.substring( 0, uri.length()-1 );
  544. int slash = u.lastIndexOf( '/' );
  545. if ( slash >= 0 && slash < u.length())
  546. msg += "<b><a href=\"" + uri.substring(0, slash+1) + "\">..</a></b><br/>";
  547. }
  548. for ( int i=0; i<files.length; ++i )
  549. {
  550. File curFile = new File( f, files[i] );
  551. boolean dir = curFile.isDirectory();
  552. if ( dir )
  553. {
  554. msg += "<b>";
  555. files[i] += "/";
  556. }
  557. msg += "<a href=\"" + encodeUri( uri + files[i] ) + "\">" +
  558. files[i] + "</a>";
  559. // Show file size
  560. if ( curFile.isFile())
  561. {
  562. long len = curFile.length();
  563. msg += " &nbsp;<font size=2>(";
  564. if ( len < 1024 )
  565. msg += curFile.length() + " bytes";
  566. else if ( len < 1024 * 1024 )
  567. msg += curFile.length()/1024 + "." + (curFile.length()%1024/10%100) + " KB";
  568. else
  569. msg += curFile.length()/(1024*1024) + "." + curFile.length()%(1024*1024)/10%100 + " MB";
  570. msg += ")</font>";
  571. }
  572. msg += "<br/>";
  573. if ( dir ) msg += "</b>";
  574. }
  575. return new Response( HTTP_OK, MIME_HTML, msg );
  576. }
  577. else
  578. {
  579. return new Response( HTTP_FORBIDDEN, MIME_PLAINTEXT,
  580. "FORBIDDEN: No directory listing." );
  581. }
  582. }
  583. try
  584. {
  585. // Get MIME type from file name extension, if possible
  586. String mime = null;
  587. int dot = f.getCanonicalPath().lastIndexOf( '.' );
  588. if ( dot >= 0 )
  589. mime = (String)theMimeTypes.get( f.getCanonicalPath().substring( dot + 1 ).toLowerCase());
  590. if ( mime == null )
  591. mime = MIME_DEFAULT_BINARY;
  592. // Support (simple) skipping:
  593. long startFrom = 0;
  594. String range = header.getProperty( "range" );
  595. if ( range != null )
  596. {
  597. if ( range.startsWith( "bytes=" ))
  598. {
  599. range = range.substring( "bytes=".length());
  600. int minus = range.indexOf( '-' );
  601. if ( minus > 0 )
  602. range = range.substring( 0, minus );
  603. try {
  604. startFrom = Long.parseLong( range );
  605. }
  606. catch ( NumberFormatException nfe ) {}
  607. }
  608. }
  609. FileInputStream fis = new FileInputStream( f );
  610. fis.skip( startFrom );
  611. Response r = new Response( HTTP_OK, mime, fis );
  612. r.addHeader( "Content-length", "" + (f.length() - startFrom));
  613. r.addHeader( "Content-range", "" + startFrom + "-" +
  614. (f.length()-1) + "/" + f.length());
  615. return r;
  616. }
  617. catch( IOException ioe )
  618. {
  619. return new Response( HTTP_FORBIDDEN, MIME_PLAINTEXT, "FORBIDDEN: Reading file failed." );
  620. }
  621. }
  622. /**
  623. * Hashtable mapping (String)FILENAME_EXTENSION -> (String)MIME_TYPE
  624. */
  625. private static Hashtable theMimeTypes = new Hashtable();
  626. static
  627. {
  628. StringTokenizer st = new StringTokenizer(
  629. "htm text/html "+
  630. "html text/html "+
  631. //orefalo: added css and js mime types
  632. "css text/css "+
  633. "js application/javascript "+
  634. "txt text/plain "+
  635. "asc text/plain "+
  636. "gif image/gif "+
  637. "jpg image/jpeg "+
  638. "jpeg image/jpeg "+
  639. "png image/png "+
  640. "mp3 audio/mpeg "+
  641. "m3u audio/mpeg-url " +
  642. "pdf application/pdf "+
  643. "doc application/msword "+
  644. "ogg application/x-ogg "+
  645. "zip application/octet-stream "+
  646. "exe application/octet-stream "+
  647. "class application/octet-stream " );
  648. while ( st.hasMoreTokens())
  649. theMimeTypes.put( st.nextToken(), st.nextToken());
  650. }
  651. /**
  652. * GMT date formatter
  653. */
  654. private static java.text.SimpleDateFormat gmtFrmt;
  655. static
  656. {
  657. gmtFrmt = new java.text.SimpleDateFormat( "E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
  658. gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));
  659. }
  660. /**
  661. * The distribution licence
  662. */
  663. private static final String LICENCE =
  664. "Copyright (C) 2001,2005-2010 by Jarno Elonen <elonen@iki.fi>\n"+
  665. "\n"+
  666. "Redistribution and use in source and binary forms, with or without\n"+
  667. "modification, are permitted provided that the following conditions\n"+
  668. "are met:\n"+
  669. "\n"+
  670. "Redistributions of source code must retain the above copyright notice,\n"+
  671. "this list of conditions and the following disclaimer. Redistributions in\n"+
  672. "binary form must reproduce the above copyright notice, this list of\n"+
  673. "conditions and the following disclaimer in the documentation and/or other\n"+
  674. "materials provided with the distribution. The name of the author may not\n"+
  675. "be used to endorse or promote products derived from this software without\n"+
  676. "specific prior written permission. \n"+
  677. " \n"+
  678. "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"+
  679. "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"+
  680. "OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"+
  681. "IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"+
  682. "INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n"+
  683. "NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"+
  684. "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"+
  685. "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"+
  686. "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"+
  687. "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.";
  688. }