Thomas Bruederli
2012-10-15 27be4e7644637255d7adbf5386ec883d9c2c64cd
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
48         foreach ((array)$this->message->parts as $idx => $part) {
49             if ($this->is_vcard($part)) {
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         $icon = 'plugins/vcard_attachments/' .$this->local_skin_path(). '/vcard_add_contact.png';
67
68         foreach ($this->vcard_parts as $part) {
3c32f3 69             $vcards = rcube_vcard::import($this->message->get_part_content($part, null, true));
48e9c1 70
T 71             // successfully parsed vcards?
72             if (empty($vcards))
73                 continue;
74
75             // remove part's body
76             if (in_array($part, $this->vcard_bodies))
77                 $p['content'] = '';
78
79             foreach ($vcards as $idx => $vcard) {
80                 $display = $vcard->displayname;
81                 if ($vcard->email[0])
82                     $display .= ' <'.$vcard->email[0].'>';
83
654ac1 84                 // add box below message body
48e9c1 85                 $p['content'] .= html::p(array('class' => 'vcardattachment'),
T 86                     html::a(array(
87                         'href' => "#",
88                         'onclick' => "return plugin_vcard_save_contact('" . JQ($part.':'.$idx) . "')",
89                         'title' => $this->gettext('addvcardmsg'),
90                         ),
91                         html::span(null, Q($display)))
92                     );
93             }
94
95             $attach_script = true;
96         }
97
98         if ($attach_script) {
99             $this->include_script('vcardattach.js');
100             $this->include_stylesheet($this->local_skin_path() . '/style.css');
101         }
102
103         return $p;
104     }
105
106     /**
107      * Handler for request action
108      */
109     function save_vcard()
110     {
111         $this->add_texts('localization', true);
112
113         $uid = get_input_value('_uid', RCUBE_INPUT_POST);
114         $mbox = get_input_value('_mbox', RCUBE_INPUT_POST);
115         $mime_id = get_input_value('_part', RCUBE_INPUT_POST);
116
3c32f3 117         $rcmail  = rcmail::get_instance();
A 118         $storage = $rcmail->get_storage();
119         $storage->set_folder($mbox);
48e9c1 120
T 121         if ($uid && $mime_id) {
122             list($mime_id, $index) = explode(':', $mime_id);
3c32f3 123             $part = $storage->get_message_part($uid, $mime_id, null, null, null, true);
48e9c1 124         }
T 125
126         $error_msg = $this->gettext('vcardsavefailed');
127
128         if ($part && ($vcards = rcube_vcard::import($part))
3c32f3 129             && ($vcard = $vcards[$index]) && $vcard->displayname && $vcard->email
A 130         ) {
0d5240 131             $CONTACTS = $this->get_address_book();
3c32f3 132             $email    = $vcard->email[0];
A 133             $contact  = $vcard->get_assoc();
134             $valid    = true;
48e9c1 135
3c32f3 136             // skip entries without an e-mail address or invalid
A 137             if (empty($email) || !$CONTACTS->validate($contact, true)) {
138                 $valid = false;
48e9c1 139             }
T 140             else {
3c32f3 141                 // We're using UTF8 internally
A 142                 $email = rcube_idn_to_utf8($email);
48e9c1 143
3c32f3 144                 // compare e-mail address
A 145                 $existing = $CONTACTS->search('email', $email, 1, false);
146                 // compare display name
147                 if (!$existing->count && $vcard->displayname) {
148                     $existing = $CONTACTS->search('name', $vcard->displayname, 1, false);
149                 }
150
151                 if ($existing->count) {
152                     $rcmail->output->command('display_message', $this->gettext('contactexists'), 'warning');
153                     $valid = false;
154                 }
155             }
156
157             if ($valid) {
48e9c1 158                 $plugin = $rcmail->plugins->exec_hook('contact_create', array('record' => $contact, 'source' => null));
T 159                 $contact = $plugin['record'];
160
3c32f3 161                 if (!$plugin['abort'] && $CONTACTS->insert($contact))
48e9c1 162                     $rcmail->output->command('display_message', $this->gettext('addedsuccessfully'), 'confirmation');
T 163                 else
164                     $rcmail->output->command('display_message', $error_msg, 'error');
165             }
166         }
3c32f3 167         else {
48e9c1 168             $rcmail->output->command('display_message', $error_msg, 'error');
3c32f3 169         }
48e9c1 170
T 171         $rcmail->output->send();
172     }
173
174     /**
175      * Checks if specified message part is a vcard data
176      *
177      * @param rcube_message_part Part object
178      *
179      * @return boolean True if part is of type vcard
180      */
181     function is_vcard($part)
182     {
183         return (
184             // Content-Type: text/vcard;
185             $part->mimetype == 'text/vcard' ||
186             // Content-Type: text/x-vcard;
187             $part->mimetype == 'text/x-vcard' ||
188             // Content-Type: text/directory; profile=vCard;
189             ($part->mimetype == 'text/directory' && (
190                 ($part->ctype_parameters['profile'] &&
191                     strtolower($part->ctype_parameters['profile']) == 'vcard')
192             // Content-Type: text/directory; (with filename=*.vcf)
193                     || ($part->filename && preg_match('/\.vcf$/i', $part->filename))
194                 )
195             )
196         );
197     }
0d5240 198
A 199     /**
200      * Getter for default (writable) addressbook
201      */
202     private function get_address_book()
203     {
204         if ($this->abook) {
205             return $this->abook;
206         }
207
208         $rcmail = rcmail::get_instance();
209         $abook  = $rcmail->config->get('default_addressbook');
210
211         // Get configured addressbook
212         $CONTACTS = $rcmail->get_address_book($abook, true);
213
214         // Get first writeable addressbook if the configured doesn't exist
215         // This can happen when user deleted the addressbook (e.g. Kolab folder)
216         if ($abook === null || $abook === '' || !is_object($CONTACTS)) {
217             $source   = reset($rcmail->get_address_sources(true));
218             $CONTACTS = $rcmail->get_address_book($source['id'], true);
219         }
220
221         return $this->abook = $CONTACTS;
222     }
48e9c1 223 }