[asterisk-scf-commits] asterisk-scf/release/sip.git branch "authexten" updated.

Commits to the Asterisk SCF project code repositories asterisk-scf-commits at lists.digium.com
Wed Feb 9 17:04:37 CST 2011


branch "authexten" has been updated
       via  783d705e83c78f171ebd2128c5dadecf8cb9e44d (commit)
       via  8c1955a5240967abe7e74b8b7d5d3bc067c71842 (commit)
       via  aff68781848311763ebb92e299750dfe4b61eb5c (commit)
       via  f0d27ad5ba9caa722ce22b36975d1d4be70be762 (commit)
       via  78914f7922bf4620086eef4a77bead6ff55cf79d (commit)
      from  f4195acb409cf8ecaac3d63e3c85a52e98bbd9b1 (commit)

Summary of changes:
 src/PJSipManager.cpp         |    6 +-
 src/PJSipManager.h           |    2 +-
 src/PJSipModule.h            |    6 +-
 src/PJSipSessionModule.cpp   |  150 +++++++++++++++++++++++++++++++++++++----
 src/PJSipSessionModule.h     |    3 +-
 src/SipSessionManagerApp.cpp |    2 +-
 6 files changed, 145 insertions(+), 24 deletions(-)


- Log -----------------------------------------------------------------
commit 783d705e83c78f171ebd2128c5dadecf8cb9e44d
Author: Mark Michelson <mmichelson at digium.com>
Date:   Wed Feb 9 17:03:57 2011 -0600

    Add XXX comments denoting good helper functions.

diff --git a/src/PJSipSessionModule.cpp b/src/PJSipSessionModule.cpp
index a34d558..bcddcc0 100644
--- a/src/PJSipSessionModule.cpp
+++ b/src/PJSipSessionModule.cpp
@@ -483,6 +483,9 @@ static pj_status_t lookup_cred(pj_pool_t *pool, const pj_str_t *realm, const pj_
 
 bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv, RequestType type)
 {
+    //XXX This could be a handy function to determine if there are
+    //any applicable hooks and then to fill in a vector of those that
+    //are applicable if so.
     //If there are no hooks, this is simple
     if (mAuthHooks.empty())
     {
@@ -511,6 +514,10 @@ bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv,
         return false;
     }
 
+    //XXX This marks the end of that function
+
+    //XXX A method to take rdata and create a RequestInfoPtr
+    //is essential
     RequestInfoPtr info(new RequestInfo);
 
     char buf[512];
@@ -570,6 +577,8 @@ bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv,
         info->transport = AsteriskSCF::SIP::ExtensionPoint::V1::UDP;
     }
 
+    //XXX End of the function to fill in a RequestInfoPtr
+
     //We have our RequestInfo created. Now start calling out to any registered hooks
     for (std::vector<AuthHookPrx>::iterator iter = hooks.begin(); iter != hooks.end(); ++iter)
     {
@@ -589,6 +598,8 @@ bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv,
 
             pjsip_tx_data *tdata;
             pjsip_inv_end_session(inv, 401, NULL, &tdata);
+            //XXX this would be another good function to isolate. It would take the sequence
+            //of digests and some tdata and ge the tdata in proper shape.
             for (DigestChallengeSeq::iterator digest = digests.begin(); digest != digests.end(); ++digest)
             {
                 pjsip_auth_srv authServer;
@@ -608,6 +619,7 @@ bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv,
                 }
                 pjsip_auth_srv_challenge(&authServer, NULL, noncePtr, opaquePtr, PJ_FALSE, tdata);
             }
+            //XXX End of function
             pjsip_inv_send_msg(inv, tdata);
             return true;
         }

commit 8c1955a5240967abe7e74b8b7d5d3bc067c71842
Author: Mark Michelson <mmichelson at digium.com>
Date:   Wed Feb 9 16:25:27 2011 -0600

    Filter hooks by what request types they apply to.
    
    The checkAuth function contains a lot of logic that will be
    beneficial to any PJSIP module, so the next plan is to isolate the
    common elements and place them in a central location.

diff --git a/src/PJSipSessionModule.cpp b/src/PJSipSessionModule.cpp
index 2e4bbf6..a34d558 100644
--- a/src/PJSipSessionModule.cpp
+++ b/src/PJSipSessionModule.cpp
@@ -481,7 +481,7 @@ static pj_status_t lookup_cred(pj_pool_t *pool, const pj_str_t *realm, const pj_
     return !PJ_SUCCESS;
 }
 
-bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv)
+bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv, RequestType type)
 {
     //If there are no hooks, this is simple
     if (mAuthHooks.empty())
@@ -489,6 +489,28 @@ bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv)
         return false;
     }
 
+    //Even if they have hooks, it may be that they do not apply to this type
+    //of request. Building a local vector of applicable hooks simplifies matters.
+    
+    std::vector<AuthHookPrx> hooks;
+    for (std::map<HookId, std::pair<AuthHookPrx, RequestTypeSeq> >::iterator iter = mAuthHooks.begin(); iter != mAuthHooks.end(); ++iter)
+    {
+        RequestTypeSeq types = iter->second.second;
+        AuthHookPrx hook = iter->second.first;
+        for (RequestTypeSeq::iterator typeIter = types.begin(); typeIter != types.end(); ++typeIter)
+        {
+            if (*typeIter == type)
+            {
+                hooks.push_back(hook);
+            }
+        }
+    }
+
+    if (hooks.empty())
+    {
+        return false;
+    }
+
     RequestInfoPtr info(new RequestInfo);
 
     char buf[512];
@@ -549,12 +571,11 @@ bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv)
     }
 
     //We have our RequestInfo created. Now start calling out to any registered hooks
-    for (std::map<HookId, std::pair<AuthHookPrx, RequestTypeSeq> >::iterator iter = mAuthHooks.begin(); iter != mAuthHooks.end(); ++iter)
+    for (std::vector<AuthHookPrx>::iterator iter = hooks.begin(); iter != hooks.end(); ++iter)
     {
         DigestChallengeSeq digests;
         HookResult result;
-        AuthHookPrx hookPrx = iter->second.first;
-        result = hookPrx->challengeRequest(info, digests);
+        result = (*iter)->challengeRequest(info, digests);
         if (result.status == Failed)
         {
             lg(Error) << "SIP Authentication hook reported a failure: " << result.info;
@@ -666,7 +687,7 @@ void PJSipSessionModule::handleNewInvite(pjsip_rx_data *rdata)
         pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
     }
 
-    bool authSent = checkAuth(rdata, inv_session);
+    bool authSent = checkAuth(rdata, inv_session, DialogEstablishing);
 
     // This means we sent a 401 to the requester,
     // so no need to go any further
diff --git a/src/PJSipSessionModule.h b/src/PJSipSessionModule.h
index 7d0ac9b..ceea718 100644
--- a/src/PJSipSessionModule.h
+++ b/src/PJSipSessionModule.h
@@ -86,7 +86,7 @@ private:
     void handleNewInvite(pjsip_rx_data *rdata);
     void handleInviteResponse(pjsip_inv_session *inv, pjsip_rx_data *rdata, pjsip_dialog *dlg);
     void handleRefer(pjsip_inv_session *inv, pjsip_rx_data *rdata);
-    bool checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv);
+    bool checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv, AsteriskSCF::SIP::ExtensionPoint::V1::RequestType type);
     void getURIParams(pjsip_uri *uri, AsteriskSCF::SIP::ExtensionPoint::V1::ParamDict &params);
     pjsip_inv_callback mInvCallback;
     pjsip_ua_init_param mUaParam;

commit aff68781848311763ebb92e299750dfe4b61eb5c
Author: Mark Michelson <mmichelson at digium.com>
Date:   Wed Feb 9 16:15:56 2011 -0600

    Get the RequestType known to the PJSIP modules.

diff --git a/src/PJSipManager.cpp b/src/PJSipManager.cpp
index ba70b3d..43e6525 100644
--- a/src/PJSipManager.cpp
+++ b/src/PJSipManager.cpp
@@ -35,15 +35,15 @@ namespace AsteriskSCF
 namespace SipSessionManager
 {
 
-void PJSipManager::addAuthHook(std::pair<HookId, AuthHookPrx> hook)
+void PJSipManager::addAuthHook(HookId id, AuthHookPrx hook, RequestTypeSeq types)
 {
     if (mSessionModule)
     {
-        mSessionModule->addAuthHook(hook);
+        mSessionModule->addAuthHook(id, hook, types);
     }
     if (mLoggingModule)
     {
-        mLoggingModule->addAuthHook(hook);
+        mLoggingModule->addAuthHook(id, hook, types);
     }
 }
 
diff --git a/src/PJSipManager.h b/src/PJSipManager.h
index 2458c6d..0abdc7e 100644
--- a/src/PJSipManager.h
+++ b/src/PJSipManager.h
@@ -82,7 +82,7 @@ public:
      * for logging incoming and outgoing SIP messages
      */
     void registerLoggingModule();
-    void addAuthHook(std::pair<AsteriskSCF::System::Hook::V1::HookId, AsteriskSCF::SIP::ExtensionPoint::V1::AuthHookPrx> hook);
+    void addAuthHook(AsteriskSCF::System::Hook::V1::HookId id, AsteriskSCF::SIP::ExtensionPoint::V1::AuthHookPrx hook, AsteriskSCF::SIP::ExtensionPoint::V1::RequestTypeSeq types);
     void removeAuthHook(const AsteriskSCF::System::Hook::V1::HookId &id);
     void clearAuthHooks();
 private:
diff --git a/src/PJSipModule.h b/src/PJSipModule.h
index 6860e46..2a0d6ca 100644
--- a/src/PJSipModule.h
+++ b/src/PJSipModule.h
@@ -47,9 +47,9 @@ public:
     virtual pj_status_t on_tx_response(pjsip_tx_data *tdata) = 0;
     virtual void on_tsx_state(pjsip_transaction *tsx, pjsip_event *event) = 0;
     pjsip_module &getModule() { return mModule; };
-    void addAuthHook(std::pair<AsteriskSCF::System::Hook::V1::HookId, AsteriskSCF::SIP::ExtensionPoint::V1::AuthHookPrx> hook)
+    void addAuthHook(AsteriskSCF::System::Hook::V1::HookId id, AsteriskSCF::SIP::ExtensionPoint::V1::AuthHookPrx hook, AsteriskSCF::SIP::ExtensionPoint::V1::RequestTypeSeq types)
     {
-        mAuthHooks.insert(hook);
+        mAuthHooks.insert(std::make_pair(id, std::make_pair(hook, types)));
     }
     void removeAuthHook(AsteriskSCF::System::Hook::V1::HookId id)
     {
@@ -63,7 +63,7 @@ protected:
     PJSipModule() { }
     virtual ~PJSipModule() { }
     pjsip_module mModule;
-    std::map<AsteriskSCF::System::Hook::V1::HookId, AsteriskSCF::SIP::ExtensionPoint::V1::AuthHookPrx> mAuthHooks;
+    std::map<AsteriskSCF::System::Hook::V1::HookId, std::pair<AsteriskSCF::SIP::ExtensionPoint::V1::AuthHookPrx, AsteriskSCF::SIP::ExtensionPoint::V1::RequestTypeSeq> > mAuthHooks;
 private:
     const std::string mName;
 };
diff --git a/src/PJSipSessionModule.cpp b/src/PJSipSessionModule.cpp
index a4df86a..2e4bbf6 100644
--- a/src/PJSipSessionModule.cpp
+++ b/src/PJSipSessionModule.cpp
@@ -549,11 +549,12 @@ bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv)
     }
 
     //We have our RequestInfo created. Now start calling out to any registered hooks
-    for (std::map<HookId, AuthHookPrx>::iterator iter = mAuthHooks.begin(); iter != mAuthHooks.end(); ++iter)
+    for (std::map<HookId, std::pair<AuthHookPrx, RequestTypeSeq> >::iterator iter = mAuthHooks.begin(); iter != mAuthHooks.end(); ++iter)
     {
         DigestChallengeSeq digests;
         HookResult result;
-        result = iter->second->challengeRequest(info, digests);
+        AuthHookPrx hookPrx = iter->second.first;
+        result = hookPrx->challengeRequest(info, digests);
         if (result.status == Failed)
         {
             lg(Error) << "SIP Authentication hook reported a failure: " << result.info;
diff --git a/src/SipSessionManagerApp.cpp b/src/SipSessionManagerApp.cpp
index 13ce54b..294dc1d 100644
--- a/src/SipSessionManagerApp.cpp
+++ b/src/SipSessionManagerApp.cpp
@@ -79,7 +79,7 @@ public:
     {
         HookId id;
         id.id = IceUtil::generateUUID();
-        mPJSipManager->addAuthHook(std::make_pair(id, hook));
+        mPJSipManager->addAuthHook(id, hook, requestTypes);
         return id;
     }
     

commit f0d27ad5ba9caa722ce22b36975d1d4be70be762
Author: Mark Michelson <mmichelson at digium.com>
Date:   Wed Feb 9 15:56:12 2011 -0600

    More progress.
    
    The extension checkAuth function is nearly complete. The problem is that
    apparently doing AKA authentication requires completely separate API
    calls into PJSIP than doing non-AKA authentication. For now, we're not
    touching the algorithm in the challenges returned from the hook.
    
    The next step is to adjust the members of PJSipModule and PJSipManager
    that add hook information to include the RequestTypeSequence.

diff --git a/src/PJSipSessionModule.cpp b/src/PJSipSessionModule.cpp
index a753539..a4df86a 100644
--- a/src/PJSipSessionModule.cpp
+++ b/src/PJSipSessionModule.cpp
@@ -462,6 +462,25 @@ void PJSipSessionModule::getURIParams(pjsip_uri *uri, ParamDict &params)
     }
 }
 
+static pj_status_t lookup_cred(pj_pool_t *pool, const pj_str_t *realm, const pj_str_t *acc_name, pjsip_cred_info *cred_info)
+{
+    //XXX I'm still not 100% sure I understand the purpose of this function. Apparently, when I call
+    //pjsip_srv_auth_verify(), it then will call into this function with a realm and account name, and
+    //I then fill in the cred_info so that pjsip can determine if the user has authenticated properly.
+    //Of course, it's not documented what I should return if things fail. It's also not documented
+    //whether I should dynamically allocate data in the cred_info or not. I mean, I'm given a pool, so...maybe?
+    
+    //Also, it would be super awesome if I were given ANYTHING from the dialog here so I might be able to
+    //pull mod_data from it and get information. Instead, I'm forced to store information in some sort of
+    //shared space, meaning potential resource contention. Lovely.
+
+    //Luckily all I'm focused on is adding the extension point into the code, the authentication itself
+    //is a separate task, so I can get to that when I get to that. For now I'll just be sure not to
+    //return PJ_SUCCESS so that it's clear that 
+
+    return !PJ_SUCCESS;
+}
+
 bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv)
 {
     //If there are no hooks, this is simple
@@ -473,12 +492,15 @@ bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv)
     RequestInfoPtr info(new RequestInfo);
 
     char buf[512];
+    size_t pos;
     pjsip_name_addr *from = (pjsip_name_addr *)rdata->msg_info.from->uri;
     info->fromName = std::string(pj_strbuf(&from->display), pj_strlen(&from->display));
 
     pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, from->uri, buf, sizeof(buf));
     std::string fromURI(buf, strlen(buf));
-    size_t pos = fromURI.find_first_of(';');
+    //pjsip_uri_print includes the URI parameters, but since we're presenting
+    //URI parameters in a separate dictionary, we need to strip those off.
+    pos = fromURI.find_first_of(';');
     info->fromURI = fromURI.substr(0, pos);
 
     if (pos != std::string::npos)
@@ -537,20 +559,35 @@ bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv)
             lg(Error) << "SIP Authentication hook reported a failure: " << result.info;
         }
         else if (result.status == Succeeded) {
-            if (!digests.empty())
+            if (digests.empty())
             {
-                pjsip_tx_data *tdata;
-                pjsip_auth_srv authServer;
-                pj_str_t realm;
-                pjsip_auth_srv_init(inv->pool, &authServer, &realm, NULL, 0);
-                pjsip_inv_end_session(inv, 401, NULL, &tdata);
-                pjsip_inv_send_msg(inv, tdata);
-                return true;
+                //Hook says not to challenge. Just return
+                return false;
             }
-            else
+
+            pjsip_tx_data *tdata;
+            pjsip_inv_end_session(inv, 401, NULL, &tdata);
+            for (DigestChallengeSeq::iterator digest = digests.begin(); digest != digests.end(); ++digest)
             {
-                return false;
+                pjsip_auth_srv authServer;
+                pj_str_t realm;
+                pj_cstr(&realm, (*digest)->realm.c_str());
+                pjsip_auth_srv_init(inv->pool, &authServer, &realm, lookup_cred, 0);
+                pj_str_t nonce, opaque, *noncePtr = NULL, *opaquePtr = NULL;
+                if (!(*digest)->opaque.empty())
+                {
+                    pj_cstr(&opaque, (*digest)->opaque.front().c_str());
+                    opaquePtr = &opaque;
+                }
+                if (!(*digest)->nonce.empty())
+                {
+                    pj_cstr(&nonce, (*digest)->nonce.front().c_str());
+                    noncePtr = &nonce;
+                }
+                pjsip_auth_srv_challenge(&authServer, NULL, noncePtr, opaquePtr, PJ_FALSE, tdata);
             }
+            pjsip_inv_send_msg(inv, tdata);
+            return true;
         }
     }
     return false;

commit 78914f7922bf4620086eef4a77bead6ff55cf79d
Author: Mark Michelson <mmichelson at digium.com>
Date:   Wed Feb 9 10:44:06 2011 -0600

    Put the URI parameters in a nice little dictionary for the hook.
    
    Next up comes getting the actual auth server portion set up properly.
    You can see where I sort of started on that a little bit, but it has
    not come to fruition.

diff --git a/src/PJSipSessionModule.cpp b/src/PJSipSessionModule.cpp
index 56690e8..a753539 100644
--- a/src/PJSipSessionModule.cpp
+++ b/src/PJSipSessionModule.cpp
@@ -425,6 +425,43 @@ pj_status_t PJSipSessionModule::unload()
     return PJ_SUCCESS;
 }
 
+void PJSipSessionModule::getURIParams(pjsip_uri *uri, ParamDict &params)
+{
+    pjsip_sip_uri *sipURI = (pjsip_sip_uri *) pjsip_uri_get_uri(uri);
+
+    if (pj_strlen(&sipURI->user_param) != 0)
+    {
+        params.insert(std::make_pair("user", std::string(pj_strbuf(&sipURI->user_param), pj_strlen(&sipURI->user_param))));
+    }
+    if (pj_strlen(&sipURI->method_param) != 0)
+    {
+        params.insert(std::make_pair("method", std::string(pj_strbuf(&sipURI->method_param), pj_strlen(&sipURI->method_param))));
+    }
+    if (pj_strlen(&sipURI->transport_param) != 0)
+    {
+        params.insert(std::make_pair("transport", std::string(pj_strbuf(&sipURI->transport_param), pj_strlen(&sipURI->transport_param))));
+    }
+    if (sipURI->ttl_param != -1)
+    {
+        std::stringstream stream;
+        stream << sipURI->ttl_param;
+        params.insert(std::make_pair("ttl", stream.str()));
+    }
+    //lr has no value associated with it. It either is there or it is not there. It is
+    //the redheaded stepchild of the SIP URI parameter world. I weep for it.
+    if (sipURI->lr_param != 0)
+    {
+        params.insert(std::make_pair("lr", std::string()));
+    }
+
+    for (pjsip_param *iter = sipURI->other_param.next; iter != &sipURI->other_param; iter = iter->next)
+    {
+        std::string name(pj_strbuf(&iter->name), pj_strlen(&iter->name));
+        std::string value(pj_strbuf(&iter->value), pj_strlen(&iter->value));
+        params.insert(std::make_pair(name, value));
+    }
+}
+
 bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv)
 {
     //If there are no hooks, this is simple
@@ -444,7 +481,10 @@ bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv)
     size_t pos = fromURI.find_first_of(';');
     info->fromURI = fromURI.substr(0, pos);
 
-    info->fromParams;
+    if (pos != std::string::npos)
+    {
+        getURIParams(from->uri, info->fromParams);
+    }
 
     pjsip_name_addr *to = (pjsip_name_addr *)rdata->msg_info.to->uri;
     info->toName = std::string(pj_strbuf(&to->display), pj_strlen(&to->display));
@@ -454,7 +494,10 @@ bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv)
     pos = toURI.find_first_of(';');
     info->toURI = toURI.substr(0, pos);
 
-    info->toParams;
+    if (pos != std::string::npos)
+    {
+        getURIParams(to->uri, info->toParams);
+    }
 
     pjsip_uri *rURI = (pjsip_uri *) pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
     pjsip_uri_print(PJSIP_URI_IN_REQ_URI, rURI, buf, sizeof(buf));
@@ -462,7 +505,10 @@ bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv)
     pos = rURIStr.find_first_of(';');
     info->requestURI = rURIStr.substr(0, pos);
 
-    info->requestURIParams;
+    if (pos != std::string::npos)
+    {
+        getURIParams(rURI, info->requestURIParams);
+    }
 
     info->IPAddr = std::string(pj_sockaddr_print((pj_sockaddr_t *) &rdata->pkt_info.src_addr, buf, rdata->pkt_info.src_addr_len, 0), rdata->pkt_info.src_addr_len);
     info->port = rdata->pkt_info.src_port;
@@ -494,6 +540,9 @@ bool PJSipSessionModule::checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv)
             if (!digests.empty())
             {
                 pjsip_tx_data *tdata;
+                pjsip_auth_srv authServer;
+                pj_str_t realm;
+                pjsip_auth_srv_init(inv->pool, &authServer, &realm, NULL, 0);
                 pjsip_inv_end_session(inv, 401, NULL, &tdata);
                 pjsip_inv_send_msg(inv, tdata);
                 return true;
diff --git a/src/PJSipSessionModule.h b/src/PJSipSessionModule.h
index 244ee60..7d0ac9b 100644
--- a/src/PJSipSessionModule.h
+++ b/src/PJSipSessionModule.h
@@ -87,6 +87,7 @@ private:
     void handleInviteResponse(pjsip_inv_session *inv, pjsip_rx_data *rdata, pjsip_dialog *dlg);
     void handleRefer(pjsip_inv_session *inv, pjsip_rx_data *rdata);
     bool checkAuth(pjsip_rx_data *rdata, pjsip_inv_session *inv);
+    void getURIParams(pjsip_uri *uri, AsteriskSCF::SIP::ExtensionPoint::V1::ParamDict &params);
     pjsip_inv_callback mInvCallback;
     pjsip_ua_init_param mUaParam;
     const std::string mName;

-----------------------------------------------------------------------


-- 
asterisk-scf/release/sip.git



More information about the asterisk-scf-commits mailing list