packages feed

dropbox-sdk 0.2.0 → 0.3.0

raw patch · 5 files changed

+333/−577 lines, 5 filesdep +monad-controldep +pemdep +resourcetdep ~conduitdep ~http-conduitdep ~http-types

Dependencies added: monad-control, pem, resourcet, template-haskell

Dependency ranges changed: conduit, http-conduit, http-types, tls, transformers

Files

Source/Dropbox.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE FlexibleContexts #-}+ module Dropbox (     -- * Configuration     mkConfig,@@ -27,10 +29,9 @@     getMetadata, getMetadataWithChildren, getMetadataWithChildrenIfChanged,     Meta(..), MetaBase(..), MetaExtra(..), FolderContents(..), FileExtra(..),     FolderHash(..), FileRevision(..),-    -- * Get files+    -- * Upload/download files     getFile, getFileBs,-    -- * Upload files-    addFile, forceFile, updateFile,+    putFile, WriteMode(..),     -- * Common data types     fileRevisionToString, folderHashToString,     ErrorMessage, URL, Path,@@ -51,31 +52,27 @@ import Text.JSON (JSON, readJSON, showJSON) import Data.ByteString (ByteString) import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS import qualified Data.ByteString.Char8 as BS8-import Data.CaseInsensitive (CI) import Data.Word (Word64) import Data.Int (Int64)-import Data.Time.Clock (UTCTime(utctDay), getCurrentTime)+import Data.Time.Clock (UTCTime) import Data.Time.Format (parseTime, formatTime) import System.Locale (defaultTimeLocale) import Control.Monad (liftM)-import qualified Control.Monad.Trans.Class as MT-import qualified Data.Conduit as C+import Control.Monad.IO.Class (MonadIO)+import Control.Monad.Trans.Control (MonadBaseControl)+import Control.Monad.Trans.Resource (ResourceT, MonadUnsafeIO, MonadThrow, MonadResource(..), runResourceT, allocate)+import Data.Conduit (Sink, Source, ($=), ($$+-)) import qualified Data.Conduit.List as CL import qualified Network.HTTP.Conduit as HC import qualified Network.HTTP.Types as HT-import qualified Network.TLS as TLS-import qualified Network.TLS.Extra as TLSExtra-import Data.Certificate.X509 (X509)-import qualified Data.Certificate.X509 as X509-import Data.Certificate.PEM as PEM-import Data.Conduit (Sink, Source, Resource)+import qualified Network.HTTP.Types.Header as HT import qualified Blaze.ByteString.Builder.ByteString as BlazeBS-import System.IO as IO-import qualified Paths_dropbox_sdk as Paths +import Dropbox.Certificates+ type ErrorMessage = String+ type URL = String  -- |Dropbox file and folder paths.  Should always start with "/".@@ -151,23 +148,8 @@     , configUserLocale :: Locale     -- ^The locale that the Dropbox service should use when returning user-visible strings.     , configAppId :: AppId           -- ^Your app's key/secret     , configAccessType :: AccessType -- ^The type of folder access your Dropbox application uses.-    , configCertVerifier :: CertVerifier -- ^The server certificate validation routine.     } deriving (Show) -type CertVerifierFunc =-    HT.Ascii                       -- ^The server's host name.-    -> [X509]                      -- ^The server's certificate chain.-    -> IO TLS.TLSCertificateUsage  -- ^Whether the certificate chain is valid or not.---- |How the server's SSL certificate will be verified.-data CertVerifier = CertVerifier-    { certVerifierName :: String -- ^The human-friendly name of the policy (only for debug prints)-    , certVerifierFunc :: CertVerifierFunc -- ^The function that implements certificate validation.-    }--instance Show CertVerifier where-    show (CertVerifier name _) = "CertVerifier " ++ show name- -- |A convenience function that constructs a 'Config'.  It's in the 'IO' monad because we read from -- a file to get the list of trusted SSL certificates, which is used to verify the server over SSL. mkConfig ::@@ -177,18 +159,11 @@     -> AccessType  -- ^'configAccessType'     -> IO Config mkConfig userLocale appKey appSecret accessType = do-    caFile <- Paths.getDataFileName "trusted-certs.crt"-    vf <- do-        r <- certVerifierFromPemFile caFile-        case r of-            Right vf -> return $ vf-            Left err -> fail $ "Unable to load root certificates from " ++ (show caFile) ++ ": " ++ err     return $ Config         { configHosts = hostsDefault         , configUserLocale = userLocale         , configAppId = AppId appKey appSecret         , configAccessType = accessType-        , configCertVerifier = vf         }  -- |Contains a 'Config' and an 'AccessToken'.  Every API call (after OAuth is complete)@@ -199,82 +174,6 @@     }  ------------------------------------------------------------------------- SSL Certificate Validation---- |A dummy implementation that doesn't perform any verification.-certVerifierInsecure :: CertVerifier-certVerifierInsecure = CertVerifier "insecure" (\_ _ -> return TLS.CertificateUsageAccept)--rightsOrFirstLeft :: [Either a b] -> Either a [b]-rightsOrFirstLeft = foldr f (Right [])-    where-        f (Left e) _ = Left e-        f _ (Left e) = Left e-        f (Right v) (Right vs) = Right (v:vs)---- |Reads certificates in PEM format from the given file and uses those as the roots when--- verifying certificates.  This function basically just loads the certificates and delegates--- to 'certVerifierFromRootCerts' for the actual checking.-certVerifierFromPemFile :: FilePath -> IO (Either ErrorMessage CertVerifier)-certVerifierFromPemFile filePath = do-    raw <- withFile filePath IO.ReadMode BS.hGetContents-    let pems = PEM.parsePEMs raw-    let es = [X509.decodeCertificate (LBS.fromChunks [stuff]) | (_, stuff) <- pems]-    case rightsOrFirstLeft es of-        Left err -> return $ Left err-        Right x509s -> return $ Right $ CertVerifier ("PEM file: " ++ show filePath) (certVerifierFromRootCerts x509s)--certAll :: [IO TLS.TLSCertificateUsage] -> IO TLS.TLSCertificateUsage-certAll [] = return TLS.CertificateUsageAccept-certAll (head:rest) = do-    r <- head-    case r of-        TLS.CertificateUsageAccept -> certAll rest-        reject -> return $ reject---- |A certificate validation routine.  It's in 'IO' to match what 'HTTP.Enumerator'--- expects, but we don't actually do any I/O.-certVerifierFromRootCerts ::-    [X509]            -- ^The set of trusted root certificates.-    -> HT.Ascii       -- ^The remote server's domain name.-    -> [X509]         -- ^The certificate chain provided by the remote server.-    -> IO TLS.TLSCertificateUsage--- TODO: Rewrite this crappy code.  SSL cert checking needs to be more correct than this.-certVerifierFromRootCerts roots domain chain = do-        utcTime <- getCurrentTime-        let day = utctDay utcTime-        certAll-            [ return $ TLSExtra.certificateVerifyDomain (BS8.unpack domain) chain-            , checkTrustChain day chain-            ]-    where-        checkTrustChain _ [] = return $ TLS.CertificateUsageReject $ TLS.CertificateRejectOther "empty chain"-        checkTrustChain day (head:rest) = do-            if isUnexpired day head-                then do-                    issuerMatch <- mapM (head `isIssuedBy`) roots-                    if any (== True) issuerMatch-                        then return $ TLS.CertificateUsageAccept-                        else case rest of-                            [] -> return $ TLS.CertificateUsageReject TLS.CertificateRejectUnknownCA-                            (next:_) -> do-                                nextOk <- TLSExtra.certificateVerifyAgainst head next-                                if nextOk-                                    then checkTrustChain day rest-                                    else return $ TLS.CertificateUsageReject $ TLS.CertificateRejectOther "break in verification chain"-                else return $ TLS.CertificateUsageReject $ TLS.CertificateRejectExpired-        isIssuedBy :: X509 -> X509 -> IO Bool-        isIssuedBy c issuer =-            if subjectDN issuer == issuerDN c-                then TLSExtra.certificateVerifyAgainst c issuer-                else return False-        subjectDN c = X509.certSubjectDN $ X509.x509Cert c-        issuerDN c = X509.certIssuerDN $ X509.x509Cert c-        isUnexpired day cert =-            let ((beforeDay, _, _), (afterDay, _, _)) = X509.certValidity (X509.x509Cert cert)-            in beforeDay < day && day <= afterDay------------------------------------------------------------------------ -- Authentication/Authorization  buildOAuthHeaderNoToken (AppId consumerKey consumerSecret) =@@ -303,7 +202,7 @@     -> Maybe URL -- ^The callback URL (optional)     -> IO (Either ErrorMessage (RequestToken, URL)) authStart mgr config callback = do-    result <- httpClientGet mgr vf uri oauthHeader (mkHandler handler)+    result <- httpClientGet mgr uri oauthHeader (mkHandler handler)     return $ mergeLefts result     where         Locale locale = configUserLocale config@@ -312,7 +211,9 @@         consumerPair = configAppId config         uri = "https://" ++ host ++ ":443/" ++ apiVersion ++ "/oauth/request_token?locale=" ++ urlEncode locale         oauthHeader = buildOAuthHeaderNoToken consumerPair-        vf = certVerifierFunc $ configCertVerifier config++        -- The handler is a callback that is executed on the response+        -- In case the OK:         handler 200 _ body = do             let sBody = UTF8.toString body  -- toString should return a Maybe, but it doesn't.  You too, Haskell?             case parseTokenParts sBody of@@ -320,10 +221,13 @@                 Right requestToken@(RequestToken requestTokenKey _) -> do                     let authorizeUrl = "https://" ++ webHost ++ "/"++apiVersion++"/oauth/authorize?locale=" ++ urlEncode locale ++ "&oauth_token=" ++ urlEncode requestTokenKey ++ callbackSuffix                     Right (requestToken, authorizeUrl)+        -- In case of an error:         handler code reason body = Left $ "server returned " ++ show code ++ ": " ++ show reason ++ ": " ++ show body+                 callbackSuffix = case callback of             Nothing -> ""             Just callbackUrl -> "&oauth_callback=" ++ urlEncode callbackUrl+                 parseTokenParts :: String -> Either String RequestToken         parseTokenParts s = do             enc <- URLEncoded.importString s@@ -335,13 +239,13 @@ -- and the user has authorized your app, call this function to get a 'RequestToken', which -- is used to make Dropbox API calls. authFinish ::-    Manager       -- ^The HTTP connection manager to use.+    Manager          -- ^The HTTP connection manager to use.     -> Config     -> RequestToken  -- ^The 'RequestToken' obtained from 'authStart'     -> IO (Either ErrorMessage (AccessToken, String))         -- ^The 'AccessToken' used to make Dropbox API calls and the user's Dropbox user ID. authFinish mgr config (RequestToken rtKey rtSecret) = do-    result <- httpClientGet mgr vf uri oauthHeader (mkHandler handler)+    result <- httpClientGet mgr uri oauthHeader (mkHandler handler)     return $ mergeLefts result     where         host = hostsApi (configHosts config)@@ -349,7 +253,6 @@         consumerPair = configAppId config         uri = "https://" ++ host ++ ":443/"++apiVersion++"/oauth/access_token?locale=" ++ urlEncode locale         oauthHeader = buildOAuthHeader consumerPair (rtKey, rtSecret)-        vf = certVerifierFunc $ configCertVerifier config         handler 200 _ body = do             let sBody = UTF8.toString body  -- toString should return a Maybe, but it doesn't.  You too, Haskell?             case parseResponse sBody of@@ -599,16 +502,17 @@ ---------------------------------------------------------------------- -- GetMetadata -checkPath :: Path -> IO (Either ErrorMessage a) -> IO (Either ErrorMessage a)+checkPath :: Monad m => Path -> m (Either ErrorMessage a) -> m (Either ErrorMessage a) checkPath ('/':_) action = action checkPath _ _            = return $ Left $ "path must start with \"/\""  -- |Get the metadata for the file or folder at the given path. getMetadata ::-    Manager    -- ^The HTTP connection manager to use.+    (MonadBaseControl IO m, MonadThrow m, MonadUnsafeIO m, MonadIO m) +    => Manager    -- ^The HTTP connection manager to use.     -> Session     -> Path      -- ^The full path (relative to your 'DbAccessType' root)-    -> IO (Either ErrorMessage Meta)+    -> m (Either ErrorMessage Meta) getMetadata mgr session path = checkPath path $ do     result <- doGet mgr session hostsApi url params (mkHandler handler)     return $ mergeLefts result@@ -622,15 +526,16 @@ -- |Get the metadata for the file or folder at the given path.  If it's a folder, -- return the metadata for the folder's immediate children as well. getMetadataWithChildren ::-    Manager    -- ^The HTTP connection manager to use.+    (MonadBaseControl IO m, MonadThrow m, MonadUnsafeIO m, MonadIO m) +    => Manager    -- ^The HTTP connection manager to use.     -> Session-    -> Path      -- ^The full path (relative to your 'DbAccessType' root)+    -> Path       -- ^The full path (relative to your 'DbAccessType' root)     -> Maybe Integer-        -- ^A limit on folder contents (max: 10,000).  If the path refers to a folder and this folder-        -- has more than the specified number of immediate children, the entire-        -- 'getMetadataWithChildren' call will fail with an HTTP 406 error code.  If unspecified, or-        -- if set to zero, the server will set this to 10,000.-    -> IO (Either ErrorMessage (Meta, Maybe FolderContents))+                  -- ^A limit on folder contents (max: 10,000).  If the path refers to a folder and this folder+                  -- has more than the specified number of immediate children, the entire+                  -- 'getMetadataWithChildren' call will fail with an HTTP 406 error code.  If unspecified, or+                  -- if set to zero, the server will set this to 10,000.+    -> m (Either ErrorMessage (Meta, Maybe FolderContents)) getMetadataWithChildren mgr session path childLimit = checkPath path $ do     result <- doGet mgr session hostsApi url params (mkHandler handler)     return $ mergeLefts result@@ -646,16 +551,16 @@ -- |Same as 'getMetadataWithChildren' except it'll return @Nothing@ if the 'FolderHash' -- of the folder on Dropbox is the same as the 'FolderHash' passed in. getMetadataWithChildrenIfChanged ::-    Manager           -- ^The HTTP connection manager to use.+    (MonadBaseControl IO m, MonadThrow m, MonadUnsafeIO m, MonadIO m) +    => Manager       -- ^The HTTP connection manager to use.     -> Session     -> Path-    -> Maybe Integer-    -> FolderHash-        -- ^For folders, the returned child metadata will include a 'folderHash' field that-        -- is a short identifier for the current state of the folder.  If the 'FolderHash'-        -- for the specified path hasn't change, this call will return @Nothing@, which-        -- indicates that the previously-retrieved metadata is still the latest.-    -> IO (Either ErrorMessage (Maybe (Meta, Maybe FolderContents)))+    -> Maybe Integer +    -> FolderHash    -- ^For folders, the returned child metadata will include a 'folderHash' field that+                     -- is a short identifier for the current state of the folder.  If the 'FolderHash'+                     -- for the specified path hasn't change, this call will return @Nothing@, which+                     -- indicates that the previously-retrieved metadata is still the latest.+    -> m (Either ErrorMessage (Maybe (Meta, Maybe FolderContents))) getMetadataWithChildrenIfChanged mgr session path childLimit (FolderHash hash) = checkPath path $ do     result <- doGet mgr session hostsApi url params (mkHandler handler)     return $ mergeLefts result@@ -675,13 +580,14 @@ -- |Gets a file's contents and metadata.  If you just want the entire contents of -- a file as a single 'ByteString', use 'getFileBs'. getFile ::-    Manager               -- ^The HTTP connection manager to use.+    (MonadBaseControl IO m, MonadThrow m, MonadUnsafeIO m, MonadIO m)+    => Manager               -- ^The HTTP connection manager to use.     -> Session     -> Path               -- ^The full path (relative to your 'DbAccessType' root)     -> Maybe FileRevision -- ^The revision of the file to retrieve.-    -> (Meta -> Sink ByteString IO r)+    -> (Meta -> Sink ByteString (ResourceT m) r)                           -- ^Given the file metadata, yield a 'Sink' to process the response body-    -> IO (Either ErrorMessage (Meta, r))+    -> m (Either ErrorMessage (Meta, r))                           -- ^This function returns whatever your 'Sink' returns, paired up with the file metadata. getFile mgr session path mrev sink = checkPath path $ do     result <- doGet mgr session hostsApiContent url params handler@@ -690,13 +596,15 @@         at = accessTypePath $ configAccessType (sessionConfig session)         url = "files/" ++ at ++ path         params = maybe [] (\(FileRevision rev) -> [("rev", rev)]) mrev+         handler (HT.Status 200 _) headers = case getHeaders "X-Dropbox-Metadata" headers of             [metaJson] -> case handleJsonBody metaJson of-                Left err -> C.Sink $ return $ C.SinkNoData (Left err)+                Left err -> return (Left err)                 Right meta -> do                     r <- sink meta                     return $ Right (meta, r)             l -> return $ Left $ "expecting response to have exactly one \"X-Dropbox-Metadata\" header, found " ++ show (length l)+                 handler (HT.Status code reason) _ = do             body <- bsSink             return $ Left $ "non-200 response from Dropbox (" ++ (show code) ++ ":" ++ (BS8.unpack reason) ++ ": " ++ (show body) ++ ")"@@ -704,66 +612,42 @@ -- |A variant of 'getFile' that just returns a strict 'ByteString' (instead of having -- you pass in a 'Sink' to process the body. getFileBs ::-    Manager               -- ^The HTTP connection manager to use.+    (MonadBaseControl IO m, MonadThrow m, MonadUnsafeIO m, MonadIO m) +    => Manager               -- ^The HTTP connection manager to use.     -> Session-    -> Path               -- ^The full path (relative to your 'DbAccessType' root)-    -> Maybe FileRevision -- ^The revision of the file to retrieve.-    -> IO (Either ErrorMessage (Meta, ByteString))+    -> Path                  -- ^The full path (relative to your 'DbAccessType' root)+    -> Maybe FileRevision    -- ^The revision of the file to retrieve.+    -> m (Either ErrorMessage (Meta, ByteString)) getFileBs mgr session path mrev = getFile mgr session path mrev (\_ -> bsSink)  ------------------------------------------------------------------------- AddFile/ForceFile/UpdateFile---- |Add a new file.  If a file or folder already exists at the given path, your--- file will be automatically renamed.  If successful, you'll get back the metadata--- for your newly-uploaded file.-addFile ::-    Manager    -- ^The HTTP connection manager to use.-    -> Session-    -> Path        -- ^The full path (relative to your 'DbAccessType' root)-    -> RequestBody -- ^The file contents.-    -> IO (Either ErrorMessage Meta)-addFile mgr session path contents = putFile mgr session path contents [("overwrite", "false")]---- |Overwrite a file with new data if the version on Dropbox matches the version--- you specify.  If the version doesn't match, create a new file with a unique--- name.  Either way, you will be returned the metdata for whichever file was--- written.-updateFile ::-    Manager    -- ^The HTTP connection manager to use.-    -> Session-    -> Path         -- ^The full path (relative to your 'DbAccessType' root)-    -> RequestBody  -- ^The file contents.-    -> FileRevision -- ^The revision of the file you expect to be writing to.-    -> IO (Either ErrorMessage Meta)-updateFile mgr session path contents (FileRevision rev) =-    putFile mgr session path contents [("parent_rev", rev)]---- |Add a file.  If a file already exists at the given path, that file will--- be overwritten.  If successful, you'll get back the metadata for your--- newly-uploaded file.-forceFile ::-    Manager    -- ^The HTTP connection manager to use.-    -> Session-    -> Path        -- ^The full path (relative to your 'DbAccessType' root)-    -> RequestBody -- ^The file contents.-    -> IO (Either ErrorMessage Meta)-forceFile mgr session path contents = putFile mgr session path contents [("overwrite", "true")]+-- putFile -------------------------------------------------------------------------- The underlying "put_file" call.+data WriteMode+    = WriteModeAdd+        -- ^If there is already a file at the specified path, rename the new file.+    | WriteModeUpdate FileRevision+        -- ^Check that there is a file there with the given revision.  If so, overwrite+        -- it.  If not, rename the new file.+    | WriteModeForce+        -- ^If there is already a file at the specified path, overwrite it.  putFile ::-    HC.Manager+    (MonadBaseControl IO m, MonadThrow m, MonadUnsafeIO m, MonadIO m) +    => Manager       -- ^The HTTP connection manager to use.     -> Session-    -> Path-    -> RequestBody-    -> [(String,String)]-    -> IO (Either ErrorMessage Meta)-putFile mgr session path contents params = checkPath path $ do+    -> Path          -- ^The full path (relative to your 'DbAccessType' root)+    -> WriteMode+    -> RequestBody m -- ^The file contents.+    -> m (Either ErrorMessage Meta)+putFile mgr session path writeMode contents = checkPath path $ do     result <- doPut mgr session hostsApiContent url params contents (mkHandler handler)     return $ mergeLefts result     where+        params = case writeMode of+            WriteModeAdd -> [("overwrite", "false")]+            WriteModeUpdate (FileRevision rev) -> [("parent_rev", rev)]+            WriteModeForce -> [("overwrite", "true")]         at = accessTypePath $ configAccessType (sessionConfig session)         url = "files_put/" ++ at ++ path         handler 200 _ body = handleJsonBody body@@ -786,64 +670,76 @@         oauthHeader = buildOAuthHeader consumerPair (atKey, atSecret)  doPut ::-    Manager+    (MonadBaseControl IO m, MonadThrow m, MonadUnsafeIO m, MonadIO m) +    => Manager     -> Session     -> (Hosts -> String)     -> String     -> [(String,String)]-    -> RequestBody-    -> Handler r-    -> IO (Either ErrorMessage r)+    -> RequestBody m+    -> Handler r m+    -> m (Either ErrorMessage r) doPut mgr session hostSelector path params requestBody handler = do     let (uri, oauthHeader) = prepRequest session hostSelector path params-    let vf = certVerifierFunc $ configCertVerifier $ sessionConfig session-    httpClientPut mgr vf uri oauthHeader handler requestBody+    httpClientPut mgr uri oauthHeader handler requestBody  doGet ::-    Manager+    (MonadBaseControl IO m, MonadThrow m, MonadUnsafeIO m, MonadIO m) +    => Manager     -> Session     -> (Hosts -> String)     -> String     -> [(String,String)]-    -> Handler r-    -> IO (Either ErrorMessage r)+    -> Handler r m+    -> m (Either ErrorMessage r) doGet mgr session hostSelector path params handler = do     let (uri, oauthHeader) = prepRequest session hostSelector path params-    let vf = certVerifierFunc $ configCertVerifier $ sessionConfig session-    httpClientGet mgr vf uri oauthHeader handler+    httpClientGet mgr uri oauthHeader handler  ---------------------------------------------------------------------- +-- |`ManagerSettings` that include the DropBox SSL certificates+managerSettings :: (MonadBaseControl IO m) => m HC.ManagerSettings+managerSettings = do +    return $ HC.def { HC.managerCheckCerts = certVerifierFunc certVerifierFromDbX509s }+ -- |The HTTP connection manager.  Using the same 'Manager' instance across -- multiple API calls  type Manager = HC.Manager  -- |A bracket around an HTTP connection manager.-withManager :: (Manager -> IO r) -> IO r-withManager inner = HC.withManager $ \manager ->-    MT.lift $ inner manager+-- Uses default 'ManagerSettings' as computed by 'managerSettings'.+withManager ::+    (MonadBaseControl IO m, MonadThrow m, MonadUnsafeIO m, MonadIO m)+    => (HC.Manager -> ResourceT m a) -> m a+withManager inner = runResourceT $ do+    ms <- managerSettings+    (_, manager) <- allocate (HC.newManager ms) HC.closeManager+    inner manager  ----------------------------------------------------------------------  type SimpleHandler r = Int -> String -> ByteString -> r  -- HTTP response-handling function.-type Handler r = HT.Status -> HT.ResponseHeaders -> (Sink ByteString IO r)+type Handler r m = HT.Status -> HT.ResponseHeaders -> (Sink ByteString (ResourceT m) r)  -- |An HTTP request body: an 'Int64' for the length and a 'Source' -- that yields the actual data.-data RequestBody = RequestBody Int64 (Source IO ByteString)+data RequestBody m = RequestBody Int64 (Source (ResourceT m) ByteString)  -- |Create a 'RequestBody' from a single 'ByteString'-bsRequestBody :: ByteString -> RequestBody+bsRequestBody :: MonadIO m => ByteString -> RequestBody m bsRequestBody bs = RequestBody length (CL.sourceList [bs])     where         length = fromInteger $ toInteger $ BS.length bs -getHeaders :: CI HT.Ascii -> [HT.Header] -> [HT.Ascii]+getHeaders :: HT.HeaderName -> [HT.Header] -> [ByteString] getHeaders name headers = [ val | (key, val) <- headers, key == name ] -mkHandler :: SimpleHandler r -> Handler r+mkHandler ::+    Monad m => SimpleHandler r +    -> Handler r m mkHandler sh (HT.Status code reason) _headers = do     bs <- bsSink     return $ sh code (BS8.unpack reason) bs@@ -854,21 +750,22 @@     Right r -> r  -- |A 'Sink' that reads in 'ByteString' chunks and constructs one concatenated 'ByteString'-bsSink :: Resource m => Sink ByteString m ByteString+bsSink :: (Monad m) => Sink ByteString m ByteString bsSink = do     chunks <- CL.consume     return $ BS.concat chunks +-- | Runs an http request with a given oauth header httpClientDo ::-    Manager-    -> HT.Ascii-    -> RequestBody-    -> CertVerifierFunc+    (MonadBaseControl IO m, MonadThrow m, MonadUnsafeIO m, MonadIO m) +    => Manager+    -> HT.Method+    -> RequestBody m     -> URL     -> String-    -> Handler r-    -> IO (Either String r)-httpClientDo mgr method (RequestBody len bsSource) vf url oauthHeader handler =+    -> Handler r m+    -> m (Either String r)+httpClientDo mgr method (RequestBody len bsSource) url oauthHeader handler =     case HC.parseUrl url of         Just baseReq -> do             let req = baseReq {@@ -876,19 +773,32 @@                 HC.method = method,                 HC.requestHeaders = headers,                 HC.requestBody = HC.RequestBodySource len builderSource,-                HC.checkCerts = vf,                 HC.checkStatus = \_ _ -> Nothing }-            HC.Response code headers body <- C.runResourceT $ HC.http req mgr-            result <- C.runResourceT (body C.$$ handler code headers)+            result <- runResourceT $ do+                HC.Response code _ headers body <- HC.http req mgr+                body $$+- handler code headers             return $ Right result         Nothing -> do             return $ Left $ "bad URL: " ++ show url     where         headers = [("Authorization", UTF8.fromString oauthHeader)]-        builderSource = bsSource C.$= (CL.map BlazeBS.fromByteString)+        builderSource = bsSource $= (CL.map BlazeBS.fromByteString) -httpClientGet :: Manager -> CertVerifierFunc -> URL -> String -> Handler r -> IO (Either String r)-httpClientGet mgr vf url oauthHeader handler = httpClientDo mgr "GET" (bsRequestBody BS.empty) vf url oauthHeader handler+httpClientGet ::+    (MonadBaseControl IO m, MonadThrow m, MonadUnsafeIO m, MonadIO m) +    => Manager+    -> URL+    -> String+    -> Handler r m+    -> m (Either String r)+httpClientGet mgr url oauthHeader handler = httpClientDo mgr HT.methodGet (bsRequestBody BS.empty) url oauthHeader handler -httpClientPut :: Manager -> CertVerifierFunc -> URL -> String -> Handler r -> RequestBody -> IO (Either String r)-httpClientPut mgr vf url oauthHeader handler requestBody = httpClientDo mgr "PUT" requestBody vf url oauthHeader handler+httpClientPut ::+    (MonadBaseControl IO m, MonadThrow m, MonadUnsafeIO m, MonadIO m) +    => Manager+    -> URL+    -> String+    -> Handler r m+    -> RequestBody m+    -> m (Either String r)+httpClientPut mgr url oauthHeader handler requestBody = httpClientDo mgr HT.methodPut requestBody url oauthHeader handler
+ Source/Dropbox/Certificates.hs view
@@ -0,0 +1,132 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}++module Dropbox.Certificates +  ( CertVerifierFunc+  , CertVerifier(..)+  , certVerifierInsecure+  , certVerifierFromPemFile+  , certVerifierFromRootCerts+  , certVerifierFromDbX509s+  ) where++import System.IO                       (withFile, IOMode(ReadMode))++import Data.ByteString                 (ByteString)+import qualified Data.ByteString       as B+import qualified Data.ByteString.Lazy  as LB+import qualified Data.ByteString.Char8 as B8++import           Data.PEM              (PEM(..))+import qualified Data.PEM              as PEM++import           Data.Certificate.X509 (X509)+import qualified Data.Certificate.X509 as X509++import           Data.Time.Clock       (UTCTime(utctDay), getCurrentTime)++import qualified Network.TLS           as TLS+import qualified Network.TLS.Extra     as TLSExtra++import Dropbox.Certificates.TH+++dbX509s :: [X509]+dbX509s = [x509File|trusted-certs.crt|]++-- | Use the buildin Dropbox certificates.+certVerifierFromDbX509s :: CertVerifier+certVerifierFromDbX509s = CertVerifier "compiled in Dropbox certificates" (certVerifierFromRootCerts dbX509s)++----------------------------------------------------------------------+-- SSL Certificate Validation++type CertVerifierFunc =+    ByteString                     -- ^The server's host name.+    -> [X509]                      -- ^The server's certificate chain.+    -> IO TLS.TLSCertificateUsage  -- ^Whether the certificate chain is valid or not.++-- |How the server's SSL certificate will be verified.+data CertVerifier = CertVerifier+    { certVerifierName :: String           -- ^The human-friendly name of the policy (only for debug prints)+    , certVerifierFunc :: CertVerifierFunc -- ^The function that implements certificate validation.+    }++instance Show CertVerifier where+    show (CertVerifier name _) = "CertVerifier " ++ show name++-- |A dummy implementation that doesn't perform any verification.+certVerifierInsecure :: CertVerifier+certVerifierInsecure = CertVerifier "insecure" (\_ _ -> return TLS.CertificateUsageAccept)++rightsOrFirstLeft :: [Either a b] -> Either a [b]+rightsOrFirstLeft = foldr f (Right [])+    where+        f (Left e) _ = Left e+        f _ (Left e) = Left e+        f (Right v) (Right vs) = Right (v:vs)++-- |Reads certificates in PEM format from the given file and uses those as the roots when+-- verifying certificates.  This function basically just loads the certificates and delegates+-- to 'certVerifierFromRootCerts' for the actual checking.+certVerifierFromPemFile :: FilePath -> IO (Either String CertVerifier)+certVerifierFromPemFile filePath = do+    raw <- withFile filePath ReadMode B.hGetContents+    case PEM.pemParseBS raw of+        Left err -> return $ Left err+        Right pems -> do+            let es = [X509.decodeCertificate (LB.fromChunks [stuff]) | PEM _ _ stuff <- pems]+            case rightsOrFirstLeft es of+                Left err -> return $ Left err+                Right x509s -> return $ Right $ CertVerifier ("PEM file: " ++ show filePath) (certVerifierFromRootCerts x509s)++certAll :: [IO TLS.TLSCertificateUsage] -> IO TLS.TLSCertificateUsage+certAll [] = return TLS.CertificateUsageAccept+certAll (head:rest) = do+    r <- head+    case r of+        TLS.CertificateUsageAccept -> certAll rest+        reject -> return $ reject++-- |A certificate validation routine.  It's in 'IO' to match what 'HTTP.Enumerator'+-- expects, but we don't actually do any I/O.+certVerifierFromRootCerts ::+    [X509]            -- ^The set of trusted root certificates.+    -> ByteString     -- ^The remote server's domain name.+    -> [X509]         -- ^The certificate chain provided by the remote server.+    -> IO TLS.TLSCertificateUsage+-- TODO: Rewrite this crappy code.  SSL cert checking needs to be more correct than this.+certVerifierFromRootCerts roots domain chain = do+        utcTime <- getCurrentTime+        let day = utctDay utcTime+        certAll+            [ return $ TLSExtra.certificateVerifyDomain (B8.unpack domain) chain+            , checkTrustChain day chain+            ]+    where+        checkTrustChain _ [] = return $ TLS.CertificateUsageReject $ TLS.CertificateRejectOther "empty chain"+        checkTrustChain day (head:rest) = do+            if isUnexpired day head+                then do+                    issuerMatch <- mapM (head `isIssuedBy`) roots+                    if any (== True) issuerMatch+                        then return $ TLS.CertificateUsageAccept+                        else case rest of+                            [] -> return $ TLS.CertificateUsageReject TLS.CertificateRejectUnknownCA+                            (next:_) -> do+                                nextOk <- TLSExtra.certificateVerifyAgainst head next+                                if nextOk+                                    then checkTrustChain day rest+                                    else return $ TLS.CertificateUsageReject $ TLS.CertificateRejectOther "break in verification chain"+                else return $ TLS.CertificateUsageReject $ TLS.CertificateRejectExpired+        isIssuedBy :: X509 -> X509 -> IO Bool+        isIssuedBy c issuer =+            if subjectDN issuer == issuerDN c+                then TLSExtra.certificateVerifyAgainst c issuer+                else return False+        subjectDN c = X509.certSubjectDN $ X509.x509Cert c+        issuerDN c = X509.certIssuerDN $ X509.x509Cert c+        isUnexpired day cert =+            let ((beforeDay, _, _), (afterDay, _, _)) = X509.certValidity (X509.x509Cert cert)+            in beforeDay < day && day <= afterDay+
+ Source/Dropbox/Certificates/TH.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE TemplateHaskell #-}+{-# OPTIONS_GHC -fno-warn-missing-fields #-}++-- | Quasi quoters for parsing PEM files and PEM encoded X509 Certificates.+--+-- Whem using hardcoded certificates, be aware that a certificate may be revoked+-- at any time before it expires.+--+module Dropbox.Certificates.TH +  ( pem+  , pemFile+  , x509+  , x509File+  ) where++import Data.ByteString.Char8 (pack)+import Data.ByteString.Lazy  (fromChunks)+import Data.PEM              (PEM(..), pemParseBS)+import Data.Certificate.X509 (X509(..), decodeCertificate)++import Language.Haskell.TH.Quote++rightsOrFirstLeft :: [Either a b] -> Either a [b]+rightsOrFirstLeft = foldr f (Right [])+    where+        f (Left e) _ = Left e+        f _ (Left e) = Left e+        f (Right v) (Right vs) = Right (v:vs)++pem :: QuasiQuoter+pem = QuasiQuoter { quoteExp = \s -> [| parsePem s |] }++pemFile :: QuasiQuoter+pemFile = quoteFile pem++parsePem :: String -> [PEM]+parsePem s = case pemParseBS $ pack s of+               Left err -> error $ "Failed to parse PEM file: " ++ err+               Right x -> x++x509 :: QuasiQuoter+x509 = QuasiQuoter { quoteExp = \s -> [| decodeCert . parsePem $ s |] } ++x509File :: QuasiQuoter+x509File = quoteFile x509++decodeCert :: [PEM] -> [X509]+decodeCert pems = +    let es = [decodeCertificate (fromChunks [stuff]) | PEM _ _ stuff <- pems]+    in  case rightsOrFirstLeft es of+          Left err -> error $ "Failed to decode X509 file: " ++ err+          Right x509s -> x509s+
dropbox-sdk.cabal view
@@ -1,5 +1,5 @@ Name: dropbox-sdk-Version: 0.2.0+Version: 0.3.0 Synopsis: A library to access the Dropbox HTTP API. Description:     A (very preliminary) library to access the Dropbox HTTP API:@@ -12,7 +12,6 @@ Category: Network APIs Build-Type: Simple Cabal-Version: >= 1.10-Data-Files: trusted-certs.crt  Library     Build-Depends:@@ -22,22 +21,25 @@         utf8-string  == 0.3.*,         urlencoded   == 0.3.*,         json         >= 0.4 && < 0.6,-        transformers == 0.2.*,+        transformers == 0.3.*,         time         == 1.4.*,         old-locale   == 1.0.*,         network      == 2.3.*,-        conduit      == 0.0.*,+        conduit      == 0.5.*,+        resourcet    == 0.3.3.*,         case-insensitive >= 0.2 && < 0.5,-        http-types   >= 0.6.5 && < 0.7,-        http-conduit == 1.1.*,+        http-types   == 0.7.*,+        http-conduit == 1.5.*,+        monad-control == 0.3.1.*,         blaze-builder == 0.3.*,-        tls          == 0.8.*,+        tls          == 0.9.*,         tls-extra    == 0.4.*,-        certificate  == 1.*+        certificate  == 1.*,+        pem          == 0.1.*,+        template-haskell     HS-Source-Dirs: Source-    Exposed-Modules: Dropbox-    Other-Modules: Paths_dropbox_sdk-    GHC-Options: -Wall -fno-warn-missing-signatures -fno-warn-name-shadowing+    Exposed-Modules: Dropbox, Dropbox.Certificates, Dropbox.Certificates.TH+    GHC-Options: -Wall -fno-warn-missing-signatures -fno-warn-name-shadowing -fwarn-unused-imports     Default-Language: Haskell2010     Default-Extensions: OverloadedStrings, ScopedTypeVariables, Rank2Types 
− trusted-certs.crt
@@ -1,341 +0,0 @@-#        Subject: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Server CA/emailAddress=server-certs@thawte.com <server-certs@thawte.com>-#        Issuer: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Server CA/emailAddress= server-certs@thawte.com------BEGIN CERTIFICATE------MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx-FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD-VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv-biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm-MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx-MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT-DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3-dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl-cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3-DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD-gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91-yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX-L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj-EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG-7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e-QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ-qdq5snUb9kLy78fyGPmJvKP/iiMucEc=------END CERTIFICATE------#        Subject: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com <premium-server@thawte.com>-#        Issuer: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com <premium-server@thawte.com>------BEGIN CERTIFICATE------MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx-FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD-VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv-biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy-dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t-MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB-MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG-A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp-b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl-cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv-bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE-VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ-ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR-uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG-9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI-hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM-pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg==------END CERTIFICATE------#        Subject: C=US, O=VeriSign, Inc., OU=Class 1 Public Primary Certification Authority-#        Issuer: C=US, O=VeriSign, Inc., OU=Class 1 Public Primary Certification Authority------BEGIN CERTIFICATE------MIICPDCCAaUCEDJQM89Q0VbzXIGtZVxPyCUwDQYJKoZIhvcNAQECBQAwXzELMAkG-A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz-cyAxIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2-MDEyOTAwMDAwMFoXDTIwMDEwNzIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV-BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmlt-YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN-ADCBiQKBgQDlGb9to1ZhLZlIcfZn3rmN67eehoAKkQ76OCWvRoiC5XOooJskXQ0f-zGVuDLDQVoQYh5oGmxChc9+0WDlrbsH2FdWoqD+qEgaNMax/sDTXjzRniAnNFBHi-TkVWaR94AoDa3EeRKbs2yWNcxeDXLYd7obcysHswuiovMaruo2fa2wIDAQABMA0G-CSqGSIb3DQEBAgUAA4GBAEtEZmBoZOSYG/OwcuaViXzde7OVwB0u2NgZ0C00PcZQ-mhCGjKo/O6gE/DdSlcPZydvN8oYGxLEb8IKIMEKOF1AcZHq4PplJdJf8rAJD+5YM-VgQlDHx8h50kp9jwMim1pN9dokzFFjKoQvZFprY2ueC/ZTaTwtLXa9zeWdaiNfhF------END CERTIFICATE------#        Subject: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority-#        Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority------BEGIN CERTIFICATE------MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG-A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz-cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2-MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV-BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt-YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN-ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE-BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is-I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G-CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do-lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc-AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k------END CERTIFICATE------#        Subject: C=US, O=RSA Data Security, Inc., OU=Secure Server Certification Authority-#        Issuer: C=US, O=RSA Data Security, Inc., OU=Secure Server Certification Authority------BEGIN CERTIFICATE------MIICNDCCAaECEAKtZn5ORf5eV288mBle3cAwDQYJKoZIhvcNAQECBQAwXzELMAkG-A1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYD-VQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk0-MTEwOTAwMDAwMFoXDTEwMDEwNzIzNTk1OVowXzELMAkGA1UEBhMCVVMxIDAeBgNV-BAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYDVQQLEyVTZWN1cmUgU2Vy-dmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGbMA0GCSqGSIb3DQEBAQUAA4GJ-ADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6OLDfO6zV4ZFQD5YRAUcm/jwjiioII-0haGN1XpsSECrXZogZoFokvJSyVmIlZsiAeP94FZbYQHZXATcXY+m3dM41CJVphI-uR2nKRoTLkoRWZweFdVJVCxzOmmCsZc5nG1wZ0jl3S3WyB57AgMBAAEwDQYJKoZI-hvcNAQECBQADfgBl3X7hsuyw4jrg7HFGmhkRuNPHoLQDQCYCPgmc4RKz0Vr2N6W3-YQO2WxZpO8ZECAyIUwxrl0nHPjXcbLm7qt9cuzovk2C2qUtN8iD3zV9/ZHuO3ABc-1/p3yjkWWW8O6tO1g39NTUJWdrTJXwT4OPjr0l91X817/OWOgHz8UA==------END CERTIFICATE------#        Subject: C=US, O=Equifax Secure Inc., CN=Equifax Secure Global eBusiness CA-1-#        Issuer: C=US, O=Equifax Secure Inc., CN=Equifax Secure Global eBusiness CA-1------BEGIN CERTIFICATE------MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc-MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT-ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw-MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj-dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l-c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC-UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc-58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/-o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH-MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr-aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA-A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA-Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv-8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV------END CERTIFICATE------#        Subject: C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Hardware-#        Issuer: C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Hardware------BEGIN CERTIFICATE------MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB-lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug-Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho-dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt-SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG-A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe-MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v-d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh-cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn-0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ-M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a-MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd-oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI-DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy-oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD-VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0-dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy-bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF-BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM-//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli-CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE-CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t-3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS-KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA==------END CERTIFICATE------#        Subject: C=US, O=Network Solutions L.L.C., CN=Network Solutions Certificate Authority-#        Issuer: C=US, O=Network Solutions L.L.C., CN=Network Solutions Certificate Authority------BEGIN CERTIFICATE------MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi-MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu-MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp-dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV-UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO-ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz-c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP-OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl-mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF-BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4-qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw-gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB-BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu-bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp-dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8-6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/-h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH-/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv-wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN-pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey------END CERTIFICATE------#        Subject: C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority-#        Issuer: C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority------BEGIN CERTIFICATE------MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh-MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE-YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3-MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo-ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg-MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN-ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA-PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w-wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi-EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY-avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+-YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE-sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h-/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5-IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj-YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD-ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy-OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P-TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ-HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER-dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf-ReYNnyicsbkqWletNw+vHX/bvZ8=------END CERTIFICATE------#        Subject: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., OU=http://certificates.godaddy.com/repository<http://certificates.godaddy.com/repository>, CN=Go Daddy Secure Certification Authority/serialNumber=07969287-#        Issuer: C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority------BEGIN CERTIFICATE------MIIE3jCCA8agAwIBAgICAwEwDQYJKoZIhvcNAQEFBQAwYzELMAkGA1UEBhMCVVMx-ITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g-RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMTYw-MTU0MzdaFw0yNjExMTYwMTU0MzdaMIHKMQswCQYDVQQGEwJVUzEQMA4GA1UECBMH-QXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5j-b20sIEluYy4xMzAxBgNVBAsTKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5j-b20vcmVwb3NpdG9yeTEwMC4GA1UEAxMnR28gRGFkZHkgU2VjdXJlIENlcnRpZmlj-YXRpb24gQXV0aG9yaXR5MREwDwYDVQQFEwgwNzk2OTI4NzCCASIwDQYJKoZIhvcN-AQEBBQADggEPADCCAQoCggEBAMQt1RWMnCZM7DI161+4WQFapmGBWTtwY6vj3D3H-KrjJM9N55DrtPDAjhI6zMBS2sofDPZVUBJ7fmd0LJR4h3mUpfjWoqVTr9vcyOdQm-VZWt7/v+WIbXnvQAjYwqDL1CBM6nPwT27oDyqu9SoWlm2r4arV3aLGbqGmu75RpR-SgAvSMeYddi5Kcju+GZtCpyz8/x4fKL4o/K1w/O5epHBp+YlLpyo7RJlbmr2EkRT-cDCVw5wrWCs9CHRK8r5RsL+H0EwnWGu1NcWdrxcx+AuP7q2BNgWJCJjPOq8lh8BJ-6qf9Z/dFjpfMFDniNoW1fho3/Rb2cRGadDAW/hOUoz+EDU8CAwEAAaOCATIwggEu-MB0GA1UdDgQWBBT9rGEyk2xF1uLuhV+auud2mWjM5zAfBgNVHSMEGDAWgBTSxLDS-kdRMEXGzYcs9of7dqGrU4zASBgNVHRMBAf8ECDAGAQH/AgEAMDMGCCsGAQUFBwEB-BCcwJTAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuZ29kYWRkeS5jb20wRgYDVR0f-BD8wPTA7oDmgN4Y1aHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBv-c2l0b3J5L2dkcm9vdC5jcmwwSwYDVR0gBEQwQjBABgRVHSAAMDgwNgYIKwYBBQUH-AgEWKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeTAO-BgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBANKGwOy9+aG2Z+5mC6IG-OgRQjhVyrEp0lVPLN8tESe8HkGsz2ZbwlFalEzAFPIUyIXvJxwqoJKSQ3kbTJSMU-A2fCENZvD117esyfxVgqwcSeIaha86ykRvOe5GPLL5CkKSkB2XIsKd83ASe8T+5o-0yGPwLPk9Qnt0hCqU7S+8MxZC9Y7lhyVJEnfzuz9p0iRFEUOOjZv2kWzRaJBydTX-RE4+uXR21aITVSzGh6O1mawGhId/dQb8vxRMDsxuxN89txJx9OjxUUAiKEngHUuH-qDTMBqLdElrRhjZkAzVvb3du6/KFUJheqwNTrZEjYx8WnM25sgVjOuH0aBsXBTWV-U+4=------END CERTIFICATE------#        Subject: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2-#        Issuer: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2------BEGIN CERTIFICATE------MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx-EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT-EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp-ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz-NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH-EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE-AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw-DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD-E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH-/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy-DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh-GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR-tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA-AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE-FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX-WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu-9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr-gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo-2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO-LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI-4uJEvlz36hz1------END CERTIFICATE------#        Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA-#        Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA------BEGIN CERTIFICATE------MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT-MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i-YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG-EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg-R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9-9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq-fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv-iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU-1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+-bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW-MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA-ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l-uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn-Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS-tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF-PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un-hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV-5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==------END CERTIFICATE------#        Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Primary Certification Authority-#        Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Primary Certification Authority------BEGIN CERTIFICATE------MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY-MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo-R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx-MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK-Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp-ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC-AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9-AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA-ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0-7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W-kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI-mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G-A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ-KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1-6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl-4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K-oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj-UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU-AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=------END CERTIFICATE------#        Subject: C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority-#        Issuer: L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 2 Policy Validation Authority, CN=http://www.valicert.com//emailAddress=info@valicert.com<http://www.valicert.com//emailAddress=info@valicert.com>------BEGIN CERTIFICATE------MIIE+zCCBGSgAwIBAgICAQ0wDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1Zh-bGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu-Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24g-QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAe-BgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTA0MDYyOTE3MDYyMFoX-DTI0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBE-YWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3MgMiBDZXJ0-aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgC-ggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv-2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+q-N1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiO-r18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lN-f4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+YihfukEH-U1jPEX44dMX4/7VpkI+EdOqXG68CAQOjggHhMIIB3TAdBgNVHQ4EFgQU0sSw0pHU-TBFxs2HLPaH+3ahq1OMwgdIGA1UdIwSByjCBx6GBwaSBvjCBuzEkMCIGA1UEBxMb-VmFsaUNlcnQgVmFsaWRhdGlvbiBOZXR3b3JrMRcwFQYDVQQKEw5WYWxpQ2VydCwg-SW5jLjE1MDMGA1UECxMsVmFsaUNlcnQgQ2xhc3MgMiBQb2xpY3kgVmFsaWRhdGlv-biBBdXRob3JpdHkxITAfBgNVBAMTGGh0dHA6Ly93d3cudmFsaWNlcnQuY29tLzEg-MB4GCSqGSIb3DQEJARYRaW5mb0B2YWxpY2VydC5jb22CAQEwDwYDVR0TAQH/BAUw-AwEB/zAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmdv-ZGFkZHkuY29tMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jZXJ0aWZpY2F0ZXMu-Z29kYWRkeS5jb20vcmVwb3NpdG9yeS9yb290LmNybDBLBgNVHSAERDBCMEAGBFUd-IAAwODA2BggrBgEFBQcCARYqaHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNv-bS9yZXBvc2l0b3J5MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOBgQC1-QPmnHfbq/qQaQlpE9xXUhUaJwL6e4+PrxeNYiY+Sn1eocSxI0YGyeR+sBjUZsE4O-WBsUs5iB0QQeyAfJg594RAoYC5jcdnplDQ1tgMQLARzLrUc+cb53S8wGd9D0Vmsf-SxOaFIqII6hR8INMqzW/Rn453HWkrugp++85j09VZw==------END CERTIFICATE------#        Subject: L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 2 Policy Validation Authority, CN=http://www.valicert.com//emailAddress=info@valicert.com<http://www.valicert.com//emailAddress=info@valicert.com>-#        Issuer: L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 2 Policy Validation Authority, CN=http://www.valicert.com//emailAddress=info@valicert.com<http://www.valicert.com//emailAddress=info@valicert.com>------BEGIN CERTIFICATE------MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0-IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz-BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y-aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG-9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy-NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y-azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs-YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw-Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl-cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY-dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9-WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS-v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v-UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu-IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC-W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd------END CERTIFICATE-------