Mail sync hack for IT-School intranet mail with normal mail server

IT-School (http://myit-school.net/) is a school management system in Hong Kong, which target for intranet mail, calender, account management, and so on. BTW, for the old version (around 2004) of IT-School, its mail system is split into 3 parts:

  • Intranet mail: Mail from and to account will fetch from MySQL DB directly, mail will NEVER deliver though normal email server router BUT save into MySQL DB as HTML format DIRECTLY. Support individual user and group of users.
  • Outgoing internet mail: Mail will send though sendmail.
  • Incoming internet mail: Mail will first received by sendmail as normal email format. User will not able to browse external mail UNLESS they press a "Check email" link from the GUI. Once they press this link, a small window will pop-up, inform user that IT-School will fetch and convert the real email as its own format, and save into its MySQL DB as intranet mail.

The send out email action preform 2 part (/usr/local/it-school/www/php/intra/savemail.php3):

  1. Handle all intranet email transaction.
                    //        send to other user
                    intramail($mail_id);
  2. Send internet email though MTA.
                    //              send e_mail
                    //        check privilege
                    if ($support_sending_email && checkprivilege($userid, 'send_email')) {
                            send_email($mail_id);
                    }

How complicated it is! In order to migrate the old system to normal MTA + eGroupWare, I did some trick in IT-School's source code for:

  1. Hack the send_email() function (/usr/local/it-school/www/php/intra/imapfunction.php3), search out all internal users and groups, generate a public email for them (if user's ~/.forward exists and contain valid forward address), and send out another copy though normal MTA:
    diff -urpN intra.20090216/imapfunction.php3 intra/imapfunction.php3
    --- intra.20090216/imapfunction.php3 2009-02-13 10:00:42.000000000 +0800
    +++ intra/imapfunction.php3 2009-02-16 09:52:34.000000000 +0800
    @@ -174,6 +174,93 @@ function send_email($mail_id) {
       get_email_list($unprocess_bpgroupid, $bcc_puser, $bcc_pemail);
       $pemail_source = array();

    +  // XXX: Check and forward intra email as internet email to mail2.
    +
    +  global $db_database;
    +  global $db_host;
    +  global $db_user;
    +  global $db_password;
    +  $db = new Mysql_DB_Sql($db_database, $db_host, $db_user, $db_password);
    +
    +  // Fetch loginid from userid, generate mail2. address, and append to internet_* as public mail.
    +  foreach (array('recipient', 'cc', 'bcc') as $target) {
    +    $_loginids = array();
    +    $_email_address_array = array();
    +    foreach (explode(',', $sourcemail['internet_' . $target]) as $address) {
    +      $_email_address_array[$address] = $address;
    +    }
    +
    +    // Grep all indiviual user IDs.
    +    if (count($sourcemail[$target]['user']) > 0) {
    +      $sql = "SELECT loginid FROM ituser WHERE userid IN (" . implode(', ', $sourcemail[$target]['user']) . ")";
    +      $db->query($sql);
    +      while ($db->next_record()) {
    +        $_loginids[] = $db->f('loginid');
    +      }
    +    }
    +
    +    // Grep all userid from group, then replace as loginid.
    +    if (count($sourcemail[$target]['group']) > 0) {
    +      $sql = "SELECT userid FROM user_usergroup WHERE groupid IN ('" . implode("', '", $sourcemail[$target]['group']) . "')";
    +      $db->query($sql);
    +      $_userid = array();
    +      while ($db->next_record()) {
    +        $_userid[] = $db->f('userid');
    +      }
    +
    +      $sql = "SELECT loginid FROM ituser WHERE userid IN (" . implode(', ', $_userid) . ")";
    +      $db->query($sql);
    +      while ($db->next_record()) {
    +        $_loginids[] = $db->f('loginid');
    +      }
    +    }
    +
    +    $_user = '[a-zA-Z0-9_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\']+';
    +    $_domain = '(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.?)+';
    +    $_ipv4 = '[0-9]{1,3}(\.[0-9]{1,3}){3}';
    +    $_ipv6 = '[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7}';
    +
    +    // If convert required, do so.
    +    if (count($_loginids) > 0) {
    +      foreach ($_loginids as $_loginid) {
    +        $path = '/home/' . $_loginid . '/.forward';
    +        if ($handle = @fopen($path, 'r')) {
    +          while (!feof($handle)) {
    +            $buffer = fgets($handle, 4096);
    +            $buffer = trim($buffer);
    +            if (preg_match("/^$_user@($_domain|(\[($_ipv4|$_ipv6)\]))$/", $buffer)) {
    +              $_email_address_array[$buffer] = $buffer;
    +            }
    +          }
    +          fclose($handle);
    +        }
    +      }
    +      $sourcemail['internet_' . $target] = implode(',', $_email_address_array);
    +    }
    +  }
    +
    +  // Print debug message within header.
    +  $array = array(
    +    'internet_recipient_orig' => $internet_recipient_orig,
    +    'internet_recipient_new' => $internet_recipient_new,
    +    'loginids' => $loginids,
    +    'sourcemail' => $sourcemail,
    +    'recipient_puser' => $recipient_puser,
    +    'recipient_pemail' => $recipient_pemail,
    +    'cc_puser' => $cc_puser,
    +    'cc_pemail' => $cc_pemail,
    +    'bcc_puser' => $bcc_puser,
    +    'bcc_pemail' => $bcc_pemail,
    +    'pemail_source' => $pemail_source,
    +  );
    +  ob_start();
    +  var_dump($array);
    +  $contents = ob_get_contents();
    +  ob_end_clean();
    +  print "\n\n<!-- $contents -->\n\n";
    +
    +  // XXX: Check and forward intra email as internet email to mail2.
    +
    //  join them
       $email_address_array = $process_userid;
    //  $email_address_array = array_merge($process_userid, $recipient_pemail, $cc_pemail, $bcc_pemail);
  2. Trigger "Check email" action though JS periodically (/usr/local/it-school/www/php/intra/index.php3):
    diff -urpN intra.20090216/index.php3 intra/index.php3
    --- intra.20090216/index.php3 2009-02-15 19:02:37.000000000 +0800
    +++ intra/index.php3 2009-02-16 09:49:22.000000000 +0800
    @@ -254,6 +254,13 @@ if ($can_check_email) {
       echo 'email = window.open("check_email.php?"+Math.random(), "CheckEmail","status=1,width=200,height=150");';
         echo 'email.focus();';
       echo '}';
    +
    +  // XXX: Auto open "Check email" window for every 5mins.
    +  echo 'var fm_timerFolderStatus;';
    +  // refresh after 5min.
    +  echo 'var refreshTimeOut = 300000;';
    +  echo 'fm_timerFolderStatus = window.setInterval("check_email()", refreshTimeOut);';
    +  // XXX: Auto open "Check email" window for every 5mins.
    }
    ?>
    </SCRIPT>
  3. Close the pop-up window though JS automatically (/usr/local/it-school/www/php/intra/check_email.php):
    diff -urpN intra.20090216/check_email.php intra/check_email.php
    --- intra.20090216/check_email.php 2009-02-15 20:43:03.000000000 +0800
    +++ intra/check_email.php 2009-02-16 09:49:36.000000000 +0800
    @@ -57,6 +57,11 @@ function refresh_parent() {
    function goBack() {
       window.close();
    }
    +
    +// XXX: Auto close confirm window after 1s.
    +fm_goBack = window.setInterval("goBack()", 1000);
    +// XXX: end Auto close confirm window after 1s.
    +
    //-->
    </SCRIPT>
    <BODY>

Now any IT-School email transaction will be cloned to the new system :D

P.S. Thanks for the experience of hacking Drupal, this hack become much simple: it only cost for 2 working days. Some checking are cloned from Drupal driectly, e.g. email address validation, pipe console output as plain text, and even the skill for patching source. Well, skills will always become weapon, and become useful whenever you need for it :D


Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <h1> <h2> <h3> <h4> <h5> <h6> <em> <strong> <code> <del> <blockquote> <q> <sub> <p> <br> <ul> <ol> <li> <dl> <dt> <dd> <a> <b> <u> <i> <sup> <acronym> <pre> <img>
  • Lines and paragraphs break automatically.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.
  • Images can be added to this post.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.