/*  Copyright (c) 2000, 2002 by Oracle Corporation. All Rights Reserved. */

package oracle.portal.provider.v1.http; 
import oracle.portal.provider.v1.PortletRenderRequest; 
import oracle.portal.provider.v1.ContentFilter; 
import oracle.portal.provider.v1.Provider; 
import oracle.portal.provider.v1.AuthenticationException; 
import oracle.portal.provider.v1.PortletRendererUtil; 
import oracle.portal.provider.v1.PortletException; 
import oracle.portal.provider.v1.PortletRenderer; 
import oracle.portal.provider.v1.ProviderSession;
import oracle.portal.provider.v1.ExternalPrincipal;
import java.io.InputStream; 
import java.io.BufferedInputStream; 
import java.io.IOException; 
import java.io.BufferedReader; 
import java.io.InputStreamReader; 
import java.io.PrintWriter; 
import java.net.URL; 

/**
 * HtmlFilter {@link oracle.portal.utils.xml.v1.ContentFilter} gets
 * HTML contents from an URL and then applies the filtering directives as specified in
 * the provider.xml.
 *
 * <ul>
 * <li>trims the HTML header contents.</li>
 * <li>replaces relative path into absolute paths.</li>
 * <li>href’s are converted to have a target as _blank.</li>
 * <li>trims footer content.</li>
 * </ul>
 *
 */
public class HtmlFilter extends BaseContentFilter 
{ 
    
    // Class variables for storing provider.xml variables
    private String mHeaderTrimTag = null; 
    private String mConvertTarget = null; 
    private String mFooterTrimTag = null; 
    
    //Debug Level
    private int debugLevel = 0; 
    
    /**
     * Setter method for trim tag specified in provider.xml
     */
    public void setHeaderTrimTag(String headerTrimTag) 
    { 
        mHeaderTrimTag = headerTrimTag; 
    } // setHeaderTrimTag()
    
    /**
     * Setter method specified in provider.xml
     * @param footerTrimTag specifies the tag after which the url contents have to be removed.
     */
    public void setFooterTrimTag(String footerTrimTag) 
    { 
        mFooterTrimTag = footerTrimTag; 
    } // setFooterTrimTag()
    
    /**
     * Specifies if link in URLContent need to be opened in new window.
     * @param convertTarget true/false
     */
    public void setConvertTarget(String convertTarget) 
    { 
        mConvertTarget = convertTarget; 
    } 
    
    /**
     * Returns value of convertTarget tag in provider.xml
     * @returns true/false
     */
    public String getConvertTarget() 
    { 
        return mConvertTarget; 
    } 
    
    /**
     * filterResponse method takes PortletRenderRequest and InputStream as arguments,
     * It does the necessary scrapping of URL contents, depending on the directives
     * in the provider.xml
     * @param PortletRenderRequest pr
     * @param java.io.InputStream urlStream
     */
    public void filterContent(PortletRenderRequest pr, InputStream urlStream) 
        throws IOException,PortletException 
    { 
        //Read debug level from zone.properties.
        debugLevel = UrlServicesUtils.readDebugLevel(pr); 
        UrlServicesUtils.logMessage( 
            pr.getProvider(), 
            "filterContent : Entering HtmlFilter filterContent "+ 
            " *****************",debugLevel, 
        UrlServicesUtils.BASIC_LOGGING); 
        StringBuffer strUrlContent = new StringBuffer(); 
        String finalUrlContent = ""; 
        UrlServicesUtils.logMessage(pr.getProvider(), 
            "filterContent : Url input stream ---- : "+ 
            urlStream,
            debugLevel); 
        String encoding = (String) pr.getAttribute(URLRenderer.CHAR_ENCODING);
        if(encoding == null)
        {
            encoding = URLRenderer.DEFAULT_CHARACTER_ENCODING;
        }
        strUrlContent = 
            UrlServicesUtils.convertStreamToStringBuffer(
                urlStream,
                encoding,
                pr.getProvider(),
                debugLevel);
        if (strUrlContent == null || strUrlContent.length() == 0) 
        { 
            throw new IOException("HtmlFilter:Null URL Content received " +
                                  "for filtering."); 
        } 
        finalUrlContent = strUrlContent.toString(); 
        String basehref = null;
        //Try to get secondary base href from this.
        StringBuffer secBaseHref = null; 
        // order of searching for base href.
        // a) provider.xml
        // b) HTML Content for <base href="..."> tag
        // c) Parsing pageUrl
            
        // a) from provider.xml
        basehref = getBaseHRef();

        // b) from HTML Content
        if(basehref == null)
        {
            basehref = popBaseHref(finalUrlContent); 
        }
            
        // c) from pageUrl
        if(basehref == null)
        {
            basehref = getBaseHRef(pr);   
        }
           
        try  
        { 
            URL url = new URL(basehref); 
            secBaseHref = new StringBuffer(url.getProtocol()); 
            secBaseHref.append("://"); 
            secBaseHref.append(url.getHost()); 
            if (url.getPort() != -1) 
            { 
                secBaseHref.append(":"); 
                secBaseHref.append(url.getPort()); 
            } 
        } 
        catch (IOException e) 
        { 
            secBaseHref = new StringBuffer(basehref); 
        } 
        
        // Check if Content type if HTML otherwise return back
        if (mHeaderTrimTag != null) 
        { 
            // Trim HTML Header Contents
            UrlServicesUtils.logMessage( 
            pr.getProvider(), 
            "filterContent : HtmlFilter Trim HTML Header Contents : ", 
            debugLevel); 
            finalUrlContent = trimHtmlHeader(pr,finalUrlContent); 
        } 
        
        //contents before footer trim tag.
        UrlServicesUtils.logMessage( 
        pr.getProvider(), 
        "filterContent : URL Contents Before footerTrimTag : "+ 
        finalUrlContent, 
        debugLevel,UrlServicesUtils.HTML_CONTENTS_LOGGING); 
        
        // Check if footer trim tag is not null
        if (mFooterTrimTag != null) 
        { 
            // Trim HTML Footer Contents
            UrlServicesUtils.logMessage( 
            pr.getProvider(), 
            "filterContent : Trimming HTML Footer Contents : ", 
            debugLevel); 
            finalUrlContent = trimHtmlFooter(pr,finalUrlContent); 
        } 
        UrlServicesUtils.logMessage( 
            pr.getProvider(), 
            "filterContent : URL Contents AFTER TRIM HTML FOOTER : "+ 
            finalUrlContent,
            debugLevel, 
            UrlServicesUtils.HTML_CONTENTS_LOGGING); 

        UrlServicesUtils.logMessage( 
            pr.getProvider(), 
            "filterContent : Processs Request : Before checking "+ 
            " base href" + basehref, debugLevel); 
        if (basehref != null) 
        { 
            
            // Convert relative Srcs to absolute Srcs
            UrlServicesUtils.logMessage( 
            pr.getProvider(),"********************filterContent : "+ 
            " Converting relative srcs to absolute srcs "+ 
            " ********************", 
            debugLevel); 
            finalUrlContent = 
                replaceTagAttribute(pr, 
                                    "src",
                                    finalUrlContent,
                                    basehref, 
                                    secBaseHref.toString());
            
            //Convert all hrefs 
            UrlServicesUtils.logMessage( 
                pr.getProvider(), 
                "filterContent : Converting relative hrefs to "+ 
                " absolute hrefs ",
                debugLevel); 
            finalUrlContent = 
                replaceTagAttribute(pr,
                                    "href",
                                    finalUrlContent,
                                    basehref, 
                                    secBaseHref.toString());
            
            // Convert relative Form Action From Relative to Absolute
            UrlServicesUtils.logMessage( 
            pr.getProvider(), 
            "filterContent : Converting relative actions to "+ 
            " absolute actions ",debugLevel); 
            finalUrlContent = 
                replaceTagAttribute(pr,
                                    "action",
                                    finalUrlContent,
                                    basehref, 
                                    secBaseHref.toString());

            // Convert relative Table background colour to absolute
            UrlServicesUtils.logMessage( 
            pr.getProvider(), 
            "filterContent : Converting relative backgrounds "+ 
            " to absolute backgrounds ",debugLevel); 
            finalUrlContent = 
                replaceTagAttribute(pr,
                                    "background",
                                    finalUrlContent,
                                    basehref, 
                                    secBaseHref.toString());
        } 
        if (getConvertTarget() != null && 
        getConvertTarget().equalsIgnoreCase(UrlServicesUtils.TRUE)) 
        { 
            
            //Convert all HREF to have a parameter of target=_blank
            UrlServicesUtils.logMessage( 
            pr.getProvider(), 
            "filterContent : Convert all href to have a "+ 
            "parameter of target=_blank",debugLevel); 
            finalUrlContent = addTargetNew(pr,finalUrlContent); 
        } 
        
        //Check for usePortalStyle
        if (getUsePortalStyle() != null && 
        getUsePortalStyle().equalsIgnoreCase(UrlServicesUtils.TRUE)) 
        { 
            //Remove any styles in the url content.
            UrlServicesUtils.logMessage( 
            pr.getProvider(), 
            "filterContent : Before removing all styles "+ 
            " in the url content.",debugLevel); 
            finalUrlContent = removeTag(finalUrlContent,"style"); 
            finalUrlContent = removeTag(finalUrlContent,"link"); 
            UrlServicesUtils.logMessage( 
            pr.getProvider(), 
            "filterContent : Before making all links portal "+ 
            " style compliant",debugLevel); 
            
            //Replace hrefs, table backgrounds and first row of every table to use portal styles.
            finalUrlContent = replaceHRefClassProperty( 
            finalUrlContent, 
            PortletRenderer.PORTLET_TEXT_2); 
            UrlServicesUtils.logMessage( 
            pr.getProvider(), 
            "filterContent : Before making Tables portal style "+ 
            " compliant",debugLevel); 
            finalUrlContent = replaceTableClassProperty( 
            finalUrlContent, 
            PortletRenderer.PORTLET_HEADER_COLOR); 
            finalUrlContent = replaceTableBackground( 
            finalUrlContent, 
            UrlServicesUtils.TABLE_BACKGROUND_COLOR); 
            UrlServicesUtils.logMessage( 
            pr.getProvider(), "filterContent : Before "+ 
            " exiting usePortalStyle.",debugLevel); 
        } 
        UrlServicesUtils.logMessage(pr.getProvider(), 
        "filterContent() Before check for AuthType: ", 
        debugLevel); 
        if ((getUseAuthLinks() != null) && (finalUrlContent != null) ) 
        { 
            UrlServicesUtils.logMessage(pr.getProvider(), 
            "filterContent() Value check for AuthType: "+ 
            getUseAuthLinks(),debugLevel); 
            if (getUseAuthLinks().equals(UrlServicesUtils.TRUE)) 
            { 
                UrlServicesUtils.logMessage( 
                pr.getProvider(), 
                "HtmlFilter : useAuthLinks = true.",debugLevel); 
                
                //Check if inline rendering has been set to true.
                //If so we do not need to perform any kind of authentication
                //as the request again comes back to URLRenderer, which takes
                //care of authenticating.
                //Check for type of authentication
                if (getInlineRendering().equalsIgnoreCase(UrlServicesUtils.FALSE)) 
                { 
                    Provider p = pr.getProvider();
                    ProviderSession pSession = pr.getUser().getSession();
                    Authentication authentication = 
                        (Authentication) ((URLProvider)p).getAuthentication();
                    boolean isBasicAuth = 
                        authentication.getAuthType().
                                       equalsIgnoreCase(UrlServicesUtils.BASIC_AUTHENTICATION);

                    ExternalPrincipal externalApp = null;
                    String user = null;
                    String password = null;
                    
                    if(pSession != null)
                    {
                        externalApp = (ExternalPrincipal) 
                            pSession.getAttribute(UrlServicesUtils.EXTERNAL_APP);
                        if(externalApp != null)
                        {
                            user = externalApp.getName();
                            password = externalApp.getPassword();
                        }
                    }
                    
                    boolean isValidUser = (user != null) && (password != null);
                    if (isBasicAuth && isValidUser) 
                    { 
                        StringBuffer authString = new StringBuffer(20);
                        authString.append(user).append(":").append(password);
                        authString.append("@");
                        //Then basic authentication
                        finalUrlContent = this.authenticateBasicLoginLinks( 
                        pr,finalUrlContent, 
                        authString.toString(), 
                        debugLevel); 
                    } 
                    else if (getRedirectUrlFieldName() != null) 
                    { 
                        //Form based authentication
                        finalUrlContent = authenticateFormLoginLinks( 
                                              pr,finalUrlContent, 
                                              getRedirectUrlFieldName(), 
                                              debugLevel); 
                    } 
                    else  
                    { 
                        UrlServicesUtils.logMessage( 
                        pr.getProvider(), 
                        "HtmlFilter : No Redirect URL Field Name specified.", 
                        debugLevel); 
                    } 
                } //end of inline rendering = false
                else  
                { 
                    
                    //Add javascript to links ...
                    finalUrlContent = 
                        addJavaScriptToLink(pr, 
                                            new StringBuffer(finalUrlContent), 
                                            basehref, 
                                            "<a", 
                                            "href", 
                                            debugLevel); 
                    finalUrlContent = 
                        addJavaScriptSubmitFunction(pr, 
                                                    finalUrlContent, 
                                                    debugLevel); 
                } //end of if inline true.
            } 
            else if (getInlineRendering().equalsIgnoreCase(UrlServicesUtils.TRUE)) 
            { 
                
                //Add javascript to links ...
                finalUrlContent = 
                    addJavaScriptToLink(pr, 
                                        new StringBuffer(finalUrlContent), 
                                        basehref, 
                                        "<a", 
                                        "href", 
                                        debugLevel); 
                finalUrlContent = 
                    addJavaScriptSubmitFunction(pr, 
                                                finalUrlContent, 
                                                debugLevel); 
            } 
        } // End of check for authilinks tag being null
        else if (getInlineRendering().equalsIgnoreCase(UrlServicesUtils.TRUE)) 
        { 
            //Add javascript to links ...
            finalUrlContent = 
                addJavaScriptToLink(pr, 
                                    new StringBuffer(finalUrlContent), 
                                    basehref, 
                                    "<a", 
                                    "href", 
                                    debugLevel); 
            finalUrlContent = 
                addJavaScriptSubmitFunction(pr, 
                                            finalUrlContent, 
                                            debugLevel); 
        } //end of useAuthLinks = false && inlinerendering = true.
        
        //Render
        renderContents(
            pr,
            finalUrlContent,
            debugLevel, 
            UrlServicesUtils.HTML_CONTENTS_LOGGING); 
        UrlServicesUtils.logMessage(pr.getProvider(),"HtmlFilter: Leaving", 
        debugLevel,UrlServicesUtils.BASIC_LOGGING); 
    } 
    
    /**
     * replaceToken is a private helper function which return string
     * Reads Input String, Replaces all occurences of old string by occurences of
     * new String
     */
    private String replaceToken(String actlString,String oldToken ,String newToken) 
    { 
        StringBuffer actlStringBuffer = new StringBuffer(actlString); 
        int iCurrPos = 0; 
        int iOldTokenPos =- 1; 
        int newTokenLen = newToken.length(); 
        int oldTokenLen = oldToken.length(); 
        while (true) 
        { 
            iOldTokenPos = actlString.indexOf(oldToken,iCurrPos); 
            if (iOldTokenPos < 0) 
            { 
                break; 
            } 
            actlStringBuffer.replace(iOldTokenPos,iOldTokenPos + oldTokenLen,newToken); 
            actlString = actlStringBuffer.toString(); 
            iCurrPos = iOldTokenPos + newTokenLen; 
        } 
        return actlStringBuffer.toString(); 
    } 
    
    /**
     * Private helper function to trim contents before defined start tag of html
     * Find last occurence of particular tag as specified in provider.xml
     * header trim tag. Then trims everything before that
     * example to be placed in provider.xml
     * /head (do not place </head> as xml complains about it
     */
    private String trimHtmlHeader(PortletRenderRequest pr,String oldUrlContent) 
    { 
        String newUrlContent = null; 
        String HTML_HEADER = mHeaderTrimTag; 
        String lowerUrlContent = oldUrlContent.toLowerCase(); 
        UrlServicesUtils.logMessage( 
        pr.getProvider(), 
        "TrimHtmlHeader:Before replacing escape characters", 
        debugLevel,UrlServicesUtils.BASIC_LOGGING); 
        HTML_HEADER = replaceToken(HTML_HEADER,"&lt;","<"); 
        HTML_HEADER = replaceToken(HTML_HEADER,"&gt;",">"); 
        HTML_HEADER = HTML_HEADER.toLowerCase(); 
        UrlServicesUtils.logMessage(pr.getProvider(), 
        "TrimHtmlHeader:Token to be replaced"+ 
        HTML_HEADER,debugLevel); 
        int iEndExcludeString = lowerUrlContent.lastIndexOf(HTML_HEADER); 
        UrlServicesUtils.logMessage(pr.getProvider(), 
        "TrimHtmlHeader:ExcludeString"+ 
        iEndExcludeString,debugLevel); 
        if (iEndExcludeString !=- 1) 
        { 
            UrlServicesUtils.logMessage(pr.getProvider(), 
            "TrimHtmlHeader:Last occurence found for: "+ 
            HTML_HEADER,debugLevel); 
            newUrlContent = oldUrlContent.substring( 
            iEndExcludeString); 
        } 
        else  
        { 
            UrlServicesUtils.logMessage(pr.getProvider(), 
            "TrimHtmlHeader:No occurence found for: "+ 
            HTML_HEADER,debugLevel); 
            newUrlContent = oldUrlContent; 
        } 
        UrlServicesUtils.logMessage(pr.getProvider(), 
        "TrimHtmlHeader: Before returing after trimming", 
        debugLevel); 
        return(newUrlContent); 
    } 
    
    /**
     * Private helper function to trim contents after footer tag of html
     * Find last occurence of particular tag as specified in provider.xml
     * example would be /body
     */
    private String trimHtmlFooter(PortletRenderRequest pr,String oldUrlContent) 
    { 
        String newUrlContent = null; 
        String lowerUrlContent = oldUrlContent.toLowerCase(); 
        String HTML_FOOTER = mFooterTrimTag; 
        UrlServicesUtils.logMessage( 
        pr.getProvider(), 
        "TrimHtmlFooter:Before replacing escape characters", 
        debugLevel,UrlServicesUtils.BASIC_LOGGING); 
        HTML_FOOTER = replaceToken(HTML_FOOTER,"&lt;","<"); 
        HTML_FOOTER = replaceToken(HTML_FOOTER,"&gt;",">"); 
        HTML_FOOTER = HTML_FOOTER.toLowerCase(); 
        UrlServicesUtils.logMessage( 
        pr.getProvider(), 
        "TrimHtmlFooter:Token to be replaced"+ 
        HTML_FOOTER,debugLevel); 
        int iEndExcludeString = lowerUrlContent.lastIndexOf(HTML_FOOTER); 
        
        // If tag is found, take a substring of urlcontents from start to end of
        // excludestring
        if (iEndExcludeString !=- 1) 
        { 
            UrlServicesUtils.logMessage(pr.getProvider(), 
            "TrimHtmlFooter:Last occurence found for: "+ 
            HTML_FOOTER,debugLevel); 
            newUrlContent = oldUrlContent.substring( 0,iEndExcludeString + HTML_FOOTER.length() ); 
        } 
        else  
        { 
            UrlServicesUtils.logMessage(pr.getProvider(), 
            "TrimHtmlFooter:No occurence found for: "+ 
            HTML_FOOTER,debugLevel); 
            newUrlContent = oldUrlContent; 
        } 
        UrlServicesUtils.logMessage( 
        pr.getProvider(), 
        "TrimHtmlFooter: Before returing after trimming", 
        debugLevel); 
        return(newUrlContent); 
    } 
    
    /**
     * Convert Hyperlinks to start in new window
     * Insert a target=_blank before end of any href tag
     * This should remove any other existing target tags thight might be there
     * This will not put target="_new" for href with javascript
     */
    private String addTargetNew(PortletRenderRequest pr,String oldUrlContent) 
    { 
        String END_TAG = ">"; 
        String START_TAG = "<"; 
        String TARGET_NEW_TOKEN = " target='_blank' "; 
        String extractedTag = null; 
        
        // First convert all capital href to lower case href
        String newUrlContent = replaceToken(oldUrlContent, 
        UrlServicesUtils.HREF_UPPERCASE, 
        UrlServicesUtils.HREF_LOWERCASE); 
        StringBuffer actlStringBuffer = new StringBuffer(newUrlContent); 
        int iCurrPos = 0; 
        int iHrefPos =- 1; 
        int iEndTagPos =- 1; 
        UrlServicesUtils.logMessage(pr.getProvider(),"addTargetNew: Inside ", 
        debugLevel,UrlServicesUtils.BASIC_LOGGING); 
        while (true) 
        { 
            
            // Find Position of href In HTML
            iHrefPos = newUrlContent.indexOf(UrlServicesUtils.HREF_LOWERCASE,iCurrPos); 
            
            //If href is not found
            if (iHrefPos < 0) 
            { 
                UrlServicesUtils.logMessage(pr.getProvider(), 
                "addTargetNew: HREF Not Found", 
                debugLevel); 
                break; 
            } 
            else  
            { 
                
                // Find Position of > after HREF tag
                iEndTagPos = newUrlContent.indexOf(END_TAG,iHrefPos); 
                if (iEndTagPos < 0) 
                { 
                    UrlServicesUtils.logMessage(pr.getProvider(),"addTargetNew: > "+ 
                    "Not Found For HREF TAG",debugLevel); 
                    break; 
                } 
                
                //Make sure it is not javascript href
                // If href is used with javascript do not add any target
                String tempStr = newUrlContent.substring(iHrefPos+ 
                UrlServicesUtils.HREF_LOWERCASE.length() + 1); 
                if (tempStr.startsWith(("javascript"))) 
                { 
                    UrlServicesUtils.logMessage(pr.getProvider(), 
                    "addTargetNew: Javascript found", 
                    debugLevel); 
                    iCurrPos = iHrefPos + UrlServicesUtils.HREF_LOWERCASE.length() + 1; 
                    continue; 
                } 
            } 
            boolean internalTag = true; 
            int tempHrefPos = iHrefPos; 
            int startTagPos; 
            while (internalTag) 
            { 
                iEndTagPos = newUrlContent.indexOf(END_TAG,tempHrefPos); 
                startTagPos = newUrlContent.indexOf(START_TAG,tempHrefPos); 
                if (startTagPos < 0 || iEndTagPos < 0) 
                { 
                    internalTag = false; 
                } 
                else  
                { 
                    if (startTagPos > iEndTagPos) 
                    { 
                        internalTag = false; 
                    } 
                    else  
                    { 
                        UrlServicesUtils.logMessage(pr.getProvider(), 
                        "Start tag exists", 
                        debugLevel); 
                        tempHrefPos = iEndTagPos + 1; 
                    } 
                } 
            } 
            
            //Check if we have already reached the end of page.
            if (iEndTagPos < 0) 
            { 
                UrlServicesUtils.logMessage(pr.getProvider(), 
                "Finished Searching ...", 
                debugLevel); 
                break; 
            } 
            extractedTag = actlStringBuffer.toString().substring(iHrefPos, 
            iEndTagPos); 
            extractedTag = extractedTag.toLowerCase(); 
            if ( extractedTag.indexOf("target",0) ==- 1) 
            actlStringBuffer.insert(iEndTagPos,TARGET_NEW_TOKEN); 
            newUrlContent = actlStringBuffer.toString(); 
            iCurrPos = iEndTagPos + TARGET_NEW_TOKEN.length() + 1; 
        } 
        UrlServicesUtils.logMessage(pr.getProvider(), 
        "addTargetNew: Before returning",debugLevel); 
        return actlStringBuffer.toString(); 
    } 
    
    /**
     * Method replaces relative URLS to Absolute URLS, in the passed content.
     * @param pr ProviderRequest for this rendering.
     * @param tag contains tag ,that contains relative url
     * @param attribute contains the attribute containing relative url
     * @param InhtmlSource String containing the HtmlSource of pageURL
     * @param baseHref contains BaseHref tag
     * @param secBaseHref contains alias path appended to domain URL.
     * @return String contents with absolute url
     *
     */
    protected String replaceTagAttribute(PortletRenderRequest pr, 
    String attribute,String InhtmlSource, 
    String baseHref,String secBaseHref) 
    { 
        StringBuffer htmlSource = null; 
        int debugLevel = UrlServicesUtils.readDebugLevel(pr); 
        if (secBaseHref == null) 
        secBaseHref = baseHref; 
        
        //If base href contains a trailing '/' , then its removed.
        if (baseHref.endsWith(UrlServicesUtils.FORWARD_SLASH)) 
        { 
            baseHref = baseHref.substring(0,baseHref.length() - 1); 
        } 
        
        //If secondary base href contains a trailing '/' , then its removed.
        if (secBaseHref.endsWith(UrlServicesUtils.FORWARD_SLASH)) 
        { 
            secBaseHref = secBaseHref.substring(0,secBaseHref.length() - 1); 
        } 
        
        //Instantiate stringbuffer object with html contents passed.
        htmlSource = new StringBuffer(InhtmlSource); 
        
        //Lengths of html contents, basehref , tag to be replaced , attribute
        int baseLength = baseHref.length(); 
        int attributeLength = attribute.length(); 
        
        // Pointers used indicate start and end of tags and attributes, while
        //parsing
        int attribOffset = 0; 
        int attribStart = 0; 
        
        //Contents Converted to lowercase. To overcome case-issues, while parsing.
        StringBuffer lowerHtmlSource = new StringBuffer(htmlSource.toString().toLowerCase()); 
        int htmlLength = htmlSource.length(); 
        UrlServicesUtils.logMessage( 
        pr.getProvider(), 
        "*********************************************", 
        debugLevel); 
        UrlServicesUtils.logMessage( 
        pr.getProvider(), 
        "Attribute to be searched for = "+ 
        attribute,debugLevel); 
        UrlServicesUtils.logMessage( 
        pr.getProvider(), 
        "**********************************************", 
        debugLevel); 
        int count = 0; 
        do  
        { 
            
            // Location of attribute.
            attribStart = lowerHtmlSource.toString().indexOf(attribute,attribStart); 
            if (attribStart ==- 1) 
            break; 
            
            //find out if it is really an attribute or not
            char c = lowerHtmlSource.charAt(attribStart - 1); 
            if ( c != ' ' && c != '\n' && c != '.') 
            { 
                attribStart++; 
                continue; 
            } 
            attribOffset = 0; 
            
            //If attribute exists, traverse till where its value begins.
            if (attribStart !=- 1) 
            { 
                attribOffset = attribStart + attributeLength; 
                while (lowerHtmlSource.charAt(attribOffset) == ' ' && 
                attribOffset < htmlLength) 
                attribOffset++; 
                while (lowerHtmlSource.charAt(attribOffset) =='='&& 
                attribOffset < htmlLength) 
                attribOffset++; 
                while (lowerHtmlSource.charAt(attribOffset) == '\n' && 
                attribOffset < htmlLength) 
                attribOffset++; 
                while (lowerHtmlSource.charAt(attribOffset) == ' ' && 
                attribOffset < htmlLength) 
                attribOffset++; 
                if (lowerHtmlSource.charAt(attribOffset) == '\'') 
                attribOffset++; 
                if (lowerHtmlSource.charAt(attribOffset) == '"') 
                attribOffset++; 
                
                //If url referered by attribute starts with / then the
                //base url is inserted there.
                if (lowerHtmlSource.charAt(attribOffset) =='/' ) 
                { 
                    lowerHtmlSource.insert(attribOffset,secBaseHref); 
                    htmlSource.insert(attribOffset,secBaseHref); 
                } 
                else  
                { 
                    
                    //If url referred by attribute doesn't start with http or
                    // mailto tags, the base url is inserted.
                    if ( (lowerHtmlSource.length() - attribOffset) < 4 || 
                    ! lowerHtmlSource.toString().substring(attribOffset).startsWith("http") ) 
                    { 
                        if ( (lowerHtmlSource.length() - attribOffset) < 6 || 
                        ! lowerHtmlSource.toString().substring(attribOffset).startsWith("mailto") ) 
                        { 
                            if ( ! (lowerHtmlSource.charAt(attribOffset) =='/' )) 
                            { 
                                
                                // check if not javascript
                                if ((lowerHtmlSource.length() - attribOffset) < 10 || 
                                ! lowerHtmlSource.toString().substring(attribOffset).startsWith("javascript") ) 
                                { 
                                    lowerHtmlSource.insert( 
                                    attribOffset, 
                                    baseHref+ 
                                    UrlServicesUtils.FORWARD_SLASH); 
                                    htmlSource.insert( 
                                    attribOffset, 
                                    baseHref+ 
                                    UrlServicesUtils.FORWARD_SLASH); 
                                } 
                            } 
                        } 
                    } 
                } 
                htmlLength = htmlSource.length(); 
                attribStart++; 
                count++; 
            } 
        } while (true);
        UrlServicesUtils.logMessage(pr.getProvider(), 
        "Total no. of occurences = "+ 
        count ,debugLevel); 
        return htmlSource.toString(); 
    } //end of replaceTagAttribute
    
    
    protected String replaceTableBackground(String urlContent, String style) 
    { 
        int sIndex = 0; 
        int eIndex = 0; 
        StringBuffer sBuf = new StringBuffer(urlContent); 
        StringBuffer lowerUrlContent = new StringBuffer(urlContent.toLowerCase()); 
        while (true) 
        { 
            sIndex = lowerUrlContent.toString().indexOf(UrlServicesUtils.TABLE_TAG_START, 
            sIndex); 
            if (sIndex ==- 1) 
            break; 
            eIndex = lowerUrlContent.toString().indexOf(">",sIndex); 
            StringBuffer tag = new StringBuffer(lowerUrlContent.substring(sIndex, 
            eIndex)); 
            
            //Find if the table has any background color.
            int bgcolorStart = tag.toString().indexOf("bgcolor"); 
            if (bgcolorStart !=- 1) 
            { 
                int bgcolorEnd = tag.toString().indexOf("=",bgcolorStart); 
                bgcolorEnd++; 
                while (tag.charAt(bgcolorEnd) == ' ') 
                bgcolorEnd++; 
                bgcolorEnd = tag.toString().indexOf(" ",bgcolorEnd); 
                if (bgcolorEnd ==- 1 || bgcolorEnd > tag.length()) 
                { 
                    bgcolorEnd = tag.length(); 
                } 
                tag.delete(bgcolorStart,bgcolorEnd); 
                tag.append(" class='" + style + "'"); 
            } 
            sBuf.replace(sIndex,eIndex,tag.toString()); 
            lowerUrlContent.replace(sIndex,eIndex,tag.toString()); 
            sIndex = eIndex + tag.length(); 
        } 
        return sBuf.toString(); 
    } 
    
    protected String replaceHRefClassProperty(String urlContent, String style) 
    { 
        int sIndex = 0; 
        int eIndex = 0; 
        StringBuffer sBuf = new StringBuffer(urlContent); 
        StringBuffer lowerUrlContent = new StringBuffer(urlContent.toLowerCase()); 
        String tag = "";
        while (true) 
        { 
            sIndex = lowerUrlContent.toString().indexOf("<a ",sIndex); 
            if (sIndex ==- 1) 
            break; 
            eIndex = lowerUrlContent.toString().indexOf(">",sIndex) + 1; 
            int closeIndex = lowerUrlContent.toString().indexOf("/a>",eIndex); 
            if (closeIndex !=- 1) 
            { 
                eIndex = closeIndex + 3; 
            } 
            tag = sBuf.substring(sIndex,eIndex); 
            tag = "<font class='" + style + "'>" + tag + "</font>"; 
            sBuf.replace(sIndex,eIndex,tag); 
            lowerUrlContent.replace(sIndex,eIndex,tag); 
            sIndex += tag.length(); 
        } 
        return sBuf.toString(); 
    } 
    
    protected String removeTag(String urlContent,String tag) 
    { 
        int start = 0; 
        int end = 0; 
        tag = tag.toLowerCase(); 
        String startTag = "<" + tag; 
        String endTag = tag + ">"; 
        StringBuffer lowerUrlContent = new StringBuffer(urlContent.toLowerCase()); 
        StringBuffer sBuf = new StringBuffer(urlContent); 
        while (true) 
        { 
            start = lowerUrlContent.toString().indexOf(startTag,start); 
            if (start ==- 1) 
            break; 
            end = lowerUrlContent.toString().indexOf(endTag,start); 
            
            //If the tag is not closed
            if (end ==- 1) 
            { 
                
                //Find the closing ">" character and remove the tag.
                end = lowerUrlContent.toString().indexOf(">",start); 
                end++; 
            } 
            else  
            { 
                end += endTag.length(); 
            } 
            sBuf.delete(start,end); 
            lowerUrlContent.delete(start,end); 
        } 
        return sBuf.toString(); 
    } 
    
    protected String modifyClassProperty(String tableRowTag, String style) 
    { 
        int bgStart = tableRowTag.indexOf("bgcolor"); 
        StringBuffer sBuf = new StringBuffer(tableRowTag); 
        if (bgStart !=- 1) 
        { 
            sBuf.delete(bgStart,tableRowTag.length()); 
            sBuf.append(" class='" + style + "'"); 
        } 
        return sBuf.toString(); 
    } 
    
    protected String replaceTableClassProperty(String urlContent, String style) 
    { 
        int sIndex = 0; 
        StringBuffer lowerUrlContent = new StringBuffer(urlContent.toLowerCase()); 
        StringBuffer sBuf = new StringBuffer(urlContent); 
        while (true) 
        { 
            sIndex = lowerUrlContent.toString().indexOf("<table",sIndex); 
            if (sIndex ==- 1) 
            break; 
            int trsIndex = lowerUrlContent.toString().indexOf("<tr",sIndex); 
            if (trsIndex ==- 1) 
            break; 
            int treIndex = lowerUrlContent.toString().indexOf(">",trsIndex); 
            if (treIndex ==- 1) 
            break; 
            String tableRow = sBuf.substring(trsIndex, treIndex); 
            tableRow = modifyClassProperty(tableRow,style); 
            sBuf.replace(trsIndex,treIndex,tableRow); 
            lowerUrlContent.replace(trsIndex,treIndex,tableRow); 
            sIndex++; 
        } 
        return sBuf.toString(); 
    } 
    
} 