]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/MailNotify.php
Disable verification of emails for corporate env.
[SourceForge/phpwiki.git] / lib / MailNotify.php
1 <?php rcs_id('$Id$');
2  * Copyright (C) 2006-2007 Reini Urban
3  * Copyright (C) 2009 Marc-Etienne Vargenau, Alcatel-Lucent
4  *
5  * This file is part of PhpWiki.
6  *
7  * PhpWiki is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * PhpWiki is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with PhpWiki; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 /**
23  * Handle the pagelist pref[notifyPages] logic for users
24  * and notify => hash ( page => (userid => userhash) ) for pages.
25  * Generate notification emails.
26  *
27  * We add WikiDB handlers and register ourself there:
28  *   onChangePage, onDeletePage, onRenamePage
29  * Administrative actions:
30  *   [Watch] WatchPage - add a page, or delete watch handlers into the users 
31  *                       pref[notifyPages] slot.
32  *   My WatchList      - view or edit list/regex of pref[notifyPages].
33  *   EMailConfirm methods: send and verify
34  *
35  * Helper functions:
36  *   getPageChangeEmails
37  *   MailAdmin
38  *   ? handle emailed confirmation links (EmailSignup, ModeratedPage)
39  *
40  * @package MailNotify
41  * @author  Reini Urban
42  */
43
44 if (!defined("MAILER_LOG"))
45     if (isWindows())
46         define("MAILER_LOG", 'c:/wikimail.log');
47     else
48         define("MAILER_LOG", '/var/log/wikimail.log');
49
50 class MailNotify {
51
52     function MailNotify($pagename) {
53         $this->pagename = $pagename; /* which page */
54         $this->emails  = array();    /* to which addresses */
55         $this->userids = array();    /* corresponding array of displayed names, 
56                                         don't display the email addresses */
57         /* From: from whom the mail appears to be */
58         $this->from = $this->fromId();
59     }
60
61     function fromId() {
62         global $request;
63         return $request->_user->getId() . '@' .  $request->get('REMOTE_HOST');
64     }
65
66     function userEmail($userid, $doverify = true) {
67         global $request;
68
69         // Disable verification of emails for corporate env.
70         if (defined('GFORGE') and GFORGE) {
71             $doverify = false;
72         }
73
74         $u = $request->getUser();
75         if ($u->UserName() == $userid) { // lucky: current user
76             $prefs = $u->getPreferences();
77             $email = $prefs->get('email');
78             // do a dynamic emailVerified check update
79             if ($doverify and !$request->_prefs->get('emailVerified'))
80                 $email = '';
81         } else {  // not current user
82             if (ENABLE_USER_NEW) {
83                 $u = WikiUser($userid);
84                 $u->getPreferences();
85                 $prefs = &$u->_prefs;
86             } else {
87                 $u = new WikiUser($request, $userid);
88                 $prefs = $u->getPreferences();
89             }
90             $email = $prefs->get('email');
91             if ($doverify and !$prefs->get('emailVerified')) {
92                 $email = '';
93             }
94         }
95         return $email;
96     }
97
98     /**
99      * getPageChangeEmails($notify)
100      * @param  $notify: hash ( page => (userid => userhash) )
101      * @return array
102      *         unique array of ($emails, $userids)
103      */
104     function getPageChangeEmails($notify) {
105         global $request;
106         $emails = array(); $userids = array();
107         foreach ($notify as $page => $users) {
108             if (glob_match($page, $this->pagename)) {
109                 foreach ($users as $userid => $user) {
110                     if (!$user) { // handle the case for ModeratePage: 
111                                   // no prefs, just userid's.
112                         $emails[] = $this->userEmail($userid, false);
113                         $userids[] = $userid;
114                     } else {
115                         if (!empty($user['verified']) and !empty($user['email'])) {
116                             $emails[]  = $user['email'];
117                             $userids[] = $userid;
118                         } elseif (!empty($user['email'])) {
119                             // do a dynamic emailVerified check update
120                             $email = $this->userEmail($userid, true);
121                             if ($email) {
122                                 $notify[$page][$userid]['verified'] = 1;
123                                 $request->_dbi->set('notify', $notify);
124                                 $emails[] = $email;
125                                 $userids[] = $userid;
126                             }
127                         }
128                         // ignore verification
129                         /*
130                         if (DEBUG) {
131                             if (!in_array($user['email'], $emails))
132                                 $emails[] = $user['email'];
133                         }
134                         */
135                     }
136                 }
137             }
138         }
139         $this->emails = array_unique($emails);
140         $this->userids = array_unique($userids);
141         return array($this->emails, $this->userids);
142     }
143     
144     function sendMail($subject, $content, 
145                       $notice = false,
146                       $silent = 0)
147     {
148         global $request;
149         if (!DEBUG and $silent === 0)
150             $silent = true;
151         $emails = $this->emails;
152         $from = $this->from;
153         if (!$notice) $notice = _("PageChange Notification of %s");
154         $ok = mail(($to = array_shift($emails)),
155                  "[".WIKI_NAME."] ".$subject, 
156                    $subject."\n".$content,
157                    "From: $from\r\nBcc: ".join(',', $emails)
158                    );
159         if (MAILER_LOG and is_writable(MAILER_LOG)) {
160             $f = fopen(MAILER_LOG, "a");
161             fwrite($f, "\n\nX-MailSentOK: " . $ok ? 'OK' : 'FAILED');
162             if (!$ok) {
163                 global $ErrorManager;
164                 // get last error message
165                 $last_err = $ErrorManager->_postponed_errors[count($ErrorHandler->_postponed_errors)-1];
166                 fwrite($f, "\nX-MailFailure: " . $last_err);
167             }
168             fwrite($f, "\nDate: " . CTime());
169             fwrite($f, "\nSubject: $subject");
170             fwrite($f, "\nFrom: $from");
171             fwrite($f, "\nTo: $to");
172             fwrite($f, "\nBcc: ".join(',', $emails));
173             fwrite($f, "\n\n". $content);
174             fclose($f);
175         }
176         if ($ok) {
177             if (!$silent)
178                 trigger_error(sprintf($notice, $this->pagename)
179                               . " "
180                               . sprintf(_("sent to %s"), join(',',$this->userids)),
181                               E_USER_NOTICE);
182             return true;
183         } else {
184             trigger_error(sprintf($notice, $this->pagename)
185                           . " "
186                           . sprintf(_("Error: Couldn't send %s to %s"), 
187                                    $subject."\n".$content, join(',',$this->userids)), 
188                           E_USER_WARNING);
189             return false;
190         }
191     }
192     
193     /**
194      * Send udiff for a changed page to multiple users.
195      * See rename and remove methods also
196      */
197     function sendPageChangeNotification(&$wikitext, $version, &$meta) {
198
199         global $request;
200
201         if (@is_array($request->_deferredPageChangeNotification)) {
202             // collapse multiple changes (loaddir) into one email
203             $request->_deferredPageChangeNotification[] = 
204                 array($this->pagename, $this->emails, $this->userids);
205             return;
206         }
207         $backend = &$request->_dbi->_backend;
208         $subject = _("Page change").' '.urlencode($this->pagename);
209         $previous = $backend->get_previous_version($this->pagename, $version);
210         if (!isset($meta['mtime'])) $meta['mtime'] = time();
211         if ($previous) {
212             $difflink = WikiURL($this->pagename, array('action'=>'diff'), true);
213             $cache = &$request->_dbi->_cache;
214             $this_content = explode("\n", $wikitext);
215             $prevdata = $cache->get_versiondata($this->pagename, $previous, true);
216             if (empty($prevdata['%content']))
217                 $prevdata = $backend->get_versiondata($this->pagename, $previous, true);
218             $other_content = explode("\n", $prevdata['%content']);
219             
220             include_once("lib/difflib.php");
221             $diff2 = new Diff($other_content, $this_content);
222             //$context_lines = max(4, count($other_content) + 1,
223             //                     count($this_content) + 1);
224             $fmt = new UnifiedDiffFormatter(/*$context_lines*/);
225             $content  = $this->pagename . " " . $previous . " " . 
226                 Iso8601DateTime($prevdata['mtime']) . "\n";
227             $content .= $this->pagename . " " . $version . " " .  
228                 Iso8601DateTime($meta['mtime']) . "\n";
229             $content .= $fmt->format($diff2);
230             
231         } else {
232             $difflink = WikiURL($this->pagename,array(),true);
233             $content = $this->pagename . " " . $version . " " .  
234                 Iso8601DateTime($meta['mtime']) . "\n";
235             $content .= _("New page");
236         }
237         $editedby = sprintf(_("Edited by: %s"), $this->from);
238         //$editedby = sprintf(_("Edited by: %s"), $meta['author']);
239         $this->sendMail($subject, 
240                         $editedby."\n".$difflink."\n\n".$content);
241     }
242
243     /** 
244      * Support mass rename / remove (not yet tested)
245      */
246     function sendPageRenameNotification ($to, &$meta) {
247         global $request;
248
249         if (@is_array($request->_deferredPageRenameNotification)) {
250             $request->_deferredPageRenameNotification[] = 
251                 array($this->pagename, $to, $meta, $this->emails, $this->userids);
252         } else {
253             $pagename = $this->pagename;
254             //$editedby = sprintf(_("Edited by: %s"), $meta['author']) . ' ' . $meta['author_id'];
255             $editedby = sprintf(_("Edited by: %s"), $this->from);
256             $subject = sprintf(_("Page rename %s to %s"), urlencode($pagename), urlencode($to));
257             $link = WikiURL($to, true);
258             $this->sendMail($subject, 
259                             $editedby."\n".$link."\n\n"."Renamed $pagename to $to");
260         }
261     }
262
263     /**
264      * The handlers:
265      */
266     function onChangePage (&$wikidb, &$wikitext, $version, &$meta) {
267         $result = true;
268         if (!isa($GLOBALS['request'],'MockRequest')) {
269             $notify = $wikidb->get('notify');
270             /* Generate notification emails? */
271             if (!empty($notify) and is_array($notify)) {
272                 if (empty($this->pagename))
273                     $this->pagename = $meta['pagename'];
274                 // TODO: Should be used for ModeratePage and RSS2 Cloud xml-rpc also.
275                 $this->getPageChangeEmails($notify);
276                 if (!empty($this->emails)) {
277                     $result = $this->sendPageChangeNotification($wikitext, $version, $meta);
278                 }
279             }
280         }
281         return $result;
282     }
283
284     function onDeletePage (&$wikidb, $pagename) {
285         $result = true;
286         /* Generate notification emails? */
287         if (! $wikidb->isWikiPage($pagename) and !isa($GLOBALS['request'],'MockRequest')) {
288             $notify = $wikidb->get('notify');
289             if (!empty($notify) and is_array($notify)) {
290                 //TODO: deferr it (quite a massive load if you remove some pages).
291                 $this->getPageChangeEmails($notify);
292                 if (!empty($this->emails)) {
293                     $editedby = sprintf(_("Removed by: %s"), $this->from); // Todo: host_id
294                     //$emails = join(',', $this->emails);
295                     $subject = sprintf(_("Page removed %s"), urlencode($pagename));
296                     $page = $wikidb->getPage($pagename);
297                     $rev = $page->getCurrentRevision(true);
298                     $content = $rev->getPackedContent();
299                     $result = $this->sendMail($subject, 
300                                               $editedby."\n"."Deleted $pagename"."\n\n".$content);
301                 }
302             }
303         }
304         //How to create a RecentChanges entry with explaining summary? Dynamically
305         /*
306           $page = $this->getPage($pagename);
307           $current = $page->getCurrentRevision();
308           $meta = $current->_data;
309           $version = $current->getVersion();
310           $meta['summary'] = _("removed");
311           $page->save($current->getPackedContent(), $version + 1, $meta);
312         */
313         return $result;
314     }
315
316     function onRenamePage (&$wikidb, $oldpage, $new_pagename) {
317         $result = true;
318         if (!isa($GLOBALS['request'], 'MockRequest')) {
319             $notify = $wikidb->get('notify');
320             if (!empty($notify) and is_array($notify)) {
321                 $this->getPageChangeEmails($notify);
322                 if (!empty($this->emails)) {
323                     $newpage = $wikidb->getPage($new_pagename);
324                     $current = $newpage->getCurrentRevision();
325                     $meta = $current->_data;
326                     $this->pagename = $oldpage;
327                     $result = $this->sendPageRenameNotification($new_pagename, $meta);
328                 }
329             }
330         }
331     }
332
333     /**
334      * Send mail to user and store the cookie in the db
335      * wikiurl?action=ConfirmEmail&id=bla
336      */
337     function sendEmailConfirmation ($email, $userid) {
338         $id = rand_ascii_readable(16);
339         $wikidb = $GLOBALS['request']->getDbh();
340         $data = $wikidb->get('ConfirmEmail');
341         while(!empty($data[$id])) { // id collision
342             $id = rand_ascii_readable(16);
343         }
344         $subject = WIKI_NAME . " " . _("e-mail address confirmation");
345         $ip = $request->get('REMOTE_HOST');
346         $expire_date = time() + 7*86400;
347         $content = fmt("Someone, probably you from IP address %s, has registered an
348 account \"%s\" with this e-mail address on %s.
349
350 To confirm that this account really does belong to you and activate
351 e-mail features on %s, open this link in your browser:
352
353 %s
354
355 If this is *not* you, don't follow the link. This confirmation code
356 will expire at %s.", 
357                        $ip, $userid, WIKI_NAME, WIKI_NAME, 
358                        WikiURL(HOME_PAGE, array('action' => 'ConfirmEmail',
359                                                 'id' => $id), 
360                                true),
361                        CTime($expire_date));
362         $this->sendMail($subject, $content, "", true);
363         $data[$id] = array('email' => $email,
364                            'userid' => $userid,
365                            'expire' => $expire_date);
366         $wikidb->set('ConfirmEmail', $data);
367         return '';
368     }
369
370     function checkEmailConfirmation () {
371         global $request;
372         $wikidb = $request->getDbh();
373         $data = $wikidb->get('ConfirmEmail');
374         $id = $request->getArg('id');
375         if (empty($data[$id])) { // id not found
376             return HTML(HTML::h1("Confirm E-mail address"),
377                         HTML::h1("Sorry! Wrong URL"));
378         }
379         // upgrade the user
380         $userid = $data['userid'];
381         $email = $data['email'];
382         $u = $request->getUser();
383         if ($u->UserName() == $userid) { // lucky: current user (session)
384             $prefs = $u->getPreferences();
385             $request->_user->_level = WIKIAUTH_USER;
386             $request->_prefs->set('emailVerified', true);
387         } else {  // not current user
388             if (ENABLE_USER_NEW) {
389                 $u = WikiUser($userid);
390                 $u->getPreferences();
391                 $prefs = &$u->_prefs;
392             } else {
393                 $u = new WikiUser($request, $userid);
394                 $prefs = $u->getPreferences();
395             }
396             $u->_level = WIKIAUTH_USER;
397             $request->setUser($u);
398             $request->_prefs->set('emailVerified', true);
399         }
400         unset($data[$id]);
401         $wikidb->set('ConfirmEmail', $data);
402         return HTML(HTML::h1("Confirm E-mail address"),
403                     HTML::p("Your e-mail address has now been confirmed."));
404     }
405 }
406
407 // Local Variables:
408 // mode: php
409 // tab-width: 8
410 // c-basic-offset: 4
411 // c-hanging-comment-ender-p: nil
412 // indent-tabs-mode: nil
413 // End:   
414 ?>