1 29 30 package com.caucho.quercus.lib; 31 32 import com.caucho.quercus.annotation.Optional; 33 import com.caucho.quercus.env.*; 34 import com.caucho.quercus.lib.file.BinaryInput; 35 import com.caucho.quercus.lib.file.BinaryStream; 36 import com.caucho.quercus.lib.file.FileModule; 37 import com.caucho.quercus.module.AbstractQuercusModule; 38 import com.caucho.util.Base64; 39 import com.caucho.util.CharBuffer; 40 import com.caucho.util.L10N; 41 import com.caucho.vfs.TempBuffer; 42 43 import java.io.IOException ; 44 import java.io.InputStream ; 45 import java.io.InputStreamReader ; 46 import java.io.LineNumberReader ; 47 import java.io.OutputStream ; 48 import java.io.OutputStreamWriter ; 49 import java.net.Socket ; 50 import java.net.URL ; 51 import java.util.LinkedHashMap ; 52 import java.util.Map ; 53 import java.util.Set ; 54 import java.util.logging.Logger ; 55 56 59 public class UrlModule extends AbstractQuercusModule { 60 private static final L10N L = new L10N(UrlModule.class); 61 private static final Logger log 62 = Logger.getLogger(UrlModule.class.getName()); 63 64 67 public static String base64_encode(InputStream is) 68 { 69 CharBuffer cb = new CharBuffer(); 70 71 TempBuffer tb = TempBuffer.allocate(); 72 byte []buffer = tb.getBuffer(); 73 74 int len; 75 int offset = 0; 76 77 try { 78 while ((len = is.read(buffer, offset, buffer.length)) >= 0) { 79 int tail = len % 3; 80 81 Base64.encode(cb, buffer, 0, len - tail); 82 83 System.arraycopy(buffer, len - tail, buffer, 0, tail); 84 offset = tail; 85 } 86 87 if (offset > 0) 88 Base64.encode(cb, buffer, 0, offset); 89 } catch (IOException e) { 90 throw new RuntimeException (e); 91 } 92 93 TempBuffer.free(tb); 94 95 return cb.toString(); 96 } 97 98 101 public static String base64_decode(String str) 102 { 103 if (str == null) 104 return ""; 105 106 return Base64.decode(str); 107 } 108 109 112 public String http_build_query(Value value, 113 @Optional String prefix) 114 { 115 StringBuilder sb = new StringBuilder (); 116 117 int index = 0; 118 if (value instanceof ArrayValue) { 119 ArrayValue array = (ArrayValue) value; 120 121 for (Map.Entry <Value,Value> entry : array.entrySet()) { 122 Value keyValue = entry.getKey(); 123 Value v = entry.getValue(); 124 125 String key; 126 127 if (keyValue.isLongConvertible()) 128 key = prefix + keyValue; 129 else 130 key = keyValue.toString(); 131 132 if (v instanceof ArrayValue) 133 http_build_query(sb, key, (ArrayValue) v); 134 else { 135 if (sb.length() > 0) 136 sb.append('&'); 137 138 sb.append(key); 139 sb.append('='); 140 urlencode(sb, v.toString()); 141 } 142 } 143 } 144 145 return sb.toString(); 146 } 147 148 151 private void http_build_query(StringBuilder sb, 152 String prefix, 153 ArrayValue array) 154 { 155 for (Map.Entry <Value,Value> entry : array.entrySet()) { 156 Value keyValue = entry.getKey(); 157 Value v = entry.getValue(); 158 159 String key = prefix + '[' + keyValue + ']'; 160 161 if (v instanceof ArrayValue) 162 http_build_query(sb, key, (ArrayValue) v); 163 else { 164 if (sb.length() > 0) 165 sb.append('&'); 166 167 sb.append(key); 168 sb.append('='); 169 urlencode(sb, v.toString()); 170 } 171 } 172 } 173 174 177 public static String urlencode(String str) 178 { 179 StringBuilder sb = new StringBuilder (); 180 181 urlencode(sb, str); 182 183 return sb.toString(); 184 } 185 186 189 private static void urlencode(StringBuilder sb, String str) 190 { 191 int len = str.length(); 192 193 for (int i = 0; i < len; i++) { 194 char ch = str.charAt(i); 195 196 if ('a' <= ch && ch <= 'z') 197 sb.append(ch); 198 else if ('A' <= ch && ch <= 'Z') 199 sb.append(ch); 200 else if ('0' <= ch && ch <= '9') 201 sb.append(ch); 202 else if (ch == '-' || ch == '_' || ch == '.') 203 sb.append(ch); 204 else if (ch == ' ') 205 sb.append('+'); 206 else { 207 sb.append('%'); 208 sb.append(toHexDigit(ch / 16)); 209 sb.append(toHexDigit(ch)); 210 } 211 } 212 } 213 214 217 public static String urldecode(String s) 218 { 219 int len = s.length(); 220 StringBuilder sb = new StringBuilder (); 221 222 for (int i = 0; i < len; i++) { 223 char ch = s.charAt(i); 224 225 if (ch == '%' && i + 2 < len) { 226 int d1 = s.charAt(i + 1); 227 int d2 = s.charAt(i + 2); 228 229 int v = 0; 230 231 if ('0' <= d1 && d1 <= '9') 232 v = 16 * (d1 - '0'); 233 else if ('a' <= d1 && d1 <= 'f') 234 v = 16 * (d1 - 'a' + 10); 235 else if ('A' <= d1 && d1 <= 'F') 236 v = 16 * (d1 - 'A' + 10); 237 else { 238 sb.append('%'); 239 continue; 240 } 241 242 if ('0' <= d2 && d2 <= '9') 243 v += (d2 - '0'); 244 else if ('a' <= d2 && d2 <= 'f') 245 v += (d2 - 'a' + 10); 246 else if ('A' <= d2 && d2 <= 'F') 247 v += (d2 - 'A' + 10); 248 else { 249 sb.append('%'); 250 continue; 251 } 252 253 i += 2; 254 sb.append((char) v); 255 } 256 else if (ch == '+') 257 sb.append(' '); 258 else 259 sb.append(ch); 260 } 261 262 return sb.toString(); 263 } 264 265 268 public static String rawurldecode(String s) 269 { 270 if (s == null) 271 return ""; 272 273 int len = s.length(); 274 StringBuilder sb = new StringBuilder (); 275 276 for (int i = 0; i < len; i++) { 277 char ch = s.charAt(i); 278 279 if (ch == '%' && i + 2 < len) { 280 int d1 = s.charAt(i + 1); 281 int d2 = s.charAt(i + 2); 282 283 int v = 0; 284 285 if ('0' <= d1 && d1 <= '9') 286 v = 16 * (d1 - '0'); 287 else if ('a' <= d1 && d1 <= 'f') 288 v = 16 * (d1 - 'a' + 10); 289 else if ('A' <= d1 && d1 <= 'F') 290 v = 16 * (d1 - 'A' + 10); 291 else { 292 sb.append('%'); 293 continue; 294 } 295 296 if ('0' <= d2 && d2 <= '9') 297 v += (d2 - '0'); 298 else if ('a' <= d2 && d2 <= 'f') 299 v += (d2 - 'a' + 10); 300 else if ('A' <= d2 && d2 <= 'F') 301 v += (d2 - 'A' + 10); 302 else { 303 sb.append('%'); 304 continue; 305 } 306 307 i += 2; 308 sb.append((char) v); 309 } 310 else 311 sb.append(ch); 312 } 313 314 return sb.toString(); 315 } 316 317 320 public static String rawurlencode(String str) 321 { 322 StringBuilder sb = new StringBuilder (); 323 324 for (int i = 0; i < str.length(); i++) { 325 char ch = str.charAt(i); 326 327 if ('a' <= ch && ch <= 'z' || 328 'A' <= ch && ch <= 'Z' || 329 '0' <= ch && ch <= '9' || 330 ch == '-' || ch == '_' || ch == '.') { 331 sb.append(ch); 332 } 333 else { 334 sb.append('%'); 335 sb.append(toHexDigit(ch >> 4)); 336 sb.append(toHexDigit(ch)); 337 } 338 } 339 340 return sb.toString(); 341 } 342 343 enum ParseUrlState { 344 INIT, USER, PASS, HOST, PORT, PATH, QUERY, FRAGMENT 345 }; 346 347 350 public static Value parse_url(Env env, String str) 351 { 352 if (str == null) 353 str = ""; 354 355 int i = 0; 356 int length = str.length(); 357 358 CharBuffer sb = new CharBuffer(); 359 360 ArrayValueImpl value = new ArrayValueImpl(); 361 value.put("path", ""); 362 363 ParseUrlState state = ParseUrlState.INIT; 364 365 String user = null; 366 367 for (; i < length; i++) { 368 char ch = str.charAt(i); 369 370 switch (ch) { 371 case ':': 372 if (state == ParseUrlState.INIT) { 373 value.put("scheme", sb.toString()); 374 sb.clear(); 375 376 if (length <= i + 1 || str.charAt(i + 1) != '/') { 377 state = ParseUrlState.PATH; 378 } 379 else if (length <= i + 2 || str.charAt(i + 2) != '/') { 380 state = ParseUrlState.PATH; 381 } 382 else if (length <= i + 3 || str.charAt(i + 3) != '/') { 383 i += 2; 384 state = ParseUrlState.USER; 385 } 386 else { 387 389 i += 2; 390 state = ParseUrlState.PATH; 391 } 392 } 393 else if (state == ParseUrlState.USER) { 394 user = sb.toString(); 395 sb.clear(); 396 state = ParseUrlState.PASS; 397 } 398 else if (state == ParseUrlState.HOST) { 399 value.put("host", sb.toString()); 400 sb.clear(); 401 state = ParseUrlState.PORT; 402 } 403 else 404 sb.append(ch); 405 break; 406 407 case '@': 408 if (state == ParseUrlState.USER) { 409 value.put("user", sb.toString()); 410 sb.clear(); 411 state = ParseUrlState.HOST; 412 } 413 else if (state == ParseUrlState.PASS) { 414 value.put("user", user); 415 value.put("pass", sb.toString()); 416 sb.clear(); 417 state = ParseUrlState.HOST; 418 } 419 else 420 sb.append(ch); 421 break; 422 423 case '/': 424 if (state == ParseUrlState.USER || state == ParseUrlState.HOST) { 425 value.put("host", sb.toString()); 426 sb.clear(); 427 state = ParseUrlState.PATH; 428 sb.append(ch); 429 } 430 else if (state == ParseUrlState.PASS) { 431 value.put("host", user); 432 value.put(new StringValueImpl("port"), 433 new LongValue(new StringValueImpl(sb.toString()).toLong())); 434 sb.clear(); 435 state = ParseUrlState.PATH; 436 sb.append(ch); 437 } 438 else if (state == ParseUrlState.PORT) { 439 value.put(new StringValueImpl("port"), 440 new LongValue(new StringValueImpl(sb.toString()).toLong())); 441 sb.clear(); 442 state = ParseUrlState.PATH; 443 sb.append(ch); 444 } 445 else 446 sb.append(ch); 447 break; 448 449 case '?': 450 if (state == ParseUrlState.USER || state == ParseUrlState.HOST) { 451 value.put("host", sb.toString()); 452 sb.clear(); 453 state = ParseUrlState.QUERY; 454 } 455 else if (state == ParseUrlState.PASS) { 456 value.put("host", user); 457 value.put(new StringValueImpl("port"), 458 new LongValue(new StringValueImpl(sb.toString()).toLong())); 459 sb.clear(); 460 state = ParseUrlState.QUERY; 461 } 462 else if (state == ParseUrlState.PORT) { 463 value.put(new StringValueImpl("port"), 464 new LongValue(new StringValueImpl(sb.toString()).toLong())); 465 sb.clear(); 466 state = ParseUrlState.QUERY; 467 } 468 else if (state == ParseUrlState.PATH) { 469 if (sb.length() > 0) 470 value.put("path", sb.toString()); 471 sb.clear(); 472 state = ParseUrlState.QUERY; 473 } 474 else 475 sb.append(ch); 476 break; 477 478 case '#': 479 if (state == ParseUrlState.USER || state == ParseUrlState.HOST) { 480 value.put("host", sb.toString()); 481 sb.clear(); 482 state = ParseUrlState.FRAGMENT; 483 } 484 else if (state == ParseUrlState.PASS) { 485 value.put("host", user); 486 value.put(new StringValueImpl("port"), 487 new LongValue(new StringValueImpl(sb.toString()).toLong())); 488 sb.clear(); 489 state = ParseUrlState.FRAGMENT; 490 } 491 else if (state == ParseUrlState.PORT) { 492 value.put(new StringValueImpl("port"), 493 new LongValue(new StringValueImpl(sb.toString()).toLong())); 494 sb.clear(); 495 state = ParseUrlState.FRAGMENT; 496 } 497 else if (state == ParseUrlState.PATH) { 498 if (sb.length() > 0) 499 value.put("path", sb.toString()); 500 sb.clear(); 501 state = ParseUrlState.FRAGMENT; 502 } 503 else if (state == ParseUrlState.QUERY) { 504 if (sb.length() > 0) 505 value.put("query", sb.toString()); 506 sb.clear(); 507 state = ParseUrlState.FRAGMENT; 508 } 509 else 510 sb.append(ch); 511 break; 512 513 default: 514 sb.append((char) ch); 515 break; 516 } 517 } 518 519 if (sb.length() == 0) { 520 } 521 else if (state == ParseUrlState.USER || 522 state == ParseUrlState.HOST) 523 value.put("host", sb.toString()); 524 else if (state == ParseUrlState.PASS) { 525 value.put("host", user); 526 value.put(new StringValueImpl("port"), 527 new LongValue(new StringValueImpl(sb.toString()).toLong())); 528 } 529 else if (state == ParseUrlState.PORT) { 530 value.put(new StringValueImpl("port"), 531 new LongValue(new StringValueImpl(sb.toString()).toLong())); 532 } 533 else if (state == ParseUrlState.QUERY) 534 value.put("query", sb.toString()); 535 else if (state == ParseUrlState.FRAGMENT) 536 value.put("fragment", sb.toString()); 537 else 538 value.put("path", sb.toString()); 539 540 return value; 541 } 542 543 public static Value http_build_query(Env env, Value formdata, 544 @Optional String numeric_prefix) 545 { 546 String result = 547 httpBuildQueryImpl(env, "", formdata, numeric_prefix).toString(); 548 549 return new StringValueImpl(result); 550 } 551 552 public static StringBuilder httpBuildQueryImpl(Env env, String path, 553 Value formdata, 554 String numeric_prefix) 555 { 556 StringBuilder result = new StringBuilder (); 557 558 Set <Map.Entry <Value,Value>> entrySet; 559 560 if (formdata.isArray()) 561 entrySet = ((ArrayValue)formdata).entrySet(); 562 else if (formdata.isObject()) { 563 Set <Map.Entry <String ,Value>> stringEntrySet 564 = ((ObjectValue)formdata).entrySet(); 565 566 LinkedHashMap <Value,Value> valueMap = new LinkedHashMap <Value,Value>(); 567 568 for (Map.Entry <String ,Value> entry : stringEntrySet) 569 valueMap.put(new StringValueImpl(entry.getKey()), entry.getValue()); 570 571 entrySet = valueMap.entrySet(); 572 } else { 573 env.warning(L.l("formdata must be an array or object")); 574 575 return result; 576 } 577 578 for (Map.Entry <Value,Value> entry : entrySet) { 579 String newPath = makeNewPath(path, entry.getKey(), numeric_prefix); 580 581 if (entry.getValue().isArray() || entry.getValue().isObject()) { 582 result.append(httpBuildQueryImpl(env, newPath, entry.getValue(), null)); 584 result.append("&"); 585 } else { 586 result.append(newPath + "="); 587 result.append(urlencode(entry.getValue().toString())); 588 result.append("&"); 589 } 590 } 591 592 if (result.length() > 0) 594 result.deleteCharAt(result.length() - 1); 595 596 return result; 597 } 598 599 private static String makeNewPath(String oldPath, Value key, 600 String numeric_prefix) 601 { 602 if (oldPath.length() == 0) { 603 if (key.isLongConvertible() && numeric_prefix != null) 604 return urlencode(numeric_prefix + key.toString()); 605 else 606 return urlencode(key.toString()); 607 } else 608 return oldPath + "[" + urlencode(key.toString()) + "]"; 609 } 610 611 615 public static Value get_headers(Env env, String urlString, 616 @Optional Value format) 617 { 618 Socket socket = null; 619 620 try { 621 URL url = new URL (urlString); 622 623 if (! url.getProtocol().equals("http") && 624 ! url.getProtocol().equals("https")) { 625 env.warning(L.l("Not an HTTP URL")); 626 return null; 627 } 628 629 int port = 80; 630 631 if (url.getPort() < 0) { 632 if (url.getProtocol().equals("http")) 633 port = 80; 634 else if (url.getProtocol().equals("https")) 635 port = 443; 636 } else { 637 port = url.getPort(); 638 } 639 640 socket = new Socket (url.getHost(), port); 641 642 OutputStream out = socket.getOutputStream(); 643 InputStream in = socket.getInputStream(); 644 645 StringBuilder request = new StringBuilder (); 646 647 request.append("HEAD "); 648 649 if (url.getPath() != null) 650 request.append(url.getPath()); 651 652 if (url.getQuery() != null) 653 request.append("?" + url.getQuery()); 654 655 if (url.getRef() != null) 656 request.append("#" + url.getRef()); 657 658 request.append(" HTTP/1.0\r\n"); 659 660 if (url.getHost() != null) 661 request.append("Host: " + url.getHost() + "\r\n"); 662 663 request.append("\r\n"); 664 665 OutputStreamWriter writer = new OutputStreamWriter (out); 666 writer.write(request.toString()); 667 writer.flush(); 668 669 LineNumberReader reader = new LineNumberReader (new InputStreamReader (in)); 670 671 ArrayValue result = new ArrayValueImpl(); 672 673 if (format.toBoolean()) { 674 for (String line = reader.readLine(); 675 line != null; 676 line = reader.readLine()) { 677 line = line.trim(); 678 679 if (line.length() == 0) 680 continue; 681 682 int colon = line.indexOf(':'); 683 684 ArrayValue values; 685 686 if (colon < 0) 687 result.put(new StringValueImpl(line.trim())); 688 else { 689 StringValueImpl key = 690 new StringValueImpl(line.substring(0, colon).trim()); 691 692 StringValueImpl value; 693 694 if (colon < line.length()) 695 value = new StringValueImpl(line.substring(colon + 1).trim()); 696 else 697 value = new StringValueImpl(""); 698 699 700 if (result.get(key) != UnsetValue.UNSET) 701 values = (ArrayValue)result.get(key); 702 else { 703 values = new ArrayValueImpl(); 704 705 result.put(key, values); 706 } 707 708 values.put(value); 709 } 710 } 711 712 for (Value key : result.keySet()) { 714 Value value = result.get(key); 715 716 if (value.isArray() && ((ArrayValue)value).getSize() == 1) 717 result.put(key, ((ArrayValue)value).get(LongValue.ZERO)); 718 } 719 } else { 720 for (String line = reader.readLine(); 721 line != null; 722 line = reader.readLine()) { 723 line = line.trim(); 724 725 if (line.length() == 0) 726 continue; 727 728 result.put(new StringValueImpl(line.trim())); 729 } 730 } 731 732 return result; 733 } catch (Exception e) { 734 env.warning(e); 735 736 return BooleanValue.FALSE; 737 } finally { 738 try { 739 if (socket != null) 740 socket.close(); 741 } catch (IOException e) { 742 env.warning(e); 743 } 744 } 745 } 746 747 750 public static Value get_meta_tags(Env env, String filename, 751 @Optional("false") boolean use_include_path) 752 { 753 InputStream in = null; 754 755 ArrayValue result = new ArrayValueImpl(); 756 757 try { 758 BinaryStream stream = 759 FileModule.fopen(env, filename, "r", use_include_path, null); 760 761 if (stream == null || ! (stream instanceof BinaryInput)) 762 return result; 763 764 BinaryInput input = (BinaryInput) stream; 765 766 while (! input.isEOF()) { 767 String tag = getNextTag(input); 768 769 if (tag.equalsIgnoreCase("meta")) { 770 String name = null; 771 String content = null; 772 773 String [] attr; 774 775 while ((attr = getNextAttribute(input)) != null) { 776 if (name == null && attr[0].equalsIgnoreCase("name")) { 777 if (attr.length > 1) 778 name = attr[1]; 779 } else if (content == null && attr[0].equalsIgnoreCase("content")) { 780 if (attr.length > 1) 781 content = attr[1]; 782 } 783 784 if (name != null && content != null) { 785 result.put(new StringValueImpl(name), 786 new StringValueImpl(content)); 787 break; 788 } 789 } 790 } else if (tag.equalsIgnoreCase("/head")) 791 break; 792 } 793 } catch (IOException e) { 794 env.warning(e); 795 } finally { 796 try { 797 if (in != null) 798 in.close(); 799 } catch (IOException e) { 800 env.warning(e); 801 } 802 } 803 804 return result; 805 } 806 807 private static String getNextTag(BinaryInput input) 808 throws IOException 809 { 810 StringBuilder tag = new StringBuilder (); 811 812 for (int ch = 0; ! input.isEOF() && ch != '<'; ch = input.read()) {} 813 814 while (! input.isEOF()) { 815 int ch = input.read(); 816 817 if (Character.isWhitespace(ch)) 818 break; 819 820 tag.append((char) ch); 821 } 822 823 return tag.toString(); 824 } 825 826 830 private static String [] getNextAttribute(BinaryInput input) 831 throws IOException 832 { 833 int ch; 834 835 consumeWhiteSpace(input); 836 837 StringBuilder attribute = new StringBuilder (); 838 839 while (! input.isEOF()) { 840 ch = input.read(); 841 842 if (isValidAttributeCharacter(ch)) 843 attribute.append((char) ch); 844 else { 845 input.unread(); 846 break; 847 } 848 } 849 850 if (attribute.length() == 0) 851 return null; 852 853 consumeWhiteSpace(input); 854 855 if (input.isEOF()) 856 return new String [] { attribute.toString() }; 857 858 ch = input.read(); 859 if (ch != '=') { 860 input.unread(); 861 862 return new String [] { attribute.toString() }; 863 } 864 865 consumeWhiteSpace(input); 866 867 int quote = ' '; 869 boolean quoted = false; 870 871 if (input.isEOF()) 872 return new String [] { attribute.toString() }; 873 874 ch = input.read(); 875 876 if (ch == '"' || ch == '\'') { 877 quoted = true; 878 quote = ch; 879 } else 880 input.unread(); 881 882 StringBuilder value = new StringBuilder (); 883 884 while (! input.isEOF()) { 885 ch = input.read(); 886 887 if ((quoted && ch == quote) || 889 (! quoted && Character.isWhitespace(ch)) || ch == '>') 890 break; 891 892 value.append((char) ch); 893 } 894 895 return new String [] { attribute.toString(), value.toString() }; 896 } 897 898 private static void consumeWhiteSpace(BinaryInput input) 899 throws IOException 900 { 901 int ch = 0; 902 903 while (! input.isEOF() && Character.isWhitespace(ch = input.read())) {} 904 905 if (! Character.isWhitespace(ch)) 906 input.unread(); 907 } 908 909 private static boolean isValidAttributeCharacter(int ch) 910 { 911 return Character.isLetterOrDigit(ch) || 912 (ch == '-') || (ch == '.') || (ch == '_') || (ch == ':'); 913 } 914 915 private static char toHexDigit(int d) 916 { 917 d = d & 0xf; 918 919 if (d < 10) 920 return (char) ('0' + d); 921 else 922 return (char) ('a' + d - 10); 923 } 924 } 925 926 | Popular Tags |