1616 */
1717package org .apache .tomcat .util .descriptor .web ;
1818
19+ import java .io .IOException ;
1920import java .io .UnsupportedEncodingException ;
2021import java .net .URL ;
21- import java .net .URLEncoder ;
22- import java .nio .charset .StandardCharsets ;
2322import java .util .ArrayList ;
2423import java .util .Collection ;
2524import java .util .EnumSet ;
2928import java .util .LinkedHashMap ;
3029import java .util .LinkedHashSet ;
3130import java .util .List ;
31+ import java .util .Locale ;
3232import java .util .Map ;
3333import java .util .Map .Entry ;
3434import java .util .Set ;
4545import org .apache .juli .logging .LogFactory ;
4646import org .apache .tomcat .util .buf .B2CConverter ;
4747import org .apache .tomcat .util .buf .UDecoder ;
48+ import org .apache .tomcat .util .buf .UEncoder ;
49+ import org .apache .tomcat .util .buf .UEncoder .SafeCharsSet ;
4850import org .apache .tomcat .util .descriptor .XmlIdentifiers ;
4951import org .apache .tomcat .util .digester .DocumentProperties ;
5052import org .apache .tomcat .util .res .StringManager ;
@@ -73,6 +75,8 @@ public WebXml() {
7375
7476 private final Log log = LogFactory .getLog (WebXml .class ); // must not be static
7577
78+ private final UEncoder urlEncoder = new UEncoder (SafeCharsSet .WITH_SLASH );
79+
7680 /**
7781 * Global defaults are overridable but Servlets and Servlet mappings need to be unique. Duplicates normally trigger
7882 * an error. This flag indicates if newly added Servlet elements are marked as overridable.
@@ -1352,6 +1356,7 @@ public String toString() {
13521356 private static final String INDENT2 = " " ;
13531357 private static final String INDENT4 = " " ;
13541358 private static final String INDENT6 = " " ;
1359+ private static final String INDENT8 = " " ;
13551360
13561361 /**
13571362 * Generate a web.xml in String form that matches the representation stored in this object.
@@ -1452,7 +1457,9 @@ public String toXml() {
14521457 appendElement (sb , INDENT4 , "param-value" , entry .getValue ());
14531458 sb .append (" </context-param>\n " );
14541459 }
1455- sb .append ('\n' );
1460+ if (!contextParams .isEmpty ()) {
1461+ sb .append ('\n' );
1462+ }
14561463
14571464 // Filters were introduced in Servlet 2.3
14581465 if (getMajorVersion () > 2 || getMinorVersion () > 2 ) {
@@ -1475,7 +1482,9 @@ public String toXml() {
14751482 }
14761483 sb .append (" </filter>\n " );
14771484 }
1478- sb .append ('\n' );
1485+ if (!filters .isEmpty ()) {
1486+ sb .append ('\n' );
1487+ }
14791488
14801489 for (FilterMap filterMap : filterMaps ) {
14811490 sb .append (" <filter-mapping>\n " );
@@ -1505,7 +1514,9 @@ public String toXml() {
15051514 }
15061515 sb .append (" </filter-mapping>\n " );
15071516 }
1508- sb .append ('\n' );
1517+ if (!filterMaps .isEmpty ()) {
1518+ sb .append ('\n' );
1519+ }
15091520 }
15101521
15111522 // Listeners were introduced in Servlet 2.3
@@ -1515,7 +1526,9 @@ public String toXml() {
15151526 appendElement (sb , INDENT4 , "listener-class" , listener );
15161527 sb .append (" </listener>\n " );
15171528 }
1518- sb .append ('\n' );
1529+ if (!listeners .isEmpty ()) {
1530+ sb .append ('\n' );
1531+ }
15191532 }
15201533
15211534 for (Map .Entry <String ,ServletDef > entry : servlets .entrySet ()) {
@@ -1566,29 +1579,54 @@ public String toXml() {
15661579 }
15671580 sb .append (" </servlet>\n " );
15681581 }
1569- sb .append ('\n' );
1582+ if (!servlets .isEmpty ()) {
1583+ sb .append ('\n' );
1584+ }
15701585
15711586 for (Map .Entry <String ,String > entry : servletMappings .entrySet ()) {
15721587 sb .append (" <servlet-mapping>\n " );
15731588 appendElement (sb , INDENT4 , "servlet-name" , entry .getValue ());
15741589 appendElement (sb , INDENT4 , "url-pattern" , encodeUrl (entry .getKey ()));
15751590 sb .append (" </servlet-mapping>\n " );
15761591 }
1577- sb .append ('\n' );
1592+ if (!servletMappings .isEmpty ()) {
1593+ sb .append ('\n' );
1594+ }
15781595
1579- if (sessionConfig != null ) {
1596+ if (sessionConfig .getSessionTimeout () != null || sessionConfig .getCookieName () != null ||
1597+ !sessionConfig .getCookieAttributes ().isEmpty () || !sessionConfig .getSessionTrackingModes ().isEmpty ()) {
15801598 sb .append (" <session-config>\n " );
15811599 appendElement (sb , INDENT4 , "session-timeout" , sessionConfig .getSessionTimeout ());
15821600 if (majorVersion >= 3 ) {
1583- sb .append (" <cookie-config>\n " );
1584- appendElement (sb , INDENT6 , "name" , sessionConfig .getCookieName ());
1585- appendElement (sb , INDENT6 , "domain" , sessionConfig .getCookieDomain ());
1586- appendElement (sb , INDENT6 , "path" , sessionConfig .getCookiePath ());
1587- appendElement (sb , INDENT6 , "comment" , sessionConfig .getCookieComment ());
1588- appendElement (sb , INDENT6 , "http-only" , sessionConfig .getCookieHttpOnly ());
1589- appendElement (sb , INDENT6 , "secure" , sessionConfig .getCookieSecure ());
1590- appendElement (sb , INDENT6 , "max-age" , sessionConfig .getCookieMaxAge ());
1591- sb .append (" </cookie-config>\n " );
1601+ if (sessionConfig .getCookieName () != null || !sessionConfig .getCookieAttributes ().isEmpty ()) {
1602+ sb .append (" <cookie-config>\n " );
1603+ appendElement (sb , INDENT6 , "name" , sessionConfig .getCookieName ());
1604+ appendElement (sb , INDENT6 , "domain" , sessionConfig .getCookieDomain ());
1605+ appendElement (sb , INDENT6 , "path" , sessionConfig .getCookiePath ());
1606+ appendElement (sb , INDENT6 , "comment" , sessionConfig .getCookieComment ());
1607+ appendElement (sb , INDENT6 , "http-only" , sessionConfig .getCookieHttpOnly ());
1608+ appendElement (sb , INDENT6 , "secure" , sessionConfig .getCookieSecure ());
1609+ appendElement (sb , INDENT6 , "max-age" , sessionConfig .getCookieMaxAge ());
1610+ for (Map .Entry <String ,String > entry : sessionConfig .getCookieAttributes ().entrySet ()) {
1611+ switch (entry .getKey ().toLowerCase (Locale .ROOT )) {
1612+ case "domain" :
1613+ case "path" :
1614+ case "comment" :
1615+ case "http-only" :
1616+ case "secure" :
1617+ case "max-age" :
1618+ // NO-OP - handled above
1619+ break ;
1620+ default : {
1621+ sb .append (" <attribute>\n " );
1622+ appendElement (sb , INDENT8 , "attribute-name" , entry .getKey ());
1623+ appendElement (sb , INDENT8 , "attribute-value" , entry .getValue ());
1624+ sb .append (" </attribute>\n " );
1625+ }
1626+ }
1627+ }
1628+ sb .append (" </cookie-config>\n " );
1629+ }
15921630 for (SessionTrackingMode stm : sessionConfig .getSessionTrackingModes ()) {
15931631 appendElement (sb , INDENT4 , "tracking-mode" , stm .name ());
15941632 }
@@ -1602,7 +1640,9 @@ public String toXml() {
16021640 appendElement (sb , INDENT4 , "mime-type" , entry .getValue ());
16031641 sb .append (" </mime-mapping>\n " );
16041642 }
1605- sb .append ('\n' );
1643+ if (!mimeMappings .isEmpty ()) {
1644+ sb .append ('\n' );
1645+ }
16061646
16071647 if (!welcomeFiles .isEmpty ()) {
16081648 sb .append (" <welcome-file-list>\n " );
@@ -1626,10 +1666,12 @@ public String toXml() {
16261666 } else if (errorPage .getErrorCode () > 0 ) {
16271667 appendElement (sb , INDENT4 , "error-code" , Integer .toString (errorCode ));
16281668 }
1629- appendElement (sb , INDENT4 , "location" , errorPage .getLocation ());
1669+ appendElement (sb , INDENT4 , "location" , errorPage .getLocation (), true );
16301670 sb .append (" </error-page>\n " );
16311671 }
1632- sb .append ('\n' );
1672+ if (!errorPages .isEmpty ()) {
1673+ sb .append ('\n' );
1674+ }
16331675
16341676 // jsp-config was added in Servlet 2.4. Prior to that, tag-libs was used
16351677 // directly and jsp-property-group did not exist
@@ -1687,7 +1729,9 @@ public String toXml() {
16871729 appendElement (sb , INDENT4 , "lookup-name" , resourceEnvRef .getLookupName ());
16881730 sb .append (" </resource-env-ref>\n " );
16891731 }
1690- sb .append ('\n' );
1732+ if (!resourceEnvRefs .isEmpty ()) {
1733+ sb .append ('\n' );
1734+ }
16911735 }
16921736
16931737 for (ContextResource resourceRef : resourceRefs .values ()) {
@@ -1710,7 +1754,9 @@ public String toXml() {
17101754 appendElement (sb , INDENT4 , "lookup-name" , resourceRef .getLookupName ());
17111755 sb .append (" </resource-ref>\n " );
17121756 }
1713- sb .append ('\n' );
1757+ if (!resourceRefs .isEmpty ()) {
1758+ sb .append ('\n' );
1759+ }
17141760
17151761 for (SecurityConstraint constraint : securityConstraints ) {
17161762 sb .append (" <security-constraint>\n " );
@@ -1733,12 +1779,20 @@ public String toXml() {
17331779 }
17341780 sb .append (" </web-resource-collection>\n " );
17351781 }
1736- if (constraint .findAuthRoles ().length > 0 ) {
1782+ if (constraint .findAuthRoles ().length > 0 || constraint . getAllRoles () || constraint . getAuthenticatedUsers () ) {
17371783 sb .append (" <auth-constraint>\n " );
17381784 for (String role : constraint .findAuthRoles ()) {
17391785 appendElement (sb , INDENT6 , "role-name" , role );
17401786 }
1787+ if (constraint .getAllRoles ()) {
1788+ appendElement (sb , INDENT6 , "role-name" , SecurityConstraint .ROLE_ALL_ROLES );
1789+ }
1790+ if (constraint .getAuthenticatedUsers ()) {
1791+ appendElement (sb , INDENT6 , "role-name" , SecurityConstraint .ROLE_ALL_AUTHENTICATED_USERS );
1792+ }
17411793 sb .append (" </auth-constraint>\n " );
1794+ } else if (constraint .getAuthConstraint ()) {
1795+ sb .append (" <auth-constraint/>\n " );
17421796 }
17431797 if (constraint .getUserConstraint () != null ) {
17441798 sb .append (" <user-data-constraint>\n " );
@@ -1747,16 +1801,18 @@ public String toXml() {
17471801 }
17481802 sb .append (" </security-constraint>\n " );
17491803 }
1750- sb .append ('\n' );
1804+ if (!securityConstraints .isEmpty ()) {
1805+ sb .append ('\n' );
1806+ }
17511807
17521808 if (loginConfig != null ) {
17531809 sb .append (" <login-config>\n " );
17541810 appendElement (sb , INDENT4 , "auth-method" , loginConfig .getAuthMethod ());
17551811 appendElement (sb , INDENT4 , "realm-name" , loginConfig .getRealmName ());
17561812 if (loginConfig .getErrorPage () != null || loginConfig .getLoginPage () != null ) {
17571813 sb .append (" <form-login-config>\n " );
1758- appendElement (sb , INDENT6 , "form-login-page" , loginConfig .getLoginPage ());
1759- appendElement (sb , INDENT6 , "form-error-page" , loginConfig .getErrorPage ());
1814+ appendElement (sb , INDENT6 , "form-login-page" , loginConfig .getLoginPage (), true );
1815+ appendElement (sb , INDENT6 , "form-error-page" , loginConfig .getErrorPage (), true );
17601816 sb .append (" </form-login-config>\n " );
17611817 }
17621818 sb .append (" </login-config>\n \n " );
@@ -1784,7 +1840,9 @@ public String toXml() {
17841840 appendElement (sb , INDENT4 , "lookup-name" , envEntry .getLookupName ());
17851841 sb .append (" </env-entry>\n " );
17861842 }
1787- sb .append ('\n' );
1843+ if (!envEntries .isEmpty ()) {
1844+ sb .append ('\n' );
1845+ }
17881846
17891847 for (ContextEjb ejbRef : ejbRefs .values ()) {
17901848 sb .append (" <ejb-ref>\n " );
@@ -1804,7 +1862,9 @@ public String toXml() {
18041862 appendElement (sb , INDENT4 , "lookup-name" , ejbRef .getLookupName ());
18051863 sb .append (" </ejb-ref>\n " );
18061864 }
1807- sb .append ('\n' );
1865+ if (!ejbRefs .isEmpty ()) {
1866+ sb .append ('\n' );
1867+ }
18081868
18091869 // ejb-local-ref was introduced in Servlet 2.3
18101870 if (getMajorVersion () > 2 || getMinorVersion () > 2 ) {
@@ -1826,7 +1886,9 @@ public String toXml() {
18261886 appendElement (sb , INDENT4 , "lookup-name" , ejbLocalRef .getLookupName ());
18271887 sb .append (" </ejb-local-ref>\n " );
18281888 }
1829- sb .append ('\n' );
1889+ if (!ejbLocalRefs .isEmpty ()) {
1890+ sb .append ('\n' );
1891+ }
18301892 }
18311893
18321894 // service-ref was introduced in Servlet 2.4
@@ -1874,7 +1936,9 @@ public String toXml() {
18741936 appendElement (sb , INDENT4 , "lookup-name" , serviceRef .getLookupName ());
18751937 sb .append (" </service-ref>\n " );
18761938 }
1877- sb .append ('\n' );
1939+ if (!serviceRefs .isEmpty ()) {
1940+ sb .append ('\n' );
1941+ }
18781942 }
18791943
18801944 if (!postConstructMethods .isEmpty ()) {
@@ -1917,7 +1981,9 @@ public String toXml() {
19171981 appendElement (sb , INDENT4 , "lookup-name" , mdr .getLookupName ());
19181982 sb .append (" </message-destination-ref>\n " );
19191983 }
1920- sb .append ('\n' );
1984+ if (!messageDestinationRefs .isEmpty ()) {
1985+ sb .append ('\n' );
1986+ }
19211987
19221988 for (MessageDestination md : messageDestinations .values ()) {
19231989 sb .append (" <message-destination>\n " );
@@ -1928,7 +1994,9 @@ public String toXml() {
19281994 appendElement (sb , INDENT4 , "lookup-name" , md .getLookupName ());
19291995 sb .append (" </message-destination>\n " );
19301996 }
1931- sb .append ('\n' );
1997+ if (!messageDestinations .isEmpty ()) {
1998+ sb .append ('\n' );
1999+ }
19322000 }
19332001
19342002 // locale-encoding-mapping-list was introduced in Servlet 2.4
@@ -1964,12 +2032,21 @@ public String toXml() {
19642032 }
19652033
19662034
1967- private String encodeUrl (String input ) {
1968- return URLEncoder .encode (input , StandardCharsets .UTF_8 );
2035+ private synchronized String encodeUrl (String input ) {
2036+ try {
2037+ return urlEncoder .encodeURL (input , 0 , input .length ()).toString ();
2038+ } catch (IOException e ) {
2039+ throw new IllegalArgumentException (input , e );
2040+ }
19692041 }
19702042
19712043
1972- private static void appendElement (StringBuilder sb , String indent , String elementName , String value ) {
2044+ private void appendElement (StringBuilder sb , String indent , String elementName , String value ) {
2045+ appendElement (sb , indent , elementName , value , false );
2046+ }
2047+
2048+ private void appendElement (StringBuilder sb , String indent , String elementName , String value , boolean encodeValue ) {
2049+
19732050 if (value == null ) {
19742051 return ;
19752052 }
@@ -1983,14 +2060,18 @@ private static void appendElement(StringBuilder sb, String indent, String elemen
19832060 sb .append ('<' );
19842061 sb .append (elementName );
19852062 sb .append ('>' );
1986- sb .append (Escape .xml (value ));
2063+ if (encodeValue ) {
2064+ sb .append (Escape .xml (encodeUrl (value )));
2065+ } else {
2066+ sb .append (Escape .xml (value ));
2067+ }
19872068 sb .append ("</" );
19882069 sb .append (elementName );
19892070 sb .append (">\n " );
19902071 }
19912072 }
19922073
1993- private static void appendElement (StringBuilder sb , String indent , String elementName , Object value ) {
2074+ private void appendElement (StringBuilder sb , String indent , String elementName , Object value ) {
19942075 if (value == null ) {
19952076 return ;
19962077 }
0 commit comments