001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.php.rt.providers.impl.local.apache;
042:
043: import java.io.BufferedReader;
044: import java.io.File;
045: import java.io.FileReader;
046: import java.io.FilenameFilter;
047: import java.io.IOException;
048: import java.util.ArrayList;
049: import java.util.Collection;
050: import java.util.Collections;
051: import java.util.HashMap;
052: import java.util.LinkedList;
053: import java.util.List;
054: import java.util.Map;
055: import java.util.StringTokenizer;
056:
057: import java.util.logging.Level;
058: import java.util.logging.Logger;
059: import java.util.regex.PatternSyntaxException;
060: import org.netbeans.modules.php.rt.providers.impl.AbstractProvider;
061:
062: /**
063: * @author ads
064: *
065: */
066: class HttpdConfig {
067:
068: public static final String ALL_IPV4_IP_ADDRESS = "0.0.0.0"; // NOI18N
069:
070: public static final String VERSION_5 = "5"; // NOI18N
071:
072: public static final String VERSION_4 = "4"; // NOI18N
073:
074: public static final String PHP = "php"; // NOI18N
075:
076: private static final String VIRTUAL_HOST_END = "</VirtualHost"; // NOI18N
077:
078: private static final String VIRTUAL_HOST_START = "<VirtualHost"; // NOI18N
079:
080: private static final String SERVER_ROOT = "ServerRoot"; // NOI18N
081:
082: private static final String INCLUDE = "Include"; // NOI18N
083:
084: private static final String COMMENT = "#"; // NOI18N
085:
086: private static final String DOCUMENT_ROOT = "DocumentRoot"; // NOI18N
087:
088: private static final String SERVER_NAME = "ServerName"; // NOI18N
089:
090: private static final String PORT = "Port"; // NOI18N
091:
092: private static final String LISTEN = "Listen"; // NOI18N
093:
094: private static final String LOAD_MODULE = "LoadModule"; // NOI18N
095:
096: private static final String ADD_TYPE = "AddType"; // NOI18N
097:
098: private static final String MODULE_NAME_END = "_module"; // NOI18N
099:
100: private static final String PHP_TYPE = "application/x-httpd-"; // NOI18N
101:
102: private static final String COLON = ":"; // NOI18N
103:
104: private static final String IPV6_BRACKET = "]"; // NOI18N
105:
106: private static final String IPV6_COLON = IPV6_BRACKET + COLON; // NOI18N
107:
108: private static Logger LOGGER = Logger
109: .getLogger(AbstractProvider.class.getName());
110:
111: //public HttpdConfig( String platformPath , String configPath ) {
112: public HttpdConfig(String configPath) {
113: init();
114:
115: myPlatformConfig = configPath;
116: if (configPath == null) {
117: myHttpdConf = null;
118: } else {
119: myHttpdConf = new File(configPath);
120: }
121: }
122:
123: /**
124: * @return list of all available hosts in config file
125: */
126: public HttpdHost[] getHosts() {
127: if (myHttpdConf != null) {
128: if (!myHttpdConf.exists()) {
129: return new HttpdHost[] {};
130: }
131: List<HttpdHost> list = new LinkedList<HttpdHost>();
132: try {
133: loadHostsFromFile(myHttpdConf, list);
134: if (myGlobalDocRoot != null) {
135: list.add(new HttpdHost(myGlobalHostName,
136: myGlobalPort, myGlobalDocRoot,
137: myPlatformConfig)
138:
139: );
140: }
141: } catch (IOException e) {
142: LOGGER.log(Level.INFO, null, e);
143: }
144: return list.toArray(new HttpdHost[list.size()]);
145: }
146: return new HttpdHost[] {};
147: }
148:
149: /**
150: * @param version version of PHP language
151: * @return full path to php .so file ( as it defined in config file )
152: */
153: public String getPhpSOPath(String version) {
154: if (myServerRoot == null) {
155: getHosts();
156: }
157: String key = PHP + version + MODULE_NAME_END;
158: String path = myPhpModules.get(key);
159: if (path == null) {
160: return null;
161: } else {
162: if (fileExists(path)) {
163: return path;
164: } else {
165: return myServerRoot == null ? path : myServerRoot
166: + File.separator + path;
167: }
168: }
169: }
170:
171: private boolean fileExists(String path) {
172: File file = new File(path);
173: return file.exists();
174: }
175:
176: /**
177: * @return collection of extensions that are recognized as php files by Apache
178: */
179: @SuppressWarnings("unchecked")
180: public Collection<String> getPhpExtensions() {
181: if (myServerRoot == null) {
182: getHosts();
183: }
184: String key = PHP_TYPE + PHP;
185: String value = myPhpTypes.get(key);
186: if (value == null) {
187: return Collections.EMPTY_LIST;
188: }
189: StringTokenizer tokenizer = new StringTokenizer(value);
190: Collection<String> coll = new LinkedList<String>();
191: while (tokenizer.hasMoreTokens()) {
192: String token = tokenizer.nextToken();
193: coll.add(token);
194: }
195: return coll;
196: }
197:
198: private void loadHostsFromFile(File file, List<HttpdHost> list)
199: throws IOException {
200: BufferedReader reader = new BufferedReader(new FileReader(file));
201: String line;
202: while ((line = reader.readLine()) != null) {
203: line = line.trim();
204: handleLine(line, list);
205: }
206: configureGlobalPort();
207: }
208:
209: /**
210: * If default server port (myGlobalPort) wasn't loaded from
211: * Port or ServerName directives, tries to calculate it.
212: * Will remove from myListenPorts all ports used by virtualhosts
213: * (stored in myVirtualHostsPorts ) and take the first element
214: */
215: private void configureGlobalPort() {
216: if (myGlobalPort != null)
217: return;
218:
219: myListenPorts.removeAll(myVirtualHostsPorts);
220: myListenPorts.remove(null);
221: if (myListenPorts.size() > 0) {
222: myGlobalPort = myListenPorts.get(0);
223: }
224: }
225:
226: private void handleLine(String line, List<HttpdHost> list) {
227: if (line.startsWith(COMMENT)) { // NOI18N
228: return;
229: }
230: // catch ServerRoot directive for determening relative paths
231: else if (line.startsWith(SERVER_ROOT)) {
232: handleServerRoot(line);
233: } else if (line.startsWith(DOCUMENT_ROOT)) {
234: handleDocumentRoot(line);
235: } else if (line.startsWith(INCLUDE)) {
236: String value = line.substring(INCLUDE.length());
237: if (!isDirective(value)) {
238: return;
239: }
240: value = clearValue(value.trim());
241: handleInclude(value, list);
242: } else if (line.startsWith(SERVER_NAME)) {
243: handleServerName(line);
244: } else if (line.startsWith(PORT)) {
245: handlePort(line);
246: } else if (line.startsWith(LISTEN)) {
247: handleListen(line);
248: } else if (line.startsWith(VIRTUAL_HOST_START)) {
249: handleVirtualHostBegin(line);
250: } else if (line.startsWith(VIRTUAL_HOST_END)) {
251: handleVirtualHostEnd(line, list);
252: } else if (line.startsWith(LOAD_MODULE)) {
253: handleLoadModule(line);
254: } else if (line.startsWith(ADD_TYPE)) {
255: handleAddType(line);
256: }
257: }
258:
259: private void handleAddType(String line) {
260: String value = line.substring(ADD_TYPE.length());
261: if (!isDirective(value)) {
262: return;
263: }
264: value = clearValue(value.trim());
265:
266: String type = null;
267:
268: StringTokenizer tokenizer = new StringTokenizer(value);
269: if (tokenizer.hasMoreTokens()) {
270: type = tokenizer.nextToken();
271: } else {
272: return;
273: }
274:
275: if (type.contains(PHP)) {
276: String typeValue = value.substring(type.length()).trim();
277: myPhpTypes.put(type, typeValue);
278: } else {
279: return;
280: }
281: }
282:
283: private void handleLoadModule(String line) {
284: String value = line.substring(LOAD_MODULE.length());
285: if (!isDirective(value)) {
286: return;
287: }
288: value = clearValue(value.trim());
289:
290: String moduleName = null;
291: String modulePath = null;
292:
293: StringTokenizer tokenizer = new StringTokenizer(value);
294: if (tokenizer.hasMoreTokens()) {
295: moduleName = tokenizer.nextToken();
296: } else {
297: return;
298: }
299:
300: if (tokenizer.hasMoreTokens()) {
301: modulePath = clearValue(tokenizer.nextToken());
302: } else {
303: return;
304: }
305:
306: myPhpModules.put(moduleName, modulePath);
307: }
308:
309: private void handleVirtualHostEnd(String line, List<HttpdHost> list) {
310: String value = line.substring(VIRTUAL_HOST_END.length());
311: if (value.length() != 0
312: && value.charAt(value.length() - 1) == '>'
313: && value.substring(0, value.length() - 1).trim()
314: .length() == 0) {
315: //myCurrentHost = null;
316: if (myCurrentDocRoot != null && myCurrentHost != null) {
317: list.add(new HttpdHost(myCurrentHost, myCurrentPort,
318: myCurrentDocRoot, myPlatformConfig));
319: }
320: isVirtualHost = false;
321: }
322: }
323:
324: private void handleVirtualHostBegin(String line) {
325: String value = line.substring(VIRTUAL_HOST_START.length());
326: if (!isDirective(value)) {
327: return;
328: }
329: myCurrentHost = null;
330: myCurrentPort = null;
331:
332: value = clearValue(value.trim());
333: if (value.indexOf('>') > 0) {
334: value = value.substring(0, value.indexOf('>'));
335: // if port will be specified in ServerName directive
336: // inside this <VirtualHost> tag,
337: // this myCurrentPort value will be rewritten
338: myCurrentPort = handleVirtualHostBeginHostPatterns(value);
339: }
340: /*if ( value.length() >0 && value.charAt( value.length()-1) =='>') {
341: myCurrentHost = value.substring( 0 , value.length() -1 ).trim();
342: }*/
343:
344: isVirtualHost = true;
345: }
346:
347: /**
348: * parses list of ports specified in <VirtualHost> tag.
349: * Stores them in myVirtualHostsPorts.
350: * @returns the first occured port
351: */
352: private String handleVirtualHostBeginHostPatterns(String line) {
353: String firstPort = null;
354: StringTokenizer tokenizer = new StringTokenizer(line, " ");
355: while (tokenizer.hasMoreTokens()) {
356: String value = tokenizer.nextToken();
357: String port = null;
358: String colon = defineIPPortSeparatorByIPV(value);
359:
360: if (value.indexOf(colon) != -1) {
361: if (value.indexOf(colon) < value.length() - 1)
362: port = value.substring(value.indexOf(colon) + 1);
363: }
364: if (port != null) {
365: if (firstPort == null)
366: firstPort = port;
367: if (!myVirtualHostsPorts.contains(port))
368: myVirtualHostsPorts.add(port);
369: }
370: }
371: return firstPort;
372: }
373:
374: /**
375: * checks which IP version is used in string value and returns
376: * IP and Port separator suitable for this version.
377: *
378: * @param value
379: * string value with ip address and port.
380: * e.g. 120.0.0.1:80 from Listen directive.
381: * @returns separator
382: * Strng used to separate ipfrom port, if any.
383: * Returns ']:' for IPv6, ':' for IPv4.
384: * Returns ':' if there is no separator in given string.
385: */
386: private String defineIPPortSeparatorByIPV(String value) {
387: if (value == null) {
388: return COLON;
389: }
390: return value.indexOf(IPV6_BRACKET) != -1 ? IPV6_COLON : COLON;
391: }
392:
393: private void handleServerName(String line) {
394: String value = line.substring(SERVER_NAME.length());
395: if (!isDirective(value)) {
396: return;
397: }
398: value = clearValue(value.trim());
399:
400: String name = null;
401: String port = null;
402: if (value.indexOf(COLON) != -1) {
403: if (value.indexOf(COLON) > 0)
404: name = value.substring(0, value.indexOf(COLON));
405: if (value.indexOf(COLON) < value.length() - 1)
406: port = value.substring(value.indexOf(COLON) + 1, value
407: .length());
408: } else {
409: name = value;
410: }
411:
412: if (isVirtualHost) {
413: myCurrentHost = name;
414: if (port != null)
415: myCurrentPort = port;
416: } else {
417: myGlobalHostName = name;
418: if (port != null)
419: myGlobalPort = port;
420: }
421: }
422:
423: private void handleListen(String line) {
424: String value = line.substring(LISTEN.length());
425: if (!isDirective(value)) {
426: return;
427: }
428: value = clearValue(value.trim());
429:
430: String ip = grabIpFromListen(value);
431: String port = grabPortFromListen(value);
432: if (ip != null && myGlobalHostName == null) {
433: myGlobalHostName = ip;
434: }
435: if (port != null) {
436: myListenPorts.add(port);
437: }
438: //
439: }
440:
441: private String grabIpFromListen(String value) {
442: String ip = null;
443: String colon = defineIPPortSeparatorByIPV(value);
444: if (!colon.equals(IPV6_COLON)) {
445: if (value.indexOf(colon) > 0) {
446: ip = value.substring(0, value.indexOf(colon));
447: }
448: }
449: if (ALL_IPV4_IP_ADDRESS.equals(ip)) {
450: ip = null;
451: }
452: return ip;
453: }
454:
455: private String grabPortFromListen(String value) {
456: String port = null;
457: String colon = defineIPPortSeparatorByIPV(value);
458:
459: if (value.indexOf(colon) != -1) {
460: if (value.indexOf(colon) < value.length() - 1) {
461: port = value.substring(value.indexOf(colon) + 1, value
462: .length());
463: }
464: } else {
465: port = value;
466: }
467: return port;
468: }
469:
470: private void handlePort(String line) {
471: String value = line.substring(PORT.length());
472: if (!isDirective(value)) {
473: return;
474: }
475: value = clearValue(value.trim());
476: // PORT is not allowed inside virtual host
477: // if it was already loaded from ServerName, do not load from Port
478:
479: if (myGlobalPort == null) {
480: myGlobalPort = value;
481: }
482: }
483:
484: private void handleDocumentRoot(String line) {
485: String value = line.substring(DOCUMENT_ROOT.length());
486: if (!isDirective(value)) {
487: return;
488: }
489: value = clearValue(value.trim());
490: /*if ( myCurrentHost == null ) {
491: list.add( new HttpdHost( value ) );
492: }
493: else {
494: list.add( new HttpdHost( myCurrentHost, value ) );
495: }*/
496: if (isVirtualHost) {
497: myCurrentDocRoot = value;
498: } else {
499: myGlobalDocRoot = value;
500: }
501: }
502:
503: private void handleServerRoot(String line) {
504: String value = line.substring(SERVER_ROOT.length());
505: if (!isDirective(value)) {
506: return;
507: }
508: if (myServerRoot == null) { // ServerRoot should be handled only in main config file
509: myServerRoot = clearValue(value.trim());
510: }
511: }
512:
513: /*
514: * - Include directory => read all files in that directory and any subdirectory
515: * - Include using wildcad => read all maching files
516: *
517: */
518: private void handleInclude(String value, List<HttpdHost> list) {
519: String includePath;
520: if (value.startsWith(File.separator)) {
521: includePath = value;
522: } else {
523: includePath = myServerRoot == null ? value : myServerRoot
524: + File.separator + value;
525: }
526: List<File> files = loadFilesByIncludePath(includePath);
527:
528: if (files == null) {
529: return;
530: } else {
531: for (File file : files) {
532: try {
533: loadHostsFromFile(file, list);
534: } catch (IOException e) {
535: // ignore it
536: }
537: }
538: }
539: }
540:
541: private List<File> loadFilesByIncludePath(String includePath) {
542: List<File> filesList = new ArrayList<File>();
543: File file = new File(includePath);
544: if (file.exists()) {
545: if (file.isDirectory()) {
546: loadFilesRecursively(file, filesList);
547: } else {
548: filesList.add(file);
549: }
550: } else {
551: loadFilesByPattern(file, filesList);
552: }
553:
554: return filesList;
555: }
556:
557: /**
558: * processed case:
559: * Include directory => read all files in that directory and any subdirectory
560: */
561: private void loadFilesRecursively(File dir, List<File> filesList) {
562: loadFilesRecursively(dir, filesList, null);
563: }
564:
565: /**
566: * inits colletions to store values we will grab from conf file
567: */
568: private void init() {
569: myPhpModules = new HashMap<String, String>();
570: myPhpTypes = new HashMap<String, String>();
571: myVirtualHostsPorts = new ArrayList<String>();
572: myListenPorts = new ArrayList<String>();
573: }
574:
575: private void loadFilesRecursively(File dir, List<File> filesList,
576: FilenameFilter filter) {
577: File[] children = dir.listFiles(filter);
578: for (File file : children) {
579: if (file.isDirectory()) {
580: // it is allowed specify pattern for elements inside one directory.
581: // all deeper elements should be loadded without pattern.
582: loadFilesRecursively(file, filesList);
583: } else {
584: filesList.add(file);
585: }
586: }
587: }
588:
589: /**
590: * processed case:
591: * Include using wildcad => read all maching files.
592: * <br/>
593: * Unlike apache, doesn't care about loading files in alphabetical order.
594: */
595: private void loadFilesByPattern(File pattern, List<File> filesList) {
596: String path = pattern.getPath();
597: int index = RegexpFileFilter.wildcardIndex(path);
598: if (index < 0) {
599: return;
600: }
601: // get the most deep dir without wildcard symbols
602: File parent = new File(path.substring(0, index))
603: .getParentFile();
604: if (!parent.exists()) {
605: return;
606: }
607: try {
608: FilenameFilter filter = new RegexpFileFilter(pattern
609: .getPath());
610: loadFilesRecursively(parent, filesList, filter);
611: } catch (PatternSyntaxException e) {
612: LOGGER.log(Level.WARNING, null, e.getMessage());
613: }
614: }
615:
616: private boolean isDirective(String rest) {
617: if (rest.length() > 0 && rest.charAt(0) != ' '
618: && rest.charAt(0) != '\t') {
619: return false;
620: }
621: return true;
622: }
623:
624: private String clearValue(String value) {
625: if (value == null || value.length() == 0) {
626: return value;
627: }
628: String result = value;
629: char first = value.charAt(0);
630: if (first == '"') {
631: result = value.substring(1);
632: }
633: char last = result.charAt(result.length() - 1);
634: if (last == '"') {
635: result = result.substring(0, result.length() - 1);
636: }
637: return result;
638: }
639:
640: private File myHttpdConf;
641:
642: private String myServerRoot;
643:
644: // contains currenly handled ServerName directive inside virtual host descr
645: private String myCurrentHost;
646:
647: // contains currenly handled port value inside virtual host descr
648: // (from ServerName or <VirtualHost *:port> )
649: private String myCurrentPort;
650:
651: // contains currenly handled DocumentRoot directive inside virtual host descr
652: private String myCurrentDocRoot;
653:
654: private boolean isVirtualHost;
655:
656: // variables for keeping globally defined DocumentRoot and Server Name
657: private String myGlobalHostName;
658:
659: private String myGlobalPort;
660:
661: private String myGlobalDocRoot;
662:
663: private String myPlatformConfig;
664:
665: private Map<String, String> myPhpModules;
666:
667: private Map<String, String> myPhpTypes;
668:
669: // list to store all ports supported by server.
670: private List<String> myListenPorts;
671:
672: // list to store ports used for virtual hosts
673: private List<String> myVirtualHostsPorts;
674:
675: }
|