2ace3614875a9372d72fe1d1de7780a58aab0739
[atutor.git] / mods / wiki / tools / cron.d / S35mail2wiki.php
1 <?php
2 /*
3    Implements an EMail-to-Wiki gateway, if you assign it a POP3 / IMAP
4    account and/or MBOX spool file to read from. An mail sent to there
5    will create a new page or append to an existing one, if either the
6    To: field or the Subject: contains a WikiWord (everything else will
7    get written onto "SpammerSubmissions"). Enriched mail bodys will be
8    checked for convertability into WikiMarkup, but HTML emails and any
9    multipart/* objects will get rejected outright.
10 */
11
12
13 // define("MAIL2WIKI_POP3", "mailuser:pw@mail.example.com");  // for POP3+IMAP
14 // define("MAIL2WIKI_MBOX", "/var/spool/mail/towiki");  // from local mbox file
15 define("MAIL2WIKI_SPAM", "SpammerSubmissions");  // to save rejected stuff onto (as-is, in rfc822 format); you could also set this to false or zero to disable
16 define("MAIL2WIKI_ATONCE", 50);  // max amount of mails to process in one run
17
18
19 #-- start
20 $incoming = array();
21
22 #-- check remote mail account
23 if (defined("MAIL2WIKI_POP3")) {
24
25    #-- open connection
26    $username = strtok(MAIL2WIKI_POP3, ":");
27    $password = strtok("@");
28    $mailserver = strtok("/");
29    ($box = strtok(".")) or
30    ($box = "INBOX");
31    if (($mx = imap_open('{'."$mailserver/pop3:110}$box", $username, $password))
32    or ($mx = imap_open('{'."$mailserver}$box", $username, $password)))
33    {
34       echo "[$cron]: opened connection to $mailserver to check for submissions\n";
35    
36       #-- loop through all messages
37       $msg_count = imap_num_msg($mx);
38       echo "[$cron]: $msg_count found\n";
39       if ($msg_count > MAIL2WIKI_ATONCE * 1.1) {
40          $msg_count = MAIL2WIKI_ATONCE;
41          echo "[$cron]: proeccsing only $msg_count now\n";
42       }
43       for ($msg=1; $msg<=$msg_count; $msg++) {
44
45          #-- get complete msg
46          $mbox = imap_fetchheader($server, $msg, FT_PREFETCHTEXT);
47          imap_delete($server, $msg);
48
49          #-- into our spool array
50          $incoming[] = $mbox;
51       }
52
53       #-- close, delete messages
54       imap_expunge($mx);
55       imap_close($mx);
56    }
57 }
58
59
60 #-- read on Unix server
61 if (defined("MAIL2WIKI_MBOX")) {
62    // open for read-write or forget it
63    if ($f = fopen(MAIL2WIKI_MBOX, "rw")) {
64       flock($f, LOCK_EX);
65
66       #-- try to read 2MB file,
67       #   but fail if we really can do that (this is far too large!!)
68       $mbox = fread($f, 1<<21);
69       if (strlen($mbox) >= (1<<21)) {
70          unset($mbox);
71       }
72       
73       #-- split mbox file into individual messages, append to $incoming[] list
74       elseif ($mbox
75       and ($mbox = preg_split("/^From [^\s]+@[^\s]+ \w\w\w?,? \w\w\w \d\d \d\d:\d\d:\d\d \d\d\d\d$/m",
76       $mbox)) ) {
77          #-- append to processing list
78          $incoming = array_merge($incoming, $mbox);
79          unset($mbox);
80       
81          #-- clear inbox file completely
82          fseek($f, 0);
83          ftruncate($f, 0);
84       }
85       
86       #-- go away
87       flock($f, LOCK_UN);
88       fclose($f);
89    }
90 }
91
92
93 #-- store it --------------------------------------------------------------
94 if ($incoming) {
95    echo "[$cron]: storing " . count($incoming) . " pages\n";
96    $rx_wiki = "/^([".EWIKI_CHARS_U."]+[".EWIKI_CHARS_L."]+){2,})$/";
97    
98    foreach ($incoming as $mbox) {
99    
100       #-- convert line breaks
101       $mbox = preg_replace('/(\r\n|\r)/', "\n", $mbox);
102       $page = substr($mbox, strpos($mbox, "\n\n")+2);  // content body
103       
104       #-- check for WikiWord
105       $ok = 0;
106       if (preg_match('/^To:\s*"(.+)"|^To:\s*<?(.+?)@/i', $mbox, $uu)) {
107          if ($id=$uu[1]) {
108             $ok = preg_match($rx_wiki, $uu[1]);
109          }
110          elseif ($id=$uu[2]) {
111             $ok = preg_match($rx_wiki, $uu[2]);
112          }
113       }
114       if (!$ok && preg_match('/^Subject:[ ]*([^\s]+)[ ]*$/i', $mbox, $uu)) {
115          $ok = preg_match($rx_wiki, $id=$uu[1]);
116       }
117       
118       #-- check content-type
119       $ok = $ok && preg_match('#^Content-Type:\s*([^.+0-9_a-z/]+)#i', $mbox, $uu);
120       if ($ok) {
121          $ct = strtok(strtolower($uu[1]), "/");
122          $st = strtok("?");
123          if ($ct != "text") {
124             $ok = 0;
125          }
126          else {
127             if (($st == "plain") || strstr($st, "wiki")) {
128                $ok = 1;
129             }
130             elseif ($st == "enriched") {
131                // how to decode that?
132             }
133             else {
134                $ok = 0;
135             }
136          }
137       }
138
139       
140       #-- save full msg as spam
141       if (!$ok) {
142          echo "[$cron]: spam/unwanted content detected\n";
143          if (MAIL2WIKI_SPAM) {
144             ewiki_db::APPEND(MAIL2WIKI_SPAM, "\n----\n$mbox\n");
145          }
146       }
147       
148       #-- save as new Wiki page
149       elseif (!ewiki_db::GET($id)) {
150          ewiki_db::APPEND($id, $page);
151       }
152       #-- as comment to existing one
153       else {
154          if (preg_match("/^From: [^\n]+/m", $mbox, $uu)) {
155             $from = from_mail_or_link($uu[1]);
156             $from = "[$from]: ";
157          }
158          else {
159             $from = "----\n\n";
160          }
161          #-- do
162          echo "[$cron]: storing mail from '$from' onto page '$id'\n";
163          $page = trim($page);
164          ewiki_db::APPEND($id, "\n\n$from$page\n");
165       }
166
167    }
168 }
169 unset($mbox);
170 unset($incoming);
171
172
173 #-- return a WikiPageUserName or [name|email@...] string back
174 function from_mail_or_link($from) {
175    $from = strtr($from, "<>\"\'", "    ");
176    $l = strpos($from, "@");
177    if ($l = strrpos($from, " ", $l)) {
178       $name = substr($from, 0, $l);
179       $mail = substr($from, $l+1);
180       $wiki = str_replace(" ", "", $name);
181       if (ewiki_db::GET($wiki)) {
182          return($wiki);
183       }
184       else {
185          $mail = trim($mail);
186          return("$wiki|$email");
187       }
188    }
189    else {
190       return($from);
191    }
192 }
193
194 ?>