} else {
// For other shares only permissions is valid.
- if ($permissions === null) {
+ if ($share->getShareType() !== \OCP\Share::SHARE_TYPE_EMAIL && $permissions === null) {
throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
- } else {
+ } elseif ($permissions !== null) {
$permissions = (int)$permissions;
$share->setPermissions($permissions);
}
+
+ if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
+ if ($expireDate === '') {
+ $share->setExpirationDate(null);
+ } else if ($expireDate !== null) {
+ try {
+ $expireDate = $this->parseDate($expireDate);
+ } catch (\Exception $e) {
+ throw new OCSBadRequestException($e->getMessage());
+ }
+ $share->setExpirationDate($expireDate);
+ }
+
+ if ($password === '') {
+ $share->setPassword(null);
+ } else if ($password !== null) {
+ $share->setPassword($password);
+ }
+ }
}
if ($permissions !== null && $share->getShareOwner() !== $this->currentUser) {
protected function createMailBody($template, $filename, $link, $owner, $initiator) {
$mailBodyTemplate = new Template('sharebymail', $template, '');
- $mailBodyTemplate->assign ('filename', $filename);
+ $mailBodyTemplate->assign ('filename', \OCP\Util::sanitizeHTML($filename));
$mailBodyTemplate->assign ('link', $link);
- $mailBodyTemplate->assign ('owner', $owner);
- $mailBodyTemplate->assign ('initiator', $initiator);
+ $mailBodyTemplate->assign ('owner', \OCP\Util::sanitizeHTML($owner));
+ $mailBodyTemplate->assign ('initiator', \OCP\Util::sanitizeHTML($initiator));
$mailBodyTemplate->assign ('onBehalfOf', $initiator !== $owner);
$mailBody = $mailBodyTemplate->fetchPage();
$this->l->t('Failed to create the E-mail'));
}
+ /**
+ * send password to recipient of a mail share
+ *
+ * @param string $filename
+ * @param string $initiator
+ * @param string $shareWith
+ */
+ protected function sendPassword($filename, $initiator, $shareWith, $password) {
+ $initiatorUser = $this->userManager->get($initiator);
+ $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
+ $subject = (string)$this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]);
+
+ $message = $this->mailer->createMessage();
+ $htmlBody = $this->createMailBodyToSendPassword('mailpassword', $filename, $initiatorDisplayName, $password);
+ $textBody = $this->createMailBodyToSendPassword('altmailpassword', $filename,$initiatorDisplayName, $password);
+ $message->setTo([$shareWith]);
+ $message->setSubject($subject);
+ $message->setBody($textBody, 'text/plain');
+ $message->setHtmlBody($htmlBody);
+ $this->mailer->send($message);
+
+ }
+
+ /**
+ * create mail body to send password to recipient
+ *
+ * @param string $filename
+ * @param string $initiator
+ * @param string $password
+ * @return string plain text mail
+ * @throws HintException
+ */
+ protected function createMailBodyToSendPassword($template, $filename, $initiator, $password) {
+
+ $mailBodyTemplate = new Template('sharebymail', $template, '');
+ $mailBodyTemplate->assign ('filename', \OCP\Util::sanitizeHTML($filename));
+ $mailBodyTemplate->assign ('password', \OCP\Util::sanitizeHTML($password));
+ $mailBodyTemplate->assign ('initiator', \OCP\Util::sanitizeHTML($initiator));
+ $mailBody = $mailBodyTemplate->fetchPage();
+
+ if (is_string($mailBody)) {
+ return $mailBody;
+ }
+
+ throw new HintException('Failed to create the E-mail',
+ $this->l->t('Failed to create the E-mail'));
+ }
+
+
/**
* generate share token
*
* Update a share
*
* @param IShare $share
+ * @param string|null $plainTextPassword
* @return IShare The share object
*/
- public function update(IShare $share) {
+ public function update(IShare $share, $plainTextPassword = null) {
+
+ $originalShare = $this->getShareById($share->getId());
+
+ // a real password was given
+ $validPassword = $plainTextPassword !== null && $plainTextPassword !== '';
+
+ if($validPassword && $originalShare->getPassword() !== $share->getPassword()) {
+ $this->sendPassword($share->getNode()->getName(), $share->getSharedBy(), $share->getSharedWith(), $plainTextPassword);
+ }
/*
- * We allow updating the permissions of mail shares
+ * We allow updating the permissions and password of mail shares
*/
$qb = $this->dbConnection->getQueryBuilder();
- $qb->update('share')
- ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
- ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
- ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
- ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
- ->execute();
+ $qb->update('share')
+ ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
+ ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
+ ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
+ ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
+ ->set('password', $qb->createNamedParameter($share->getPassword()))
+ ->execute();
return $share;
}
$shareTime->setTimestamp((int)$data['stime']);
$share->setShareTime($shareTime);
$share->setSharedWith($data['share_with']);
+ $share->setPassword($data['password']);
if ($data['uid_initiator'] !== null) {
$share->setShareOwner($data['uid_owner']);
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/** @var OC_Theme $theme */
+/** @var array $_ */
+print_unescaped($l->t("Hey there,\n\n%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n\nIt is protected with the following password: %s\n\n", [$_['initiator'], $_['filename'], $_['password']]));
+// TRANSLATORS term at the end of a mail
+p($l->t("Cheers!"));
+print_unescaped("\n");
+?>
+
+ --
+<?php p($theme->getName() . ' - ' . $theme->getSlogan()); ?>
+<?php print_unescaped("\n".$theme->getBaseUrl());
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/** @var OC_Theme $theme */
+/** @var array $_ */
+?>
+
+<table cellspacing="0" cellpadding="0" border="0" width="100%">
+ <tr><td>
+ <table cellspacing="0" cellpadding="0" border="0" width="600px">
+ <tr>
+ <td colspan="2" bgcolor="<?php p($theme->getMailHeaderColor());?>">
+ <img src="<?php p(\OC::$server->getURLGenerator()->getAbsoluteURL(image_path('', 'logo-mail.png'))); ?>" alt="<?php p($theme->getName()); ?>"/>
+ </td>
+ </tr>
+ <tr><td colspan="2"> </td></tr>
+ <tr>
+ <td width="20px"> </td>
+ <td style="font-weight:normal; font-size:0.8em; line-height:1.2em; font-family:verdana,'arial',sans;">
+ <?php
+ print_unescaped($l->t('Hey there,<br><br>%s shared <i>%s</i> with you.<br>You should have already received a separate mail with a link to access it.<br><br>It is protected with the following password: %s<br><br>', [$_['initiator'], $_['filename'], $_['password']]));
+ // TRANSLATORS term at the end of a mail
+ p($l->t('Cheers!'));
+ ?>
+ </td>
+ </tr>
+ <tr><td colspan="2"> </td></tr>
+ <tr>
+ <td width="20px"> </td>
+ <td style="font-weight:normal; font-size:0.8em; line-height:1.2em; font-family:verdana,'arial',sans;">--<br>
+ <?php p($theme->getName()); ?> -
+ <?php p($theme->getSlogan()); ?>
+ <br><a href="<?php p($theme->getBaseUrl()); ?>"><?php p($theme->getBaseUrl());?></a>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2"> </td>
+ </tr>
+ </table>
+ </td></tr>
+</table>
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\Mail\IMailer;
+use OCP\Security\IHasher;
use OCP\Security\ISecureRandom;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager;
.popovermenu .datepicker {
margin-left: 35px;
}
+
+.popovermenu .passwordField {
+ margin-left: 35px;
+ width: inherit !important;
+}
+
/* globals Handlebars */
(function() {
+
+ var PASSWORD_PLACEHOLDER = '**********';
+ var PASSWORD_PLACEHOLDER_MESSAGE = t('core', 'Choose a password for the mail share');
+
if (!OC.Share) {
OC.Share = {};
}
'</div>' +
'</span>' +
'</li>' +
- '<li>' +
+ '{{#if isMailShare}}' +
+ '<li>' +
+ '<span class="shareOption menuitem">' +
+ '<input id="password-{{cid}}-{{shareId}}" type="checkbox" name="password" class="password checkbox" {{#if isPasswordSet}}checked="checked"{{/if}}" />' +
+ '<label for="password-{{cid}}-{{shareId}}">{{passwordLabel}}</label>' +
+ '<div class="passwordContainer-{{cid}}-{{shareId}} {{#unless isPasswordSet}}hidden{{/unless}}">' +
+ ' <label for="passwordField-{{cid}}-{{shareId}}" class="hidden-visually" value="{{password}}">{{passwordLabel}}</label>' +
+ ' <input id="passwordField-{{cid}}-{{shareId}}" class="passwordField" type="password" placeholder="{{passwordPlaceholder}}" value="{{passwordValue}}" />' +
+ ' <span class="icon-loading-small hidden"></span>' +
+ '</div>' +
+ '</span>' +
+ '</li>' +
+ '{{/if}}' +
+ '<li>' +
'<a href="#" class="unshare"><span class="icon-loading-small hidden"></span><span class="icon icon-delete"></span><span>{{unshareLabel}}</span></a>' +
'</li>' +
'</ul>' +
'click .unshare': 'onUnshare',
'click .icon-more': 'onToggleMenu',
'click .permissions': 'onPermissionChange',
- 'click .expireDate' : 'onExpireDateChange'
+ 'click .expireDate' : 'onExpireDateChange',
+ 'click .password' : 'onMailSharePasswordProtectChange',
+ 'keyup input.passwordField': 'onMailSharePasswordKeyUp',
+ 'focusout input.passwordField': 'onMailSharePasswordEntered'
},
initialize: function(options) {
shareWithTitle = shareWith;
}
+ var share = this.model.get('shares')[shareIndex];
+ var password = share.password;
+ var hasPassword = password !== null && password !== '';
+
+
return _.extend(hasPermissionOverride, {
cid: this.cid,
hasSharePermission: this.model.hasSharePermission(shareIndex),
isRemoteShare: shareType === OC.Share.SHARE_TYPE_REMOTE,
isMailShare: shareType === OC.Share.SHARE_TYPE_EMAIL,
isCircleShare: shareType === OC.Share.SHARE_TYPE_CIRCLE,
- isFileSharedByMail: shareType === OC.Share.SHARE_TYPE_EMAIL && !this.model.isFolder()
+ isFileSharedByMail: shareType === OC.Share.SHARE_TYPE_EMAIL && !this.model.isFolder(),
+ isPasswordSet: hasPassword,
+ passwordPlaceholder: hasPassword ? PASSWORD_PLACEHOLDER : PASSWORD_PLACEHOLDER_MESSAGE,
});
},
updatePermissionLabel: t('core', 'can change'),
deletePermissionLabel: t('core', 'can delete'),
expireDateLabel: t('core', 'set expiration data'),
+ passwordLabel: t('core', 'password protect'),
crudsLabel: t('core', 'access control'),
triangleSImage: OC.imagePath('core', 'actions/triangle-s'),
isResharingAllowed: this.configModel.get('isResharingAllowed'),
}
},
+ onMailSharePasswordProtectChange: function(event) {
+ var element = $(event.target);
+ var li = element.closest('li[data-share-id]');
+ var shareId = li.data('share-id');
+ var passwordContainerClass = '.passwordContainer-' + this.cid + '-' + shareId;
+ var passwordContainer = $(passwordContainerClass);
+ var inputClass = '#passwordField-' + this.cid + '-' + shareId;
+ var passwordField = $(inputClass);
+ var state = element.prop('checked');
+ passwordContainer.toggleClass('hidden', !state);
+ if (!state) {
+ this.model.updateShare(shareId, {password: ''});
+ passwordField.attr('value', '');
+ passwordField.attr('placeholder', PASSWORD_PLACEHOLDER_MESSAGE);
+ } else {
+ var passwordField = '#passwordField-' + this.cid + '-' + shareId;
+ this.$(passwordField).focus();
+ }
+ },
+
+ onMailSharePasswordKeyUp: function(event) {
+ if(event.keyCode === 13) {
+ this.onMailSharePasswordEntered(event);
+ }
+ },
+
+ onMailSharePasswordEntered: function(event) {
+ var passwordField = $(event.target);
+ var li = passwordField.closest('li[data-share-id]');
+ var shareId = li.data('share-id');
+ var passwordContainerClass = '.passwordContainer-' + this.cid + '-' + shareId;
+ var loading = this.$el.find(passwordContainerClass + ' .icon-loading-small');
+ if (!loading.hasClass('hidden')) {
+ // still in process
+ return;
+ }
+
+ passwordField.removeClass('error');
+ var password = passwordField.val();
+ // in IE9 the password might be the placeholder due to bugs in the placeholders polyfill
+ if(password === '' || password === PASSWORD_PLACEHOLDER || password === PASSWORD_PLACEHOLDER_MESSAGE) {
+ return;
+ }
+
+ loading
+ .removeClass('hidden')
+ .addClass('inlineblock');
+
+
+ this.model.updateShare(shareId, {
+ password: password
+ }, {
+ error: function(model, msg) {
+ // destroy old tooltips
+ passwordField.tooltip('destroy');
+ loading.removeClass('inlineblock').addClass('hidden');
+ passwordField.addClass('error');
+ passwordField.attr('title', msg);
+ passwordField.tooltip({placement: 'bottom', trigger: 'manual'});
+ passwordField.tooltip('show');
+ },
+ success: function(model, msg) {
+ passwordField.blur();
+ passwordField.attr('value', '');
+ passwordField.attr('placeholder', PASSWORD_PLACEHOLDER);
+ loading.removeClass('inlineblock').addClass('hidden');
+ }
+ });
+ },
+
onPermissionChange: function(event) {
event.preventDefault();
event.stopPropagation();
}
}
+ $plainTextPassword = null;
+ if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK || $share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
+ // Password updated.
+ if ($share->getPassword() !== $originalShare->getPassword()) {
+ //Verify the password
+ $this->verifyPassword($share->getPassword());
+
+ // If a password is set. Hash it!
+ if ($share->getPassword() !== null) {
+ $plainTextPassword = $share->getPassword();
+ $share->setPassword($this->hasher->hash($plainTextPassword));
+ }
+ }
+ }
+
$this->pathCreateChecks($share->getNode());
// Now update the share!
$provider = $this->factory->getProviderForType($share->getShareType());
- $share = $provider->update($share);
+ if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
+ $share = $provider->update($share, $plainTextPassword);
+ } else {
+ $share = $provider->update($share);
+ }
if ($expirationDateUpdated === true) {
\OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [
* @return bool
*/
public function checkPassword(\OCP\Share\IShare $share, $password) {
- if ($share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK) {
+ $passwordProtected = $share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK
+ || $share->getShareType() !== \OCP\Share::SHARE_TYPE_EMAIL;
+ if (!$passwordProtected) {
//TODO maybe exception?
return false;
}