View file gmail-mobile-0.11/libgmailer.php

File size: 38.35Kb
<?php

   /**
      references:
         - "GMail RSS feed in PHP" by thimal
         - "GMail as an online backup system" by Ilia Alshanetsky
         - "Gmail Agent API"  by Johnvey Hwang
         
      homepages:
         http://gmail-lite.sourceforge.net/
         http://sourceforge.net/projects/gmail-lite/
   **/
   
   /** library libgmailer **/

   /** some constants **/
   define("GM_LNK_GMAIL", "https://gmail.google.com/gmail");
   define("GM_LNK_LOGIN", "https://www.google.com/accounts/ServiceLoginBoxAuth");
   define("GM_LNK_LOGOUT", "https://gmail.google.com/gmail?logout");
   define("GM_LNK_REFER", "https://www.google.com/accounts/ServiceLoginBox?service=mail&continue=https%3A%2F%2Fgmail.google.com%2Fgmail");
   define("GM_LNK_CONTACT", "https://gmail.google.com/gmail?view=page&name=address");
   define("GM_LNK_ATTACHMENT", "https://gmail.google.com/gmail?view=att&disp=att");
   define("GM_USER_AGENT", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4b) Gecko/20040612 Mozilla Firebird/0.9");
   define("GM_VER", "0.6.4");
   
   define("GM_COOKIE_KEY",       "LIBGMAILER");
   define("GM_USE_COOKIE",       0x001);
   define("GM_USE_PHPSESSION",   0x002);
   
   define("GM_STANDARD",      0x001);
   define("GM_LABEL",         0x002);
   define("GM_CONVERSATION",  0x004);
   define("GM_QUERY",         0x008);
   define("GM_CONTACT",       0x010);
   
   define("GM_ACT_APPLYLABEL",   1);
   define("GM_ACT_REMOVELABEL",  2);
   define("GM_ACT_STAR",         3);
   define("GM_ACT_UNSTAR",       4);
   define("GM_ACT_SPAM",         5);
   define("GM_ACT_UNSPAM",       6);
   define("GM_ACT_READ",         7);
   define("GM_ACT_UNREAD",       8);
   define("GM_ACT_TRASH",        9);
   define("GM_ACT_DELFOREVER",   10);
   define("GM_ACT_ARCHIVE",      11);
   define("GM_ACT_INBOX",        12);
   define("GM_ACT_UNTRASH",      13);
   
   /** for debugger **/
   define(D_FILE, "debug.php");   // the debugging data file is in PHP for security reason
   define(D_ON,   0);
   
   
   class GMailer {

      var $cookie_str;
      var $login;
      var $pwd;
      var $raw;            // raw packets
      var $contact_raw;    // raw packets for address book
      var $timezone;
      var $created;
      var $use_session;
      /** proxy-related **/
      var $proxy_host;
      var $proxy_auth;      
      
      
      /** methods list *************************
         setLoginInfo(login, pass, timezone)
         connect()
         connectNoCookie()
         isConnected()
         fetch(query)
         fetchBox(type, box, position)
         fetchContact()
         getAttachmentsOf(Snapshot->conversation, path_to_store_files)
         getAttachment(attachment_id, message_containing, filename)
         send(to, subject, body, cc, bcc, message_replying, thread_replying, attached_files)
         getCookieFromBrowser()
         saveCookieToBrowser()
         removeCookieFromBrowser()
         disconnect()
         dump(query)
         getSnapshot(type)
         getStandardBox()
      ******************************************/
      
      function GMailer() {
         // GMailer needs "curl" extension to work
         Debugger::say("Constructing GMailer...");
         $this->created = true;
         if (!extension_loaded('curl')) {
            if (!dl('php_curl.dll') && !dl('curl.so')) {
               Debugger::say("Constructing failed: unable to load curl extension.");
               $this->created = false;
            }
         }
         if (!function_exists("curl_setopt")) {         
            Debugger::say("Constructing failed: No curl.");
            $this->created = false;
         }
         $this->proxy_host = "";
         $this->proxy_auth = "";
         $this->use_session = 2;
         Debugger::say("Constructing completed.");
      }
      
      function setLoginInfo($my_login, $my_pwd, $my_tz) {
         $this->login = $my_login;
         $this->pwd = $my_pwd;
         $this->timezone = strval($my_tz*-60);
         Debugger::say("LoginInfo set.");
      }
      
      function setProxy($host, $username, $pwd) {
         $this->proxy_host = $host;
         if (strlen($username) > 0 || strlen($pwd) > 0) {
            $this->proxy_auth = $username.":".$pwd;
         }
      }
      
      function setSessionMethod($met) {
         if ($met & GM_USE_PHPSESSION) {
            if (!extension_loaded('session')) {
               if (!dl('php_session.dll') && !dl('session.so')) {
                  Debugger::say("Session: unable to load PHP session extension.");
                  $this->setSessionMethod(GM_USE_COOKIE | !GM_USE_PHPSESSION);  // forced to use custom cookie
                  return;
               }
            }
            if (!($met & GM_USE_COOKIE)) {
               @ini_set("session.use_cookies",   0);
               @ini_set("session.use_trans_sid", 1);               
               Debugger::say("Session: do not using cookie.");
            } else {
               @ini_set("session.use_cookies",   1);
               @ini_set("session.use_trans_sid", 0);
               Debugger::say("Session: using cookie.");               
            }
            @ini_set("session.name", "sess");
            session_start();
            Debugger::say("Session: using PHP session.");
            $this->use_session = true;         
         } else {
            Debugger::say("Session: using cookie.");
            $this->use_session = false;
         }
      }
        
      
      function connectNoCookie() {
         Debugger::say("Start connecting without cookie...");
         
         $postdata = "service=mail&Email=".urlencode($this->login)."&Passwd=".urlencode($this->pwd)."&null=Sign%20in&continue=https%3A%2F%2Fgmail.google.com%2Fgmail";
         
         $c = curl_init();
         curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
         curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
         curl_setopt($c, CURLOPT_URL, GM_LNK_LOGIN);
         curl_setopt($c, CURLOPT_SSL_VERIFYHOST,  2);
         curl_setopt($c, CURLOPT_USERAGENT, GM_USER_AGENT);
         $this->CURL_PROXY($c);
         curl_setopt($c, CURLOPT_POST, 1);
         curl_setopt($c, CURLOPT_HEADER, 1);
         curl_setopt($c, CURLOPT_POSTFIELDS, $postdata);
         curl_setopt($c, CURLOPT_SSL_VERIFYPEER, FALSE);
         curl_setopt($c, CURLOPT_REFERER, GM_LNK_REFER);
         $result = curl_exec($c);
         curl_close($c);
         
         if (strpos($result, "errormsg") > 0) {
            $this->cookie_str = "";
            Debugger::say("Connect failed: ".$result);
            return 0;
         }
         
         /** from here we have to perform "cookie-handshaking"... **/
         Debugger::say("Start cookie-handshaking...");
         
         $cookies = GMailer::get_cookies($result);
         Debugger::say("1st phase cookie obtained: ".$cookie);
         // js forward path
         $a = strpos($result, "top.location = \"");
         $b = strpos($result, ";", $a);
         $forward = substr($result, $a+16, $b-($a+16)-1);
         
         Debugger::say("Redirecting: ".$forward);
            
         $c = curl_init();
         curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
         curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
         curl_setopt($c, CURLOPT_URL, "https://gmail.google.com/accounts/".$forward);
         curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
         curl_setopt($c, CURLOPT_USERAGENT, GM_USER_AGENT);
         curl_setopt($c, CURLOPT_HEADER, 1);
         $this->CURL_PROXY($c);
         curl_setopt($c, CURLOPT_COOKIE, $cookies);
         curl_setopt($c, CURLOPT_SSL_VERIFYPEER, FALSE);
         curl_setopt($c, CURLOPT_REFERER, GM_LNK_REFER);
         $ret = curl_exec($c);
         curl_close($c); 
         
         $data = GMailer::get_cookies($ret);
         $d = ($data)? $data.$gv : $cookies;
         $d = $d.";TZ=".$this->timezone;
         
         Debugger::say("2nd phase cookie obtained: ".$d);
                
         $this->cookie_str = $d;  // the cookie string obtained from gmail
         
         Debugger::say("Finished connecting without cookie.");
         return 1;
      }     
      
      function connect() {
         if ($this->use_session == 2)
            $this->setSessionMethod(GM_USE_COOKIE | GM_USE_PHPSESSION);   // by default
         
         if (!$this->getSessionFromBrowser()) {         
            return $this->connectNoCookie() && $this->saveSessionToBrowser();
         } else {
            Debugger::say("Connect completed by getting cookie from browser.");
            return 1;
         }
      }
      
      function isConnected() {
         return (strlen($this->cookie_str) > 0);
      }
      
      function fetch($query) {
         if ($this->isConnected() == true) {
            Debugger::say("Start fetching query: ".$query);
            $query .= "&zv=".round(rand(0,1999)*2147483.648);   // to fool proxy
            $c = curl_init();
            curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
            curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);   
            curl_setopt($c, CURLOPT_URL, GM_LNK_GMAIL."?".$query);
            curl_setopt($c, CURLOPT_COOKIE, $this->cookie_str);
            $this->CURL_PROXY($c);
            curl_setopt($c, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($c, CURLOPT_USERAGENT, GM_USER_AGENT);
            curl_setopt($c, CURLOPT_REFERER, GM_LNK_REFER);
            $inbox = curl_exec($c);
            curl_close($c);
            
            $inbox = str_replace("\n", "", $inbox);
            $inbox = str_replace("D([", "\nD([", $inbox);
            $inbox = str_replace("]);", "]);\n", $inbox);
            
            $regexp = "|D\(\[(.*)\]\);|U";   
            preg_match_all($regexp, $inbox, $matches, PREG_SET_ORDER); 
            $packets = array();
         	for ($i = 0; $i < count($matches); $i++) {
         	   $off = 0;
         	   $tmp = GMailer::parse_data_packet("[".$matches[$i][1]."]", $off);
         	   if (array_key_exists($tmp[0], $packets) || ($tmp[0]=="mi"||$tmp[0]=="mb")) {
         	      if ($tmp[0]=="t"||$tmp[0]=="ts")
         	         $packets[$tmp[0]] = array_merge($packets[$tmp[0]], array_slice($tmp, 1));
         	      if ($tmp[0]=="mi"||$tmp[0]=="mb") {
         	         if (array_key_exists("mg", $packets))
         	            array_push($packets["mg"],$tmp);
         	         else
         	            $packets["mg"] = array($tmp);
         	      }	         
         	   } else {
         	      $packets[$tmp[0]] = $tmp;
         	   }
         	}
         	$this->raw = $packets;
         	Debugger::say("Fetch completed.");
         	return 1;
         
         } else {   // not logged in yet            
            Debugger::say("Fetch failed: not connected.");
            return 0;
            
         }
      }
      
      function fetchBox($type, $box, $pos) {
         if ($this->isConnected() == true) {
            switch ($type) {
               case GM_STANDARD:
                  $q = "search=".$box."&view=tl&start=".$pos;
                  break;
               case GM_LABEL:
                  $q = "search=cat&cat=".$box."&view=tl&start=".$pos;
                  break;
               case GM_CONVERSATION:
                  $q = "search=inbox&view=cv";
                  if (is_array($box)) {
                     $q .= "&th=".$box[0];
                     for ($i = 1; $i < count($box); $i++)
                        $q .= "&msgs=".$box[$i];
                  } else {
                     $q .= "&th=".$box;
                  }
                  break;
               case GM_QUERY:
                  $q = "search=query&q=".urlencode($box)."&view=tl&start=".$pos;
                  break;
               default:
                  $q = "search=inbox&view=tl&start=0&init=1";
                  break;
            }
            $this->fetch($q);
            return 1;
         } else {
            return 0;
         }
      }
      
      
      function fetchContact() {
         if ($this->isConnected() == true) {
            Debugger::say("Fetching contact list...");            
            $c = curl_init();
            curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
            curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);   
            curl_setopt($c, CURLOPT_URL, GM_LNK_CONTACT);
            $this->CURL_PROXY($c);
            curl_setopt($c, CURLOPT_COOKIE, $this->cookie_str);
            curl_setopt($c, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($c, CURLOPT_USERAGENT, GM_USER_AGENT);
            curl_setopt($c, CURLOPT_REFERER, GM_LNK_REFER);
            $inbox = curl_exec($c);
            curl_close($c);
            
            $inbox = str_replace("\n", "", $inbox);
            $inbox = str_replace("\r", "", $inbox);
            
            $regexp = "|\[\[(.*)\]\];|U";   
            preg_match_all($regexp, $inbox, $matches, PREG_SET_ORDER); 
            $packets = array();
      	   $off = 0;
      	   $tmp = GMailer::parse_data_packet("[[".$matches[0][1]."]]", $off);
      	   $packets = array("co"=>$tmp);
         	$this->contact_raw = $packets;
         	
         	Debugger::say("Fetched entried: ".count($this->contact_raw));
         	Debugger::say("Completed fetching contact list.");         	
         	return 1;
         
         } else {   // not logged in yet
            
            Debugger::say("Failed to fetch contact list: not connected.");
            return 0;
            
         }
      }
      
      function getAttachmentsOf($convs, $path) {
         if ($this->isConnected() == true) {
            if (!is_array($convs)) {
               $convs = array($convs);  // array wrapper
            }
            $final = array();
            foreach ($convs as $v) {
               if (count($v["attachment"]) > 0) {
                  foreach ($v["attachment"] as $vv) {
                     $f = $path."/".$vv["filename"];
                     while (file_exists($f)) {
                        $f = $path."/".$vv["filename"].".".round(rand(0,1999));
                     }
                     if ($this->getAttachment($vv["id"],$v["id"],$f)) {
                        array_push($final, $f);
                     }
                  }
               }
            }
            return $final;
         } else {
            return 0;
         }
      }                       
      
      function getAttachment($attid, $msgid, $filename) {
         if ($this->isConnected() == true) {
            Debugger::say("Start getting attachment...");
            
            $query = "&attid=".urlencode($attid)."&th=".urlencode($msgid);               
            $fp = fopen($filename, "wb");
            if ($fp) {
               $c = curl_init();
               curl_setopt($c, CURLOPT_FILE, $fp);
               curl_setopt($c, CURLOPT_COOKIE, $this->cookie_str);
               curl_setopt($c, CURLOPT_URL, GM_LNK_ATTACHMENT.$query);
               curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
               $this->CURL_PROXY($c);
               curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);   
               curl_setopt($c, CURLOPT_SSL_VERIFYPEER, FALSE);
               curl_setopt($c, CURLOPT_USERAGENT, GM_USER_AGENT);
               curl_setopt($c, CURLOPT_REFERER, GM_LNK_REFER);
               curl_exec($c);
               curl_close($c);
               fclose($fp);
            } else {
               Debugger::say("Failed to get attachment: cannot fopen the file.");
               return 0;
            }
            Debugger::say("Completed getting attachment.");
            return 1;
         } else {
            Debugger::say("Failed to get attachment: not connected.");
            return 0;
         }
      }  
      
            
      function dump($query) {
         if ($this->isConnected() == true) {
            Debugger::say("Dumping...");
            $query .= "&zv=".round(rand(0,1999)*2147483.648);   // to fool proxy
            $c = curl_init();
            curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
            curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);   
            curl_setopt($c, CURLOPT_URL, GM_LNK_GMAIL."?".$query);
            curl_setopt($c, CURLOPT_COOKIE, $this->cookie_str);
            $this->CURL_PROXY($c);
            curl_setopt($c, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($c, CURLOPT_USERAGENT, GM_USER_AGENT);
            curl_setopt($c, CURLOPT_REFERER, GM_LNK_REFER);
            $page = curl_exec($c);
            curl_close($c);
            return $page;            
         	Debugger::say("Finished dumping ".strlen($page)." bytes.");         
         } else {   // not logged in yet
            
            Debugger::say("Failed to dump: not connected.");
            return 0;
            
         }
      }
      
      
      function send($to, $subj, $body, $cc, $bcc, $mid, $tid, $files) {
         if ($this->isConnected()) {         
            Debugger::say("Starting to send mail...");
            
            $postdata = array();
            $postdata["view"] = "sm";
            $postdata["msgbody"] = stripslashes($body);
            $postdata["to"] = stripslashes($to);
            $postdata["subject"] = stripslashes($subj);
            $postdata["cc"] = stripslashes($cc);
            $postdata["bcc"] = stripslashes($bcc);
            $postdata["rm"] = $mid;
            $postdata["th"] = $tid;
            $postdata["draft"] = "";
            $postdata["cmid"] = 1;            
            
            //echo $this->cookie_str;            
            $cc = split(";", $this->cookie_str);
            foreach ($cc as $cc_part) {
               //echo "***".$cc_part."****<br>";            
               $cc_parts = split("=", $cc_part);
               //echo "***".$cc_parts[0]."******".$cc_parts[1]."****<br>";
               if (trim($cc_parts[0]) == "GMAIL_AT") {
                  $postdata["at"] = $cc_parts[1];
                  break;
               }
            }

            if (is_array($files)) {
               for ($i = 0; $i < count($files); $i++) {
                  $postdata["file".$i] = "@".realpath($files[$i]);
               }
            }
            //echo $postdata;
            set_time_limit(150);
            $c = curl_init();
            curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
            curl_setopt($c, CURLOPT_URL, "https://gmail.google.com/gmail");
            curl_setopt($c, CURLOPT_SSL_VERIFYHOST,  2);
            curl_setopt($c, CURLOPT_USERAGENT, GM_USER_AGENT);
            curl_setopt($c, CURLOPT_COOKIE, $this->cookie_str);            
            curl_setopt($c, CURLOPT_POST, 1);
            $this->CURL_PROXY($c);
            curl_setopt($c, CURLOPT_HEADER, 1);
            curl_setopt($c, CURLOPT_POSTFIELDS, $postdata);
            curl_setopt($c, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($c, CURLOPT_REFERER, GM_LNK_REFER);
            $result = curl_exec($c);
            curl_close($c);
            
            Debugger::say("Finished sending email.");
            return 1;
         } else {
            Debugger::say("Failed to send email: not connected.");
            return 0;
         }
      }
      
      
      function performAction($act, $id, $para) {
         if ($this->isConnected()) {
            Debugger::say("Start performing action...");
            
            if ($act == GM_ACT_DELFOREVER)
               $this->performAction(GM_ACT_TRASH, $id, 0);  // trash it before
            
            $postdata = "act=";
            
            $action_codes = array("ib", "ac_", "rc_", "st", "xst", "sp", "us", "rd", "ur", "tr", "dl", "rc_^i", "ib", "ib");
            $postdata .= isset($action_codes[$act]) ? $action_codes[$act] : $action_codes[GM_ACT_INBOX];
            if ($act == GM_ACT_APPLYLABEL || $act == GM_ACT_REMOVELABEL) {
               $postdata .= $para;
            }
            $cc = split(";", $this->cookie_str);
            foreach ($cc as $cc_part) {
               $cc_parts = split("=", $cc_part);
               if (trim($cc_parts[0]) == "GMAIL_AT") {
                  $postdata .= "&at=".$cc_parts[1];
                  break;
               }
            }
            
            if (is_array($id)) {
               foreach ($id as $t) {
                  $postdata .= "&t=".$t;
               }
            } else {
               $postdata .= "&t=".$id;
            }
            $postdata .= "&vp=";
            
            if ($act == GM_ACT_UNTRASH || $act == GM_ACT_DELFOREVER)
               $link = "https://gmail.google.com/gmail?search=trash&view=tl&start=0";
            else
               $link = "https://gmail.google.com/gmail?search=query&q=&view=tl&start=0";

            $c = curl_init();
            curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
            curl_setopt($c, CURLOPT_URL, $link);
            curl_setopt($c, CURLOPT_SSL_VERIFYHOST,  2);
            curl_setopt($c, CURLOPT_USERAGENT, GM_USER_AGENT);
            curl_setopt($c, CURLOPT_COOKIE, $this->cookie_str);            
            $this->CURL_PROXY($c);
            curl_setopt($c, CURLOPT_POST, 1);
            curl_setopt($c, CURLOPT_HEADER, 1);
            curl_setopt($c, CURLOPT_POSTFIELDS, $postdata);
            curl_setopt($c, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($c, CURLOPT_REFERER, GM_LNK_REFER);
            $result = curl_exec($c);
            curl_close($c);
            
            Debugger::say("Finished performing action.");
            return 1;
         } else {
            Debugger::say("Failed to perform action: not connected.");
            return 0;
         }
      }  
         
      
      
      function getSessionFromBrowser() {
         Debugger::say("Start getting session from browser...");
         
         if (!$this->use_session) {
            return $this->getCookieFromBrowser();
         }
         if (isset($_SESSION[GM_COOKIE_KEY])) {
            $this->cookie_str = base64_decode($_SESSION[GM_COOKIE_KEY]);
            Debugger::say("completed getting session from browser: ".$this->cookie_str);
            return 1;
         } else {
            Debugger::say("Failed to read session ".GM_COOKIE_KEY." from browser.");
            return 0;
         }
      }
      function getCookieFromBrowser() {
         Debugger::say("Start getting cookie from browser...");
         
         if (!isset($_COOKIE)) {
            Debugger::say("Failed to get any cookie from browser.");
            return 0;
         }
         if (count($_COOKIE) == 0) {
            Debugger::say("Failed to get non-empty cookie array from browser.");
            return 0;
         }
         if (isset($_COOKIE[GM_COOKIE_KEY])) {
            $this->cookie_str = base64_decode($_COOKIE[GM_COOKIE_KEY]);
            Debugger::say("Completed getting cookie from browser: ".$this->cookie_str);
            return 1;
         } else {
            Debugger::say("Failed to read cookie ".GM_COOKIE_KEY." from browser.");
            return 0;
         }
      }
      
      
      function saveSessionToBrowser() {
         Debugger::say("Start saving session to browser...");
         
         if ($this->isConnected()) {
            if (!$this->use_session)
               return $this->saveCookieToBrowser();            
            
            $_SESSION[GM_COOKIE_KEY] = base64_encode($this->cookie_str);
            Debugger::say("Just saved session: ".GM_COOKIE_KEY."=".base64_encode($this->cookie_str));
            Debugger::say("Completed saving session to browser.");
            return 1;
         }
         Debugger::say("Failed to save session to browser: not connected.");
         return 0;
      }
      function saveCookieToBrowser() {         
         Debugger::say("Start saving cookie to browser...");         
         if ($this->isConnected()) {
            
            if (strpos($_SERVER["HTTP_HOST"],":"))
               $domain = substr($_SERVER["HTTP_HOST"],0,strpos($_SERVER["HTTP_HOST"],":"));
            else
               $domain = $_SERVER["HTTP_HOST"];
            Debugger::say("Saving cookies with domain=".$domain);
               
            header("Set-Cookie: ".GM_COOKIE_KEY."=".base64_encode($this->cookie_str)."; Domain=".$domain.";");
            Debugger::say("Just saved cookie: ".GM_COOKIE_KEY."=".base64_encode($this->cookie_str));
            Debugger::say("Completed saving cookie to browser.");
            return 1;
         }
         Debugger::say("Failed to save cookie to browser: not connected.");
         return 0;
      }
      
      function removeSessionFromBrowser() {
         Debugger::say("Start removing session from browser...");
         
         if (!$this->use_session)
            return $this->removeCookieFromBrowser();
         
         if (isset($_SESSION[GM_COOKIE_KEY])) {
            unset($_SESSION[GM_COOKIE_KEY]);
            Debugger::say("Just removed session: ".GM_COOKIE_KEY);
         } else {
            Debugger::say("Cannot find libgmailer session: ".GM_COOKIE_KEY);
         }
         Debugger::say("Finished removing session from browser.");
      }
      function removeCookieFromBrowser() {
         Debugger::say("Start removing cookie from browser...");
         if (isset($_COOKIE)) {
            if (isset($_COOKIE[GM_COOKIE_KEY])) {
               // libgmailer cookie exists
               
               if (strpos($_SERVER["HTTP_HOST"],":"))
                  $domain = substr($_SERVER["HTTP_HOST"],0,strpos($_SERVER["HTTP_HOST"],":"));
               else
                  $domain = $_SERVER["HTTP_HOST"];
               Debugger::say("Removing cookies with domain=".$domain);               
               
               header("Set-Cookie: ".GM_COOKIE_KEY."=1; Discard; Max-Age=0; Domain=".$domain.";");
               Debugger::say("Just removed cookie: ".GM_COOKIE_KEY);
            } else {
               Debugger::say("Cannot find libgmailer cookie: ".GM_COOKIE_KEY);
            }
         } else {
            Debugger::say("Cannot find any cookie from browser.");
         }
         Debugger::say("Finished removing cookie from browser.");
      }         
      
      
      
      function disconnect() {
         Debugger::say("Start disconnecting...");
         
         /** logout from gamil.google.com too **/
         $c = curl_init();
         curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
         curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
         curl_setopt($c, CURLOPT_URL, GM_LNK_LOGOUT);
         curl_setopt($c, CURLOPT_SSL_VERIFYHOST,  2);
         $this->CURL_PROXY($c);
         curl_setopt($c, CURLOPT_USERAGENT, GM_USER_AGENT);
         curl_setopt($c, CURLOPT_COOKIE, $this->cookie_str);            
         curl_setopt($c, CURLOPT_SSL_VERIFYPEER, FALSE);
         curl_setopt($c, CURLOPT_REFERER, GM_LNK_REFER);
         curl_exec($c);
         curl_close($c);
         Debugger::say("Logged-out from GMail.");
         
         $this->removeSessionFromBrowser();
         $this->cookie_str = "";
         
         Debugger::say("Completed disconnecting.");
      }
      
      function getSnapshot($type) {
         if ($type & (GM_STANDARD|GM_LABEL|GM_CONVERSATION|GM_QUERY))
            return new GMailSnapshot($type, $this->raw);
         elseif ($type & GM_CONTACT)
            return new GMailSnapshot($type, $this->contact_raw);
         else
            return new GMailSnapshot(GM_STANDARD, $this->raw);  // assuming normal by default
      }
      
      function getStandardBox() {
         return array("Inbox","Starred","Sent","All","Spam","Trash");
      }
      
      
      
      /** some helper (static/private) functions **/      
      function CURL_PROXY($cc) {
         if (strlen($this->proxy_host) > 0) {
            curl_setopt($cc, CURLOPT_PROXY, $this->proxy_host);
            if (strlen($this->proxy_auth) > 0)
               curl_setopt($cc, CURLOPT_PROXYUSERPWD, $this->proxy_auth);
         }
      }
      function get_cookies($header) {
         preg_match_all('!Set-Cookie: ([^;\s]+)($|;)!', $header, $match);   
         $cookie = "";
         foreach ($match[1] as $val) {
            if ($val{0} == '=') {
               continue;
            }
            $cookie .= $val . ";";
         }
         return substr($cookie, 0, -1);
      }
   
      function parse_data_packet($input, &$offset) {
         $output = array();
         
         // state variables
         $isQuoted = false;      // track when we are inside quotes
         $dataHold = "";      // temporary data container
         $lastCharacter = " ";
   
         // walk through the entire string
         for($i=1; $i < strlen($input); $i++) {
            switch($input[$i]) {
               case "[":   // handle start of array marker
                  if(!$isQuoted) {
                     // recurse any nested arrays
                     array_push($output, GMailer::parse_data_packet(substr($input,$i), $offset));
                     
                     // the returning recursive function write out the total length of the characters consumed
                     $i += $offset;
                     
                     // assume that the last character is a closing bracket
                     $lastCharacter = "]";
                  } else {
                     $dataHold .= "[";
                  }
                  break;
   
               case "]":   // handle end of array marker
                  if(!$isQuoted) {
                     if($dataHold != "") {
                        array_push($output, $dataHold);
                     }
                     
                     // determine total number of characters consumed (write to reference)
                     $offset = $i;
                     return $output;
                  } else {
                     $dataHold .= "]";
                     break;
                  }
   
               case '"':   // toggle quoted state
                  if($isQuoted) {
                     $isQuoted = false;
                  } else {
                     $isQuoted = true;
                     $lastCharacter = '"';
                  }
                  break;
   
               case ',':   // find end of element marker and add to array
                  if(!$isQuoted) {
                     if($dataHold != "") {   // this is to filter out adding extra elements after an empty array
                        array_push($output, $dataHold);
                        $dataHold = "";
                     } else if($lastCharacter == '"') {   // this is to catch empty strings
                        array_push($output, "");
                     }
                  } else {
                     $dataHold .= ",";
                  }
                  break;
                  
               case '\\':
                  if ($i < strlen($input) - 1) { 
                     switch($input[$i+1]) {
                        case "\\":                    /* for the case \\ */
                           $dataHold .= '\\';
                           $lastCharacter = '\\';
                           break;
                        case '"':                     /* for the case \" */
                           $dataHold .= '"';
                           $lastCharacter = '\"';
                           $i += 1;
                           break;
                        case "'":                     /* for the case \' */
                           $dataHold .= "'";
                           $lastCharacter = "\'";
                           $i += 1;
                           break;
                        case "n":                     /* for the case \n */
                           $dataHold .= "\n";
                           $lastCharacter = "\n";
                           $i += 1;
                           break;
                        case "r":                     /* for the case \r */                        
                           $dataHold .= "\r";
                           $lastCharacter = "\r";
                           $i += 1;
                           break;
                        default:
                     }                     
                  }
                  break;
   
               default:   // regular characters are added to the data container
                  $dataHold .= $input[$i];
                  break;
            }
         }   
         return $output;
      }
      
   }
   
    
   class GMailSnapshot {

      function GMailSnapshot($type, $raw) {
         // input: raw packet generated by GMailer
         if (!is_array($raw))
            return null;
         if (count($raw) == 0)
            return null;
         
         if ($type & (GM_STANDARD|GM_LABEL|GM_CONVERSATION|GM_QUERY)) {
            $this->gmail_ver = $raw["v"][1];
            $this->quota_mb = $raw["qu"][1];
            $this->quota_per = $raw["qu"][3];
            $this->std_box_new = array_slice($raw["ds"],1);
            if (isset($raw["p"])) {
               $this->owner = $raw["p"][5][1];
               $this->owner_email = $raw["p"][6][1];
            }
            $this->have_invit = $raw["i"][1];
            $this->label_list = array();
            $this->label_new = array();
            foreach ($raw["ct"][1] as $v) {
               array_push($this->label_list, $v[0]);
               array_push($this->label_new, $v[1]);
            }         
            if (isset($raw["ts"])) {
               $this->view = (GM_STANDARD|GM_LABEL|GM_QUERY);
               $this->box_name = $raw["ts"][5];
               $this->box_total = $raw["ts"][3];
               $this->box_pos = $raw["ts"][1];
            }
            if (isset($raw["t"])) {
               $this->box = array();
               foreach ($raw["t"] as $t) {
                  if ($t == "t") continue;
                  $b = array(
                     "id"=>$t[0],
                     "is_read"=>($t[1]==1?1:0),
                     "is_starred"=>($t[2]==1?1:0),
                     "date"=>strip_tags($t[3]),
                     "sender"=>strip_tags($t[4],"<b>"),
                     "flag"=>$t[5],
                     "subj"=>strip_tags($t[6],"<b>"),
                     "snippet"=>$t[7],
                     "labels"=>(count($t[8])==0?0:$t[8]),
                     "attachment"=>strlen($t[9])==0?array():explode(",",$t[9]),
                     "msgid"=>$t[10]
                  );
                  array_push($this->box, $b);
               }
            }
            if (isset($raw["cs"])) {
               $this->view = GM_CONVERSATION;            
               $this->conv_title = $raw["cs"][2];
               $this->conv_total = $raw["cs"][8];
               $this->conv_id = $raw["cs"][1];
               $this->conv_labels = count($raw["cs"][5])==0?0:$raw["cs"][5];   
               $ij = array_search("^i", $this->conv_labels); 
               if (!($ij === false)) {
                  $this->conv_labels[$ij] = "Inbox";
               }
               $this->conv_starred = false;
               
               $this->conv = array();            
               for ($i = 0; $i < count($raw["mg"]); $i++) {
                  if ($raw["mg"][$i][0] == "mb" && $i > 0) {
                     $b["body"] .= $raw["mg"][$i][1];
                     if ($raw["mg"][$i][2] == 0) {
                        array_push($this->conv, $b);
                        unset($b);
                     }
                  } elseif ($raw["mg"][$i][0] == "mi") {
                     if (isset($b)) {
                        array_push($this->conv, $b);
                        unset($b);
                     }
                     $b = array();
                     $b["index"] = $raw["mg"][$i][2];
                     $b["id"] = $raw["mg"][$i][3];
                     $b["is_star"] = $raw["mg"][$i][4];
                     if ($b["is_star"] == 1)
                        $this->conv_starred = true;
                     $b["sender"] = $raw["mg"][$i][6];
                     $b["sender_email"] = str_replace("\"", "", $raw["mg"][$i][7]);   // remove annoying d-quotes in address
                     $b["recv"] = $raw["mg"][$i][8];
                     $b["recv_email"] = str_replace("\"", "", $raw["mg"][$i][10]);
                     $b["reply_email"] = str_replace("\"", "", $raw["mg"][$i][13]);
                     $b["dt_easy"] = $raw["mg"][$i][9];
                     $b["dt"] = $raw["mg"][$i][14];
                     $b["subj"] = $raw["mg"][$i][15];
                     $b["snippet"] = $raw["mg"][$i][16];
                     $b["attachment"] = array();
                     foreach ($raw["mg"][$i][17] as $bb) {
                        array_push($b["attachment"], array("id"=>$bb[0],"filename"=>$bb[1],"type"=>$bb[2],"size"=>$bb[3]));
                     }
                     //$b["attachment"] = count($raw["mg"][$i][17])==0 ? 0 : 1;
                     $b["body"] = "";
                  }
               }
               if (isset($b)) array_push($this->conv, $b);
            }
         } elseif ($type & GM_CONTACT) {
            $this->contacts = array();
            $this->contacts_freq = array();
            foreach ($raw["co"] as $a) {
               $b = array(
                  "name"=>$a[2],
                  "email"=>str_replace("\"", "", $a[1]),
                  "notes"=>$a[3],
                  "is_freq"=>$a[4]
               );
               array_push($this->contacts, $b);
               if ($a[4] == 1)
                  array_push($this->contacts_freq, $b);
            }
            $this->view = GM_CONTACT;
         } else {
            return null;
         }  
      }
   }

   
   class Debugger {      
      function say($str) {
         if (D_ON) {
            $fd = fopen(D_FILE, "a+");
            fwrite($fd, "<?php ".$str." ?>\n");
            fclose($fd);
         }
      }
   }
   Debugger::say("======== ".date("j M Y H:i:s")." libgmailer STARTED! ===============================");
         
         
   
   
?>