Index: /branches/release-38/default_templates/comment_response.mtml
===================================================================
--- /branches/release-38/default_templates/comment_response.mtml (revision 2191)
+++ /branches/release-38/default_templates/comment_response.mtml (revision 2393)
@@ -16,5 +16,5 @@
     <mt:ignore><!-- Error message when comment submission fails --></mt:ignore>
     <MTSetVar name="page_title" value="<__trans phrase="Comment Submission Error">">
-    <MTSetVarBlock name="message"><__trans phrase="Your comment submission failed for the following reasons: [_1]" params="<$MTErrorMessage$>"></MTSetVarBlock>
+    <MTSetVarBlock name="message"><p><__trans phrase="Your comment submission failed for the following reasons: [_1]" params="<$MTErrorMessage$>"></p></MTSetVarBlock>
 </MTIf>
 
Index: /branches/release-38/default_templates/javascript.mtml
===================================================================
--- /branches/release-38/default_templates/javascript.mtml (revision 2377)
+++ /branches/release-38/default_templates/javascript.mtml (revision 2393)
@@ -130,4 +130,15 @@
 <mt:Ignore>
 /***
+ * Called when an input field on the comment form receives focus.
+ */
+</mt:Ignore>
+function mtCommentFormOnFocus() {
+    // if CAPTCHA is enabled, this causes the captcha image to be
+    // displayed if it hasn't been already.
+    mtShowCaptcha();
+}
+
+<mt:Ignore>
+/***
  * Displays a captcha field for anonymous commenters.
  */
@@ -218,4 +229,5 @@
     if (u.profile) str += "profile:'" + mtEscapeJS(u.profile) + "';";
     if (u.userpic) str += "userpic:'" + mtEscapeJS(u.userpic) + "';";
+    if (u.sid) str += "sid:'" + mtEscapeJS(u.sid) + "';";
     str += "is_trusted:'" + (u.is_trusted ? "1" : "0") + "';";
     str += "is_author:'" + (u.is_author ? "1" : "0") + "';";
@@ -238,5 +250,5 @@
     var u = {};
     var m;
-    while (m = s.match(/^((name|url|email|is_authenticated|profile|userpic|is_trusted|is_author|is_banned|can_post|can_comment):'([^']+?)';?)/)) {
+    while (m = s.match(/^((name|url|email|is_authenticated|profile|userpic|sid|is_trusted|is_author|is_banned|can_post|can_comment):'([^']+?)';?)/)) {
         s = s.substring(m[1].length);
         if (m[2].match(/^(is|can)_/)) // boolean fields
@@ -286,4 +298,5 @@
  */
 </mt:Ignore>
+var mtFetchedUser = false;
 function mtFetchUser(cb) {
     if (!cb) cb = 'mtSetUser';
@@ -296,4 +309,5 @@
         // we aren't using AJAX for this, since we may have to request
         // from a different domain. JSONP to the rescue.
+        mtFetchedUser = true;
         var script = document.createElement('script');
         script.src = '<$mt:CGIPath$><$mt:CommentScript$>?__mode=session_js&blog_id=<$mt:BlogID$>&jsonp=' + cb;
@@ -337,7 +351,32 @@
         if (f.preview_button) f.preview_button.disabled = true;
         if (f.post) f.post.disabled = true;
+
+        var u = mtGetUser();
+        if ( !is_preview && ( u && u.is_authenticated ) ) {
+            // validate session; then submit
+            mtFetchedUser = false;
+            mtFetchUser('mtCommentSessionVerify');
+            return false;
+        }
+
         return true;
     }
     return false;
+}
+
+function mtCommentSessionVerify(app_user) {
+    var u = mtGetUser();
+    var f = document['comments_form'];
+    if ( u && app_user && app_user.sid && ( u.sid == app_user.sid ) ) {
+        f.submit();
+    } else {
+        alert('<__trans phrase="Your session has expired. Please sign in again to comment." escape="js">');
+        mtClearUser();
+        mtFireEvent('usersignin');
+<mt:IfRegistrationRequired>
+        mtShow('comments-form');
+        mtHide('comments-open-footer');
+</mt:IfRegistrationRequired>
+    }
 }
 
@@ -377,13 +416,13 @@
                 if (cf.bakecookie)
                     cf.bakecookie.checked = u.name || u.email;
+            } else {
+                if (u && u.sid && cf.sid)
+                    cf.sid.value = u.sid;
             }
-            if (cf.post.disabled) {
+            if (cf.post.disabled)
                 cf.post.disabled = false;
-                cf.post.value = '<__trans phrase="Submit">';
-            }
-            if (cf.preview_button.disabled) {
+            if (cf.preview_button.disabled)
                 cf.preview_button.disabled = false;
-                cf.preview_button.value = '<__trans phrase="Preview">';
-            }
+            mtRequestSubmitted = false;
         }
     }
@@ -478,5 +517,5 @@
     var reg_reqd = <mt:IfRegistrationRequired>true<mt:Else>false</mt:IfRegistrationRequired>;
 
-    var cf = document.comments_form;
+    var cf = document['comments_form'];
     if (!cf) return;
 
@@ -615,4 +654,5 @@
 </mt:Ignore>
 function mtClearUser() {
+    user = null;
     mtDeleteCookie(mtCookieName, mtCookiePath, mtCookieDomain,
         location.protocol == 'https:');
Index: /branches/release-38/default_templates/comments.mtml
===================================================================
--- /branches/release-38/default_templates/comments.mtml (revision 2376)
+++ /branches/release-38/default_templates/comments.mtml (revision 2393)
@@ -61,16 +61,17 @@
                 <input type="hidden" name="armor" value="1" />
                 <input type="hidden" name="preview" value="" />
+                <input type="hidden" name="sid" value="" />
                 <div id="comments-open-data">
                     <div id="comment-form-name">
                         <label for="comment-author"><__trans phrase="Name"></label>
-                        <input id="comment-author" name="author" size="30" value="" onfocus="mtShowCaptcha()" />
+                        <input id="comment-author" name="author" size="30" value="" onfocus="mtCommentFormOnFocus()" />
                     </div>
                     <div id="comment-form-email">
                         <label for="comment-email"><__trans phrase="Email Address"></label>
-                        <input id="comment-email" name="email" size="30" value="" onfocus="mtShowCaptcha()" />
+                        <input id="comment-email" name="email" size="30" value="" onfocus="mtCommentFormOnFocus()" />
                     </div>
                     <div id="comment-form-url">
                         <label for="comment-url"><__trans phrase="URL"></label>
-                        <input id="comment-url" name="url" size="30" value="" onfocus="mtShowCaptcha()" />
+                        <input id="comment-url" name="url" size="30" value="" onfocus="mtCommentFormOnFocus()" />
                     </div>
                     <div id="comment-form-remember-me">
@@ -86,5 +87,5 @@
                     <label for="comment-text"><__trans phrase="Comments">
                     <MTIfAllowCommentHTML><__trans phrase="(You may use HTML tags for style)"></MTIfAllowCommentHTML></label>
-                    <textarea id="comment-text" name="text" rows="15" cols="50" onfocus="mtShowCaptcha()"></textarea>
+                    <textarea id="comment-text" name="text" rows="15" cols="50" onfocus="mtCommentFormOnFocus()"></textarea>
                 </div>
                 <div id="comments-open-captcha"></div>
Index: /branches/release-38/lib/MT/App/Comments.pm
===================================================================
--- /branches/release-38/lib/MT/App/Comments.pm (revision 2377)
+++ /branches/release-38/lib/MT/App/Comments.pm (revision 2393)
@@ -114,15 +114,14 @@
     my $session_key;
 
-    if (my $blog_id = $q->param('blog_id')) {
-        if (my $blog = MT::Blog->load($blog_id)) {
-            my $auths = $blog->commenter_authenticators || '';
-            if ( $auths =~ /MovableType/ ) {
-                # First, check for a real MT user login. If one exists,
-                # return that as the commenter identity
-                my ($user, $first_time) = $app->SUPER::login();
-                if ( $user ) {
-                    my $sess = $app->session;
-                    return ( $sess->id, $user );
-                }
+    my $blog = $app->blog;
+    if ($blog) {
+        my $auths = $blog->commenter_authenticators || '';
+        if ( $auths =~ /MovableType/ ) {
+            # First, check for a real MT user login. If one exists,
+            # return that as the commenter identity
+            my ($user, $first_time) = $app->SUPER::login();
+            if ( $user ) {
+                my $sess = $app->session;
+                return ( $sess->id, $user );
             }
         }
@@ -149,5 +148,4 @@
       )
     {
-        $app->log("session is invalid; sess_obj = $sess_obj; key = $session_key; user_id = $user_id; user = $user");
         $app->_invalidate_commenter_session( \%cookies );
         return ( undef, undef );
@@ -858,4 +856,16 @@
         return $app->handle_error(
             $app->translate("Comment text is required.") );
+    }
+
+    # validate session parameter
+    if ( my $sid = $q->param('sid') ) {
+        my ( $session, $commenter ) = $app->_get_commenter_session();
+        if ( $session && $commenter && ( $session eq $sid ) ) {
+            # well, everything is okay
+        } else {
+            return $app->handle_error(
+                $app->translate("Your session has expired. Please sign in again to comment.")
+            );
+        }
     }
 
@@ -1446,5 +1456,4 @@
     if ( $blog_id && $blog ) {
         my ( $session, $commenter ) = $app->_get_commenter_session();
-use Data::Dumper;
         if ( $session && $commenter ) {
             my $blog_perms = $commenter->blog_perm($blog_id);
@@ -1467,4 +1476,5 @@
             my $can_post = ($blog_perms && $blog_perms->can_create_post) ? "1" : "0";
             $c = {
+                sid => $sessobj->id,
                 name => $commenter->nickname,
                 url => $commenter->url,
