Aleksander Machniak
2016-03-25 aa2486beb3a5dec4315f2a9cf29358c523030e78
commit | author | age
48e9c1 1 <?php
T 2
3 /**
4  * Detect VCard attachments and show a button to add them to address book
5  *
6  * @version @package_version@
7  * @license GNU GPLv3+
8  * @author Thomas Bruederli, Aleksander Machniak
9  */
10 class vcard_attachments extends rcube_plugin
11 {
12     public $task = 'mail';
13
14     private $message;
15     private $vcard_parts = array();
16     private $vcard_bodies = array();
17
18     function init()
19     {
20         $rcmail = rcmail::get_instance();
21         if ($rcmail->action == 'show' || $rcmail->action == 'preview') {
22             $this->add_hook('message_load', array($this, 'message_load'));
23             $this->add_hook('template_object_messagebody', array($this, 'html_output'));
24         }
25         else if (!$rcmail->output->framed && (!$rcmail->action || $rcmail->action == 'list')) {
26             $icon = 'plugins/vcard_attachments/' .$this->local_skin_path(). '/vcard.png';
27             $rcmail->output->set_env('vcard_icon', $icon);
28             $this->include_script('vcardattach.js');
29         }
30
31         $this->register_action('plugin.savevcard', array($this, 'save_vcard'));
32     }
33
34     /**
35      * Check message bodies and attachments for vcards
36      */
37     function message_load($p)
38     {
39         $this->message = $p['object'];
40
41         // handle attachments vcard attachments
42         foreach ((array)$this->message->attachments as $attachment) {
43             if ($this->is_vcard($attachment)) {
44                 $this->vcard_parts[] = $attachment->mime_id;
45             }
46         }
47         // the same with message bodies
2193f6 48         foreach ((array)$this->message->parts as $part) {
48e9c1 49             if ($this->is_vcard($part)) {
T 50                 $this->vcard_parts[] = $part->mime_id;
51                 $this->vcard_bodies[] = $part->mime_id;
52             }
53         }
54
55         if ($this->vcard_parts)
56             $this->add_texts('localization');
57     }
58
59     /**
60      * This callback function adds a box below the message content
61      * if there is a vcard attachment available
62      */
63     function html_output($p)
64     {
65         $attach_script = false;
66
67         foreach ($this->vcard_parts as $part) {
aa2486 68             $vcards = rcube_vcard::import($this->message->get_part_content($part, null, true));
48e9c1 69
T 70             // successfully parsed vcards?
e27a61 71             if (empty($vcards)) {
48e9c1 72                 continue;
e27a61 73             }
48e9c1 74
T 75             // remove part's body
e27a61 76             if (in_array($part, $this->vcard_bodies)) {
48e9c1 77                 $p['content'] = '';
e27a61 78             }
48e9c1 79
T 80             foreach ($vcards as $idx => $vcard) {
e27a61 81                 // skip invalid vCards
AM 82                 if (empty($vcard->email) || empty($vcard->email[0])) {
83                     continue;
84                 }
85
86                 $display = $vcard->displayname . ' <'.$vcard->email[0].'>';
48e9c1 87
654ac1 88                 // add box below message body
48e9c1 89                 $p['content'] .= html::p(array('class' => 'vcardattachment'),
T 90                     html::a(array(
91                         'href' => "#",
61be82 92                         'onclick' => "return plugin_vcard_save_contact('" . rcube::JQ($part.':'.$idx) . "')",
48e9c1 93                         'title' => $this->gettext('addvcardmsg'),
T 94                         ),
61be82 95                         html::span(null, rcube::Q($display)))
aa2486 96                 );
48e9c1 97             }
T 98
99             $attach_script = true;
100         }
101
102         if ($attach_script) {
103             $this->include_script('vcardattach.js');
104             $this->include_stylesheet($this->local_skin_path() . '/style.css');
105         }
106
107         return $p;
108     }
109
110     /**
111      * Handler for request action
112      */
113     function save_vcard()
114     {
e27a61 115         $this->add_texts('localization', true);
48e9c1 116
61be82 117         $uid     = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST);
AM 118         $mbox    = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST);
119         $mime_id = rcube_utils::get_input_value('_part', rcube_utils::INPUT_POST);
48e9c1 120
3c32f3 121         $rcmail  = rcmail::get_instance();
aa2486 122         $message = new rcube_message($uid, $mbox);
48e9c1 123
T 124         if ($uid && $mime_id) {
125             list($mime_id, $index) = explode(':', $mime_id);
aa2486 126             $part = $message->get_part_content($mime_id, null, true);
48e9c1 127         }
T 128
129         $error_msg = $this->gettext('vcardsavefailed');
130
131         if ($part && ($vcards = rcube_vcard::import($part))
3c32f3 132             && ($vcard = $vcards[$index]) && $vcard->displayname && $vcard->email
A 133         ) {
0d5240 134             $CONTACTS = $this->get_address_book();
3c32f3 135             $email    = $vcard->email[0];
A 136             $contact  = $vcard->get_assoc();
137             $valid    = true;
48e9c1 138
3c32f3 139             // skip entries without an e-mail address or invalid
A 140             if (empty($email) || !$CONTACTS->validate($contact, true)) {
141                 $valid = false;
48e9c1 142             }
T 143             else {
3c32f3 144                 // We're using UTF8 internally
61be82 145                 $email = rcube_utils::idn_to_utf8($email);
48e9c1 146
3c32f3 147                 // compare e-mail address
A 148                 $existing = $CONTACTS->search('email', $email, 1, false);
149                 // compare display name
150                 if (!$existing->count && $vcard->displayname) {
151                     $existing = $CONTACTS->search('name', $vcard->displayname, 1, false);
152                 }
153
154                 if ($existing->count) {
155                     $rcmail->output->command('display_message', $this->gettext('contactexists'), 'warning');
156                     $valid = false;
157                 }
158             }
159
160             if ($valid) {
48e9c1 161                 $plugin = $rcmail->plugins->exec_hook('contact_create', array('record' => $contact, 'source' => null));
T 162                 $contact = $plugin['record'];
163
3c32f3 164                 if (!$plugin['abort'] && $CONTACTS->insert($contact))
48e9c1 165                     $rcmail->output->command('display_message', $this->gettext('addedsuccessfully'), 'confirmation');
T 166                 else
167                     $rcmail->output->command('display_message', $error_msg, 'error');
168             }
169         }
3c32f3 170         else {
48e9c1 171             $rcmail->output->command('display_message', $error_msg, 'error');
3c32f3 172         }
48e9c1 173
T 174         $rcmail->output->send();
175     }
176
177     /**
178      * Checks if specified message part is a vcard data
179      *
180      * @param rcube_message_part Part object
181      *
182      * @return boolean True if part is of type vcard
183      */
184     function is_vcard($part)
185     {
186         return (
187             // Content-Type: text/vcard;
188             $part->mimetype == 'text/vcard' ||
189             // Content-Type: text/x-vcard;
190             $part->mimetype == 'text/x-vcard' ||
191             // Content-Type: text/directory; profile=vCard;
192             ($part->mimetype == 'text/directory' && (
193                 ($part->ctype_parameters['profile'] &&
194                     strtolower($part->ctype_parameters['profile']) == 'vcard')
195             // Content-Type: text/directory; (with filename=*.vcf)
196                     || ($part->filename && preg_match('/\.vcf$/i', $part->filename))
197                 )
198             )
199         );
200     }
0d5240 201
A 202     /**
203      * Getter for default (writable) addressbook
204      */
205     private function get_address_book()
206     {
207         if ($this->abook) {
208             return $this->abook;
209         }
210
211         $rcmail = rcmail::get_instance();
212         $abook  = $rcmail->config->get('default_addressbook');
213
214         // Get configured addressbook
215         $CONTACTS = $rcmail->get_address_book($abook, true);
216
217         // Get first writeable addressbook if the configured doesn't exist
218         // This can happen when user deleted the addressbook (e.g. Kolab folder)
219         if ($abook === null || $abook === '' || !is_object($CONTACTS)) {
220             $source   = reset($rcmail->get_address_sources(true));
221             $CONTACTS = $rcmail->get_address_book($source['id'], true);
222         }
223
224         return $this->abook = $CONTACTS;
225     }
48e9c1 226 }