Coverage Report - org.apache.myfaces.lifecycle.CODIClientSideWindow
 
Classes in this File Line Coverage Branch Coverage Complexity
CODIClientSideWindow
0%
0/104
0%
0/46
3.067
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.myfaces.lifecycle;
 20  
 
 21  
 import java.io.BufferedReader;
 22  
 import java.io.IOException;
 23  
 import java.io.InputStream;
 24  
 import java.io.InputStreamReader;
 25  
 import java.io.OutputStream;
 26  
 import java.util.HashMap;
 27  
 import java.util.Map;
 28  
 import javax.faces.FacesException;
 29  
 import javax.faces.application.Resource;
 30  
 import javax.faces.context.ExternalContext;
 31  
 import javax.faces.context.FacesContext;
 32  
 import javax.faces.lifecycle.ClientWindow;
 33  
 import javax.faces.render.ResponseStateManager;
 34  
 import javax.servlet.http.Cookie;
 35  
 import javax.servlet.http.HttpServletResponse;
 36  
 
 37  
 /**
 38  
  *
 39  
  * @author lu4242
 40  
  */
 41  
 public class CODIClientSideWindow extends ClientWindow
 42  
 {
 43  
     /**
 44  
      * Key for storing the window-id e.g. in URLs
 45  
      */
 46  
     //private static final String WINDOW_CONTEXT_ID_PARAMETER_KEY = 
 47  
     //        ResponseStateManager.CLIENT_WINDOW_URL_PARAM;
 48  
 
 49  
     /**
 50  
      * Value which can be used as "window-id" by external clients which aren't aware of windows.
 51  
      * It deactivates e.g. the redirect for the initial request.
 52  
      */
 53  
     private static final String AUTOMATED_ENTRY_POINT_PARAMETER_KEY = "automatedEntryPoint";    
 54  
     
 55  
     private static final long serialVersionUID = 5293942986187078113L;
 56  
 
 57  
     private static final String WINDOW_ID_COOKIE_PREFIX = "jfwid-";
 58  
     private static final String CODI_REQUEST_TOKEN = "mfRid";
 59  
 
 60  
     private static final String UNINITIALIZED_WINDOW_ID_VALUE = "uninitializedWindowId";
 61  
     private static final String WINDOW_ID_REPLACE_PATTERN = "$$windowIdValue$$";
 62  
     private static final String NOSCRIPT_URL_REPLACE_PATTERN = "$$noscriptUrl$$";
 63  
     private static final String NOSCRIPT_PARAMETER = "mfDirect";
 64  
 
 65  
     private final ClientConfig clientConfig;
 66  
 
 67  
     private final WindowContextConfig windowContextConfig;
 68  
     
 69  
     private final TokenGenerator clientWindowTokenGenerator;    
 70  
     
 71  
     private String windowId;
 72  
     
 73  0
     private String unparsedWindowHandlerHtml = null;
 74  
     
 75  
     private Map<String,String> queryParamsMap;
 76  
 
 77  
     /*
 78  
     protected CODIClientSideWindow()
 79  
     {
 80  
         // needed for proxying
 81  
     }*/
 82  
 
 83  
     protected CODIClientSideWindow(TokenGenerator clientWindowTokenGenerator,
 84  
             WindowContextConfig windowContextConfig,
 85  
             ClientConfig clientConfig)
 86  0
     {
 87  0
         this.windowContextConfig = windowContextConfig;
 88  0
         this.clientConfig = clientConfig;
 89  0
         this.clientWindowTokenGenerator = clientWindowTokenGenerator;
 90  0
     }
 91  
 
 92  
     /**
 93  
      * {@inheritDoc}
 94  
      */
 95  
     /*
 96  
     public String restoreWindowId(ExternalContext externalContext)
 97  
     {
 98  
         if (this.clientConfig.isJavaScriptEnabled())
 99  
         {
 100  
             return (String) externalContext.getRequestMap().get(
 101  
                     WINDOW_CONTEXT_ID_PARAMETER_KEY);
 102  
         }
 103  
         else
 104  
         {
 105  
             // fallback
 106  
             //if(!this.useWindowAwareUrlEncoding)
 107  
             //{
 108  
             //    return null;
 109  
             //}
 110  
 
 111  
             return externalContext.getRequestParameterMap().get(WINDOW_CONTEXT_ID_PARAMETER_KEY);
 112  
         }
 113  
     }*/
 114  
 
 115  
     /**
 116  
      * {@inheritDoc}
 117  
      */
 118  
     //public void beforeLifecycleExecute(FacesContext facesContext)
 119  
     public void decode(FacesContext facesContext)
 120  
     {
 121  0
         if (facesContext.isPostback())
 122  
         {
 123  
             // In postback, we can safely ignore the query param, because it is not useful
 124  0
             if (getId() == null)
 125  
             {
 126  0
                  setId(calculateWindowIdFromPost(facesContext));
 127  
             }
 128  
         }
 129  
 
 130  0
         if (!isClientSideWindowHandlerRequest(facesContext))
 131  
         {
 132  0
             return;
 133  
         }
 134  
         
 135  0
         ExternalContext externalContext = facesContext.getExternalContext();
 136  
 
 137  0
         if (isNoscriptRequest(externalContext))
 138  
         {
 139  
             // the client has JavaScript disabled
 140  0
             clientConfig.setJavaScriptEnabled(false);
 141  0
             return;
 142  
         }
 143  
 
 144  0
         String windowId = getWindowIdFromCookie(externalContext);
 145  0
         if (windowId == null)
 146  
         {
 147  
             // GET request without windowId - send windowhandlerfilter.html to get the windowId
 148  0
             sendWindowHandlerHtml(facesContext, null);
 149  0
             facesContext.responseComplete();
 150  
         }
 151  
         else
 152  
         {
 153  0
             if (AUTOMATED_ENTRY_POINT_PARAMETER_KEY.equals(windowId) ||
 154  
                 (!windowContextConfig.isUnknownWindowIdsAllowed() /*&&
 155  
                  !ConversationUtils.isWindowActive(this.windowContextManager, windowId)*/))
 156  
             {
 157  
                 // no or invalid windowId --> create new one
 158  
                 // don't use createWindowId() the following call will ensure the max. window context count,...
 159  
                 //windowId = this.windowContextManager.getCurrentWindowContext().getId();
 160  0
                 windowId = createWindowId(facesContext);
 161  
 
 162  
                 // GET request with NEW windowId - send windowhandlerfilter.html to set and re-get the windowId
 163  0
                 sendWindowHandlerHtml(facesContext, windowId);
 164  0
                 facesContext.responseComplete();
 165  
             }
 166  
             else
 167  
             {
 168  
                 // we have a valid windowId - set it and continue with the request
 169  
                 // TODO only set internally and provide via restoreWindowId()? 
 170  
                 //externalContext.getRequestMap().put(WINDOW_CONTEXT_ID_PARAMETER_KEY, windowId);
 171  0
                 setId(windowId);
 172  
             }
 173  
         }
 174  0
     }
 175  
 
 176  
     public String calculateWindowIdFromPost(FacesContext context)
 177  
     {
 178  
         //1. If it comes as parameter, it takes precedence over any other choice, because
 179  
         //   no browser is capable to do a POST and create a new window at the same time.
 180  0
         String windowId = context.getExternalContext().getRequestParameterMap().get(
 181  
                 ResponseStateManager.CLIENT_WINDOW_PARAM);
 182  0
         if (windowId != null)
 183  
         {
 184  0
             return windowId;
 185  
         }
 186  0
         return null;
 187  
     }
 188  
     
 189  
     private boolean isClientSideWindowHandlerRequest(FacesContext facesContext)
 190  
     {
 191  
         // no POST request and javascript enabled
 192  
         // NOTE that for POST-requests the windowId is saved in the state (see WindowContextIdHolderComponent)
 193  0
         return !facesContext.isPostback() && clientConfig.isClientSideWindowHandlerRequest(facesContext);
 194  
     }
 195  
 
 196  
     private boolean isNoscriptRequest(ExternalContext externalContext)
 197  
     {
 198  0
         String noscript = externalContext.getRequestParameterMap().get(NOSCRIPT_PARAMETER);
 199  
 
 200  0
         return (noscript != null && "true".equals(noscript));
 201  
     }
 202  
 
 203  
     private void sendWindowHandlerHtml(FacesContext facesContext, String windowId)
 204  
     {
 205  0
         HttpServletResponse httpResponse = (HttpServletResponse) facesContext.getExternalContext().getResponse();
 206  
 
 207  
         try
 208  
         {
 209  0
             httpResponse.setStatus(HttpServletResponse.SC_OK);
 210  0
             httpResponse.setContentType("text/html");
 211  
 
 212  0
             if (unparsedWindowHandlerHtml == null)
 213  
             {
 214  0
                 Resource resource = facesContext.getApplication().getResourceHandler().createResource(
 215  
                         "windowhandler.html", "org.apache.myfaces.windowId");
 216  
                 
 217  0
                 unparsedWindowHandlerHtml = convertStreamToString(resource.getInputStream());
 218  
             }
 219  
             
 220  0
             String windowHandlerHtml = unparsedWindowHandlerHtml;
 221  
 
 222  0
             if (windowId == null)
 223  
             {
 224  0
                 windowId = UNINITIALIZED_WINDOW_ID_VALUE;
 225  
             }
 226  
 
 227  
             // set the windowId value in the javascript code
 228  0
             windowHandlerHtml = windowHandlerHtml.replace(WINDOW_ID_REPLACE_PATTERN, windowId);
 229  
 
 230  
             // set the noscript-URL for users with no JavaScript
 231  0
             windowHandlerHtml = windowHandlerHtml.replace(
 232  
                     NOSCRIPT_URL_REPLACE_PATTERN, getNoscriptUrl(facesContext.getExternalContext()));
 233  
 
 234  0
             OutputStream os = httpResponse.getOutputStream();
 235  
             try
 236  
             {
 237  0
                 os.write(windowHandlerHtml.getBytes());
 238  
             }
 239  
             finally
 240  
             {
 241  0
                 os.close();
 242  0
             }
 243  
         }
 244  0
         catch (IOException ioe)
 245  
         {
 246  0
             throw new FacesException(ioe);
 247  0
         }
 248  0
     }
 249  
     
 250  
     private static String convertStreamToString(InputStream is)
 251  
     {
 252  0
         StringBuilder sb = new StringBuilder();
 253  
         try
 254  
         {
 255  0
             BufferedReader reader = new BufferedReader(new InputStreamReader(is));
 256  0
             String line = null;
 257  0
             while ((line = reader.readLine()) != null)
 258  
             {
 259  0
               sb.append(line + "\n");
 260  
             }
 261  
         }
 262  0
         catch (IOException e)
 263  
         {
 264  0
             throw new FacesException(e);
 265  
         }
 266  
         finally
 267  
         {
 268  0
             if (is != null)
 269  
             {
 270  
                 try
 271  
                 {
 272  0
                     is.close();
 273  
                 }
 274  0
                 catch (IOException e)
 275  
                 {
 276  
                     //No op
 277  0
                 }                
 278  
             }
 279  
         }
 280  0
         return sb.toString();
 281  
     }    
 282  
 
 283  
     private String getNoscriptUrl(ExternalContext externalContext)
 284  
     {
 285  0
         String url = externalContext.getRequestPathInfo();
 286  0
         if (url == null)
 287  
         {
 288  0
             url = "";
 289  
         }
 290  
 
 291  
         // only use the very last part of the url
 292  0
         int lastSlash = url.lastIndexOf('/');
 293  0
         if (lastSlash != -1)
 294  
         {
 295  0
             url = url.substring(lastSlash + 1);
 296  
         }
 297  
 
 298  
         // add request parameter
 299  0
         url = addParameters(externalContext, url, true, true, true);
 300  
 
 301  
         // add noscript parameter
 302  0
         if (url.contains("?"))
 303  
         {
 304  0
             url = url + "&";
 305  
         }
 306  
         else
 307  
         {
 308  0
             url = url + "?";
 309  
         }
 310  0
         url = url + NOSCRIPT_PARAMETER + "=true";
 311  
 
 312  
         // NOTE that the url could contain data for an XSS attack
 313  
         // like e.g. ?"></a><a href%3D"http://hacker.org/attack.html?a
 314  
         // DO NOT REMOVE THE FOLLOWING LINES!
 315  0
         url = url.replace("\"", "");
 316  0
         url = url.replace("\'", "");
 317  
 
 318  0
         return url;
 319  
     }
 320  
 
 321  
     /**
 322  
      * Adds the current request-parameters to the given url
 323  
      * @param externalContext current external-context
 324  
      * @param url current url
 325  
      * @param addRequestParameter flag which indicates if the request params should be added or not
 326  
      * @param addPageParameter flag which indicates if the view params should be added or not {@see ViewParameter}
 327  
      * @param encodeValues flag which indicates if parameter values should be encoded or not
 328  
      * @return url with request-parameters
 329  
      */
 330  
     public static String addParameters(ExternalContext externalContext, String url,
 331  
                                        boolean addRequestParameter, boolean addPageParameter, boolean encodeValues)
 332  
     {
 333  0
         StringBuilder finalUrl = new StringBuilder(url);
 334  0
         boolean existingParameters = url.contains("?");
 335  0
         boolean urlContainsWindowId = url.contains(ResponseStateManager.CLIENT_WINDOW_URL_PARAM + "=");
 336  
 
 337  
         /* TODO: implement me
 338  
         for(RequestParameter requestParam :
 339  
                 getParameters(externalContext, true, addRequestParameter, addPageParameter))
 340  
         {
 341  
             String key = requestParam.getKey();
 342  
 
 343  
             //TODO eval if we should also filter the other params
 344  
             if(WindowContextManager.WINDOW_CONTEXT_ID_PARAMETER_KEY.equals(key) && urlContainsWindowId)
 345  
             {
 346  
                 continue;
 347  
             }
 348  
 
 349  
             for(String parameterValue : requestParam.getValues())
 350  
             {
 351  
                 if(!url.contains(key + "=" + parameterValue) &&
 352  
                         !url.contains(key + "=" + encodeURLParameterValue(parameterValue, externalContext)))
 353  
                 {
 354  
                     if(!existingParameters)
 355  
                     {
 356  
                         finalUrl.append("?");
 357  
                         existingParameters = true;
 358  
                     }
 359  
                     else
 360  
                     {
 361  
                         finalUrl.append("&");
 362  
                     }
 363  
                     finalUrl.append(key);
 364  
                     finalUrl.append("=");
 365  
 
 366  
                     if(encodeValues)
 367  
                     {
 368  
                         finalUrl.append(encodeURLParameterValue(parameterValue, externalContext));
 369  
                     }
 370  
                     else
 371  
                     {
 372  
                         finalUrl.append(parameterValue);
 373  
                     }
 374  
                 }
 375  
             }
 376  
         }
 377  
         */
 378  0
         return finalUrl.toString();
 379  
     }
 380  
     
 381  
     protected String createWindowId(FacesContext context)
 382  
     {
 383  0
         String windowId = clientWindowTokenGenerator._getNextToken();
 384  0
         setId(windowId);
 385  0
         return windowId;
 386  
     }
 387  
     
 388  
     private String getWindowIdFromCookie(ExternalContext externalContext)
 389  
     {
 390  0
         String cookieName = WINDOW_ID_COOKIE_PREFIX + getRequestToken(externalContext);
 391  0
         Cookie cookie = (Cookie) externalContext.getRequestCookieMap().get(cookieName);
 392  
 
 393  0
         if (cookie != null)
 394  
         {
 395  
             // manually blast the cookie away, otherwise it pollutes the
 396  
             // cookie storage in some browsers. E.g. Firefox doesn't
 397  
             // cleanup properly, even if the max-age is reached.
 398  0
             cookie.setMaxAge(0);
 399  
 
 400  0
             return cookie.getValue();
 401  
         }
 402  
 
 403  0
         return null;
 404  
     }
 405  
 
 406  
     private String getRequestToken(ExternalContext externalContext)
 407  
     {
 408  0
         String requestToken = externalContext.getRequestParameterMap().get(CODI_REQUEST_TOKEN);
 409  0
         if (requestToken != null)
 410  
         {
 411  0
             return requestToken;
 412  
         }
 413  
 
 414  0
         return "";
 415  
     }
 416  
 
 417  
     @Override
 418  
     public String getId()
 419  
     {
 420  0
         return windowId;
 421  
     }
 422  
     
 423  
     public void setId(String id)
 424  
     {
 425  0
         windowId = id;
 426  0
         queryParamsMap = null;
 427  0
     }
 428  
     
 429  
     @Override
 430  
     public Map<String, String> getQueryURLParameters(FacesContext context)
 431  
     {
 432  0
         if (queryParamsMap == null)
 433  
         {
 434  0
             String id = context.getExternalContext().getClientWindow().getId();
 435  0
             if (id != null)
 436  
             {
 437  0
                 queryParamsMap = new HashMap<String, String>(2,1);
 438  0
                 queryParamsMap.put(ResponseStateManager.CLIENT_WINDOW_URL_PARAM, id);
 439  
             }
 440  
         }
 441  0
         return queryParamsMap;
 442  
     }
 443  
 }