diff --git a/inc/actions.php b/inc/actions.php
index f6c00f14a6e70313cc9e7fcd19d4b6fc01773c9c..5818993c9d1bdaf8724d62704d547b47716b2643 100644
--- a/inc/actions.php
+++ b/inc/actions.php
@@ -34,6 +34,10 @@ function act_dispatch(){
   //login stuff
   if(in_array($ACT,array('login','logout')))
     $ACT = act_auth($ACT);
+
+  //check if user is asking to track a page
+  if($ACT == 'track' || $ACT == 'ignore')
+    $ACT = act_track($ACT);
  
   //check permissions
   $ACT = act_permcheck($ACT);
@@ -102,7 +106,7 @@ function act_clean($act){
 
   if(array_search($act,array('login','logout','register','save','edit',
                              'preview','search','show','check','index','revisions',
-                             'diff','recent','backlink','admin',)) === false
+                             'diff','recent','backlink','admin','track','ignore',)) === false
      && substr($act,0,7) != 'export_' ) {
     msg('Unknown command: '.htmlspecialchars($act),-1);
     return 'show';
@@ -268,5 +272,37 @@ function act_export($act){
   return 'show';
 }
 
+/**
+ * Handle 'track', 'ignore'
+ *
+ * @author Steven Danz <steven-danz@kc.rr.com>
+ */
+function act_track($act){
+  global $ID;
+  global $INFO;
+
+  $tracking = tracking($ID, $_SERVER['REMOTE_USER']);
+  $file=wikiMN($ID);
+  if ($act=='track' && !$tracking){
+    if ($INFO['userinfo']['mail']){
+      if (io_appendFile($file,$_SERVER['REMOTE_USER']."\n")) {
+        msg('Added '.$INFO['userinfo']['name'].' to tracking list for '.$ID,0);
+      } else {
+        msg('Error adding '.$INFO['userinfo']['name'].' to tracking list for '.$ID,0);
+      }
+    } else {
+      msg('There is no address associated with your login, you cannot be added to the tracking list',-1);
+    }
+  } elseif ($act=='ignore' && $tracking){
+    if (io_deleteFromFile($file,$_SERVER['REMOTE_USER']."\n")) {
+      msg('Removed '.$INFO['userinfo']['name'].' from the tracking list for '.$ID,0);
+    } else {
+      msg('Error removing '.$INFO['userinfo']['name'].' to tracking list for '.$ID,0);
+    }
+  }
+
+  return 'show';
+}
+
 
 //Setup VIM: ex: et ts=2 enc=utf-8 :
diff --git a/inc/common.php b/inc/common.php
index ad4de9ffd47ee4631f5cc31866c364709f75507e..676787e7399d2a0123dc5b5cc65629ef57bb5f74 100644
--- a/inc/common.php
+++ b/inc/common.php
@@ -621,6 +621,10 @@ function saveWikiText($id,$text,$summary){
   if (empty($text)){
     // remove empty files
     @unlink($file);
+    $mfile=wikiMN($id);
+    if (file_exists($mfile)) {
+      @unlink($mfile);
+    }
     $del = true;
     //autoset summary on deletion
     if(empty($summary)) $summary = $lang['deleted'];
@@ -673,7 +677,15 @@ function notify($id,$rev="",$summary=""){
   global $lang;
   global $conf;
   $hdrs ='';
-  if(empty($conf['notify'])) return; //notify enabled?
+
+  $mlist = array();
+
+  $file=wikiMN($id);
+  if (file_exists($file)) {
+    $mlist = file($file);
+  }
+
+  if(empty($conf['notify']) && count($mlist) == 0) return; //notify enabled?
   
   $text = rawLocale('mailtext');
   $text = str_replace('@DATE@',date($conf['dformat']),$text);
@@ -701,7 +713,25 @@ function notify($id,$rev="",$summary=""){
   $text = str_replace('@DIFF@',$diff,$text);
   $subject = '['.$conf['title'].'] '.$subject;
 
-  mail_send($conf['notify'],$subject,$text,$conf['mailfrom']);
+  $bcc = '';
+  if(count($mlist) > 0) {
+    foreach ($mlist as $who) {
+      $who = rtrim($who);
+      $info = auth_getUserData($who);
+      $level = auth_aclcheck($id,$who,$info['grps']);
+      if ($level >= AUTH_READ) {
+        if (strcasecmp($info['mail'],$conf['notify']) != 0) {
+          if (empty($bcc)) {
+            $bcc = $info['mail'];
+          } else {
+            $bcc = "$bcc,".$info['mail'];
+          }
+        }
+      }
+    }
+  }
+
+  mail_send($conf['notify'],$subject,$text,$conf['mailfrom'],'',$bcc);
 }
 
 /**
@@ -891,5 +921,23 @@ function check(){
   }
 }
 
+/**
+ * Let us know if a user is tracking a page
+ *
+ * @author Steven Danz <steven-danz@kc.rr.com>
+ */
+function tracking($id,$uid){
+  $file=wikiMN($id);
+  if (file_exists($file)) {
+    $mlist = file($file);
+    foreach ($mlist as $who) {
+      $who = rtrim($who);
+      if ($who==$uid) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
 
 //Setup VIM: ex: et ts=2 enc=utf-8 :
diff --git a/inc/io.php b/inc/io.php
index 46d2185610e8a937dbd02be8a98fc659a419d201..922d2af028e0e10713458413c86f4a271b3be90e 100644
--- a/inc/io.php
+++ b/inc/io.php
@@ -79,6 +79,88 @@ function io_saveFile($file,$content){
   return true;
 }
 
+/**
+ * Appends $content to $file.
+ *
+ * Uses gzip if extension is .gz
+ *
+ * @author Steven Danz <steven-danz@kc.rr.com>
+ * @return bool true on success
+ */
+function io_appendFile($file,$content){
+  io_makeFileDir($file);
+  io_lock($file);
+  if(substr($file,-3) == '.gz'){
+    $fh = @gzopen($file,'ab9');
+    if(!$fh){
+      msg("Appending to $file failed",-1);
+      return false;
+    }
+    gzwrite($fh, $content);
+    gzclose($fh);
+  }else{
+    $fh = @fopen($file,'ab');
+    if(!$fh){
+      msg("Appending to $file failed",-1);
+      return false;
+    }
+    fwrite($fh, $content);
+    fclose($fh);
+  }
+  io_unlock($file);
+  return true;
+}
+
+/**
+ * Delete exact match for $content from $file.
+ *
+ * Uses gzip if extension is .gz
+ *
+ * @author Steven Danz <steven-danz@kc.rr.com>
+ * @return bool true on success
+ */
+function io_deleteFromFile($file,$remove){
+  if (@file_exists($file)) {
+    io_lock($file);
+    $content = '';
+    if(substr($file,-3) == '.gz'){
+      $mlist = gzfile($file);
+    }else{
+      $mlist = file($file);
+    }
+    foreach ($mlist as $entry) {
+      if ($entry != $remove) {
+        $content = $content . $entry;
+      }
+    }
+
+    if (!empty($content)) {
+      if(substr($file,-3) == '.gz'){
+        $fh = @gzopen($file,'wb9');
+        if(!$fh){
+          msg("Removing content from $file failed",-1);
+          return false;
+        }
+        gzwrite($fh, $content);
+        gzclose($fh);
+      }else{
+        $fh = @fopen($file,'wb');
+        if(!$fh){
+          msg("Removing content from $file failed",-1);
+          return false;
+        }
+        fwrite($fh, $content);
+        fclose($fh);
+      }
+    } else {
+      @unlink($file);
+    }
+
+    io_unlock($file);
+  }
+  return true;
+}
+
 /**
  * Tries to lock a file
  *
diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php
index fcc4ed696eb214e5d7cf2540974800389ea29104..f80c484aaeb4b294ff52aa4418c74fdd77fc2da6 100644
--- a/inc/lang/en/lang.php
+++ b/inc/lang/en/lang.php
@@ -32,6 +32,8 @@ $lang['btn_update'] = 'Update';
 $lang['btn_delete'] = 'Delete';
 $lang['btn_back']   = 'Back';
 $lang['btn_backtomedia'] = 'Back to Mediafile Selection';
+$lang['btn_track']  = 'Email Changes';
+$lang['btn_ignore'] = 'End Changes Email';
 
 $lang['loggedinas'] = 'Logged in as';
 $lang['user']       = 'Username';
diff --git a/inc/pageutils.php b/inc/pageutils.php
index 8477792e0447ef7fcfc850d14ecd435e0218305e..b04f6e5962080ab30ab0b033b0bda6752ef88f2f 100644
--- a/inc/pageutils.php
+++ b/inc/pageutils.php
@@ -130,6 +130,21 @@ function wikiFN($id,$rev=''){
   return $fn;
 }
 
+/**
+ * returns the full path to the mailist specified by ID
+ *
+ * The filename is URL encoded to protect Unicode chars
+ *
+ * @author Steven Danz <steven-danz@kc.rr.com>
+ */
+function wikiMN($id){
+  global $conf;
+  $id = cleanID($id);
+  $id = str_replace(':','/',$id);
+  $fn = $conf['metadir'].'/'.utf8_encodeFN($id).'.mlist';
+  return $fn;
+}
+
 /**
  * returns the full path to the mediafile specified by ID
  *
diff --git a/inc/template.php b/inc/template.php
index 4f6511e97a1666a6ed9f67fc93c5366e626731bb..9baf19348cd52f7227890ad5df55f8c9e8616ee6 100644
--- a/inc/template.php
+++ b/inc/template.php
@@ -118,6 +118,12 @@ function tpl_content(){
     case 'admin':
       tpl_admin();
       break;
+    case 'track':
+      html_track();
+      break;
+    case 'ignore':
+      html_ignore();
+      break;
     default:
 			msg("Failed to handle command: ".hsc($ACT),-1); 
   }
@@ -292,6 +298,7 @@ function tpl_getparent($ID){
  * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
  */
 function tpl_button($type){
+  global $ACT;
   global $ID;
   global $NS;
   global $INFO;
@@ -333,6 +340,17 @@ function tpl_button($type){
       break;
     case 'backtomedia':
       print html_backtomedia_button(array('ns' => $NS),'b');
+      break;
+    case 'track':
+      if($conf['useacl'] && $ACT == 'show'){
+        if($_SERVER['REMOTE_USER']){
+          if(tracking($ID,$_SERVER['REMOTE_USER'])){
+            print html_btn('ignore',$ID,'',array('do' => 'ignore',));
+          } else {
+            print html_btn('track',$ID,'',array('do' => 'track',));
+          }
+        }
+      }
       break;
 		default:
 			print '[unknown button type]';
@@ -420,6 +438,17 @@ function tpl_actionlink($type,$pre='',$suf=''){
       if($INFO['perm'] == AUTH_ADMIN)
         tpl_link(wl($ID,'do=admin'),$pre.$lang['btn_admin'].$suf,'class="action"');
       break;
+   case 'track':
+      if($conf['useacl'] && $ACT == 'show'){
+        if($_SERVER['REMOTE_USER']){
+          if(tracking($ID,$_SERVER['REMOTE_USER'])){
+            tpl_link(wl($ID,'do=ignore'),$pre.$lang['btn_ignore'].$suf,'class="action"');
+          } else {
+            tpl_link(wl($ID,'do=track'),$pre.$lang['btn_track'].$suf,'class="action"');
+          }
+        }
+      }
+      break;
     default:
       print '[unknown link type]';
   }
diff --git a/lib/tpl/default/main.php b/lib/tpl/default/main.php
index 7d3c1c46f774046e17515a40597feca2f984ffbc..fe1188efb4ebbfdd5b22120281817a53a74ab35c 100644
--- a/lib/tpl/default/main.php
+++ b/lib/tpl/default/main.php
@@ -116,6 +116,7 @@
       <div class="bar-right" id="bar_bottomright">
         <?php tpl_button('admin')?>
         <?php tpl_button('login')?>
+        <?php tpl_button('track')?>
         <?php tpl_button('index')?>
         <?php tpl_button('top')?>&nbsp;
       </div>