juergen     2002/08/16 09:16:45

  Modified:    src/webdav/server/org/apache/slide/webdav/util
                        WebdavUtils.java
  Log:
  Adoption for Tomcat 4.1.9 (and higher). The request URL received from Tomcat is now 
utf-8 encoded.
  
  Revision  Changes    Path
  1.7       +366 -356  
jakarta-slide/src/webdav/server/org/apache/slide/webdav/util/WebdavUtils.java
  
  Index: WebdavUtils.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-slide/src/webdav/server/org/apache/slide/webdav/util/WebdavUtils.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- WebdavUtils.java  5 Aug 2002 13:52:37 -0000       1.6
  +++ WebdavUtils.java  16 Aug 2002 16:16:45 -0000      1.7
  @@ -94,180 +94,179 @@
    * @version $Revision$
    **/
   public class WebdavUtils {
  -    
  -    
  -    private static final String PRINCIPAL_ATTRIBUTE =
  -        "org.apache.slide.webdav.method.principal";
  -    
  -    
  -    // --------------------------------------------------------- Public Methods
  -    
  -    static private void printHexString(String path, String enc) {
  -        try {
  -            byte[] ba;
  -            if (enc == null) ba = path.getBytes();
  -            else             ba = path.getBytes("ISO-8859-1");
  -            System.out.print("@@@@ bytes= " + enc + "  " );
  -            for(int i=0; i < ba.length; i++) {
  -                System.out.print(convertHexDigit(ba[i]));
  -                if( i < ba.length-1 )
  -                    System.out.print(" ");
  -                else {
  -                    String s;
  -                    if (enc == null) s = new String(ba);
  -                    else             s = new String(ba, enc);
  -                    System.out.print("  ( " + s  + " )"+ "\n");
  -                }
  -            }
  -        }
  -        catch( Exception x ) {
  -            x.printStackTrace();
  -        }
  -    }
  -    
  -    
  -    
  -    static private void printString(String path) {
  -        System.out.println("");
  -        System.out.println("@@@@ string="+path);
  -        printHexString(path, Configuration.urlEncoding());
  -        printHexString(path, "UTF-8");
  -        printHexString(path, "SHIFT_JIS");
  -        printHexString(path, null);
  -    }
  -    
  -    protected static final String[] hexadecimal =
  -    {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
  -            "A", "B", "C", "D", "E", "F"};
  -    
  -    
  -    private static String convertHexDigit( byte toEncode ) {
  -        String result;
  -        int low = (int) (toEncode & 0x0f);
  -        int high = (int) ((toEncode & 0xf0) >> 4);
  -        result = hexadecimal[high] + hexadecimal[low];
  -        return result;
  -    }
  -    
  -    /**
  -     * Return a context-relative path, beginning with a "/", that represents
  -     * the canonical version of the specified path after ".." and "." elements
  -     * are resolved out.  If the specified path attempts to go outside the
  -     * boundaries of the current context (i.e. too many ".." path elements
  -     * are present), return <code>null</code> instead.
  -     *
  -     * @param path the path to be normalized
  -     **/
  -    public static String decodeURL(String path) {
  -        return decodeURL(path, Configuration.urlEncoding());
  -    }
  -    
  -    /**
  -     * Return a context-relative path, beginning with a "/", that represents
  -     * the canonical version of the specified path after ".." and "." elements
  -     * are resolved out.  If the specified path attempts to go outside the
  -     * boundaries of the current context (i.e. too many ".." path elements
  -     * are present), return <code>null</code> instead.
  -     *
  -     * @param path the path to be normalized
  -     **/
  -    public static String decodeURL(String path, String enc) {
  -        
  -        // @@@@@@@@@ TEMP START @@@@@@@@@@@@@@@@@
  -        //System.out.println("BEFORE ENCODING");
  -    //  printString(path);
  -        // @@@@@@@@@ TEMP END @@@@@@@@@@@@@@@@@
  -        
  -        
  -        // @@@@@@@@@ TEMP START @@@@@@@@@@@@@@@@@
  +     
  +     
  +     private static final String PRINCIPAL_ATTRIBUTE =
  +             "org.apache.slide.webdav.method.principal";
  +     
  +     
  +     // --------------------------------------------------------- Public Methods
  +     
  +     static private void printHexString(String path, String enc) {
  +             try {
  +                     byte[] ba;
  +                     if (enc == null) ba = path.getBytes();
  +                     else             ba = path.getBytes("ISO-8859-1");
  +                     System.out.print("@@@@ bytes= " + enc + "  \t" );
  +                     for(int i=0; i < ba.length; i++) {
  +                             System.out.print(convertHexDigit(ba[i]));
  +                             if( i < ba.length-1 )
  +                                     System.out.print(" ");
  +                             else {
  +                                     String s;
  +                                     if (enc == null) s = new String(ba);
  +                                     else             s = new String(ba, enc);
  +                                     System.out.print("  ( " + s  + " )"+ "\n");
  +                             }
  +                     }
  +             }
  +             catch( Exception x ) {
  +                     x.printStackTrace();
  +             }
  +     }
  +     
  +     
  +     
  +     static private void printString(String path) {
  +             System.out.println("");
  +             System.out.println("@@@@ string="+path);
  +             printHexString(path, Configuration.urlEncoding());
  +             printHexString(path, "UTF-8");
  +//      printHexString(path, "SHIFT_JIS");
  +             printHexString(path, null);
  +     }
  +     
  +     protected static final String[] hexadecimal =
  +     {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
  +                     "A", "B", "C", "D", "E", "F"};
  +     
  +     
  +     private static String convertHexDigit( byte toEncode ) {
  +             String result;
  +             int low = (int) (toEncode & 0x0f);
  +             int high = (int) ((toEncode & 0xf0) >> 4);
  +             result = hexadecimal[high] + hexadecimal[low];
  +             return result;
  +     }
  +     
  +     /**
  +      * Return a context-relative path, beginning with a "/", that represents
  +      * the canonical version of the specified path after ".." and "." elements
  +      * are resolved out.  If the specified path attempts to go outside the
  +      * boundaries of the current context (i.e. too many ".." path elements
  +      * are present), return <code>null</code> instead.
  +      *
  +      * @param path the path to be normalized
  +      **/
  +     public static String decodeURL(String path) {
  +             return decodeURL(path, Configuration.urlEncoding());
  +     }
  +     
  +     /**
  +      * Return a context-relative path, beginning with a "/", that represents
  +      * the canonical version of the specified path after ".." and "." elements
  +      * are resolved out.  If the specified path attempts to go outside the
  +      * boundaries of the current context (i.e. too many ".." path elements
  +      * are present), return <code>null</code> instead.
  +      *
  +      * @param path the path to be normalized
  +      **/
  +     public static String decodeURL(String path, String enc) {
  +             
  +             // @@@@@@@@@ TEMP START @@@@@@@@@@@@@@@@@
  +             //System.out.println("BEFORE ENCODING");
  +     //  printString(path);
  +             // @@@@@@@@@ TEMP END @@@@@@@@@@@@@@@@@
  +             
  +             
  +             // @@@@@@@@@ TEMP START @@@@@@@@@@@@@@@@@
   //      System.out.println("After ENCODING");
   //      printString(path);
   //      System.out.println("");
   //      System.out.println("");
  -        // @@@@@@@@@ TEMP END @@@@@@@@@@@@@@@@@
  -        
  -        
  -        
  -        
  -        if (path == null)
  -            return null;
  -        
  -        // Resolve encoded characters in the normalized path,
  -        // which also handles encoded spaces so we can skip that later.
  -        // Placed at the beginning of the chain so that encoded
  -        // bad stuff(tm) can be caught by the later checks
  -        String normalized = URLUtil.URLDecode(path, enc);
  -        
  -        if (normalized == null)
  -            return (null);
  -        
  -        // Normalize the slashes and add leading slash if necessary
  -        if (normalized.indexOf('\\') >= 0)
  -            normalized = normalized.replace('\\', '/');
  -        if (!normalized.startsWith("/"))
  -            normalized = "/" + normalized;
  -        
  -        // Resolve occurrences of "//" in the normalized path
  -        while (true) {
  -            int index = normalized.indexOf("//");
  -            if (index < 0)
  -                break;
  -            normalized = normalized.substring(0, index) +
  -                normalized.substring(index + 1);
  -        }
  -        
  -        // Resolve occurrences of "/./" in the normalized path
  -        while (true) {
  -            int index = normalized.indexOf("/./");
  -            if (index < 0)
  -                break;
  -            normalized = normalized.substring(0, index) +
  -                normalized.substring(index + 2);
  -        }
  -        
  -        // Resolve occurrences of "/../" in the normalized path
  -        while (true) {
  -            int index = normalized.indexOf("/../");
  -            if (index < 0)
  -                break;
  -            if (index == 0)
  -                return (null);  // Trying to go outside our context
  -            int index2 = normalized.lastIndexOf('/', index - 1);
  -            normalized = normalized.substring(0, index2) +
  -                normalized.substring(index + 3);
  -        }
  -        
  -        // @@@@@@@@@ TEMP START @@@@@@@@@@@@@@@@@
  -        //printString(normalized);
  -        // @@@@@@@@@ TEMP END @@@@@@@@@@@@@@@@@
  -        
  -        
  -        // Return the normalized path that we have completed
  -        return (normalized);
  -    }
  -    
  -    
  -    /**
  -     * URL rewriter.
  -     *
  -     * @param path the path to be rewritten
  -     **/
  -    public static String encodeURL(String path) {
  -        return URLUtil.URLEncode(path, Configuration.urlEncoding());
  -    }
  -    
  -    
  -    /**
  -     * URL rewriter.
  -     *
  -     * @param path the path to be rewritten
  -     * @param enc the encoding
  -     **/
  -    public static String encodeURL(String path, String enc) {
  -        return URLUtil.URLEncode(path, enc);
  -    }
  -    
  -    
  +             // @@@@@@@@@ TEMP END @@@@@@@@@@@@@@@@@
  +             
  +             
  +             
  +             
  +             if (path == null)
  +                     return null;
  +             
  +             // Resolve encoded characters in the normalized path,
  +             // which also handles encoded spaces so we can skip that later.
  +             // Placed at the beginning of the chain so that encoded
  +             // bad stuff(tm) can be caught by the later checks
  +             String normalized = URLUtil.URLDecode(path, enc);
  +             
  +             if (normalized == null)
  +                     return (null);
  +             
  +             // Normalize the slashes and add leading slash if necessary
  +             if (normalized.indexOf('\\') >= 0)
  +                     normalized = normalized.replace('\\', '/');
  +             if (!normalized.startsWith("/"))
  +                     normalized = "/" + normalized;
  +             
  +             // Resolve occurrences of "//" in the normalized path
  +             while (true) {
  +                     int index = normalized.indexOf("//");
  +                     if (index < 0)
  +                             break;
  +                     normalized = normalized.substring(0, index) +
  +                             normalized.substring(index + 1);
  +             }
  +             
  +             // Resolve occurrences of "/./" in the normalized path
  +             while (true) {
  +                     int index = normalized.indexOf("/./");
  +                     if (index < 0)
  +                             break;
  +                     normalized = normalized.substring(0, index) +
  +                             normalized.substring(index + 2);
  +             }
  +             
  +             // Resolve occurrences of "/../" in the normalized path
  +             while (true) {
  +                     int index = normalized.indexOf("/../");
  +                     if (index < 0)
  +                             break;
  +                     if (index == 0)
  +                             return (null);  // Trying to go outside our context
  +                     int index2 = normalized.lastIndexOf('/', index - 1);
  +                     normalized = normalized.substring(0, index2) +
  +                             normalized.substring(index + 3);
  +             }
  +             
  +             // @@@@@@@@@ TEMP START @@@@@@@@@@@@@@@@@
  +             //printString(normalized);
  +             // @@@@@@@@@ TEMP END @@@@@@@@@@@@@@@@@
  +             
  +             
  +             // Return the normalized path that we have completed
  +             return (normalized);
  +     }
  +     
  +     
  +     /**
  +      * URL rewriter.
  +      *
  +      * @param path the path to be rewritten
  +      **/
  +     public static String encodeURL(String path) {
  +             return URLUtil.URLEncode(path, Configuration.urlEncoding());
  +     }
  +     
  +     
  +     /**
  +      * URL rewriter.
  +      *
  +      * @param path the path to be rewritten
  +      * @param enc the encoding
  +      **/
  +     public static String encodeURL(String path, String enc) {
  +             return URLUtil.URLEncode(path, enc);
  +     }
  +     
       /**
        * Maps the URI of a node in the Slide namespace to an external URI as seen
        * by clients. This involves adding the context and servlet path to the 
  @@ -298,186 +297,197 @@
       }
       
       
  -    /**
  -     * Maps the request URI of a HTTP request to a URI in the Slide namespace
  -     * (this does not necessarily mean that a node exists at that URI).
  -     *
  -     * @param req the request object
  -     * @param config configuration of the WebdavServlet
  -     *
  -     * @return the request URI mapped into the Slide namespace
  -     **/
  -    public static String getRelativePath
  -        (HttpServletRequest req, WebdavServletConfig config) {
  -        
  -        // get the requested path, depending on whether the servlet is mapped
  -        // as default servlet.
  -        String result = null;
  -        if (config.isDefaultServlet()) {
  -            result = req.getServletPath();
  -        } else {
  -            result = req.getPathInfo();
  -        }
  -        
  -        // default to the namespace root if no path-info is specified
  -        if ((result == null) || (result.length() == 0)) {
  -            result = "/";
  -        }
  -        
  -        // prefix the URI with the configured scope
  -        result = config.getScope() + result;
  -        
  -        return decodeURL(fixTomcatURL(result));
  -    }
  -    
  -    
  -    /**
  -     * Returns a SlideToken using the authentication information of an HTTP
  -     * request.
  -     *
  -     * @param req the HTTP request
  -     *
  -     * @return a new SlideToken instance
  -     **/
  -    public static String fixTomcatURL(String input) {
  -        String result = null;
  -        try {
  -            result = new String(input.getBytes("ISO-8859-1"), 
Configuration.urlEncoding());
  -        } catch (Exception e) { e.printStackTrace(); }
  -        return result;
  -    }
  -        
  -        
  -        
  -        
  -        
  -    /**
  -     * Returns a SlideToken using the authentication information of an HTTP
  -     * request.
  -     *
  -     * @param req the HTTP request
  -     *
  -     * @return a new SlideToken instance
  -     **/
  -    public static SlideToken getSlideToken
  -        (HttpServletRequest req) {
  -        
  -        Principal principal = req.getUserPrincipal();
  -        HttpSession session = req.getSession();
  -        
  -        // store the current principal in the session, to get around a bug in
  -        // IE 5 where the authentication info is not submitted by IE when
  -        // doing a HEAD request.
  -        if (principal == null) {
  -            principal = (Principal) session.getAttribute(PRINCIPAL_ATTRIBUTE);
  -        } else {
  -            session.setAttribute(PRINCIPAL_ATTRIBUTE, principal);
  -        }
  -        
  -        CredentialsToken credentials;
  -        if (principal == null) {
  -            credentials = new CredentialsToken("");
  -        } else {
  -            credentials = new CredentialsToken(principal);
  -        }
  -        
  -        SlideToken token = new SlideTokenImpl(credentials);
  -        token.setEnforceLockTokens(true);
  -        
  -        return token;
  -    }
  -    
  -    
  -    /**
  -     * Tests whether a the requested URI maps to a collection resource.
  -     *
  -     * @param req the HTTP request
  -     * @param config configuration of the WebDAV servlet
  -     * @param token the namespace access token
  -     *
  -     * @return true if the requested resource is a collection, false otherwise
  -     **/
  -    public static boolean isCollection
  -        (NamespaceAccessToken token, HttpServletRequest req,
  -         WebdavServletConfig config) {
  -        
  -        return isCollection(token, getSlideToken(req),
  -                            getRelativePath(req, config));
  -    }
  -    
  -    
  -    /**
  -     * Tests whether a given path maps to a URI in the Slide namespace that
  -     * identifies a collection resource.
  -     *
  -     * @param token the namespace access token
  -     * @param slideToken the slide token
  -     * @param path relative path of the resource
  -     *
  -     * @return true if the requested resource is a collection, false otherwise
  -     **/
  -    public static boolean isCollection
  -    (NamespaceAccessToken token, SlideToken slideToken,
  -    String path) {
  -        
  -        slideToken = new SlideTokenWrapper(slideToken, false); // check only, no 
enlistment
  -             
  -        // Added for DeltaV --start--
  -        if( Configuration.useVersionControl() ) {
  -            UriHandler uh = UriHandler.getUriHandler( path );
  -            if( uh.isWorkspaceUri() )
  -                return true;
  -            if( uh.isHistoryUri() )
  -                return true;
  -            if( uh.isVersionUri() )
  -                return false;
  -        }
  -        // Added for DeltaV --end--
  -             
  -        try {
  -            Content content = token.getContentHelper();
  -            NodeRevisionDescriptors revisionDescriptors =
  -                content.retrieve(slideToken, path);
  -            if (revisionDescriptors.hasRevisions()) {
  -                NodeRevisionDescriptor revisionDescriptor =
  -                    content.retrieve(slideToken, revisionDescriptors);
  -                return isCollection(revisionDescriptor);
  -            } else {
  -                return true;
  -            }
  -        } catch(ObjectNotFoundException e) {
  -            // if the Object is not found return false for no 207 is generated
  -            return false;
  -        } catch(SlideException e) {
  -            // this is the default
  -            return true;
  -        }
  -    }
  -    
  -    
  -    /**
  -     * Tests whether a resource is a collection resource.
  -     *
  -     * @param revisionDescriptor revision descriptor of the resource
  -     *
  -     * @return true if the descriptor represents a collection, false otherwise
  -     **/
  -    public static boolean isCollection
  -        (NodeRevisionDescriptor revisionDescriptor) {
  -        
  -        boolean result = false;
  -        
  -        if (revisionDescriptor == null)
  -            return true;
  -        
  -        if (revisionDescriptor.propertyValueContains(
  -                    revisionDescriptor.RESOURCE_TYPE ,"collection")) {
  -            result = true;
  -        }
  -        
  -        return result;
  -    }
  -    
  -    
  +     
  +     /**
  +      * Maps the request URI of a HTTP request to a URI in the Slide namespace
  +      * (this does not necessarily mean that a node exists at that URI).
  +      *
  +      * @param req the request object
  +      * @param config configuration of the WebdavServlet
  +      *
  +      * @return the request URI mapped into the Slide namespace
  +      **/
  +     public static String getRelativePath
  +             (HttpServletRequest req, WebdavServletConfig config) {
  +             
  +             // get the requested path, depending on whether the servlet is mapped
  +             // as default servlet.
  +             String result = null;
  +             if (config.isDefaultServlet()) {
  +                     result = req.getServletPath();
  +             } else {
  +                     result = req.getPathInfo();
  +             }
  +             
  +             // default to the namespace root if no path-info is specified
  +             if ((result == null) || (result.length() == 0)) {
  +                     result = "/";
  +             }
  +             
  +             // prefix the URI with the configured scope
  +             result = config.getScope() + result;
  +                      
  +                     
  +             
  +             return decodeURL(fixTomcatURL(result, "UTF-8"));  // the request URL 
is utf-8 encoded
  +     }
  +     
  +     
  +     /**
  +      * Returns an URL based on input. The input URL is encoded with "fromEncoding".
  +      *    The resulting URL is encoded as specified in Configuration.urlEncoding()
  +      *
  +      * @param input the input URL
  +      * @param fromEncoding the used encoding of the input URL
  +      *
  +      * @return a new URL encoded in Configuration.urlEncoding()
  +      **/
  +     public static String fixTomcatURL(String input, String fromEncoding) {
  +             String result = null;
  +             try {
  +                     
  +//                   printString(input.substring(27,input.length()));
  +//                   byte[] a = input.getBytes("UTF-8");
  +                     result = new String(input.getBytes(fromEncoding), 
Configuration.urlEncoding());
  +//                   printString(result.substring(27,result.length()));
  +//                   byte[] b = result.getBytes(Configuration.urlEncoding());
  +//                   System.out.println("Length b " + b.length);
  +             } catch (Exception e) { e.printStackTrace(); }
  +//           System.out.println("Length = " + input.length());
  +//           System.out.println("Length = " + result.length());
  +             return result;
  +     }
  +             
  +             
  +             
  +             
  +             
  +     /**
  +      * Returns a SlideToken using the authentication information of an HTTP
  +      * request.
  +      *
  +      * @param req the HTTP request
  +      *
  +      * @return a new SlideToken instance
  +      **/
  +     public static SlideToken getSlideToken
  +             (HttpServletRequest req) {
  +             
  +             Principal principal = req.getUserPrincipal();
  +             HttpSession session = req.getSession();
  +             
  +             // store the current principal in the session, to get around a bug in
  +             // IE 5 where the authentication info is not submitted by IE when
  +             // doing a HEAD request.
  +             if (principal == null) {
  +                     principal = (Principal) 
session.getAttribute(PRINCIPAL_ATTRIBUTE);
  +             } else {
  +                     session.setAttribute(PRINCIPAL_ATTRIBUTE, principal);
  +             }
  +             
  +             CredentialsToken credentials;
  +             if (principal == null) {
  +                     credentials = new CredentialsToken("");
  +             } else {
  +                     credentials = new CredentialsToken(principal);
  +             }
  +             
  +             SlideToken token = new SlideTokenImpl(credentials);
  +             token.setEnforceLockTokens(true);
  +             
  +             return token;
  +     }
  +     
  +     
  +     /**
  +      * Tests whether a the requested URI maps to a collection resource.
  +      *
  +      * @param req the HTTP request
  +      * @param config configuration of the WebDAV servlet
  +      * @param token the namespace access token
  +      *
  +      * @return true if the requested resource is a collection, false otherwise
  +      **/
  +     public static boolean isCollection
  +             (NamespaceAccessToken token, HttpServletRequest req,
  +              WebdavServletConfig config) {
  +             
  +             return isCollection(token, getSlideToken(req),
  +                                                     getRelativePath(req, config));
  +     }
  +     
  +     
  +     /**
  +      * Tests whether a given path maps to a URI in the Slide namespace that
  +      * identifies a collection resource.
  +      *
  +      * @param token the namespace access token
  +      * @param slideToken the slide token
  +      * @param path relative path of the resource
  +      *
  +      * @return true if the requested resource is a collection, false otherwise
  +      **/
  +     public static boolean isCollection
  +     (NamespaceAccessToken token, SlideToken slideToken,
  +     String path) {
  +             
  +             slideToken = new SlideTokenWrapper(slideToken, false); // check only, 
no enlistment
  +                      
  +             // Added for DeltaV --start--
  +             if( Configuration.useVersionControl() ) {
  +                     UriHandler uh = UriHandler.getUriHandler( path );
  +                     if( uh.isWorkspaceUri() )
  +                             return true;
  +                     if( uh.isHistoryUri() )
  +                             return true;
  +                     if( uh.isVersionUri() )
  +                             return false;
  +             }
  +             // Added for DeltaV --end--
  +                      
  +             try {
  +                     Content content = token.getContentHelper();
  +                     NodeRevisionDescriptors revisionDescriptors =
  +                             content.retrieve(slideToken, path);
  +                     if (revisionDescriptors.hasRevisions()) {
  +                             NodeRevisionDescriptor revisionDescriptor =
  +                                     content.retrieve(slideToken, 
revisionDescriptors);
  +                             return isCollection(revisionDescriptor);
  +                     } else {
  +                             return true;
  +                     }
  +             } catch(ObjectNotFoundException e) {
  +                     // if the Object is not found return false for no 207 is 
generated
  +                     return false;
  +             } catch(SlideException e) {
  +                     // this is the default
  +                     return true;
  +             }
  +     }
  +     
  +     
  +     /**
  +      * Tests whether a resource is a collection resource.
  +      *
  +      * @param revisionDescriptor revision descriptor of the resource
  +      *
  +      * @return true if the descriptor represents a collection, false otherwise
  +      **/
  +     public static boolean isCollection
  +             (NodeRevisionDescriptor revisionDescriptor) {
  +             
  +             boolean result = false;
  +             
  +             if (revisionDescriptor == null)
  +                     return true;
  +             
  +             if (revisionDescriptor.propertyValueContains(
  +                                     revisionDescriptor.RESOURCE_TYPE 
,"collection")) {
  +                     result = true;
  +             }
  +             
  +             return result;
  +     }
  +     
  +     
   }
  -
  
  
  

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to