1 22 23 package org.meshcms.core; 24 25 import java.io.*; 26 import java.nio.charset.*; 27 import java.util.*; 28 import javax.servlet.*; 29 import javax.servlet.http.*; 30 import org.apache.commons.fileupload.*; 31 import org.meshcms.util.*; 32 import com.thoughtworks.xstream.*; 33 import com.thoughtworks.xstream.io.StreamException; 34 import com.thoughtworks.xstream.io.xml.*; 35 36 public class WebSite { 37 public static final String APP_NAME = "MeshCMS"; 38 public static final String VERSION_ID = "3.0.4"; 39 public static final String SYSTEM_CHARSET; 40 public static final boolean IS_MULTIBYTE_SYSTEM_CHARSET; 41 42 45 public static final String BACKUP_PREFIX = "_bak_"; 46 47 50 public static final String BACKUP_DIR_PREFIX = "_dirbak_"; 51 52 55 public static final String TEMP_PREFIX = "_tmp_"; 56 57 60 public static final String ADMIN_THEME = "theme"; 61 62 public static final String CMS_ID_FILE = "meshcms_id"; 63 64 public static final String ADMIN_ID_FILE = "meshcms_admin_id"; 65 66 static { 67 String s = System.getProperty("file.encoding", "ISO-8859-1"); 68 boolean multibyte = true; 69 70 try { 71 Charset c = Charset.forName(s); 72 s = c.toString(); 73 multibyte = c.newEncoder().maxBytesPerChar() > 1.0F; 74 } catch (Exception ex) {} 75 76 SYSTEM_CHARSET = s; 77 IS_MULTIBYTE_SYSTEM_CHARSET = multibyte; 78 } 79 80 protected ServletContext sc; 81 protected String [] welcomeFiles; 82 83 protected File rootFile; 84 protected long lastAdminThemeBlock; 85 protected long statsZero; 86 protected int statsLength; 87 protected Configuration configuration; 88 protected SiteInfo siteInfo; 89 protected SiteMap siteMap; 90 protected Path rootPath; 91 92 protected Path cmsPath; 93 protected Path adminPath; 94 protected Path adminThemePath; 95 protected Path adminModulesPath; 96 protected Path adminThemesPath; 97 protected Path adminScriptsPath; 98 protected Path privatePath; 99 protected Path usersPath; 100 protected Path virtualSitesPath; 101 protected Path repositoryPath; 102 protected Path customModulesPath; 103 protected Path generatedFilesPath; 104 protected Path moduleDataPath; 105 protected Path customThemesPath; 106 107 protected Path configFilePath; 108 protected Path propertiesFilePath; 109 protected Path sitesFilePath; 110 111 protected WebSite() { 112 } 113 114 protected static WebSite create(ServletContext sc, 115 String [] welcomeFiles, File rootFile, Path rootPath, Path cmsPath) { 116 WebSite webSite = new WebSite(); 117 webSite.init(sc, welcomeFiles, rootFile, rootPath, cmsPath); 118 return webSite; 119 } 120 121 protected void init(ServletContext sc, String [] welcomeFiles, File rootFile, 122 Path rootPath, Path cmsPath) { 123 this.sc = sc; 124 this.welcomeFiles = welcomeFiles; 125 this.rootFile = rootFile; 126 this.rootPath = rootPath; 127 this.cmsPath = cmsPath; 128 129 if (cmsPath != null) { 131 adminPath = cmsPath.add("admin"); 132 adminThemePath = adminPath.add(ADMIN_THEME); 133 adminModulesPath = adminPath.add("modules"); 134 adminThemesPath = adminPath.add("themes"); 135 adminScriptsPath = adminPath.add("scripts"); 136 privatePath = cmsPath.add("private"); 137 createDir(usersPath = privatePath.add("users")); 138 virtualSitesPath = cmsPath.add("sites"); 139 createDir(repositoryPath = privatePath.add("repository")); 140 createDir(customModulesPath = cmsPath.add("modules")); 141 createDir(generatedFilesPath = cmsPath.add("generated")); 142 createDir(moduleDataPath = cmsPath.add("moduledata")); 143 createDir(customThemesPath = cmsPath.add("themes")); 144 configFilePath = privatePath.add("configuration.xml"); 145 propertiesFilePath = privatePath.add("siteinfo.xml"); 146 sitesFilePath = privatePath.add("sites.xml"); 147 148 configuration = Configuration.load(this); 149 statsLength = configuration.getStatsLength(); 150 siteInfo = SiteInfo.load(this); 151 updateSiteMap(true); 152 } 153 } 154 155 protected ServletContext getServletContext() { 156 return sc; 157 } 158 159 169 public void updateSiteMap(boolean force) { 170 if (force) { 171 new SiteMap(this).process(); 172 } else if (System.currentTimeMillis() - siteMap.getLastModified() > 173 configuration.getUpdateIntervalMillis()) { 174 new SiteMap(this).start(); 175 new DirectoryCleaner(getFile(repositoryPath), 176 configuration.getBackupLifeMillis()).start(); 177 new DirectoryCleaner(getFile(generatedFilesPath), 178 configuration.getBackupLifeMillis()).start(); 179 } 180 } 181 182 void setSiteMap(SiteMap siteMap) { 183 this.siteMap = siteMap; 184 } 185 186 194 public SiteMap getSiteMap() { 195 return siteMap; 196 } 197 198 201 public Configuration getConfiguration() { 202 return configuration; 203 } 204 205 210 void setSiteInfo(SiteInfo siteInfo) { 211 this.siteInfo = siteInfo; 212 } 213 214 220 public SiteInfo getSiteInfo() { 221 return siteInfo; 222 } 223 224 228 public int getStatsIndex() { 229 long now = System.currentTimeMillis(); 230 long days = (now - statsZero) / Configuration.LENGTH_OF_DAY; 231 232 if (days >= statsLength) { 233 statsZero = now; 234 return 0; 235 } else { 236 return (int) days; 237 } 238 } 239 240 243 public int getStatsLength() { 244 return statsLength; 245 } 246 247 256 public boolean createDirectory(UserInfo user, Path dirPath) { 257 File newDir = getFile(dirPath); 258 259 if (newDir.isDirectory()) { 260 return true; 261 } 262 263 return user != null && user.canWrite(this, dirPath) && newDir.mkdir(); 264 } 265 266 276 public boolean createFile(UserInfo user, Path filePath) { 277 if (user == null || !user.canWrite(this, filePath)) { 278 return false; 279 } 280 281 File newFile = getFile(filePath); 282 283 if (newFile.exists()) { 284 return !newFile.isDirectory(); 285 } 286 287 try { 288 if (FileTypes.isPage(filePath.getLastElement())) { 289 if (newFile.exists()) { 290 return false; 291 } 292 293 return saveToFile(user, getHTMLTemplate(null), filePath); 294 } else { 295 return newFile.createNewFile(); 296 } 297 } catch (IOException ex) { 298 sc.log("Can't create file " + newFile, ex); 299 } 300 301 return false; 302 } 303 304 314 public boolean copyFile(UserInfo user, Path filePath, String newName) { 315 return copyFile(user, filePath, filePath.getParent().add(newName)); 316 } 317 318 328 public boolean copyFile(UserInfo user, Path oldPath, Path newPath) { 329 File oldFile = getFile(oldPath); 330 331 if (!oldFile.exists()) { 332 return false; 333 } 334 335 File newFile = getFile(newPath); 336 337 if (user == null || !user.canWrite(this, newPath)) { 338 return false; 339 } 340 341 if (oldFile.isDirectory()) { 342 return Utils.copyDirectory(oldFile, newFile, false, false, false); 343 } else { 344 try { 345 return Utils.copyFile(oldFile, newFile, false, false); 346 } catch (IOException ex) { 347 sc.log("Can't copy file " + oldFile + " to file " + newFile, ex); 348 } 349 } 350 351 return false; 352 } 353 354 363 public boolean rename(UserInfo user, Path filePath, String newName) { 364 return move(user, filePath, filePath.getParent().add(newName)); 365 } 366 367 376 public boolean move(UserInfo user, Path oldPath, Path newPath) { 377 File oldFile = getFile(oldPath); 378 379 if (!oldFile.exists()) { 380 return false; 381 } 382 383 if (newPath.isContainedIn(oldPath)) { 384 return false; 385 } 386 387 if (user == null || 388 !(user.canWrite(this, oldPath) && user.canWrite(this, newPath))) { 389 return false; 390 } 391 392 File newFile = getFile(newPath); 393 394 if (Utils.forceRenameTo(oldFile, newFile, false)) { 395 return true; 396 } else { 397 sc.log("Can't move file " + oldFile + " to file " + newFile); 398 } 399 400 return false; 401 } 402 403 409 public boolean delete(UserInfo user, Path filePath) { 410 return delete(user, filePath, true); 411 } 412 413 423 public boolean delete(UserInfo user, Path filePath, 424 boolean deleteNonEmptyDirectories) { 425 if (user == null || !user.canWrite(this, filePath)) { 426 return false; 427 } 428 429 if (cmsPath.isContainedIn(filePath) || 431 filePath.isChildOf(cmsPath) || 432 filePath.isContainedIn(adminPath) || 433 filePath.isChildOf(privatePath)) { 434 return false; 435 } 436 437 File file = getFile(filePath); 438 439 if (!file.exists()) { 440 return false; 441 } 442 443 if (file.isDirectory()) { 444 446 return file.delete() || 447 (deleteNonEmptyDirectories && backupDir(user, filePath)); 448 } else { 449 return backupFile(user, filePath); 450 } 451 } 452 453 461 public boolean touch(UserInfo user, Path filePath) { 462 return setFileTime(user, filePath, System.currentTimeMillis()); 463 } 464 465 public boolean setFileTime(UserInfo user, Path filePath, long time) { 466 if (user == null || !user.canWrite(this, filePath)) { 467 return false; 468 } 469 470 File file = getFile(filePath); 471 472 if (!file.exists()) { 473 return false; 474 } 475 476 file.setLastModified(time); 477 return true; 478 } 479 480 501 public boolean saveToFile(UserInfo user, Object saveThis, Path filePath) { 502 if (user == null || !user.canWrite(this, filePath)) { 503 return false; 504 } 505 506 File file = getFile(filePath); 507 File dir = file.getParentFile(); 508 dir.mkdirs(); 509 File tempFile = null; 510 511 String fileName = file.getName(); 512 int dot = fileName.lastIndexOf('.'); 513 String fileExt = (dot == -1) ? ".bak" : fileName.substring(dot); 514 515 if (file.exists()) { 516 tempFile = getRepositoryFile(filePath, TEMP_PREFIX + 517 System.currentTimeMillis() + fileExt); 518 } 519 520 File writeTo = tempFile == null ? file : tempFile; 521 522 if (saveThis instanceof byte[]) { 523 byte[] b = (byte[]) saveThis; 524 FileOutputStream fos = null; 525 526 try { 527 fos = new FileOutputStream(writeTo); 528 fos.write(b); 529 } catch (IOException ex) { 530 sc.log("Can't write byte array to file " + writeTo, ex); 531 return false; 532 } finally { 533 if (fos != null) { 534 try { 535 fos.close(); 536 } catch (IOException ex) { 537 sc.log("Can't close file " + writeTo, ex); 538 } 539 } 540 } 541 } else if (saveThis instanceof InputStream) { 542 try { 543 InputStream is = (InputStream) saveThis; 544 Utils.copyStream(is, new FileOutputStream(writeTo), true); 545 is.close(); 546 } catch (Exception ex) { 547 sc.log("Can't write stream to file " + writeTo, ex); 548 return false; 549 } 550 } else if (saveThis instanceof FileItem) { 551 try { 552 ((FileItem) saveThis).write(writeTo); 553 } catch (Exception ex) { 554 sc.log("Can't write uploaded file to file " + writeTo, ex); 555 return false; 556 } 557 } else { 558 Writer writer = null; 559 560 try { 561 writer = new BufferedWriter(new FileWriter(writeTo)); 562 writer.write(saveThis.toString()); 563 } catch (IOException ex) { 564 sc.log("Can't write generic object to file " + writeTo, ex); 565 return false; 566 } finally { 567 if (writer != null) { 568 try { 569 writer.close(); 570 } catch (IOException ex) { 571 sc.log("Can't close file " + writeTo, ex); 572 } 573 } 574 } 575 } 576 577 if (tempFile != null) { 578 if (!backupFile(user, filePath)) { 579 return false; 580 } 581 582 if (!Utils.forceRenameTo(tempFile, file, true)) { 583 sc.log("Can't rename temporary file " + tempFile + " to file " + file); 584 return false; 585 } 586 } 587 588 return true; 589 } 590 591 602 public File getRepositoryFile(Path filePath, String fileName) { 603 File repoDir = getFile(repositoryPath.add(filePath)); 604 repoDir.mkdirs(); 605 return new File(repoDir, fileName); 606 } 607 608 private boolean backupFile(UserInfo user, Path filePath) { 609 614 615 File file = getFile(filePath); 616 String fileName = file.getName(); 617 int dot = fileName.lastIndexOf('.'); 618 String fileExt = (dot == -1) ? ".bak" : fileName.substring(dot); 619 620 File bakFile = getRepositoryFile(filePath, BACKUP_PREFIX + 621 user.getUsername() + "_" + 622 WebUtils.numericDateFormatter.format(new Date()) + fileExt); 623 624 if (Utils.forceRenameTo(file, bakFile, true)) { 625 return true; 626 } 627 628 sc.log("Can't backup path " + filePath); 629 return false; 630 } 631 632 private boolean backupDir(UserInfo user, Path dirPath) { 633 634 635 File dir = getFile(dirPath); 636 File bakFile = getRepositoryFile(dirPath, BACKUP_DIR_PREFIX + 637 user.getUsername() + "_" + 638 WebUtils.numericDateFormatter.format(new Date()) + ".zip"); 639 OutputStream os; 640 641 try { 642 os = new BufferedOutputStream(new FileOutputStream(bakFile)); 643 } catch (FileNotFoundException ex) { 644 sc.log("can't create stream on " + bakFile, ex); 645 return false; 646 } 647 648 new ZipArchiver(dir, os).process(); 649 650 try { 651 os.close(); 652 } catch (IOException ex) { 653 sc.log("Can't close file " + bakFile, ex); 654 return false; 655 } 656 657 DirectoryRemover dr = new DirectoryRemover(dir); 658 dr.process(); 659 return dr.getResult(); 660 } 661 662 public File createDir(Path path) { 663 File dir = getFile(path); 664 dir.mkdirs(); 665 return dir.isDirectory() ? dir : null; 666 } 667 668 676 public File getFile(Path path) { 677 return (path == null || path.isRelative()) ? null : 678 new File(rootFile, path.toString()); 679 } 680 681 public File getRootFile() { 682 return rootFile; 683 } 684 685 688 public Path getRootPath() { 689 return rootPath; 690 } 691 692 697 public Path getPath(File file) { 698 return new Path(Utils.getRelativePath(rootFile, file, "/")); 699 } 700 701 public Path getRequestedPath(HttpServletRequest request) { 702 return new Path(request.getServletPath()); 703 } 704 705 public Path getServedPath(HttpServletRequest request) { 706 return new Path(request.getServletPath()); 707 } 708 709 public Path getServedPath(Path requestedPath) { 710 return requestedPath; 711 } 712 713 720 public boolean isDirectory(Path path) { 721 return getFile(path).isDirectory(); 722 } 723 724 733 public Path getDirectory(Path path) { 734 if (path == null) { 737 return null; 738 } 739 740 if (getFile(path).isDirectory()) { 741 return path; 742 } 743 744 path = path.getParent(); 745 746 if (!path.isRelative() && getFile(path).isDirectory()) { 747 return path; 748 } 749 750 return null; 751 } 752 753 767 public String [] getLinkList(PageInfo[] pages, String contextPath, 768 String target, String style) { 769 if (pages == null) { 770 return null; 771 } 772 773 String [] links = new String [pages.length]; 774 target = Utils.isNullOrEmpty(target) ? "" : " target=\"" + target + "\""; 775 style = Utils.isNullOrEmpty(style) ? "" : " class=\"" + style + "\""; 776 777 for (int i = 0; i < pages.length; i++) { 778 if (pages[i] == null) { 779 links[i] = "..."; 780 } else { 781 links[i] = "<a HREF=\"" + contextPath + getLink(pages[i]) + 782 "\"" + target + style + ">" + siteInfo.getPageTitle(pages[i]) + "</a>"; 783 } 784 } 785 786 return links; 787 } 788 789 public String getLink(Path path) { 790 return getFile(path).isDirectory() ? path.getAsLink() + '/' : path.getAsLink(); 791 } 792 793 public String getLink(PageInfo pageInfo) { 794 return getLink(pageInfo.getPath()); 795 } 796 797 805 public String [] getTitles(PageInfo[] pages) { 806 if (pages == null) { 807 return null; 808 } 809 810 String [] titles = new String [pages.length]; 811 812 for (int i = 0; i < pages.length; i++) { 813 titles[i] = siteInfo.getPageTitle(pages[i]); 814 } 815 816 return titles; 817 } 818 819 834 public boolean isSystem(Path path) { 835 if (path == null || path.isRoot()) { 836 return false; 837 } 838 839 if (path.isRelative()) { 840 return true; 841 } 842 843 if (adminPath != null && (path.isContainedIn(adminPath) || 844 path.isContainedIn(privatePath) || path.equals(cmsPath.add(CMS_ID_FILE)))) { 845 return true; 846 } 847 848 String level1 = path.getElementAt(0).toLowerCase(); 849 850 return level1.equals("web-inf") || level1.equals("meta-inf") || 851 level1.equals("cgi-bin"); 852 } 853 854 858 public boolean isVisuallyEditable(Path path) { 859 return Utils.searchString(configuration.getVisualExtensions(), 860 Utils.getExtension(path, false), true) != -1; 861 } 862 863 866 public Path getModulePath(String moduleName) { 867 if (moduleName.endsWith(".jsp")) { 868 moduleName = moduleName.substring(0, moduleName.length() - 4); 870 } 871 872 return (Path) siteMap.getModulesMap().get(moduleName); 873 } 874 875 878 public Path getAdminPath() { 879 return adminPath; 880 } 881 882 public Path getCMSPath() { 883 return cmsPath; 884 } 885 886 public boolean isVirtual() { 887 return false; 888 } 889 890 895 public String getAdminMetaThemeTag() { 896 Path themePath = getThemePath(adminPath); 897 return "<meta name=\"decorator\" content=\"/" + getServedPath(themePath) + 898 "/" + SiteMap.THEME_DECORATOR + "\" />"; 899 } 900 901 public String getDummyMetaThemeTag() { 902 return "<meta name=\"decorator\" content=\"/" + adminPath + "/theme/dummy.jsp\" />"; 903 } 904 905 913 public String getHTMLTemplate(String pageTitle) throws IOException { 914 String text = Utils.readFully(getFile(getAdminPath().add("template.html"))); 915 916 if (pageTitle != null) { 917 int idx = text.indexOf("New Page"); 918 919 if (idx >= 0) { 920 text = text.substring(0, idx) + pageTitle + text.substring(idx + 8); 921 } 922 } 923 924 return text; 925 } 926 927 public boolean isInsideModules(Path path) { 928 return path.isContainedIn(customModulesPath) || 929 path.isContainedIn(adminModulesPath); 930 } 931 932 public boolean isInsideThemes(Path path) { 933 return path.isContainedIn(customThemesPath) || 934 path.isContainedIn(adminThemesPath); 935 } 936 937 940 protected void log(String s) { 941 sc.log(s); 942 } 943 944 948 public void log(String message, Throwable throwable) { 949 sc.log(message, throwable); 950 } 951 952 958 public boolean isWelcomeFileName(String fileName) { 959 return Utils.searchString(welcomeFiles, fileName, false) != -1; 960 } 961 962 968 public String [] getWelcomeFileNames() { 969 return welcomeFiles; 970 } 971 972 980 public Path findCurrentWelcome(Path dirPath) { 981 File dirFile = getFile(dirPath); 982 983 if (dirFile.isDirectory()) { 984 for (int i = 0; i < welcomeFiles.length; i++) { 985 Path wPath = dirPath.add(welcomeFiles[i]); 986 987 if (getFile(wPath).exists()) { 988 return wPath; 989 } 990 } 991 } 992 993 return null; 994 } 995 996 public WebSite getWebSite(ServletRequest request) { 997 return this; 998 } 999 1000 public HttpServletRequest wrapRequest(ServletRequest request) { 1001 return (HttpServletRequest) request; 1002 } 1003 1004 public String getTypeDescription() { 1005 return "single web site"; 1006 } 1007 1008 public String toString() { 1009 return "Type: " + getTypeDescription() + "; path: /" + rootPath + "; CMS: " + 1010 (cmsPath == null ? "disabled" : "enabled on path /" + cmsPath); 1011 } 1012 1013 public Object loadFromXML(Path path) { 1014 File file = getFile(path); 1015 1016 if (file.exists()) { 1017 InputStream is = null; 1018 1019 try { 1020 is = new BufferedInputStream(new FileInputStream(file)); 1021 XStream xStream = new XStream(new DomDriver()); 1022 XStreamPathConverter pConv = new XStreamPathConverter(); 1023 pConv.setPrependSlash(true); 1024 xStream.registerConverter(pConv); 1025 1026 try { 1027 return xStream.fromXML(new InputStreamReader(is, "UTF-8")); 1028 } catch (StreamException ex) { 1029 return xStream.fromXML(is); 1030 } 1031 } catch (IOException ex) { 1032 ex.printStackTrace(); 1033 } finally { 1034 if (is != null) { 1035 try { 1036 is.close(); 1037 } catch (IOException ex) { 1038 sc.log("Can't close file " + file, ex); 1039 } 1040 } 1041 } 1042 } 1043 1044 return null; 1045 } 1046 1047 public boolean storeToXML(Object o, Path path) { 1048 File file = getFile(path); 1049 OutputStreamWriter osw = null; 1050 1051 try { 1052 osw = new OutputStreamWriter 1053 (new BufferedOutputStream(new FileOutputStream(file)), "UTF-8"); 1054 XStream xStream = new XStream(new DomDriver()); 1055 XStreamPathConverter pConv = new XStreamPathConverter(); 1056 pConv.setPrependSlash(true); 1057 xStream.registerConverter(pConv); 1058 xStream.toXML(o, osw); 1059 return true; 1060 } catch (IOException ex) { 1061 ex.printStackTrace(); 1062 } finally { 1063 if (osw != null) { 1064 try { 1065 osw.close(); 1066 } catch (IOException ex) { 1067 sc.log("Can't close file " + file, ex); 1068 } 1069 } 1070 } 1071 1072 return false; 1073 } 1074 1075 1084 public Path getThemePath(Path pagePath) { 1085 Path themePath = siteInfo.getThemePath(pagePath); 1086 1087 if (pagePath.isContainedIn(adminPath)) { 1088 if (configuration.isUseAdminTheme() || themePath == null || 1089 System.currentTimeMillis() - lastAdminThemeBlock < 300000L || !getFile(themePath.add(SiteMap.THEME_DECORATOR)).exists()) { 1091 themePath = adminThemePath; 1092 } 1093 } 1094 1095 return themePath; 1096 } 1097 1098 public Path getAdminThemePath() { 1099 return adminThemePath; 1100 } 1101 1102 public Path getAdminModulesPath() { 1103 return adminModulesPath; 1104 } 1105 1106 public Path getModuleDataPath() { 1107 return moduleDataPath; 1108 } 1109 1110 public Path getAdminThemesPath() { 1111 return adminThemesPath; 1112 } 1113 1114 public Path getAdminScriptsPath() { 1115 return adminScriptsPath; 1116 } 1117 1118 public Path getPrivatePath() { 1119 return privatePath; 1120 } 1121 1122 public Path getUsersPath() { 1123 return usersPath; 1124 } 1125 1126 public Path getVirtualSitesPath() { 1127 return virtualSitesPath; 1128 } 1129 1130 public Path getRepositoryPath() { 1131 return repositoryPath; 1132 } 1133 1134 public Path getCustomModulesPath() { 1135 return customModulesPath; 1136 } 1137 1138 public Path getGeneratedFilesPath() { 1139 return generatedFilesPath; 1140 } 1141 1142 public Path getCustomThemesPath() { 1143 return customThemesPath; 1144 } 1145 1146 public Path getConfigFilePath() { 1147 return configFilePath; 1148 } 1149 1150 public Path getPropertiesFilePath() { 1151 return propertiesFilePath; 1152 } 1153 1154 public Path getSitesFilePath() { 1155 return sitesFilePath; 1156 } 1157 1158 public long getLastAdminThemeBlock() { 1159 return lastAdminThemeBlock; 1160 } 1161 1162 public void setLastAdminThemeBlock(long lastAdminThemeBlock) { 1163 this.lastAdminThemeBlock = lastAdminThemeBlock; 1164 } 1165} 1166 | Popular Tags |