public void OnError(object obj, EventArgs args)
        {
            // At this point we have information about the error
            log4net.ILog log = PtaUtil.getLog4netLogger(this.GetType().FullName + ".OnError(): ");

            log.Debug("Entered GlobalErrorHandler.OnError");
            HttpContext  ctx      = HttpContext.Current;
            HttpResponse response = ctx.Response;
            HttpRequest  request  = ctx.Request;

            Exception exception = ctx.Server.GetLastError();
            String    ex_html   = PtaUtil.GetExceptionAndEnvHtml(exception);

            String url = HttpContext.Current.Request.Url.ToString();

            log.Debug("HttpContext.Current.Request.FilePath: " + HttpContext.Current.Request.FilePath);
            log.Debug("HttpContext.Current.Request.ApplicationPath: " + HttpContext.Current.Request.ApplicationPath);
            //!! test when proxy is not root
            if (HttpContext.Current.Request.FilePath.StartsWith(
                    PtaUtil.GetProxyWebFolder() + PtaUtil.PTA_SUBFOLDER))
            {
                log.Info(ex_html);
            }
            else
            {
                response.Write(ex_html);
                ctx.Server.ClearError();
                // --------------------------------------------------
                // To let the page finish running we clear the error
                // --------------------------------------------------
            }
        }
        /// <summary>
        /// Reads BBMenuLink and Param tables from database in form of XML
        /// </summary>
        public static String GetBBMenuLinkParamXML(String bbCode)
        {
            log4net.ILog  log = PtaUtil.getLog4netLogger(typeof(PtaUtil).FullName + ".GetBBMenuLinkParamXML(): ");
            SqlConnection con = GetSqlConnection();
            String        sql = "SELECT * from BBMenuLink join Param "
                                + " on BBMenuLink.BBoardCode = Param.BBoardCode and BBMenuLink.BBLinkPath = Param.BBLinkPath "
                                + " where BBMenuLink.BBoardCode = @BBoardCode ORDER BY BBMenuLink.BBLinkPath, Param.ParamOrderCode for xml auto";

            System.Data.SqlClient.SqlParameter param = new System.Data.SqlClient.SqlParameter("BBoardCode", bbCode);
            log.Info("sql: " + sql);
            System.Data.SqlClient.SqlDataReader dr =
                Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteReader(PtaUtil.GetSqlConnection(),
                                                                         System.Data.CommandType.Text, sql, param);
            String xml = null;

            if (dr.HasRows)
            {
                xml = "";
                while (dr.Read())
                {
                    xml += dr.GetString(0);
                }

                xml = xml.Replace("><", ">" + System.Environment.NewLine + "<");
            }
            return(xml);
        }
コード例 #3
0
 protected virtual void Action()
 {
     log4net.ILog log = PtaUtil.getLog4netLogger(this.GetType().FullName + ".Action(): ");
     log.Debug("Request.ContentEncoding.EncodingName: " + Request.ContentEncoding.EncodingName);
     log.Debug("Response.ContentEncoding.EncodingName: " + Response.ContentEncoding.EncodingName);
     Request.ContentEncoding  = System.Text.Encoding.UTF8;
     Response.ContentEncoding = System.Text.Encoding.UTF8;
 }
        public static void LogExceptionAndFormParameters(log4net.ILog log, Exception exception)
        {
            //just in case if we get some more exceptions during GetExceptionAndEnvHtml()
            log.Error(exception);
            //ex_html by itself will include exception info too, traced back to all InnerExceptions
            String ex_html = PtaUtil.GetExceptionAndEnvHtml(exception);

            log.Info(ex_html);
        }
        /// <summary>
        /// Entry point of registration processing, called by static DoCall() of base class.
        /// Builds XML, generates SharedSecret, calls ContextWS register() method and updates database upon success.
        /// </summary>
        public override void Call()
        {
            log4net.ILog log  = PtaUtil.getLog4netLogger(typeof(RegisterToolWSCall).FullName + ".Call(): ");
            String       desc = GetRegistrationXML(wsWrapper.GetBBHostCode());

            if (!IsReregister)
            {
                initialSharedSecret = System.Guid.NewGuid().ToString();
                initialSharedSecret = Regex.Replace(initialSharedSecret, "-", "");
                initialSharedSecret = Regex.Replace(initialSharedSecret, ":", "_");
            }
            else
            {
                initialSharedSecret = PtaUtil.GetDBReaderStringField(wsWrapper.GetBBoardDR(), "SharedSecret");
            }


            RegisterToolResultVO res;

            string[] requiredToolMethods   = null;
            string[] requiredTicketMethods = null;
            //commented for security reasons
            //log.Debug("toolRegistrationPassword: "******"desc: " + desc);
            //commented for security reasons
            //log.Debug("initialSharedSecret: " + initialSharedSecret);
            String proxy_code = PtaUtil.GetDBReaderStringField(wsWrapper.GetBBoardDR(), "BBoardProxyCode");

            res = wsWrapper.GetContextWS().registerTool("IDLA", proxy_code, toolRegistrationPassword, desc, initialSharedSecret, requiredToolMethods, requiredTicketMethods);
            String str = res.ToString();

            str = res.proxyToolGuid;
            if (res.status)
            {
                String sql = "UPDATE BBoard SET IsRegistered = @IsRegistered, SharedSecret = @SharedSecret, GUID = @GUID, ProxyState='Inactive'  WHERE BBoardCode = @BBoardCode";
                log.Info("sql: " + sql);
                SqlParameter[] update_bb_params = new SqlParameter[] {
                    new SqlParameter("@IsRegistered", "Y"),
                    new SqlParameter("@SharedSecret", initialSharedSecret),
                    new SqlParameter("@GUID", res.proxyToolGuid),
                    new SqlParameter("@BBoardCode", wsWrapper.GetBBHostCode())
                };
                log.Info("res.proxyToolGuid: " + res.proxyToolGuid);
                log.Info("ws_wrapper.bbHostCode: " + wsWrapper.GetBBHostCode());
                Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteNonQuery(PtaUtil.GetSqlConnection(),
                                                                           System.Data.CommandType.Text, sql, update_bb_params);
            }
            else
            {
                String failure_errors = "";
                for (int i = 0; i < res.failureErrors.Count(); i++)
                {
                    failure_errors += "BB Falire Err.#" + i + ": " + res.failureErrors[i] + ";\r\n ";
                }
                throw new PtaRegisterToolException(wsWrapper.GetContextWS().GetType().FullName + ".registerTool() failed with following error(s): \r\n " + failure_errors, res);
            }
        }
コード例 #6
0
        protected override void Action()
        {
            base.Action();
            String guid = Request[PtaServerConstants.OURGUID_KEY];

            bboardDR = PtaUtil.GetBBoardDataReaderByGuid(guid);
            RequestValidator rv = new RequestValidator(bboardDR);

            rv.Validate(Request);
        }
        /// <summary>
        /// Saves XML to BBMenuLink and Param tables (relies on SetBBMenuLinkParamXML stored procedure)
        /// </summary>
        public static void SetBBMenuLinkParamXML(String bbCode, String xml)
        {
            log4net.ILog   log       = PtaUtil.getLog4netLogger(typeof(PtaUtil).FullName + ".SetBBMenuLinkParamXML(): ");
            SqlParameter[] param_arr = new SqlParameter[] {
                new System.Data.SqlClient.SqlParameter("@BBoardCode", bbCode),
                new System.Data.SqlClient.SqlParameter("@Xml", xml)
            };
            SqlConnection con = GetSqlConnection();

            Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteNonQuery(
                con, CommandType.StoredProcedure, "SetBBMenuLinkParamXML", param_arr);
        }
 /// <summary>
 /// Null-safe and logged access of String fields from SqlDataReader.
 /// </summary>
 public static String GetDBReaderStringField(System.Data.SqlClient.SqlDataReader dbReader, String fieldName)
 {
     log4net.ILog log = PtaUtil.getLog4netLogger(typeof(PtaUtil).FullName + ".GetDBReaderStringField(): ");
     log.Debug("fieldName: " + fieldName);
     if (dbReader[fieldName] != System.DBNull.Value)
     {
         return((string)dbReader[fieldName]);
     }
     else
     {
         return(null);
     }
 }
コード例 #9
0
//        static public void PerformAction(ActionPage a_page) {
        //this way it may get overriden in Actions.Config, but still does not look like comfortable enough design
        public virtual void PerformAction(ActionPage a_page)
        {
            log4net.ILog log = PtaUtil.getLog4netLogger(typeof(ActionPage).FullName + ".PerformAction(): ");
            try {
                a_page.Action();
            } catch (System.Threading.ThreadAbortException) {
                //this is normal processing of Server.Transfer() and Response.End()
                throw;
            } catch (Exception e) {
                PtaUtil.LogExceptionAndFormParameters(log, e);
                HttpContext.Current.Response.Output.Write(PtaServerConstants.SIMPLE_ERROR_RESPONSE);
            } finally {
                PtaUtil.CloseDataReader(a_page.bboardDR);
            }
        }
 /*
  * Just some code that may be useful in the future
  * //Microsoft.Web.Services3.WebServicesClientProtocol
  * //http://msdn.microsoft.com/en-us/library/microsoft.web.services3.webservicesclientprotocol.aspx
  * //Thread Safety
  * //Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
  * //caching of established/initialized/authenticated webservices does not seem important task by this time
  * static Hashtable webservices = new Hashtable();
  * static ContextWS GetContextWS(String bbHostURL) {
  *  String ws_key = bbHostURL + "/" + typeof(ContextWS).FullName;
  *  ContextWS ctx;
  *  lock (webservices) {
  *      ctx = (ContextWS)webservices[ws_key];
  *      if (ctx == null) {
  *              ctx = new  ContextWS();
  *              webservices.Add(ws_key, ctx);
  *      }
  *  }
  *  return ctx;
  * }
  */
 /// <summary>
 /// Conditionally initializes connection to Blackboard
 /// (this is not of some practical use by this moment as long as there are no series of calls to Blackboard - just register() one)
 /// and performs centrilized exception handling and resources cleanup for calls of Blackboard webservices by PtaProxy.
 /// Caught exceptions are logged and re-thrown.
 /// </summary>
 static public void DoCall(WSCall ws_call)
 {
     log4net.ILog log = PtaUtil.getLog4netLogger(typeof(WSCall).FullName + ".DoCall(): ");
     //try {
     try {
         if (ws_call.NeedsInitialize() && !ws_call.wsWrapper.IsInitialized())
         {
             ws_call.wsWrapper.initialize();
         }
         ws_call.Call();
     } catch (Exception e) {
         PtaUtil.LogExceptionAndFormParameters(log, e);
         throw;
     } finally {
         PtaUtil.CloseDataReader(ws_call.wsWrapper.GetBBoardDR());
     }
 }
        /// <summary>
        /// Opens single BBoard record by URL (identification of unregistered BBoard record by pta_proxy\Admin\Register.aspx
        /// </summary>
        public static System.Data.SqlClient.SqlDataReader GetBBoardDataReaderByCode(String bbCode)
        {
            log4net.ILog log = PtaUtil.getLog4netLogger(typeof(PtaUtil).FullName + ".GetBBoardDataReaderByURL(): ");
            System.Data.SqlClient.SqlConnection con = PtaUtil.GetSqlConnection();
            String sql = "SELECT * FROM BBoard WHERE BBoardCode = @BBoardCode";

            System.Data.SqlClient.SqlParameter param = new System.Data.SqlClient.SqlParameter("BBoardCode", bbCode);
            log.Info("sql: " + sql + ";bbCode: " + bbCode);
            System.Data.SqlClient.SqlDataReader dr =
                Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteReader(PtaUtil.GetSqlConnection(),
                                                                         System.Data.CommandType.Text, sql, param);
            if (!dr.HasRows)
            {
                throw new PtaException("Unregistered Blackboard Code: " + bbCode);
            }
            dr.Read();
            return(dr);
        }
        /// <summary>
        /// Standard entry point of IHttpHandler, main processing flow.
        /// </summary>
        public void ProcessRequest(HttpContext context)
        {
            log4net.ILog log = PtaUtil.getLog4netLogger(this.GetType().FullName + ".ProcessRequest(): ");
            try {
                this.context = context;
                String guid = context.Request.Form["ourguid"];
                bboardDR = PtaUtil.GetBBoardDataReaderByGuid(guid);
                onErrorServerTransfer = PtaUtil.GetDBReaderStringField(bboardDR, "OnErrorServerTransfer");
                RequestValidator rv = new RequestValidator(bboardDR);
                rv.Validate(context.Request);
                Redirect();
            } catch (System.Threading.ThreadAbortException) {
                //this is normal processing of Server.Transfer() and Response.End()
                throw;
            } catch (Exception e) {
                PtaUtil.LogExceptionAndFormParameters(log, e);
                //String on_err_st = onErrorServerTransfer;
                //if (on_err_st == null) on_err_st = PtaUtil.GetProxyWebFolder() + "ErrorPages/PtaDefaultErrPage.aspx";
                //context.Server.Transfer(on_err_st, true);
                String back_link = context.Request.Form["tcbaseurl"] + context.Request.Form["returnurl"];
                String err_page  = PtaUtil.GenerateSimpleErrorPage(back_link);
                context.Response.Write(err_page);
                log.Debug("onErrorServerTransfer: " + onErrorServerTransfer);
                if (onErrorServerTransfer != null)
                {
                    context.Server.Transfer(onErrorServerTransfer, true);
                }

                /*                else {
                 *                  back_link = context.Request.Form["tcbaseurl"] + context.Request.Form["returnurl"];
                 *                  context.Response.Write("Your request could not be processed. <br/>");
                 *                  context.Response.Write("Back to Blackboard: " + "<a href=\"" + back_link + "\">" + back_link + "</a><br/>");
                 *              }*/

                //throw;
            } finally {
                PtaUtil.CloseDataReader(drParam);
                PtaUtil.CloseDataReader(drMenuLink);
                PtaUtil.CloseDataReader(bboardDR);
            }
        }
コード例 #13
0
        /// <summary>
        /// Initializes communication channel with Blackboard.
        /// Depending on BBoard.BBoardHTTPSchema DB setting first tries to establish https
        /// and in case of fail - http one. Or makes single attempt on one of them if restricted to such behavior.
        /// </summary>
        public void initialize()
        {
            log4net.ILog log            = PtaUtil.getLog4netLogger(this.GetType().FullName + ".initialize(): ");
            String       url_prefix     = "http://";
            Int16        port_num       = PtaUtil.GetDBReaderInt16Field(bboardDR, "BBoardHTTPPort");
            String       bb_http_schema = "";

            try {
                bb_http_schema = PtaUtil.GetDBReaderStringField(bboardDR, "BBoardHTTPSchema");
                log.Debug("bb_http_schema: " + bb_http_schema);
                if ("HTTPS".Equals(bb_http_schema) ||
                    "BOTH".Equals(bb_http_schema)
                    )
                {
                    url_prefix = "https://";
                    port_num   = PtaUtil.GetDBReaderInt16Field(bboardDR, "BBoardHTTPSPort");
                }
                else if (!"HTTP".Equals(bb_http_schema))
                {
                    throw new PtaException("BBoardHTTPSchema has to contain either HTTP, HTTPS or BOTH.");
                }
                contextWS.Url = url_prefix + GetBBHostURL() + ":" + port_num.ToString() + "/webapps/ws/services" + "/Context.WS";
                initializeInternal();
            } catch (System.Net.WebException we) {
                //checking of message text looks to be too strong condition
                //When certificate is incorrect message is "The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel."
                //When certificate is incorrect message is "The underlying connection was closed: An unexpected error occurred on a send" and inner exception "Authentication failed because the remote party has closed the transport stream."
                //if (we.Message.Equals("The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.")
                log.Warn(we);
                if ("BOTH".Equals(bb_http_schema))
                {
                    port_num      = PtaUtil.GetDBReaderInt16Field(bboardDR, "BBoardHTTPPort");
                    contextWS.Url = "http://" + GetBBHostURL() + ":" + port_num.ToString() + "/webapps/ws/services" + "/Context.WS";
                    initializeInternal();
                }
                else
                {
                    throw;
                }
            }
        }
コード例 #14
0
        /// <summary>
        /// Separarted technical initialization processing, called by initialize()
        /// </summary>
        protected void initializeInternal()
        {
            log4net.ILog log = PtaUtil.getLog4netLogger(this.GetType().FullName + ".initializeInternal(): ");
            log.Debug("Before contextWS.initialize - contextWS.RequestSoapContext.ContentType: " + contextWS.RequestSoapContext.ContentType);
            //contextWS.ResponseSoapContext is null here
            //log.Debug("Before contextWS.initialize - contextWS.ResponseSoapContext.ContentType: " + contextWS.ResponseSoapContext.ContentType);
            contextWS.RequestEncoding = System.Text.Encoding.UTF8;
            UsernameToken userToken = new UsernameToken("session", "nosession", PasswordOption.SendPlainText);

            contextWS.RequestSoapContext.Security.Tokens.Clear();
            contextWS.RequestSoapContext.Security.Tokens.Add(userToken);
            //NOTE that this session is only valid for 5 minutes - in other words you must login within 5 minutes or call initialize again.
            initializeResponse ir        = contextWS.initialize();
            String             sessionId = ir.@return;

            log.Debug("After contextWS.initialize - contextWS.ResponseSoapContext.ContentType: " + contextWS.ResponseSoapContext.ContentType);
            userToken = new UsernameToken("session", sessionId, PasswordOption.SendPlainText);
            contextWS.RequestSoapContext.Security.Tokens.Clear();
            contextWS.RequestSoapContext.Security.Tokens.Add(userToken);
            initializedFlag = true;
        }
 public void Init(HttpApplication app)
 {
     log4net.ILog log = PtaUtil.getLog4netLogger(this.GetType().FullName + ".Init(): ");
     log.Info("Instantiated");
     app.Error += new System.EventHandler(OnError);
 }
コード例 #16
0
        /// <summary>
        /// Performs series of actions necessary for validation of passed by Blackboard parameters.
        /// See http://www.edugarage.com/display/BBDN/Proxy+Tool+SSO , internal method comments and java client sample code for details.
        /// Performs periodical cleanup of collected nonces upon every NonceCleanupCount invocation
        /// </summary>
        public void Validate(HttpRequest request)
        {
            log4net.ILog log = PtaUtil.getLog4netLogger(this.GetType().FullName + ".Validate(): ");
            request.ContentEncoding = System.Text.Encoding.UTF8;


            if (bboardDR == null)
            {
                bboardDR = PtaUtil.GetBBoardDataReaderByGuid(guid);
            }

            String s_secret = PtaUtil.GetDBReaderStringField(bboardDR, "SharedSecret");

            //http://www.edugarage.com/display/BBDN/Proxy+Tool+SSO
            // 1. Sort the posted data based on the key names and then concatenate all of the values together in the sorted order
            String[] sorted_form_keys = request.Form.AllKeys;
            Array.Sort(sorted_form_keys);
            System.Text.StringBuilder data_string = new System.Text.StringBuilder("");
            foreach (String form_key in sorted_form_keys)
            {
                /* from proxy\java\src\com\blackboard\test\proxy\GenericParameters.java:
                 * // ignore extra parameters added after the mac was generated/set
                 *  if ( key.startsWith( "top_" ) || key.startsWith( "bottom_" ) )
                 *      {
                 *          //ignore
                 *      }
                 * */
                if (form_key.StartsWith("top_", StringComparison.OrdinalIgnoreCase) ||
                    form_key.StartsWith("bottom_", StringComparison.OrdinalIgnoreCase)
                    //MAC cannot be calculated over itself
                    || form_key.Equals("mac", StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }
                data_string.Append(request.Form[form_key]);
            }
            log.Debug("data_string: " + data_string);
            // 2. append the shared secret
            data_string.Append(s_secret);
            // 3. Turn this string into a string of utf-8 bytes
            byte[] data_buffer = System.Text.Encoding.UTF8.GetBytes(data_string.ToString());
            // 4. Create a message digest from these bytes
            System.Security.Cryptography.MD5 md = new System.Security.Cryptography.MD5CryptoServiceProvider();
            byte[] md5_hash = md.ComputeHash(data_buffer);
            // 5. Convert the resulting byte array into a base64 encoded string
            String md5_hash_64 = System.Convert.ToBase64String(md5_hash);

            log.Debug("md5_hash_64: " + md5_hash_64);
            //relace operations below are copied from proxy\java\src\com\blackboard\test\proxy\ProxyUtil.java  generateMac()
            md5_hash_64.Replace("\r", "");
            md5_hash_64.Replace("\n", "");
            log.Debug("md5_hash_64: " + md5_hash_64);

            String bb_mac = request.Form[PtaServerConstants.MAC_KEY];

            log.Debug("bb_mac: " + bb_mac);

            //compare received and calculated MACs
            if (!bb_mac.Equals(md5_hash_64))
            {
                throw new PtaException("MAC mismatch. md5_hash_64: " + md5_hash_64 + "; bb_mac: " + bb_mac);
            }

            //check that timestamp is expected timeframe
            String bb_ts     = request.Form[PtaServerConstants.TIMESTAMP_KEY];
            long   bb_millis = Int64.Parse(bb_ts);

            log.Debug("bb_millis: " + bb_millis);

            long mac_life_ms = Int64.Parse(WebConfigurationManager.AppSettings["MacLifetimeInMinutes"]);

            log.Debug("mac_life_ms: " + mac_life_ms);
            long mac_life_millis = mac_life_ms * 1000 * 60;

            //http://bytes.com/topic/c-sharp/answers/557734-java-system-currenttimemillis-equivalent
            //- about java System.currentTimeMillis() in .net
            DateTime utc_now = DateTime.UtcNow;

            log.Debug("utc_now: " + utc_now);
            DateTime base_time = new DateTime(1970, 1, 1, 0, 0, 0);

            log.Debug("base_time: " + base_time);
            long proxy_millis = (utc_now - base_time).Ticks / 10000;

            log.Debug("proxy_millis: " + proxy_millis);

            if (Math.Abs(proxy_millis - bb_millis) > mac_life_millis)   //added abs() - not to be much earlier too. Assuming that earlier timestamp may be possible due to non-synchronized system time on BB and proxy server
            {
                throw new PtaException("Timestamp is out of max request delay. proxy_millis(secs): " + proxy_millis + "(" + proxy_millis / 1000 + "); bb_millis(secs): " + bb_millis + "(" + bb_millis / 1000 + "); mac_life_ms(secs): " + mac_life_millis + "(" + mac_life_millis / 1000 + ")");
            }

            String nonce = request.Form[PtaServerConstants.NONCE_KEY];

            if (nonce == null)
            {
                throw new PtaException("Invalid null Nonce");
            }
            if (RequestValidator.knownNonces.Contains(nonce))
            {
                throw new PtaException("Invalid duplicated Nonce. nonce: " + nonce);
            }
            int nonce_cleanup_count = Int16.Parse(WebConfigurationManager.AppSettings["NonceCleanupCount"]);

            lock (typeof(RequestValidator)) {
                knownNonces.Add(nonce, proxy_millis);
                nonceCleanupCounter++;

                if (nonceCleanupCounter > nonce_cleanup_count)
                {
                    //!! test/debug
                    Hashtable non_expired_nonces = new Hashtable();
                    foreach (DictionaryEntry nonce_de in RequestValidator.knownNonces)
                    {
                        long nonce_millis = (long)nonce_de.Value;
                        if (nonce_millis >= proxy_millis - mac_life_millis)
                        {
                            non_expired_nonces.Add(nonce_de.Key, nonce_millis);
                        }
                    }
                    knownNonces         = non_expired_nonces;
                    nonceCleanupCounter = 0;
                }
            }
        }
コード例 #17
0
 /// <summary>
 /// Opens bboardDR
 /// </summary>
 public WSWrapper(String bbHostCode)
 {
     bboardDR             = PtaUtil.GetBBoardDataReaderByCode(bbHostCode);
     contextWS            = new ContextWS();
     this.initializedFlag = false;
 }
コード例 #18
0
 private String GetBBHostURL()
 {
     return(PtaUtil.GetDBReaderStringField(bboardDR, "BBoardURL"));
 }
コード例 #19
0
 public String GetBBHostCode()
 {
     return(PtaUtil.GetDBReaderStringField(bboardDR, "BBoardCode"));
 }
        /// <summary>
        /// Implements redirection logic.
        /// Please see "V. Link redirection processing definition" in pta_proxy\readme.txt for description of processing.
        /// Parameters provided by Blackboard along with redirection link are described at
        /// http://www.edugarage.com/display/BBDN/Generic+Proxy+Tool+Request+Arguments
        /// Link types at
        /// http://www.edugarage.com/display/BBDN/Link+Type
        /// Response status codes and header fields:
        /// http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6
        /// http://grokable.com/understanding-asp-net-response-redirect/ 301 vs. 302: Response.Redirect sets an HTTP 302 header along with the URL to be redirected to. The 302 status code essentially says, "this item has moved temporarily". Search engines crawling your site are not guaranteed to follow 302 redirects, nor should they. By following a 302 the search engine could incorrectly index the location of content.
        /// Response.Redirect with POST instead of Get?
        /// http://stackoverflow.com/questions/46582/response-redirect-with-post-instead-of-get
        /// </summary>
        protected void Redirect()
        {
            log4net.ILog log              = PtaUtil.getLog4netLogger(this.GetType().FullName + ".Redirect(): ");
            String       bb_code          = PtaUtil.GetDBReaderStringField(bboardDR, "BBoardCode");
            String       proxy_web_folder = PtaUtil.GetProxyWebFolder();
            String       bb_link          = context.Request.Path.Replace(proxy_web_folder + PtaUtil.PTA_SUBFOLDER, "");

            log.Debug("bb_code: " + bb_code + "; proxy_web_folder: " + proxy_web_folder + "; bb_link: " + bb_link);

            System.Data.SqlClient.SqlConnection con = PtaUtil.GetSqlConnection();
            String sql = "SELECT BBLinkType FROM BBMenuLink WHERE BBoardCode = @BBoardCode AND BBLinkPath = @BBLinkPath";

            System.Data.SqlClient.SqlParameter[] sel_ml_params = new System.Data.SqlClient.SqlParameter[] {
                new System.Data.SqlClient.SqlParameter("@BBoardCode", bb_code),
                new System.Data.SqlClient.SqlParameter("@BBLinkPath", bb_link)
            };
            log.Info("sql: " + sql);
            drMenuLink = Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteReader(PtaUtil.GetSqlConnection(),
                                                                                  System.Data.CommandType.Text, sql, sel_ml_params);
            if (!drMenuLink.HasRows)
            {
                throw new PtaException("Unknown proxy link. " + "bb_code: " + bb_code + "; proxy_web_folder: " + proxy_web_folder + "; bb_link: " + bb_link);
            }
            drMenuLink.Read();

            sql  = "SELECT ParamTag, ParamOrderCode, ParamKey, ParamInputType, ParamOutputType, ParamInputTemplate, ";
            sql += "RegExp, RegExpOperation, RegExpReplaceString, RegExpMatchGroup, RegExpMatchCapture, SProcName ";
            sql += "FROM Param WHERE BBoardCode = @BBoardCode AND BBLinkPath = @BBLinkPath ORDER BY ParamOrderCode";
            log.Info("sql: " + sql);
            System.Data.SqlClient.SqlDataReader drParam =
                Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteReader(PtaUtil.GetSqlConnection(),
                                                                         System.Data.CommandType.Text, sql, sel_ml_params);
            if (!drParam.HasRows)
            {
                throw new PtaException("No defined link parameter transformations. " + "bb_code: " + bb_code + "; proxy_web_folder: " + proxy_web_folder + "; bb_link: " + bb_link);
            }

            System.Collections.Hashtable param_tags_hash = new System.Collections.Hashtable();
            while (drParam.Read())
            {
                String param_tag    = PtaUtil.GetDBReaderStringField(drParam, "ParamTag");
                String param_input  = PtaUtil.GetDBReaderStringField(drParam, "ParamInputType");
                String param_output = PtaUtil.GetDBReaderStringField(drParam, "ParamOutputType");
                String input_templ  = PtaUtil.GetDBReaderStringField(drParam, "ParamInputTemplate");
                String param_key    = PtaUtil.GetDBReaderStringField(drParam, "ParamKey");
                String param_value  = null;
                log.Debug("param_tag: " + param_tag + "; param_input: " + param_input + "; param_output: " + param_output + "; input_templ: " + input_templ + "; param_key: " + param_key);
                switch (param_input)
                {
                case "Param.InputTemplate":
                    MatchCollection mc = Regex.Matches(input_templ, "{[^}]*}");
                    foreach (Match match in mc)
                    {
                        GroupCollection groups = match.Groups;
                        foreach (Group grp in groups)
                        {
                            String tag       = grp.Value;
                            String tag_value = (string)param_tags_hash[tag];
                            input_templ = Regex.Replace(input_templ, tag, tag_value);
                            log.Debug("tag: " + tag + "; tag_value: " + tag_value + "; input_templ: " + input_templ);
                        }
                    }
                    param_value = input_templ;
                    break;

                case "ProxyLinkPath":
                    param_value = bb_link;
                    break;

                case "ProxyLinkType":
                    param_value = PtaUtil.GetDBReaderStringField(drMenuLink, "BBLinkType");
                    break;

                case "ProxyPath":
                    param_value = proxy_web_folder;
                    break;

                case "Request.ApplicationPath":
                    param_value = context.Request.ApplicationPath;
                    if (param_value.Equals("/"))
                    {
                        param_value = "";
                    }
                    break;

                case "Request.FilePath":
                    param_value = context.Request.FilePath;
                    break;

                case "Request.Form":
                    param_value = context.Request.Form[param_key];
                    break;

                case "Request.InputStream":
                    System.IO.StreamReader reader = new System.IO.StreamReader(context.Request.InputStream);
                    param_value = reader.ReadToEnd();
                    break;

                case "Request.QueryString":
                    throw new PtaException("ParamInputType=Request.QueryString is not implemented. Supplied URL query string parameters are ignored and cut off by Blackboard.");

                case "Request.PathInfo":
                    param_value = context.Request.PathInfo;
                    break;

                case "Request.Url.ToString":
                    log.Debug("Request.Url.AbsolutePath(): " + context.Request.Url.AbsolutePath + "; context.Request.Url.ToString(): " + context.Request.Url.ToString());
                    param_value = context.Request.Url.ToString();
                    break;

                case "SProcCall":
                    //defined this way list provides correct conversion psp_params.ToArray() accepted by SqlHelper.ExecuteNonQuery()
                    System.Collections.Generic.List <System.Data.SqlClient.SqlParameter> psp_params =
                        new System.Collections.Generic.List <System.Data.SqlClient.SqlParameter>();

                    MatchCollection mc1 = Regex.Matches(input_templ, "{[^}]*}");
                    foreach (Match match in mc1)
                    {
                        GroupCollection groups = match.Groups;
                        foreach (Group grp in groups)
                        {
                            String tag       = grp.Value;
                            String tag_value = (string)param_tags_hash[tag];
                            tag = tag.Replace("{", "").Replace("}", "");
                            log.Debug("tag: " + tag + "; tag_value: " + tag_value);
                            psp_params.Add(new System.Data.SqlClient.SqlParameter("@" + tag, tag_value));
                        }
                    }
                    String sp_out_param_name = param_key;
                    System.Data.SqlClient.SqlParameter out_param = null;
                    if (sp_out_param_name != null)
                    {
                        out_param = //had problems with getting of output value, required ParameterDirection - this is why called constructor is so complex
                                    //Initializes a new instance of the SqlParameter class that uses the parameter name, the type of the parameter, the size of the parameter, a ParameterDirection, the precision of the parameter, the scale of the parameter, the source column, a DataRowVersion to use, and the value of the parameter.
                                    new System.Data.SqlClient.SqlParameter("@" + sp_out_param_name, SqlDbType.NVarChar,
                                                                           255, ParameterDirection.InputOutput, true, 0, 0, null,
                                                                           DataRowVersion.Current, "");
                        log.Debug("sp_out_param_name: " + sp_out_param_name);
                        psp_params.Add(out_param);
                    }
                    String sproc_name = PtaUtil.GetDBReaderStringField(drParam, "SProcName");
                    log.Debug("sproc_name: " + sproc_name);
                    if (psp_params.Count > 0)
                    {
                        Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteNonQuery(con, CommandType.StoredProcedure, sproc_name, psp_params.ToArray());
                    }
                    else
                    {
                        Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteNonQuery(con, CommandType.StoredProcedure, sproc_name);
                    }
                    if (out_param != null)
                    {
                        param_value = (string)out_param.Value;
                    }
                    break;

                case "BBWSCall":
                    throw new PtaException("ParamInputType=BBWSCall is not implemented at this time. It may provide possibility to get additional information from Blackboard via WS calls in future.");

                default:
                    throw new PtaException("ParamInputType=" + param_input + " is not implemented. Unknown, possible inconsistensy of code and ParamInputCheck DB constraint.");
                }
                log.Debug("param_value after input processing: " + param_value);

                String regexp_oper = PtaUtil.GetDBReaderStringField(drParam, "RegExpOperation");
                String regexp      = PtaUtil.GetDBReaderStringField(drParam, "RegExp");
                String regexp_rs   = PtaUtil.GetDBReaderStringField(drParam, "RegExpReplaceString");
                int    regexp_mc   = PtaUtil.GetDBReaderInt16Field(drParam, "RegExpMatchCapture");
                int    regexp_mg   = PtaUtil.GetDBReaderInt16Field(drParam, "RegExpMatchGroup");
                log.Debug("regexp_oper: " + regexp_oper + "; regexp: " + regexp + "; regexp_rs: " + regexp_rs + "; regexp_mc: " + regexp_mc + "; regexp_mg: " + regexp_mg);
                switch (regexp_oper)
                {
                case "Assign":
                    break;

                case "REMatch":
                    param_value = "";
                    MatchCollection mc3 = Regex.Matches(param_value, regexp);
                    log.Debug("mc3.Count: " + mc3.Count);
                    if (mc3.Count < regexp_mc)
                    {
                        Match m = mc3[regexp_mc];
                        log.Debug("m.Groups.Count: " + m.Groups.Count);
                        if (m.Groups.Count < regexp_mg)
                        {
                            param_value = m.Groups[regexp_mg].Value;
                        }
                    }
                    break;

                case "REReplace":
                    param_value = Regex.Replace(param_value, regexp, regexp_rs);
                    break;

                default:
                    throw new PtaException("RegExpOperation=" + regexp_oper + " is not implemented. Unknown, possible inconsistensy of code and RegExpOperationCheck DB constraint.");
                }
                log.Info("final param_tag: " + param_tag + "; param_value: " + param_value);
                param_tags_hash.Add(param_tag, param_value);

                switch (param_output)
                {
                case "None":
                    break;

                case "FormsAuthentication.SetAuthCookie":
                    System.Web.Security.FormsAuthentication.SetAuthCookie(param_value, false);
                    break;

                case "Context.Items":
                    context.Items[param_key] = param_value;
                    break;

                case "Context.Session":
                    context.Session.Add(param_key, param_value);
                    break;

                case "Response.AppendHeader":
                    context.Response.AppendHeader(param_key, param_value);
                    break;

                case "Response.Output":
                    context.Response.Output.Write(param_value);
                    break;

                case "Response.Status":
                    context.Response.Status = param_value;
                    break;

                case "Server.Transfer":
                    context.Server.Transfer(param_value, true);
                    break;

                default:
                    throw new PtaException("ParamOutputType=" + param_output + " is not implemented. Unknown, possible inconsistensy of code and ParamOutputCheck DB constraint.");
                }
            } //while (drParam.Read()) {
              //http://weblogs.asp.net/bleroy/archive/2004/08/03/Don_2700_t-redirect-after-setting-a-Session-variable-_2800_or-do-it-right_2900_.aspx
              //recommends use of Response.Redirect("~/default.aspx", false);
              //In comments:
              //I was lossing session data after clicking on a new page on a web site I was building only one of the pages out of about 25 was having this issue and it was always the same page.  As soon as I copied the page on to the IIS server on my computer the problem went away.  I think that something was going wrong on the server that visual web developer 2005 was running.

            //context.Response.End(); -- does not preserve session variables
            //finally ended with no specific processing here - everything seems fine this way.
            //probably enabling of Flush() should be tried first if something will stop working as required
            //context.Response.Flush();
        }
        /// <summary>
        /// Made as static for simplicity of registration XML preview (pta_proxy\Admin\Register.aspx).
        /// </summary>
        static public String GetRegistrationXML(String bbCode)
        {
            log4net.ILog log = PtaUtil.getLog4netLogger(typeof(RegisterToolWSCall).FullName + ".getRegistrationXML: ");
            String       desc;
            //method is static because can be called for preview, i.e. WSCall.wsWrapper.GetBBoardDR() is unavailvable
            SqlDataReader bboardDR = PtaUtil.GetBBoardDataReaderByCode(bbCode);

            try {
                if ("Y".Equals(PtaUtil.GetDBReaderStringField(bboardDR, "IsRegistered")))
                {
                    //??probably some logic on preliminary handling of this can be placed here,
                    //but capability to reregister even with this flag set is necessary
                    //because it is possible proxy to be unregistered at Blackboard but not updated at proxy
                    //(if proxy is not accessibel at the moment of unregistration),
                    //i.e. IsRegistered (as well as ProxyState) can be out of sync with Blackboard
                }
                String proxy_http_url  = WebConfigurationManager.AppSettings["PtaProxyHttpUrl"];
                String proxy_https_url = WebConfigurationManager.AppSettings["PtaProxyHttpsURL"];
                String proxy_s2s_url   = WebConfigurationManager.AppSettings["PtaProxyServerToServerURL"];
                String base_urls       = "";
                if ("Y".Equals(PtaUtil.GetDBReaderStringField(bboardDR, "RegisterHttpsURL")))
                {
                    base_urls += "<base-url type=\"https\">" + proxy_https_url + "</base-url>\n";
                }
                if ("Y".Equals(PtaUtil.GetDBReaderStringField(bboardDR, "RegisterHttpURL")))
                {
                    base_urls += "<base-url type=\"http\">" + proxy_http_url + "</base-url>\n";
                }
                if ("Y".Equals(PtaUtil.GetDBReaderStringField(bboardDR, "RegisterS2SURL")))
                {
                    base_urls += "<base-url type=\"server-to-server\">" + proxy_s2s_url + "</base-url>\n";
                }
                desc = Regex.Replace(REG_XML, "{BASE_URLS}", base_urls);
                if ("Y".Equals(PtaUtil.GetDBReaderStringField(bboardDR, "RegisterActions")))
                {
                    desc = Regex.Replace(desc, "{HTTP_ACTIONS}", HTTP_ACTIONS_XML);
                }
                String proxy_code = PtaUtil.GetDBReaderStringField(bboardDR, "BBoardProxyCode");
                desc = Regex.Replace(desc, "{PROXY_CODE}", proxy_code);


                //String desc = Regex.Replace(REG_XML, "{BASE_HTTP_URL}", proxy_http_url);
                //desc = Regex.Replace(desc, "{BASE_HTTPS_URL}", proxy_https_url);
                desc = Regex.Replace(desc, "{PROXY_WEB_FOLDER}", PtaUtil.GetProxyWebFolder());


                String sql = "SELECT BBLinkPath, BBLinkType, BBLinkName, BBLinkIconPath FROM BBMenuLink WHERE BBoardCode = @BBoardCode AND Enabled = 'Y'";
                log.Info("sql: " + sql);
                SqlConnection con = PtaUtil.GetSqlConnection();
                System.Data.SqlClient.SqlParameter param
                    = new System.Data.SqlClient.SqlParameter("BBoardCode", PtaUtil.GetDBReaderStringField(bboardDR, "BBoardCode"));
                SqlDataReader drMenuLink =
                    Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteReader(con, System.Data.CommandType.Text,
                                                                             sql, param);
                String xml_links = "";
                while (drMenuLink.Read())
                {
                    String xml_part  = MENU_LINK_XML;
                    String link_path = PtaUtil.GetProxyWebFolder() + PtaUtil.PTA_SUBFOLDER + PtaUtil.GetDBReaderStringField(drMenuLink, "BBLinkPath");
                    String link_type = PtaUtil.GetDBReaderStringField(drMenuLink, "BBLinkType");
                    String link_name = PtaUtil.GetDBReaderStringField(drMenuLink, "BBLinkName");
                    String icon_path = PtaUtil.GetDBReaderStringField(drMenuLink, "BBLinkIconPath");
                    xml_part = Regex.Replace(xml_part, "{LINK_PATH}", link_path);
                    xml_part = Regex.Replace(xml_part, "{MENU_LINK_TYPE}", link_type);
                    xml_part = Regex.Replace(xml_part, "{LINK_NAME}", "<name locale:key=\"" + link_name + "\"></name>");
                    if (icon_path != null)
                    {
                        xml_part = Regex.Replace(xml_part, "{LINK_ICON}", "<icon platform=\"blackboard\" style=\"listitem\" >" + icon_path + "</icon>");
                    }
                    else
                    {
                        xml_part = Regex.Replace(xml_part, "{LINK_ICON}", "");
                    }
                    xml_links = xml_links + xml_part;
                }
                desc = Regex.Replace(desc, "{MENU_LINKS}", xml_links);
            } finally {
                PtaUtil.CloseDataReader(bboardDR);
            }
            return(desc);
        }