forked from extern/egroupware
* Addressbook: store S/Mime & PGP pubkey and photo (SQL backend only) in filesystem
This commit is contained in:
parent
23e654ab89
commit
48554590f4
@ -35,7 +35,7 @@ class addressbook_bo extends Api\Contacts
|
||||
*/
|
||||
public function get_pgp_keys($recipients)
|
||||
{
|
||||
return $this->get_keys($recipients, self::$pgp_key_regexp, '%-----BEGIN PGP PUBLIC KEY BLOCK-----%');
|
||||
return $this->get_keys($recipients, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,7 +106,7 @@ class addressbook_bo extends Api\Contacts
|
||||
*/
|
||||
public function ajax_set_pgp_keys($keys, $allow_user_updates=null)
|
||||
{
|
||||
$message = $this->set_keys($keys, self::$pgp_key_regexp, $allow_user_updates);
|
||||
$message = $this->set_keys($keys, true, $allow_user_updates);
|
||||
// add all keys to public keyserver too
|
||||
$message .= "\n".lang('%1 key(s) added to public keyserver "%2".',
|
||||
self::set_pgp_keyserver($keys), PARSE_URL(self::KEYSERVER_ADD, PHP_URL_HOST));
|
||||
@ -154,16 +154,28 @@ class addressbook_bo extends Api\Contacts
|
||||
return $added;
|
||||
}
|
||||
|
||||
/**
|
||||
* Where to store public key delpending on type and storage backend
|
||||
*
|
||||
* @param boolean $pgp true: PGP, false: S/Mime
|
||||
* @param array $contact =null contact array to pass to get_backend()
|
||||
* @return boolean true: store as file, false: store with contact
|
||||
*/
|
||||
public function pubkey_use_file($pgp, array $contact=null)
|
||||
{
|
||||
return $pgp || empty($contact) || get_class($this->get_backend($contact)) == 'EGroupware\\Api\\Contacts\\Sql';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set keys for given email or account_id and key type based on regexp (SMIME or PGP), if user has necessary rights
|
||||
*
|
||||
* @param array $keys email|account_id => public key pairs to store
|
||||
* @param string $key_regexp regular expresion for key type indication (SMIME|PGP)
|
||||
* @param boolean $pgp true: PGP, false: S/Mime
|
||||
* @param boolean $allow_user_updates = null for admins, set config to allow regular users to store their key
|
||||
*
|
||||
* @return string message of the update operation result
|
||||
*/
|
||||
public function set_keys ($keys, $key_regexp, $allow_user_updates = null)
|
||||
public function set_keys ($keys, $pgp, $allow_user_updates = null)
|
||||
{
|
||||
if (isset($allow_user_updates) && isset($GLOBALS['egw_info']['user']['apps']['admin']))
|
||||
{
|
||||
@ -183,6 +195,15 @@ class addressbook_bo extends Api\Contacts
|
||||
Config::save_value('own_account_acl', $this->own_account_acl, 'phpgwapi');
|
||||
}
|
||||
}
|
||||
|
||||
$key_regexp = $pgp ? self::$pgp_key_regexp : Api\Mail\Smime::$certificate_regexp;
|
||||
$file = $pgp ? Api\Contacts::FILES_PGP_PUBKEY : Api\Contacts::FILES_SMIME_PUBKEY;
|
||||
|
||||
if (!preg_match($key_regexp, $key))
|
||||
{
|
||||
return lang('File is not a %1 public key!', $pgp ? lang('PGP') : lang('S/MIME'));
|
||||
}
|
||||
|
||||
$criteria = array();
|
||||
foreach($keys as $recipient => $key)
|
||||
{
|
||||
@ -210,7 +231,21 @@ class addressbook_bo extends Api\Contacts
|
||||
{
|
||||
$key = $keys[$contact['email']];
|
||||
}
|
||||
if (empty($contact['pubkey']) || !preg_match($key_regexp, $contact['pubkey']))
|
||||
|
||||
// key is stored in file for sql backend or allways for pgp key
|
||||
$path = null;
|
||||
if ($contact['id'] && $this->pubkey_use_file($pgp, $contact))
|
||||
{
|
||||
$path = Api\Link::vfs_path('addressbook', $contact['id'], $file);
|
||||
$contact['contact_files'] |= $pgp ? self::FILES_BIT_PGP_PUBKEY : self::FILES_BIT_SMIME_PUBKEY;
|
||||
// remove evtl. existing old pubkey
|
||||
if (preg_match($key_regexp, $contact['pubkey']))
|
||||
{
|
||||
$contact['pubkey'] = preg_replace($key_regexp, '', $contact['pubkey']);
|
||||
}
|
||||
$updated++;
|
||||
}
|
||||
elseif (empty($contact['pubkey']) || !preg_match($key_regexp, $contact['pubkey']))
|
||||
{
|
||||
$contact['pubkey'] .= $key;
|
||||
}
|
||||
@ -219,10 +254,20 @@ class addressbook_bo extends Api\Contacts
|
||||
$contact['pubkey'] = preg_replace($key_regexp, $key, $contact['pubkey']);
|
||||
}
|
||||
if ($this->check_perms(Acl::EDIT, $contact) && $this->save($contact))
|
||||
{
|
||||
if ($path)
|
||||
{
|
||||
// check_perms && save check ACL, in case of access only via own-account we have to use root to allow the update
|
||||
$backup = Api\Vfs::$is_root; Api\Vfs::$is_root = true;
|
||||
if (file_put_contents($path, $key)) ++$updated;
|
||||
Api\Vfs::$is_root = $backup;
|
||||
}
|
||||
else
|
||||
{
|
||||
++$updated;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($criteria == array('egw.addressbook.account_id' => array((int)$GLOBALS['egw_info']['user']['account_id'])))
|
||||
{
|
||||
$message = !$updated ? lang('Permissiong denied! Ask your administrator to allow regular uses to update their public keys.') :
|
||||
@ -241,17 +286,27 @@ class addressbook_bo extends Api\Contacts
|
||||
* EMail addresses are lowercased to make search case-insensitive
|
||||
*
|
||||
* @param string|int|array $recipients (array of) email addresses or numeric account-ids
|
||||
* @param string $key_regexp
|
||||
* @param string $criteria_filter
|
||||
*
|
||||
* @param boolean $pgp true: PGP, false: S/Mime public keys
|
||||
* @return array email|account_id => key pairs
|
||||
*/
|
||||
public function get_keys ($recipients, $key_regexp, $criteria_filter)
|
||||
protected function get_keys ($recipients, $pgp)
|
||||
{
|
||||
if (!$recipients) return array();
|
||||
|
||||
if (!is_array($recipients)) $recipients = array($recipients);
|
||||
|
||||
if ($pgp)
|
||||
{
|
||||
$key_regexp = self::$pgp_key_regexp;
|
||||
$criteria_filter = '%-----BEGIN PGP PUBLIC KEY BLOCK-----%';
|
||||
$file = Api\Contacts::FILES_PGP_PUBKEY;
|
||||
}
|
||||
else
|
||||
{
|
||||
$key_regexp = Api\Mail\Smime::$certificate_regexp;
|
||||
$criteria_filter = '%-----BEGIN CERTIFICATE-----%';
|
||||
$file = Api\Contacts::FILES_SMIME_PUBKEY;
|
||||
}
|
||||
$criteria = $result = array();
|
||||
foreach($recipients as &$recipient)
|
||||
{
|
||||
@ -264,11 +319,14 @@ class addressbook_bo extends Api\Contacts
|
||||
$criteria['contact_email'][] = $recipient = strtolower($recipient);
|
||||
}
|
||||
}
|
||||
foreach($this->search($criteria, array('account_id', 'contact_email', 'contact_pubkey'), '', '', '', false, 'OR', false,
|
||||
"contact_pubkey LIKE '". $criteria_filter ."'" ) as $contact)
|
||||
foreach($this->search($criteria, array('account_id', 'contact_email', 'contact_pubkey'),
|
||||
'', '', '', false, 'OR', false, null) as $contact)
|
||||
{
|
||||
$matches = null;
|
||||
if (preg_match($key_regexp, $contact['pubkey'], $matches))
|
||||
// first check for file and second for pubkey field (LDAP, AD or old SQL)
|
||||
if (($content = @file_get_contents(vfs_path('addressbook', $contact['id'], $file))) &&
|
||||
preg_match($key_regexp, $content, $matches) ||
|
||||
preg_match($key_regexp, $contact['pubkey'], $matches))
|
||||
{
|
||||
$contact['email'] = strtolower($contact['email']);
|
||||
if (empty($criteria['account_id']) || in_array($contact['email'], $recipients))
|
||||
@ -294,7 +352,7 @@ class addressbook_bo extends Api\Contacts
|
||||
*/
|
||||
public function get_smime_keys($recipients)
|
||||
{
|
||||
return $this->get_keys($recipients, Api\Mail\Smime::$certificate_regexp, '%-----BEGIN CERTIFICATE-----%');
|
||||
return $this->get_keys($recipients, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -307,6 +365,6 @@ class addressbook_bo extends Api\Contacts
|
||||
*/
|
||||
public function set_smime_keys($keys, $allow_user_updates=null)
|
||||
{
|
||||
return $this->set_keys($keys, Api\Mail\Smime::$certificate_regexp, $allow_user_updates);
|
||||
return $this->set_keys($keys, false, $allow_user_updates);
|
||||
}
|
||||
}
|
||||
|
@ -861,7 +861,7 @@ class addressbook_ui extends addressbook_bo
|
||||
unset($query['col_filter']['org_name']);
|
||||
unset($query['col_filter']['org_unit']);
|
||||
unset($query['col_filter']['adr_one_locality']);
|
||||
foreach(static::$duplicate_fields as $field => $label)
|
||||
foreach(array_keys(static::$duplicate_fields) as $field)
|
||||
{
|
||||
unset($query['col_filter'][$field]);
|
||||
}
|
||||
@ -1429,7 +1429,7 @@ window.egw_LAB.wait(function() {
|
||||
* Used for action on organisation and duplicate views
|
||||
* @param string/int $action 'delete', 'vcard', 'csv' or nummerical account_id to move contacts to that addessbook
|
||||
* @param array $checked contact id's to use if !$use_all
|
||||
* @param boolean $use_all if true use all contacts of the current selection (in the session)
|
||||
* @param boolean $use_all if true use all contacts of the current selection in the session (NOT used!)
|
||||
* @param int &$success number of succeded actions
|
||||
* @param int &$failed number of failed actions (not enought permissions)
|
||||
* @param string &$action_msg translated verb for the actions, to be used in a message like %1 contacts 'deleted'
|
||||
@ -1439,6 +1439,7 @@ window.egw_LAB.wait(function() {
|
||||
*/
|
||||
protected function find_grouped_ids($action,&$checked,$use_all,&$success,&$failed,&$action_msg,$session_name,&$msg)
|
||||
{
|
||||
unset($use_all);
|
||||
$grouped_contacts = array();
|
||||
foreach((array)$checked as $n => $id)
|
||||
{
|
||||
@ -2019,10 +2020,13 @@ window.egw_LAB.wait(function() {
|
||||
// unset the duplicate_filed after submit because we don't need to warn user for second time about contact duplication
|
||||
unset($content['presets_fields']);
|
||||
}
|
||||
$content['photo_unchanged'] = true; // hint no need to store photo
|
||||
/* seems not to be used any more in favor or ajax_update_photo
|
||||
if ($content['delete_photo'])
|
||||
{
|
||||
$content['jpegphoto'] = null;
|
||||
unset($content['delete_photo']);
|
||||
$content['photo_unchanged'] = false;
|
||||
}
|
||||
if (is_array($content['upload_photo']) && !empty($content['upload_photo']['tmp_name']) &&
|
||||
$content['upload_photo']['tmp_name'] != 'none' &&
|
||||
@ -2031,7 +2035,8 @@ window.egw_LAB.wait(function() {
|
||||
$content['jpegphoto'] = $this->resize_photo($f);
|
||||
fclose($f);
|
||||
unset($content['upload_photo']);
|
||||
}
|
||||
$content['photo_unchanged'] = false;
|
||||
}*/
|
||||
$links = false;
|
||||
if (!$content['id'] && is_array($content['link_to']['to_id']))
|
||||
{
|
||||
@ -3081,8 +3086,7 @@ window.egw_LAB.wait(function() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax method to update edited avatar photo via
|
||||
* avatar widget.
|
||||
* Ajax method to update edited avatar photo via avatar widget
|
||||
*
|
||||
* @param int $contact_id
|
||||
* @param file string $file = null null means to delete
|
||||
@ -3094,14 +3098,16 @@ window.egw_LAB.wait(function() {
|
||||
if ($file)
|
||||
{
|
||||
$filteredFile = substr($file, strpos($file, ",")+1);
|
||||
$decoded = base64_decode($filteredFile);
|
||||
// resize photo if wider then default width of 240pixel (keeping aspect ratio)
|
||||
$decoded = $this->resize_photo(base64_decode($filteredFile));
|
||||
}
|
||||
$contact['jpegphoto'] = is_null($file) ? $file : $decoded;
|
||||
$contact['photo_unchanged'] = false; // hint photo is changed
|
||||
|
||||
$success = $this->save($contact);
|
||||
if (!$success)
|
||||
{
|
||||
$response->alert($message);
|
||||
$response->alert($this->error);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3122,12 +3128,15 @@ window.egw_LAB.wait(function() {
|
||||
{
|
||||
$contact_id = $GLOBALS['egw']->accounts->id2name(substr($contact_id,8),'person_id');
|
||||
}
|
||||
if (!($contact = $this->read($contact_id)) || !$contact['jpegphoto'])
|
||||
if (!($contact = $this->read($contact_id)) ||
|
||||
empty($contact['jpegphoto']) && // LDAP/AD (not updated SQL)
|
||||
!(($contact['files'] & Api\Contacts::FILES_BIT_PHOTO) && // new SQL in VFS
|
||||
($size = filesize($url=Api\Link::vfs_path('addressbook', $contact_id, Api\Contacts::FILES_PHOTO)))))
|
||||
{
|
||||
Egw::redirect(Api\Image::find('addressbook','photo'));
|
||||
}
|
||||
// use an etag over the image mapp
|
||||
$etag = '"'.$contact['id'].':'.$contact['etag'].'"';
|
||||
$etag = '"'.$contact_id.':'.$contact['etag'].'"';
|
||||
if (!ob_get_contents())
|
||||
{
|
||||
header('Content-type: image/jpeg');
|
||||
@ -3143,11 +3152,16 @@ window.egw_LAB.wait(function() {
|
||||
{
|
||||
header("HTTP/1.1 304 Not Modified");
|
||||
}
|
||||
else
|
||||
elseif(!empty($contact['jpegphoto']))
|
||||
{
|
||||
header('Content-length: '.bytes($contact['jpegphoto']));
|
||||
echo $contact['jpegphoto'];
|
||||
}
|
||||
else
|
||||
{
|
||||
header('Content-length: '.$size);
|
||||
readfile($url);
|
||||
}
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ class addressbook_vcal extends Api\Contacts
|
||||
'X-ASSISTANT-TEL' => array('tel_assistent'),
|
||||
'UID' => array('uid'),
|
||||
'REV' => array('modified'),
|
||||
//'KEY' multivalued with mime-type to export PGP and S/Mime public keys
|
||||
//set for Apple: 'X-ABSHOWAS' => array('fileas_type'), // Horde vCard class uses uppercase prop-names!
|
||||
);
|
||||
|
||||
|
@ -176,6 +176,14 @@
|
||||
<description value="Next date"/>
|
||||
<link id="next_link"/>
|
||||
</row>
|
||||
<row valign="top">
|
||||
<description value="SMIME key"/>
|
||||
<vfs-upload id="addressbook:$cont[id]:.files/smime-pubkey.crt" accept=".crt,.pem,application/x-x509-ca-cert,application/x-x509-user-cert" mime="/application\/x-x509-(ca|user)-cert/"/>
|
||||
</row>
|
||||
<row valign="top">
|
||||
<description value="PGP key"/>
|
||||
<vfs-upload id="addressbook:$cont[id]:.files/pgp-pubkey.asc" accept=".asc,application/pgp-keys" mime="application/pgp-keys"/>
|
||||
</row>
|
||||
<row valign="top">
|
||||
<description for="pubkey" value="Public key"/>
|
||||
<textbox multiline="true" id="pubkey" rows="4" resize_ratio="0" class="et2_fullWidth"/>
|
||||
|
@ -12,7 +12,7 @@
|
||||
/* Basic information about this app */
|
||||
$setup_info['api']['name'] = 'api';
|
||||
$setup_info['api']['title'] = 'EGroupware API';
|
||||
$setup_info['api']['version'] = '16.9.002';
|
||||
$setup_info['api']['version'] = '16.9.004';
|
||||
$setup_info['api']['versions']['current_header'] = '1.29';
|
||||
// maintenance release in sync with changelog in doc/rpm-build/debian.changes
|
||||
$setup_info['api']['versions']['maintenance_release'] = $setup_info['api']['version'];
|
||||
@ -130,5 +130,3 @@ $setup_info['groupdav']['author'] = $setup_info['groupdav']['maintainer'] = arra
|
||||
$setup_info['groupdav']['license'] = 'GPL';
|
||||
$setup_info['groupdav']['hooks']['preferences'] = 'EGroupware\\Api\\CalDAV\\Hooks::menus';
|
||||
$setup_info['groupdav']['hooks']['settings'] = 'EGroupware\\Api\\CalDAV\\Hooks::settings';
|
||||
|
||||
|
||||
|
@ -253,13 +253,13 @@ $phpgw_baseline = array(
|
||||
'contact_creator' => array('type' => 'int','meta' => 'user','precision' => '4','nullable' => False,'comment' => 'account id of the creator'),
|
||||
'contact_modified' => array('type' => 'int','meta' => 'timestamp','precision' => '8','nullable' => False,'comment' => 'timestamp of the last modified'),
|
||||
'contact_modifier' => array('type' => 'int','meta' => 'user','precision' => '4','comment' => 'account id of the last modified'),
|
||||
'contact_jpegphoto' => array('type' => 'blob','comment' => 'photo of the contact (attachment)'),
|
||||
'account_id' => array('type' => 'int','meta' => 'user','precision' => '4','comment' => 'account id'),
|
||||
'contact_etag' => array('type' => 'int','precision' => '4','default' => '0','comment' => 'etag of the changes'),
|
||||
'contact_uid' => array('type' => 'ascii','precision' => '128','comment' => 'unique id of the contact'),
|
||||
'adr_one_countrycode' => array('type' => 'ascii','precision' => '2','comment' => 'countrycode (business)'),
|
||||
'adr_two_countrycode' => array('type' => 'ascii','precision' => '2','comment' => 'countrycode (private)'),
|
||||
'carddav_name' => array('type' => 'ascii','precision' => '128','comment' => 'name part of CardDAV URL, if specified by client')
|
||||
'carddav_name' => array('type' => 'ascii','precision' => '128','comment' => 'name part of CardDAV URL, if specified by client'),
|
||||
'contact_files' => array('type' => 'int','precision' => '1','default' => '0','comment' => '&1: photo, &2: pgp, &4: smime')
|
||||
),
|
||||
'pk' => array('contact_id'),
|
||||
'fk' => array(),
|
||||
|
@ -235,3 +235,164 @@ function api_upgrade16_9_001()
|
||||
return $GLOBALS['setup_info']['api']['currentver'] = '16.9.002';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add contact_files bit-field and strip jpeg photo, PGP & S/Mime pubkeys from table
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function api_upgrade16_9_002()
|
||||
{
|
||||
$GLOBALS['egw_setup']->oProc->AddColumn('egw_addressbook','contact_files',array(
|
||||
'type' => 'int',
|
||||
'precision' => '1',
|
||||
'default' => '0',
|
||||
'comment' => '&1: photo, &2: pgp, &4: smime'
|
||||
));
|
||||
|
||||
$junk_size = 100;
|
||||
$total = 0;
|
||||
Api\Vfs::$is_root = true;
|
||||
do {
|
||||
$n = 0;
|
||||
foreach($GLOBALS['egw_setup']->db->query("SELECT contact_id,contact_jpegphoto,contact_pubkey
|
||||
FROM egw_addressbook
|
||||
WHERE contact_jpegphoto IS NOT NULL OR contact_pubkey IS NOT NULL AND contact_pubkey LIKE '%-----%'",
|
||||
__LINE__, __FILE__, 0, $junk_size, false, Api\Db::FETCH_ASSOC) as $row)
|
||||
{
|
||||
$row['contact_files'] = 0;
|
||||
$contact_id = $row['contact_id'];
|
||||
unset($row['contact_id']);
|
||||
if ($row['contact_jpegphoto'] && ($fp = Api\Vfs::string_stream($row['contact_jpegphoto'])))
|
||||
{
|
||||
if (Api\Link::attach_file('addressbook', $contact_id, array(
|
||||
'name' => Api\Contacts::FILES_PHOTO,
|
||||
'type' => 'image/jpeg',
|
||||
'tmp_name' => $fp,
|
||||
)))
|
||||
{
|
||||
$row['contact_files'] |= Api\Contacts::FILES_BIT_PHOTO;
|
||||
$row['contact_jpegphoto'] = null;
|
||||
}
|
||||
fclose($fp);
|
||||
}
|
||||
foreach(array(
|
||||
array(addressbook_bo::$pgp_key_regexp, Api\Contacts::FILES_PGP_PUBKEY, Api\Contacts::FILES_BIT_PGP_PUBKEY, 'application/pgp-keys'),
|
||||
array(Api\Mail\Smime::$pubkey_regexp, Api\Contacts::FILES_SMIME_PUBKEY, Api\Contacts::FILES_BIT_SMIME_PUBKEY, 'application/x-pem-file'),
|
||||
) as $data)
|
||||
{
|
||||
list($regexp, $file, $bit, $mime) = $data;
|
||||
$matches = null;
|
||||
if ($row['contact_pubkey'] && preg_match($regexp, $row['contact_pubkey'], $matches) &&
|
||||
($fp = Api\Vfs::string_stream($matches[0])))
|
||||
{
|
||||
if (Api\Link::attach_file('addressbook', $contact_id, array(
|
||||
'name' => $file,
|
||||
'type' => $mime,
|
||||
'tmp_name' => $fp,
|
||||
)))
|
||||
{
|
||||
$row['contact_files'] |= $bit;
|
||||
$row['contact_pubkey'] = str_replace($matches[0], '', $row['contact_pubkey']);
|
||||
}
|
||||
fclose($fp);
|
||||
}
|
||||
}
|
||||
if (!trim($row['contact_pubkey'])) $row['contact_pubkey'] = null;
|
||||
|
||||
if ($row['contact_files'])
|
||||
{
|
||||
$GLOBALS['egw_setup']->db->update('egw_addressbook', $row, array('contact_id' => $contact_id), __LINE__, __FILE__);
|
||||
$total++;
|
||||
}
|
||||
$n++;
|
||||
}
|
||||
}
|
||||
while($n == $junk_size);
|
||||
Api\Vfs::$is_root = false;
|
||||
|
||||
return $GLOBALS['setup_info']['api']['currentver'] = '16.9.003';
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop contact_jpegphoto column
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function api_upgrade16_9_003()
|
||||
{
|
||||
$GLOBALS['egw_setup']->oProc->DropColumn('egw_addressbook',array(
|
||||
'fd' => array(
|
||||
'contact_id' => array('type' => 'auto','nullable' => False),
|
||||
'contact_tid' => array('type' => 'char','precision' => '1','default' => 'n'),
|
||||
'contact_owner' => array('type' => 'int','meta' => 'account','precision' => '8','nullable' => False,'comment' => 'account or group id of the adressbook'),
|
||||
'contact_private' => array('type' => 'int','precision' => '1','default' => '0','comment' => 'privat or personal'),
|
||||
'cat_id' => array('type' => 'ascii','meta' => 'category','precision' => '255','comment' => 'Category(s)'),
|
||||
'n_family' => array('type' => 'varchar','precision' => '64','comment' => 'Family name'),
|
||||
'n_given' => array('type' => 'varchar','precision' => '64','comment' => 'Given Name'),
|
||||
'n_middle' => array('type' => 'varchar','precision' => '64'),
|
||||
'n_prefix' => array('type' => 'varchar','precision' => '64','comment' => 'Prefix'),
|
||||
'n_suffix' => array('type' => 'varchar','precision' => '64','comment' => 'Suffix'),
|
||||
'n_fn' => array('type' => 'varchar','precision' => '128','comment' => 'Full name'),
|
||||
'n_fileas' => array('type' => 'varchar','precision' => '255','comment' => 'sort as'),
|
||||
'contact_bday' => array('type' => 'varchar','precision' => '12','comment' => 'Birtday'),
|
||||
'org_name' => array('type' => 'varchar','precision' => '128','comment' => 'Organisation'),
|
||||
'org_unit' => array('type' => 'varchar','precision' => '64','comment' => 'Department'),
|
||||
'contact_title' => array('type' => 'varchar','precision' => '64','comment' => 'jobtittle'),
|
||||
'contact_role' => array('type' => 'varchar','precision' => '64','comment' => 'role'),
|
||||
'contact_assistent' => array('type' => 'varchar','precision' => '64','comment' => 'Name of the Assistent (for phone number)'),
|
||||
'contact_room' => array('type' => 'varchar','precision' => '64','comment' => 'room'),
|
||||
'adr_one_street' => array('type' => 'varchar','precision' => '64','comment' => 'street (business)'),
|
||||
'adr_one_street2' => array('type' => 'varchar','precision' => '64','comment' => 'street (business) - 2. line'),
|
||||
'adr_one_locality' => array('type' => 'varchar','precision' => '64','comment' => 'city (business)'),
|
||||
'adr_one_region' => array('type' => 'varchar','precision' => '64','comment' => 'region (business)'),
|
||||
'adr_one_postalcode' => array('type' => 'varchar','precision' => '64','comment' => 'postalcode (business)'),
|
||||
'adr_one_countryname' => array('type' => 'varchar','precision' => '64','comment' => 'countryname (business)'),
|
||||
'contact_label' => array('type' => 'text','comment' => 'currently not used'),
|
||||
'adr_two_street' => array('type' => 'varchar','precision' => '64','comment' => 'street (private)'),
|
||||
'adr_two_street2' => array('type' => 'varchar','precision' => '64','comment' => 'street (private) - 2. line'),
|
||||
'adr_two_locality' => array('type' => 'varchar','precision' => '64','comment' => 'city (private)'),
|
||||
'adr_two_region' => array('type' => 'varchar','precision' => '64','comment' => 'region (private)'),
|
||||
'adr_two_postalcode' => array('type' => 'varchar','precision' => '64','comment' => 'postalcode (private)'),
|
||||
'adr_two_countryname' => array('type' => 'varchar','precision' => '64','comment' => 'countryname (private)'),
|
||||
'tel_work' => array('type' => 'varchar','precision' => '40','comment' => 'phone-number (business)'),
|
||||
'tel_cell' => array('type' => 'varchar','precision' => '40','comment' => 'mobil phone (business)'),
|
||||
'tel_fax' => array('type' => 'varchar','precision' => '40','comment' => 'fax-number (business)'),
|
||||
'tel_assistent' => array('type' => 'varchar','precision' => '40','comment' => 'phone-number assistent'),
|
||||
'tel_car' => array('type' => 'varchar','precision' => '40'),
|
||||
'tel_pager' => array('type' => 'varchar','precision' => '40','comment' => 'pager'),
|
||||
'tel_home' => array('type' => 'varchar','precision' => '40','comment' => 'phone-number (private)'),
|
||||
'tel_fax_home' => array('type' => 'varchar','precision' => '40','comment' => 'fax-number (private)'),
|
||||
'tel_cell_private' => array('type' => 'varchar','precision' => '40','comment' => 'mobil phone (private)'),
|
||||
'tel_other' => array('type' => 'varchar','precision' => '40','comment' => 'other phone'),
|
||||
'tel_prefer' => array('type' => 'varchar','precision' => '32','comment' => 'prefered phone-number'),
|
||||
'contact_email' => array('type' => 'varchar','precision' => '128','comment' => 'email address (business)'),
|
||||
'contact_email_home' => array('type' => 'varchar','precision' => '128','comment' => 'email address (private)'),
|
||||
'contact_url' => array('type' => 'varchar','precision' => '128','comment' => 'website (business)'),
|
||||
'contact_url_home' => array('type' => 'varchar','precision' => '128','comment' => 'website (private)'),
|
||||
'contact_freebusy_uri' => array('type' => 'ascii','precision' => '128','comment' => 'freebusy-url for calendar of the contact'),
|
||||
'contact_calendar_uri' => array('type' => 'ascii','precision' => '128','comment' => 'url for users calendar - currently not used'),
|
||||
'contact_note' => array('type' => 'varchar','precision' => '8192','comment' => 'notes field'),
|
||||
'contact_tz' => array('type' => 'varchar','precision' => '8','comment' => 'timezone difference'),
|
||||
'contact_geo' => array('type' => 'ascii','precision' => '32','comment' => 'currently not used'),
|
||||
'contact_pubkey' => array('type' => 'ascii','precision' => '16384','comment' => 'public key'),
|
||||
'contact_created' => array('type' => 'int','meta' => 'timestamp','precision' => '8','comment' => 'timestamp of the creation'),
|
||||
'contact_creator' => array('type' => 'int','meta' => 'user','precision' => '4','nullable' => False,'comment' => 'account id of the creator'),
|
||||
'contact_modified' => array('type' => 'int','meta' => 'timestamp','precision' => '8','nullable' => False,'comment' => 'timestamp of the last modified'),
|
||||
'contact_modifier' => array('type' => 'int','meta' => 'user','precision' => '4','comment' => 'account id of the last modified'),
|
||||
'account_id' => array('type' => 'int','meta' => 'user','precision' => '4','comment' => 'account id'),
|
||||
'contact_etag' => array('type' => 'int','precision' => '4','default' => '0','comment' => 'etag of the changes'),
|
||||
'contact_uid' => array('type' => 'ascii','precision' => '128','comment' => 'unique id of the contact'),
|
||||
'adr_one_countrycode' => array('type' => 'ascii','precision' => '2','comment' => 'countrycode (business)'),
|
||||
'adr_two_countrycode' => array('type' => 'ascii','precision' => '2','comment' => 'countrycode (private)'),
|
||||
'carddav_name' => array('type' => 'ascii','precision' => '128','comment' => 'name part of CardDAV URL, if specified by client'),
|
||||
'contact_files' => array('type' => 'int','precision' => '1','default' => '0','comment' => '&1: photo, &2: pgp, &4: smime')
|
||||
),
|
||||
'pk' => array('contact_id'),
|
||||
'fk' => array(),
|
||||
'ix' => array('contact_owner','cat_id','n_fileas','contact_modified','contact_uid','carddav_name',array('n_family','n_given'),array('n_given','n_family'),array('org_name','n_family','n_given')),
|
||||
'uc' => array('account_id')
|
||||
),'contact_jpegphoto');
|
||||
|
||||
return $GLOBALS['setup_info']['api']['currentver'] = '16.9.004';
|
||||
}
|
||||
|
@ -734,7 +734,7 @@ class Contacts extends Contacts\Storage
|
||||
$data[$name] = DateTime::server2user($data[$name], $date_format);
|
||||
}
|
||||
}
|
||||
$data['photo'] = $this->photo_src($data['id'],$data['jpegphoto'] || $data['files'] & self::FILES_PHOTO,'',$data['etag']);
|
||||
$data['photo'] = $this->photo_src($data['id'],$data['jpegphoto'] || ($data['files'] & self::FILES_BIT_PHOTO), '', $data['etag']);
|
||||
|
||||
// set freebusy_uri for accounts
|
||||
if (!$data['freebusy_uri'] && !$data['owner'] && $data['account_id'] && !is_object($GLOBALS['egw_setup']))
|
||||
@ -1054,7 +1054,9 @@ class Contacts extends Contacts\Storage
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes photo to 60*80 pixel and returns it
|
||||
* Resize photo down to 240pixel width and returns it
|
||||
*
|
||||
* Also makes sures photo is a JPEG.
|
||||
*
|
||||
* @param string|FILE $photo string with image or open filedescribtor
|
||||
* @param int $dst_w =240 max width to resize to
|
||||
|
@ -1022,6 +1022,30 @@ class Sql extends Api\Storage
|
||||
{
|
||||
$update['carddav_name'] = $this->data['id'].'.vcf';
|
||||
}
|
||||
// update photo in entry-directory, unless hinted it is unchanged
|
||||
if (!$err && $this->data['photo_unchanged'] !== true)
|
||||
{
|
||||
// in case files bit-field is not available read it from DB
|
||||
if (!isset($this->data['files']))
|
||||
{
|
||||
$this->data['files'] = (int)$this->db->select($this->table_name, 'contact_files', array(
|
||||
'contact_id' => $this->data['id'],
|
||||
), __LINE__, __FILE__)->fetchColumn();
|
||||
}
|
||||
$path = Api\Link::vfs_path('addressbook', $this->data['id'], Api\Contacts::FILES_PHOTO);
|
||||
$backup = Api\Vfs::$is_root; Api\Vfs::$is_root = true;
|
||||
if (empty($this->data['jpegphoto']))
|
||||
{
|
||||
unlink($path);
|
||||
$update['files'] = $this->data['files'] & ~Api\Contacts::FILES_BIT_PHOTO;
|
||||
}
|
||||
else
|
||||
{
|
||||
file_put_contents($path, $this->data['jpegphoto']);
|
||||
$update['files'] = $this->data['files'] | Api\Contacts::FILES_BIT_PHOTO;
|
||||
}
|
||||
Api\Vfs::$is_root = $backup;
|
||||
}
|
||||
if (!$err && $update)
|
||||
{
|
||||
parent::update($update);
|
||||
|
@ -159,6 +159,21 @@ class Storage
|
||||
*/
|
||||
var $content_types = array();
|
||||
|
||||
/**
|
||||
* Directory to store striped photo or public keys in VFS directory of entry
|
||||
*/
|
||||
const FILES_DIRECTORY = '.files';
|
||||
const FILES_PHOTO = '.files/photo.jpeg';
|
||||
const FILES_PGP_PUBKEY = '.files/pgp-pubkey.asc';
|
||||
const FILES_SMIME_PUBKEY = '.files/smime-pubkey.crt';
|
||||
|
||||
/**
|
||||
* Constant for bit-field "contact_files" storing what files are available
|
||||
*/
|
||||
const FILES_BIT_PHOTO = 1;
|
||||
const FILES_BIT_PGP_PUBKEY = 2;
|
||||
const FILES_BIT_SMIME_PUBKEY = 4;
|
||||
|
||||
/**
|
||||
* These fields are options for checking for duplicate contacts
|
||||
*
|
||||
@ -886,7 +901,7 @@ class Storage
|
||||
foreach($rows as $n => $row)
|
||||
{
|
||||
$rows[$n]['id'] = 'duplicate:';
|
||||
foreach(static::$duplicate_fields as $by => $by_label)
|
||||
foreach(array_keys(static::$duplicate_fields) as $by)
|
||||
{
|
||||
if (strpos($row[$by],'&')!==false) $row[$by] = str_replace('&','*AND*',$row[$by]);
|
||||
if($row[$by])
|
||||
|
@ -84,10 +84,10 @@ class Vfs extends File
|
||||
// Single file, missing extension in path
|
||||
else if (substr($path, -1) != '/' && !Api\Vfs::file_exists($path) && $relpath && substr($relpath,-4,1) !== '.')
|
||||
{
|
||||
$find = Api\Vfs::find(substr($path,0, - strlen($relpath)), array(
|
||||
$find = Api\Vfs::find(Api\Vfs::dirname($path), array(
|
||||
'type' => 'f',
|
||||
'maxdepth' => 1,
|
||||
'name' => $relpath . '*'
|
||||
'name' => Api\Vfs::basename($path).'.*',
|
||||
));
|
||||
foreach($find as $file)
|
||||
{
|
||||
@ -264,11 +264,14 @@ class Vfs extends File
|
||||
{
|
||||
// add extension to path
|
||||
$parts = explode('.',$filename);
|
||||
if (($extension = array_pop($parts)) && Api\MimeMagic::ext2mime($extension)) // really an extension --> add it to path
|
||||
// check if path already contains a valid extension --> dont add an other one
|
||||
$path_parts = explode('.', $path);
|
||||
if (count($path_parts) > 2 && (!($extension = array_pop($path_parts)) || !Api\MimeMagic::ext2mime($extension)) &&
|
||||
($extension = array_pop($parts)) && Api\MimeMagic::ext2mime($extension)) // really an extension --> add it to path
|
||||
{
|
||||
$path .= '.'.$extension;
|
||||
$file['name'] = Api\Vfs::basename($path);
|
||||
}
|
||||
$file['name'] = Api\Vfs::basename($path);
|
||||
}
|
||||
else if ($path) // multiple upload with dir given (trailing slash)
|
||||
{
|
||||
|
@ -2534,6 +2534,34 @@ class Vfs
|
||||
{
|
||||
return Vfs\StreamWrapper::load_wrapper($scheme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return stream with given string as content
|
||||
*
|
||||
* @param string $string
|
||||
* @return boolean|resource stream or false on error
|
||||
*/
|
||||
static function string_stream($string)
|
||||
{
|
||||
if (!($fp = fopen('php://temp', 'rw')))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$pos = 0;
|
||||
$len = strlen($string);
|
||||
do {
|
||||
if (!($written = fwrite($fp, substr($string, $pos))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$pos += $written;
|
||||
}
|
||||
while ($len < $pos);
|
||||
|
||||
rewind($fp);
|
||||
|
||||
return $fp;
|
||||
}
|
||||
}
|
||||
|
||||
Vfs::init_static();
|
||||
|
Loading…
Reference in New Issue
Block a user