diff options
author | Camil Staps | 2020-02-03 09:47:21 +0100 |
---|---|---|
committer | Camil Staps | 2020-02-03 09:47:21 +0100 |
commit | 2af4ffce19df7cdb34d4b509a66bc916ffcb88d1 (patch) | |
tree | 481bc542d1298e1bcb3e39c107a22b39f13f0395 /mail.php |
Initial commit
Diffstat (limited to 'mail.php')
-rw-r--r-- | mail.php | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/mail.php b/mail.php new file mode 100644 index 0000000..6d2d425 --- /dev/null +++ b/mail.php @@ -0,0 +1,196 @@ +<?php +require_once ('config.php'); +require_once ('db.php'); + +$imap_options=['DISABLE_AUTHENTICATOR' => 'CRAM_MD5']; +$imap=imap_open (IMAP_CONN,IMAP_USER,IMAP_PASS,NULL,1,$imap_options); + +if (!$imap){ + echo 'IMAP errors:<br/>'; + foreach (imap_errors() as $err) + echo $err.'<br/>'; + die(); +} + +function verify_signature ($data,$sig) +{ + $gnupg=gnupg_init(); + $sig_details=gnupg_verify ($gnupg,$data,$sig); + if (!$sig_details) + throw new Exception ('signature verification failed'); + + $summary=$sig_details[0]['summary']; + if ($summary & 0x0004) + throw new Exception ('bad signature'); + if ($summary & 0x0010) + throw new Exception ('key has been revoked'); + if ($summary & 0x0020) + throw new Exception ('key has expired'); + if ($summary & 0x0040) + throw new Exception ('signature has expired'); + if ($summary & 0x0080) + throw new Exception ('can\'t verify: key missing'); + if ($summary!=3) + throw new Exception ('unknown signature verification problem ('.$summary.')'); + + return $sig_details[0]['fingerprint']; +} + +function mail_fetch_message ($msg) +{ + global $imap; + + $guessed_source=null; + + /* Fetch user */ + $info=imap_headerinfo ($imap,$msg); + $from=$info->from[0]->mailbox.'@'.$info->from[0]->host; + $user=get_user_by_email ($from); + + /* Guess source */ + if (strpos (imap_utf8 ($info->subject),'[agade]')!==false) + $guessed_source='Agade'; + + /* Fetch the multipart tree */ + $structure=imap_fetchstructure ($imap,$msg); + if ($structure->type!=TYPEMULTIPART) + throw new Exception ('not a multipart message'); + if ($structure->subtype!='SIGNED') + throw new Exception ('not a signed message'); + + /* Find the body and signature */ + $body_id=null; + $sig_id=null; + foreach ($structure->parts as $id => $part){ + if ($part->type==TYPEAPPLICATION && $part->subtype=='PGP-SIGNATURE') + $sig_id=$id+1; + else + $body_id=$id+1; + } + if (is_null ($sig_id)) + throw new Exception ('no signature attachment'); + if (is_null ($body_id)) + throw new Exception ('no message body'); + + /* Get contents */ + $headers=imap_fetchmime ($imap,$msg,1,FT_INTERNAL | FT_PEEK); + $body=imap_fetchbody ($imap,$msg,$body_id,FT_INTERNAL | FT_PEEK); + $sig=imap_fetchbody ($imap,$msg,$sig_id,FT_PEEK); + + /* Verify signature */ + $fpr=verify_signature ($headers.$body,$sig); + if ($fpr!=$user['pgp_fingerprint']) + throw new Exception ('illegal key used for signature'); + + /* Fetch plain-text body */ + $body_part=$structure->parts[$body_id-1]; + $plain_body=null; + if ($body_part->type==TYPETEXT && $body_part->subtype=='PLAIN'){ + $plain_body=$body; + } else if ($body_part->type==TYPEMULTIPART){ + foreach ($body_part->parts as $id => $part) + if ($part->type==TYPETEXT && $part->subtype=='PLAIN') + $plain_body=imap_fetchbody ($imap,$msg,$body_id.'.'.($id+1),FT_PEEK); + } else { + throw new Exception ('unknown body part type '.$body_part->type); + } + if (is_null ($plain_body)) + throw new Exception ('no plain-text body'); + + $plain_body=imap_qprint ($plain_body); + + return [ + 'user' => $user, + 'body' => $plain_body, + 'guessed_source' => $guessed_source + ]; +} + +function mail_get_unseen_messages () +{ + global $imap; + + $unseen_msgs=imap_search ($imap,'UNSEEN'); + if (!$unseen_msgs) + $unseen_msgs=[]; + + return $unseen_msgs; +} + +function mail_mark_seen ($msg) +{ + global $imap; + imap_setflag_full ($imap,$msg,'\Seen'); +} + +function mail_finish () +{ + global $imap; + imap_close ($imap); +} + +function mail_parse_message ($body,$prefill_info=[]) +{ + $info=[ + 'title' => null, + 'location' => null, + 'start_date' => null, + 'end_date' => null, + 'keywords' => [], + 'source' => null, + 'description' => null + ]; + $warnings=[]; + + foreach ($prefill_info as $key => $val) + $info[$key]=$val; + + $lines=explode ("\n",$body); + $i=0; + foreach ($lines as $line){ + $i++; + if (trim ($line)=='') + break; + + $parts=explode (':',$line,2); + $key=trim ($parts[0]); + $val=trim ($parts[1]); + + switch ($key){ + case 'Title': + $info['title']=$val; + break; + case 'Location': + $info['location']=$val; + break; + case 'Source': + $info['source']=$val; + break; + case 'Dates': + $parts=explode ('to',$val); + if (count ($parts)==2){ + $info['start_date']=trim ($parts[0]); + $info['end_date']=trim ($parts[1]); + } else { + $info['start_date']=$info['end_date']=$val; + } + break; + case 'Keywords': + $info['keywords']=array_map ('trim',explode (';',$val)); + break; + default: + $warnings[]='Unknown key '.$key; + } + } + + $info['description']=trim (implode ("\n",array_slice ($lines,$i))); + + foreach (array_keys ($info) as $key) + if (is_null ($info[$key])) + $warnings[]='No '.$key.' found'; + + return [ + 'info' => $info, + 'warnings' => $warnings + ]; +} |