svn.filsa.org wordpress_dev

Rev

Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
10 phil 1
<?php
2
/*
3
*/
4
 
5
/** Make sure that the WordPress bootstrap has run before continuing. */
6
require(dirname(__FILE__) . '/../../../wp-load.php');
17 phil 7
include_once( ABSPATH . 'wp-admin/includes/admin.php' );
10 phil 8
 
9
/** Get the POP3 class with which to access the mailbox. */
10
require_once( ABSPATH . WPINC . '/class-pop3.php' );
11
/** Include mimeparser lib from Manuel Lemos. */
12
require_once('lib/rfc822_addresses.php');
13
require_once('lib/mime_parser.php');
14
 
15
 
16
class mail2wp {
17
 
18
        var $run_id; // unique path
19
        var $image_id = 1;
20
        var $upload_dir;
21
        var $blog_gmt_offset;
17 phil 22
        var $options;
10 phil 23
 
22 phil 24
        var $set_meta_image = false;
20 phil 25
        var $meta_image = '';
26
 
10 phil 27
        /**
28
        * Process one mail message, and execute the post-by-mail.
29
        * - handle attachments correctly.
30
        - parse with mime_parser.
31
        - get headers.
32
        - process body (including attachments)
33
          - move attachments to upload folder
34
          - concatenate body text and insert <img? tags to attachment files
35
        - create new post
36
        - delete message
37
        @param $msg_text - full text of the rfc822 message
38
        @param $result - array that will be returned as the result
39
        @return True if successful, False if not.
40
        */
41
        function process_message($msg_text, $result) {
42
                //$this->log("process_message: start.");
20 phil 43
                $this->meta_image = '';
10 phil 44
 
45
                $parser = new mime_parser_class;
46
                $parseparams = array ('Data'=>$msg_text);
47
                //$parseparams['SaveBody'] = 'temp/';
48
                //$parseparams['SkipBody'] = '1';
49
                //var_dump($parseparams);
50
                $result['status'] = 'INCOMPLETE';
17 phil 51
                // --------------------- PARSE with mime_parser ----------------------------
10 phil 52
                if(!$parser->Decode($parseparams, $decoded)) {
53
                        $result['error'] =  'MIME message decoding error: '.$mime->error.' at position '.$mime->error_position. 'Aborting this message.' . "\n";
54
                        $this->log($result['error'], 'ERR');
55
                return False;
56
                }
57
                $msgparse = $decoded[0];
58
 
59
                if(!$parser->Analyze($msgparse, $analyzed)) {
60
                        $result['error'] =  'MIME message analyzing error: '.$mime->error.' at position '.$mime->error_position. 'Aborting this message.' . "\n";
61
                        $this->log($result['error'], 'ERR');
62
                        return False;
63
                }
64
                $this->log(' - Parsing data from message titled: ' . $analyzed['Subject']);
17 phil 65
                // ------- DO AUTH CHECK -- at this point we have enough info to figure this out
10 phil 66
                //var_dump($analyzed);
20 phil 67
 
68
                $authorized = false;
69
                $auth_emails = split(',', $this->options['auth_approved_senders']);
70
 
71
                $this->log('==> Email Auth Check', 'INFO');
72
                //var_dump($analyzed['From']);
73
                //var_dump($auth_emails);
74
                //$this->log('admin settings: auth_approved_senders:' . $this->options['auth_approved_senders']);
75
 
76
                foreach($analyzed['From'] as $sender) {
77
                        $this->log('== Checking Sender: ' . $sender['address']);
78
                        if ( in_array($sender['address'], $auth_emails)) {
79
                                $authorized = true;
80
                        }
81
                }
82
                if ( ! $authorized) {
83
                        $this->log('No valid email found.', 'WARN');
84
                        return false;
85
                }
86
                //---- END AUTHORIZATION ----
87
 
10 phil 88
                //get headers
89
                // 'post_content','post_title','post_date','post_date_gmt','post_author','post_category', 'post_status'
90
                $post_author   = $this->_get_post_author($msgparse);
91
                $post_category = array(get_option('default_email_category'));
92
                $post_date     = $this->_parse_rfc822date($analyzed['Date']);
93
                $post_date_gmt = $this->_parse_rfc822date($analyzed['Date'], True);
94
                //$post_title    = $this->_decode_text($msgparse['Headers']['subject:']); // using decoded
95
                $post_title = $this->_decode_text($analyzed['Subject']);
17 phil 96
                $post_content  = $analyzed['Data'];; // content, unprocessed, for now.
97
                $post_status   = 'draft';
10 phil 98
 
99
                if (empty($post_author)) {
100
                        $post_author=1; // dumb but easy assumption that user 1, admin, will always exist.                     
101
                }
102
 
103
                // prep post
104
                $post_data = compact('post_content','post_title','post_date','post_date_gmt','post_author','post_category', 'post_status');
17 phil 105
                //var_dump($post_data);
10 phil 106
                $post_data = add_magic_quotes($post_data);
107
                // send post to WP
108
                $post_ID = wp_insert_post($post_data);
109
                if ( is_wp_error( $post_ID ) ) {
110
                        $this->log(' WP Post failed. '. $post_ID->get_error_message(), 'ERR');
111
                        return False;
112
                        }
113
                // We couldn't post, for whatever reason. Better move forward to the next email.
114
                if ( empty( $post_ID ) )
115
                        return False;
116
 
17 phil 117
                // process mail body into post content
118
                $post_content = $this->process_analyzed_body($analyzed, $post_ID); // process via analyzed
119
                $post_content = apply_filters('phone_content', $post_content);// WP filter hook
25 phil 120
                $post_content = str_replace('Sent from my iPhone','', $post_content); // clean sigs
121
 
17 phil 122
                if (function_exists('mb_convert_encoding') &&
123
                        $analyzed['Encoding'] == 'iso-2022-jp') {
124
                        $this->log(' ! Detected iso-2022-jp encoding. Making adjustments.');
125
                        $post_title = mb_convert_encoding($post_title, "UTF-8", "ISO-2022-JP");
126
                        $post_content = mb_convert_encoding($post_content, "UTF-8", "ISO-2022-JP");
127
                }
128
                $user = new WP_User($post_author);
129
                $post_status = ( $user->has_cap('publish_posts') ) ? 'publish' : 'pending';
130
 
131
                // update post content and title
132
                $update = compact('post_content','post_title', 'post_status');
133
                $update['ID'] = $post_ID;
134
                $update_post_id = wp_update_post($update);
20 phil 135
 
136
                // add "image" meta tag to post. to support image templates in the mailcanvas theme.
137
                if ($this->set_meta_image && !empty($this->meta_image))
138
                        add_post_meta($post_ID, 'image', $this->meta_image);
139
 
17 phil 140
                if ( 0 == $update_post_id ) {
141
                        $this->log(' WP Post failed  after content update. Check for a new draft with no content.');
142
                        return False;
143
                        }
144
 
10 phil 145
                do_action('publish_phone', $post_ID); // WP hook
146
                $this->log(" * Posted title: $post_title, Author id: $post_author, status: $post_status.");
147
                //var_dump($post_data);
148
                //echo 'POST_CONTENT:<br/>' . $post_content . '<br />';
17 phil 149
                return true;
10 phil 150
 
151
        }
152
 
153
        /**
154
        Returns a string containing the body of a mail message.
155
        The body is modified based on the attachments or related fileparts that are included with the message.
156
        @param $mail is an array returned from the $mime_parser_class->Analyze() function.
157
        */
17 phil 158
        function process_analyzed_body($mail, $post_id) {
10 phil 159
                $result = '';
17 phil 160
                $attach_text = '';
10 phil 161
                if ($mail['Type'] == 'text') $result = $this->_decode_text($mail['Data']);
162
 
163
                //handle Type=html and Related
17 phil 164
                // Related images are already in the $result text.
10 phil 165
                if ($mail['Type'] == 'html') {
166
                        $this->log (' - HTML message type.');
17 phil 167
                        $result = $mail['Data'];
10 phil 168
                        if (!empty($mail['Related'])) {
169
                        $this->log(' - Processing related items.');                                    
170
                                foreach ($mail['Related'] as $attachment) {
171
                                        if (! 'image' == $attachment['Type']) continue; //skip non-images for now
172
                                        $fname = $this->_get_safe_filename($attachment['FileName'], $attachment['SubType']);
173
                                        if ($this->_save_file($fname, $attachment['Data'])) {
17 phil 174
                                                $mimetype = $attachment['Type'] . '/' . $attachment['SubType'];
175
                                                $attachname = $this->_insert_attachment($fname, $post_id, $mimetype, false);
176
 
177
                                                $urlpath = $this->upload_dir['url'] . '/' . $attachname['file'];
10 phil 178
                                                $cid = 'cid:' . $attachment['ContentID'];
179
                                                $result = str_replace($cid, $urlpath, $result);
180
                                        }
181
                                }
182
                        }
183
                }
184
 
185
                // handle attachments
186
                if (!empty($mail['Attachments'])) {
187
                        $this->log(' - Processing attachments.');
17 phil 188
                        $attach_text .= "\n<div class='attachments'>\n";
10 phil 189
                        foreach ($mail['Attachments'] as $attachment) {
26 phil 190
                                if ( 'image' != $attachment['Type']) continue; //skip non-images for now
10 phil 191
                                $fname = $this->_get_safe_filename($attachment['FileName'], $attachment['SubType']);
192
                                if ($this->_save_file($fname, $attachment['Data'])) {
17 phil 193
                                        $mimetype = $attachment['Type'] . '/' . $attachment['SubType'];        
194
                                        $attachname = $this->_insert_attachment($fname, $post_id, $mimetype);
195
                                        $urlpath = $this->upload_dir['url'] . '/' . $attachname['file'];
20 phil 196
 
197
                                        if (!$this->set_meta_image)
198
                                                $attach_text .= '<img src="' . $urlpath .'" class="photomail attachments"/>'. "\n";
10 phil 199
                                }
200
                        }
17 phil 201
                        $attach_text .= "\n</div>\n";
202
 
203
                $result = $attach_text . $result;
204
                }
205
                return $result;
206
        }
207
 
208
        /*******
209
         * registers image as an attachment, so it shows in media browser and is associated with the post
210
         * creates thumbnails and other images sizes
211
         *  @param $filename - name of the file (from $this->_get_safe_filename() )
212
         *  @param $parent_post_id - the post id of the mail message
213
         *  @param $resize - resize the image to no larger than the configured size. defaults to true.
214
         *  @returns An array with keys "file, height,width"
215
        */
216
        function _insert_attachment($fname, $parent_post_id, $mimetype, $resize=true ) {
217
                $result = array('file'=>$fname);
218
                $fpath = $this->upload_dir['path'] . '/'. $fname;
219
                $att_params = array();
220
                $att_params['post_title'] = $fname;
221
                $att_params['post_status'] = 'inherit';
222
                $att_params['post_content'] = '';
223
                $att_params['post_mime_type'] = $mimetype;
224
                $att_params['guid'] = $this->upload_dir['url'] . '/'. $fname;
225
 
226
 
227
                $this->log(' - _insert_attachment: ' . $fname .',path:'.$fpath.', postid:'. $parent_post_id);
228
                $attach_id = wp_insert_attachment( $att_params, $fpath, $parent_post_id );
229
                $this->log(' - got attachment_id=' . $attach_id);
230
                $attach_data = wp_generate_attachment_metadata( $attach_id, $fpath );
231
 
232
                //var_dump($attach_data);
233
                wp_update_attachment_metadata( $attach_id,  $attach_data );
234
                $result['height'] = $attach_data['height'];
235
                $result['width'] = $attach_data['width'];
236
 
237
                // return a resized image filename if the attachment image is larger than a $good_size image.
238
                // @@TODO - make this a config option.
239
                $good_size = 'medium';
240
                $this->log(' - _insert_attachment: h=' . $attach_data['height'] .', w='.$attach_data['width'] );
10 phil 241
 
17 phil 242
                if ($resize) {
243
                        if ( ($attach_data['width']  > get_option("{$good_size}_size_w")) ||
244
                                 ($attach_data['height'] > get_option("{$good_size}_size_h"))  ) {
245
                                 if (isset($attach_data['sizes'][$good_size])) {
246
                                        $this->log(' - _insert_attachment inside resize: good-size=' . $good_size);
247
                                        //var_dump($attach_data['sizes'][$good_size]);
248
                                        $result = $attach_data['sizes'][$good_size];
249
                                }
250
                        }
10 phil 251
                }
17 phil 252
                $this->log(' - _insert_attachment is done, returning:' . $result['file']);
253
 
20 phil 254
                if ($this->set_meta_image) $this->meta_image = $this->upload_dir['url'] . '/' .$result['file'];
255
 
10 phil 256
                return $result;
17 phil 257
 
10 phil 258
        }
259
        /**
260
        Returns a string, that represents a safe filename.
261
        - safe filename is not encoded in a weird way
262
        - safe filename is unique and is not a
263
        */
264
        function _get_safe_filename($f, $subtype){
265
                $fext = array('jpeg'=>'.jpg', 'gif'=>'.gif', 'png'=>'.png');
266
                // fix encoded filenames - just generate a safe name.
267
                if ($f != $this->_decode_text($f)) {   
17 phil 268
                        $f = 'pic_'.$this->run_id . '-' . $this->image_id++ . $fext[$subtype];
10 phil 269
                        $f = $this->_decode_text($f);
270
                }
271
                $f = str_replace(' ', '_', $f); // change spaces to underscores.
272
                $filechar = 65;
273
                $fpath = $this->upload_dir['path'] . '/' . $f;
274
                // makes sure file doesnt already exist.
275
                while (file_exists($fpath)) {
276
                        $fparts = explode('.', $f);
277
                        $f = $fparts[0];
278
                        if (substr($f, -2, 1) == '-') {
279
                                $f = substr($f, 0, strlen($f)-2);
280
                        }
281
                        $f = $f . '-' . chr($filechar++) . '.' . $fparts[1];
282
                        $fpath = $this->upload_dir['path'] . '/' . $f; 
283
                }
284
 
285
                return $f;
286
        }
287
        /*
288
 
289
        */
290
        function _save_file($fname, $data) {
291
                $result = true;
292
                $fpath = $this->upload_dir['path'] . '/' . $fname;
293
 
294
                if ($fh = fopen($fpath, 'w')) {
295
                        fwrite($fh, $data);
296
                        $this->log(' - File saved: ' . $fpath);
297
                } else {
298
                        $this->log("Attachment not written: $fname.", 'ERR' );
299
                        $result = false;
300
                }
301
                fclose($fh);
302
 
303
                return $result;
304
        }
305
 
306
        /**
17 phil 307
        *Easy logging
308
        * @@TODO: log to a file, implement log levels that work.
10 phil 309
        */
310
        function log($s, $level='INFO') {
311
                $t = date('Y-m-d H:i:s');
312
                echo "$t [$level] $s" . "<br/>\n";
313
        }
314
        /**
315
        * Loop through messages on pop3 server.
316
        * @param $msg_count - # of message on the server.
317
        * @param $pop3 - variable that holds reference to an active POP3 instance.
318
        */
319
        function process_messages($msg_count, $pop3) {
320
                $this->startup();
321
                // loop through messages
322
                // if message process is True, then delete from pop3.
323
                for ( $i = 1; $i <= $msg_count; $i++ ) {
324
                        // get message
325
                        $this->log("Processing message #$i.");
326
 
327
                        $message = $pop3->get($i);
328
                        $msg_text = implode ('',  $message); // convert message array to string
329
                        // process message
330
                        $msg_status = $this->process_message($msg_text, $msg_result);
331
                        // delete message if message process was successful.
332
                        if ($msg_status) {
333
                                $this->log ("Message #$i OK. Attempting POP3 server delete...");
334
 
335
                                if(!$pop3->delete($i)) {
336
                                        $this->log( 'Message delete failure: ' . wp_specialchars($pop3->ERROR), 'ERR');
337
                                        $pop3->reset();
338
                                        exit;
339
                                } else {
340
                                        $this->log ("Message <strong>$i</strong> deleted.");
341
                                }      
342
                        } else {
343
                                $this->log ("process message $i failed: " . $result['error'], "ERR");
344
                        }
345
 
346
                }
21 phil 347
                $pop3->quit(); // comment out to prevent deletion of messages.
10 phil 348
                $this->shutdown();
349
 
350
        }
351
        function startup() {           
352
                $this->run_id = dechex(time());
353
                $this->upload_dir = wp_upload_dir(date('Y/m'));
354
                $this->blog_gmt_offset = absint(get_option('gmt_offset')) * 3600;
17 phil 355
                $this->options = get_option( 'm2wp_options' );
20 phil 356
                var_dump($this->options);
10 phil 357
        }
358
 
359
        function shutdown() {
360
        }
361
 
362
 
363
        /*-- PRIVATE FUNCTIONS --*/
364
        /**
365
        Converts a rfc822date to a php and db friendly format.
366
        From - Thu, 1 Jan 2009 23:33:40 +0900
367
        To - 2009-01-01 22:33:40
368
 
369
        @param dataheader - string that reflects rfc822date
370
        @param gmt - returns a GMT date. default is false.
371
        */
372
        function _parse_rfc822date($dateheader, $gmt=False) {
373
                $dmonths = array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
374
 
375
                $ddate = trim($dateheader);
376
                if (strpos($ddate, ',')) {
377
                        $ddate = trim(substr($ddate, strpos($ddate, ',') + 1, strlen($ddate)));
378
                }
379
                $date_arr = explode(' ', $ddate);
380
                $date_time = explode(':', $date_arr[3]);
381
 
382
                $ddate_H = $date_time[0];
383
                $ddate_i = $date_time[1];
384
                $ddate_s = $date_time[2];
385
 
386
                $ddate_m = $date_arr[1];
387
                $ddate_d = $date_arr[0];
388
                $ddate_Y = $date_arr[2];
389
                for ( $j = 0; $j < 12; $j++ ) {
390
                        if ( $ddate_m == $dmonths[$j] ) {
391
                                $ddate_m = $j+1;
392
                        }
393
                }
394
 
395
                $time_zn = intval($date_arr[4]) * 36;
396
                $ddate_U = gmmktime($ddate_H, $ddate_i, $ddate_s, $ddate_m, $ddate_d, $ddate_Y);
397
                if ($gmt) {$ddate_U = $ddate_U - $time_zn;}
398
                return gmdate('Y-m-d H:i:s', $ddate_U); //post_date_gmt
399
        }
400
        function _rfc822date($dateheader, $gmt=False) {
401
                $epoch_secs = strtotime(trim($dateheader));
402
 
403
        }
404
        /**
405
        Converts subject text to match blog settings using iconv
406
        */     
407
        function _decode_text($text) {
408
                // Captures any text in the subject before $phone_delim as the subject
409
                if ( function_exists('iconv_mime_decode') ) {
410
                        $result = iconv_mime_decode($text, 2, get_option('blog_charset'));
411
                } else {
412
                        $result = wp_iso_descrambler($text);
413
                }
414
                return $result;
415
        }
416
        /**
417
        * Collects email addresses from mail headers, then attempts to identify a WP author with that email address.
418
        */
419
        function _get_post_author($msgparse) {
420
                //Find $post_author based on from: (and reply-to)headers, set $author_found
421
                $authors = array();
422
                $result = NULL;
423
                $addr_fields = array('from:','reply-to:','return-path:');
424
 
425
                foreach ($addr_fields as $field){ // collect emails
426
                        if (! empty($msgparse['ExtractedAddresses'][$field])) {
427
                        foreach ($msgparse['ExtractedAddresses'][$field] as $email) {
428
                                $authors[] = $email['address'];
429
                                }
430
                        }
431
                }
432
                foreach ($authors as $author) { // find authors based on email
433
                        if ( is_email($author) ) {
434
                                $userdata = get_user_by_email($author);
435
                                if ( ! empty($userdata) ) {
436
                                        $result = $userdata->ID;
437
                                        break;
438
                                }
439
                        }
440
                }
441
                return $result;
442
        }
443
}
444
 
445
 
446
function do_photomail() {
447
        // fetch mail via POP3
448
        $mail_server = new POP3();
449
        $photomail = new mail2wp();
450
 
13 phil 451
        $is_active = get_option('m2wp_enabled');
20 phil 452
        if (false /*! $is_active*/) { // this doesnt seem to be working.
13 phil 453
                $photomail->log("Deactivated. Activate the mail2wordpress plugin to start processing emails.");
454
                return true;
455
        }
456
        ;
457
 
10 phil 458
        if ( ! $mail_server->connect(get_option('mailserver_url'), get_option('mailserver_port') ) ||
459
                ! $mail_server->user(get_option('mailserver_login')) ||
460
                ( ! $msg_count = $mail_server->pass(get_option('mailserver_pass')) ) ) {
461
                        $mail_server->quit();
462
                        wp_die( ( 0 === $msg_count ) ? __("There doesn't seem to be any new mail.") : wp_specialchars($mail_server->ERROR) );
463
        }
464
        // loop through mail.
465
 
466
        $photomail->log("Connected to POP3 server: There are $msg_count message(s).");
467
        $photomail->process_messages($msg_count, $mail_server);
468
}
469
 
470
do_photomail();
471
 
472
 
473
 
474
?>
475
 
476
 
477
 
478