getUser(); $this->mountManager = $mountManager; $this->uid = $user ? $user->getUID() : null; } /** * add new server-to-server share * * @param string $remote * @param string $token * @param string $password * @param string $name * @param string $owner * @param int $shareType * @param boolean $accepted * @param string $user * @param string $remoteId * @param int $parent * @return Mount|null * @throws \Doctrine\DBAL\Exception */ public function addShare($remote, $token, $password, $name, $owner, $shareType, $accepted = false, $user = null, $remoteId = '', $parent = -1) { $user = $user ?? $this->uid; $accepted = $accepted ? IShare::STATUS_ACCEPTED : IShare::STATUS_PENDING; $name = Filesystem::normalizePath('/' . $name); if ($accepted !== IShare::STATUS_ACCEPTED) { // To avoid conflicts with the mount point generation later, // we only use a temporary mount point name here. The real // mount point name will be generated when accepting the share, // using the original share item name. $tmpMountPointName = '{{TemporaryMountPointName#' . $name . '}}'; $mountPoint = $tmpMountPointName; $hash = md5($tmpMountPointName); $data = [ 'remote' => $remote, 'share_token' => $token, 'password' => $password, 'name' => $name, 'owner' => $owner, 'user' => $user, 'mountpoint' => $mountPoint, 'mountpoint_hash' => $hash, 'accepted' => $accepted, 'remote_id' => $remoteId, 'share_type' => $shareType, ]; $i = 1; while (!$this->connection->insertIfNotExist('*PREFIX*share_external', $data, ['user', 'mountpoint_hash'])) { // The external share already exists for the user $data['mountpoint'] = $tmpMountPointName . '-' . $i; $data['mountpoint_hash'] = md5($data['mountpoint']); $i++; } return null; } $mountPoint = Files::buildNotExistingFileName('/', $name); $mountPoint = Filesystem::normalizePath('/' . $mountPoint); $hash = md5($mountPoint); $this->writeShareToDb($remote, $token, $password, $name, $owner, $user, $mountPoint, $hash, $accepted, $remoteId, $parent, $shareType); $options = [ 'remote' => $remote, 'token' => $token, 'password' => $password, 'mountpoint' => $mountPoint, 'owner' => $owner ]; return $this->mountShare($options, $user); } /** * write remote share to the database * * @param $remote * @param $token * @param $password * @param $name * @param $owner * @param $user * @param $mountPoint * @param $hash * @param $accepted * @param $remoteId * @param $parent * @param $shareType * * @return void * @throws \Doctrine\DBAL\Driver\Exception */ private function writeShareToDb($remote, $token, $password, $name, $owner, $user, $mountPoint, $hash, $accepted, $remoteId, $parent, $shareType): void { $query = $this->connection->prepare(' INSERT INTO `*PREFIX*share_external` (`remote`, `share_token`, `password`, `name`, `owner`, `user`, `mountpoint`, `mountpoint_hash`, `accepted`, `remote_id`, `parent`, `share_type`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) '); $query->execute([$remote, $token, $password, $name, $owner, $user, $mountPoint, $hash, $accepted, $remoteId, $parent, $shareType]); } private function fetchShare(int $id): array|false { $getShare = $this->connection->prepare(' SELECT `id`, `remote`, `remote_id`, `share_token`, `name`, `owner`, `user`, `mountpoint`, `accepted`, `parent`, `share_type`, `password`, `mountpoint_hash` FROM `*PREFIX*share_external` WHERE `id` = ?'); $result = $getShare->execute([$id]); $share = $result->fetch(); $result->closeCursor(); return $share; } /** * get share by token * * @param string $token * @return mixed share of false */ private function fetchShareByToken($token) { $getShare = $this->connection->prepare(' SELECT `id`, `remote`, `remote_id`, `share_token`, `name`, `owner`, `user`, `mountpoint`, `accepted`, `parent`, `share_type`, `password`, `mountpoint_hash` FROM `*PREFIX*share_external` WHERE `share_token` = ?'); $result = $getShare->execute([$token]); $share = $result->fetch(); $result->closeCursor(); return $share; } private function fetchUserShare($parentId, $uid) { $getShare = $this->connection->prepare(' SELECT `id`, `remote`, `remote_id`, `share_token`, `name`, `owner`, `user`, `mountpoint`, `accepted`, `parent`, `share_type`, `password`, `mountpoint_hash` FROM `*PREFIX*share_external` WHERE `parent` = ? AND `user` = ?'); $result = $getShare->execute([$parentId, $uid]); $share = $result->fetch(); $result->closeCursor(); if ($share !== false) { return $share; } return null; } public function getShare(int $id, ?string $user = null): array|false { $user = $user ?? $this->uid; $share = $this->fetchShare($id); if ($share === false) { return false; } // check if the user is allowed to access it if ($this->canAccessShare($share, $user)) { return $share; } return false; } /** * Get share by token * * @param string $token * @return array|false */ public function getShareByToken(string $token): array|false { $share = $this->fetchShareByToken($token); // We do not check if the user is allowed to access it here, // as this is not used from a user context. if ($share === false) { return false; } return $share; } private function canAccessShare(array $share, string $user): bool { $validShare = isset($share['share_type']) && isset($share['user']); if (!$validShare) { return false; } // If the share is a user share, check if the user is the recipient if ((int)$share['share_type'] === IShare::TYPE_USER && $share['user'] === $user) { return true; } // If the share is a group share, check if the user is in the group if ((int)$share['share_type'] === IShare::TYPE_GROUP) { $parentId = (int)$share['parent']; if ($parentId !== -1) { // we just retrieved a sub-share, switch to the parent entry for verification $groupShare = $this->fetchShare($parentId); } else { $groupShare = $share; } $user = $this->userManager->get($user); if ($this->groupManager->get($groupShare['user'])->inGroup($user)) { return true; } } return false; } /** * Updates accepted flag in the database * * @param int $id */ private function updateAccepted(int $shareId, bool $accepted) : void { $query = $this->connection->prepare(' UPDATE `*PREFIX*share_external` SET `accepted` = ? WHERE `id` = ?'); $updateResult = $query->execute([$accepted ? 1 : 0, $shareId]); $updateResult->closeCursor(); } /** * accept server-to-server share * * @param int $id * @return bool True if the share could be accepted, false otherwise */ public function acceptShare(int $id, ?string $user = null) { // If we're auto-accepting a share, we need to know the user id // as there is no session available while processing the share // from the remote server request. $user = $user ?? $this->uid; if ($user === null) { $this->logger->error('No user specified for accepting share'); return false; } $share = $this->getShare($id, $user); $result = false; if ($share) { \OC_Util::setupFS($user); $shareFolder = Helper::getShareFolder(null, $user); $mountPoint = Files::buildNotExistingFileName($shareFolder, $share['name']); $mountPoint = Filesystem::normalizePath($mountPoint); $hash = md5($mountPoint); $userShareAccepted = false; if ((int)$share['share_type'] === IShare::TYPE_USER) { $acceptShare = $this->connection->prepare(' UPDATE `*PREFIX*share_external` SET `accepted` = ?, `mountpoint` = ?, `mountpoint_hash` = ? WHERE `id` = ? AND `user` = ?'); $userShareAccepted = $acceptShare->execute([1, $mountPoint, $hash, $id, $user]); } else { $parentId = (int)$share['parent']; if ($parentId !== -1) { // this is the sub-share $subshare = $share; } else { $subshare = $this->fetchUserShare($id, $user); } if ($subshare !== null) { try { $acceptShare = $this->connection->prepare(' UPDATE `*PREFIX*share_external` SET `accepted` = ?, `mountpoint` = ?, `mountpoint_hash` = ? WHERE `id` = ? AND `user` = ?'); $acceptShare->execute([1, $mountPoint, $hash, $subshare['id'], $user]); $result = true; } catch (Exception $e) { $this->logger->emergency('Could not update share', ['exception' => $e]); $result = false; } } else { try { $this->writeShareToDb( $share['remote'], $share['share_token'], $share['password'], $share['name'], $share['owner'], $user, $mountPoint, $hash, 1, $share['remote_id'], $id, $share['share_type']); $result = true; } catch (Exception $e) { $this->logger->emergency('Could not create share', ['exception' => $e]); $result = false; } } } if ($userShareAccepted !== false) { $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'accept'); $event = new FederatedShareAddedEvent($share['remote']); $this->eventDispatcher->dispatchTyped($event); $this->eventDispatcher->dispatchTyped(new InvalidateMountCacheEvent($this->userManager->get($user))); $result = true; } } // Make sure the user has no notification for something that does not exist anymore. $this->processNotification($id, $user); return $result; } /** * decline server-to-server share * * @param int $id * @return bool True if the share could be declined, false otherwise */ public function declineShare(int $id, ?string $user = null) { $user = $user ?? $this->uid; if ($user === null) { $this->logger->error('No user specified for declining share'); return false; } $share = $this->getShare($id, $user); $result = false; if ($share && (int)$share['share_type'] === IShare::TYPE_USER) { $removeShare = $this->connection->prepare(' DELETE FROM `*PREFIX*share_external` WHERE `id` = ? AND `user` = ?'); $removeShare->execute([$id, $user]); $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline'); $this->processNotification($id, $user); $result = true; } elseif ($share && (int)$share['share_type'] === IShare::TYPE_GROUP) { $parentId = (int)$share['parent']; if ($parentId !== -1) { // this is the sub-share $subshare = $share; } else { $subshare = $this->fetchUserShare($id, $user); } if ($subshare !== null) { try { $this->updateAccepted((int)$subshare['id'], false); $result = true; } catch (Exception $e) { $this->logger->emergency('Could not update share', ['exception' => $e]); $result = false; } } else { try { $this->writeShareToDb( $share['remote'], $share['share_token'], $share['password'], $share['name'], $share['owner'], $user, $share['mountpoint'], $share['mountpoint_hash'], 0, $share['remote_id'], $id, $share['share_type']); $result = true; } catch (Exception $e) { $this->logger->emergency('Could not create share', ['exception' => $e]); $result = false; } } $this->processNotification($id, $user); } return $result; } public function processNotification(int $remoteShare, ?string $user = null): void { $user = $user ?? $this->uid; if ($user === null) { $this->logger->error('No user specified for processing notification'); return; } $share = $this->fetchShare($remoteShare); if ($share === false) { return; } $filter = $this->notificationManager->createNotification(); $filter->setApp('files_sharing') ->setUser($user) ->setObject('remote_share', (string)$remoteShare); $this->notificationManager->markProcessed($filter); } /** * inform remote server whether server-to-server share was accepted/declined * * @param string $remote * @param string $token * @param string $remoteId Share id on the remote host * @param string $feedback * @return boolean */ private function sendFeedbackToRemote($remote, $token, $remoteId, $feedback) { $result = $this->tryOCMEndPoint($remote, $token, $remoteId, $feedback); if (is_array($result)) { return true; } $federationEndpoints = $this->discoveryService->discover($remote, 'FEDERATED_SHARING'); $endpoint = $federationEndpoints['share'] ?? '/ocs/v2.php/cloud/shares'; $url = rtrim($remote, '/') . $endpoint . '/' . $remoteId . '/' . $feedback . '?format=' . Share::RESPONSE_FORMAT; $fields = ['token' => $token]; $client = $this->clientService->newClient(); try { $response = $client->post( $url, [ 'body' => $fields, 'connect_timeout' => 10, ] ); } catch (\Exception $e) { return false; } $status = json_decode($response->getBody(), true); return ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200); } /** * try send accept message to ocm end-point * * @param string $remoteDomain * @param string $token * @param string $remoteId id of the share * @param string $feedback * @return array|false */ protected function tryOCMEndPoint($remoteDomain, $token, $remoteId, $feedback) { switch ($feedback) { case 'accept': $notification = $this->cloudFederationFactory->getCloudFederationNotification(); $notification->setMessage( 'SHARE_ACCEPTED', 'file', $remoteId, [ 'sharedSecret' => $token, 'message' => 'Recipient accept the share' ] ); return $this->cloudFederationProviderManager->sendNotification($remoteDomain, $notification); case 'decline': $notification = $this->cloudFederationFactory->getCloudFederationNotification(); $notification->setMessage( 'SHARE_DECLINED', 'file', $remoteId, [ 'sharedSecret' => $token, 'message' => 'Recipient declined the share' ] ); return $this->cloudFederationProviderManager->sendNotification($remoteDomain, $notification); } return false; } /** * remove '/user/files' from the path and trailing slashe
package com.vaadin.tests;

import com.vaadin.server.LegacyApplication;
import com.vaadin.ui.Accordion;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.LegacyWindow;
import com.vaadin.ui.Panel;
import com.vaadin.ui.TabSheet;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.VerticalSplitPanel;
import com.vaadin.ui.Window;
import com.vaadin.v7.ui.OptionGroup;
import com.vaadin.v7.ui.Table;

public class ScrollbarStressTest extends LegacyApplication {

    final LegacyWindow main = new LegacyWindow("Scrollbar Stress Test");

    final Panel panel = new Panel("Panel");
    final VerticalSplitPanel splitPanel = new VerticalSplitPanel();
    final Accordion accordion = new Accordion();
    final TabSheet tabsheet = new TabSheet();
    final Window subwindow = new Window("Subwindow");

    final OptionGroup width = new OptionGroup("LO Width");
    final OptionGroup height = new OptionGroup("LO Height");

    private boolean getTable;

    @Override
    public void init() {
        setTheme("tests-tickets");
        setMainWindow(main);
        createControlWindow();
        subwindow.setWidth("400px");
        subwindow.setHeight("400px");
    }

    private void createControlWindow() {
        final OptionGroup context = new OptionGroup("Context");
        context.addItem("Main window");
        context.addItem("ExpandLayout");
        context.addItem("Subwindow");
        context.addItem("Panel");
        context.addItem("Split Panel");
        context.addItem("TabSheet");
        context.addItem("Accordion");
        context.setValue("Main window");

        final OptionGroup testComponent = new OptionGroup(
                "TestComponent 100%x100%");
        testComponent.addItem("Label");
        testComponent.addItem("Table");
        testComponent.setValue("Label");

        width.addItem("100%");
        width.addItem("50%");
        width.addItem("150%");
        width.addItem("100px");
        width.addItem("500px");
        width.setValue("100%");

        height.addItem("100%");
        height.addItem("50%");
        height.addItem("150%");
        height.addItem("100px");
        height.addItem("500px");
        height.setValue("100%");

        final Button set = new Button("Set", event -> {
            getTable = testComponent.getValue().equals("Table");

            if (context.getValue() == "Main window") {
                drawInMainWindow();
            } else if (context.getValue() == "Subwindow") {
                drawInSubwindow();
            } else if (context.getValue() == "Panel") {
                drawInPanel();
            } else if (context.getValue() == "Split Panel") {
                drawInSplitPanel();
            } else if (context.getValue() == "TabSheet") {
                drawInTabSheet(false);
            } else if (context.getValue() == "Accordion") {
                drawInTabSheet(true);
            } else if (context.getValue() == "ExpandLayout") {
                drawInExpandLayout();
            }
        });

        HorizontalLayout ol = new HorizontalLayout();
        ol.addComponent(context);
        ol.addComponent(testComponent);
        ol.addComponent(width);
        ol.addComponent(height);
        ol.addComponent(set);
        ol.setSpacing(true);
        ol.setMargin(true);

        Window controller = new Window("Controller");
        controller.setContent(ol);
        main.addWindow(controller);
    }

    protected void drawInExpandLayout() {
        main.removeAllComponents();
        main.getContent().setSizeFull();

        VerticalLayout ol = new VerticalLayout();

        VerticalLayout el = new VerticalLayout();

        el.removeAllComponents();

        ol.setWidth((String) width.getValue());
        ol.setHeight((String) height.getValue());

        ol.addComponent(getTestComponent());

        el.addComponent(ol);

        main.addComponent(el);
        main.removeWindow(subwindow);

    }

    protected void drawInTabSheet(boolean verticalAkaAccordion) {
        main.removeAllComponents();
        main.getContent().setSizeFull();

        VerticalLayout ol = new VerticalLayout();
        ol.setCaption("Tab 1");
        VerticalLayout ol2 = new VerticalLayout();
        ol2.setCaption("Tab 2");

        TabSheet ts = (verticalAkaAccordion ? accordion : tabsheet);
        ts.setSizeFull();

        ts.removeAllComponents();

        ts.addComponent(ol);
        ts.addComponent(ol2);

        ol.setWidth((String) width.getValue());
        ol.setHeight((String) height.getValue());
        ol2.setWidth((String) width.getValue());
        ol2.setHeight((String) height.getValue());

        ol.addComponent(getTestComponent());

        ol2.addComponent(getTestComponent());

        main.addComponent(ts);
        main.removeWindow(subwindow);
    }

    private void drawInSplitPanel() {
        main.removeAllComponents();
        main.getContent().setSizeFull();

        VerticalLayout ol = new VerticalLayout();
        VerticalLayout ol2 = new VerticalLayout();

        splitPanel.setFirstComponent(ol);
        splitPanel.setSecondComponent(ol2);

        ol.setWidth((String) width.getValue());
        ol.setHeight((String) height.getValue());
        ol2.setWidth((String) width.getValue());
        ol2.setHeight((String) height.getValue());

        ol.addComponent(getTestComponent());

        ol2.addComponent(getTestComponent());

        main.addComponent(splitPanel);
        main.removeWindow(subwindow);
    }

    private void drawInPanel() {
        main.removeAllComponents();
        main.getContent().setSizeFull();

        VerticalLayout ol = new VerticalLayout();
        panel.setSizeFull();
        panel.setContent(ol);

        ol.setWidth((String) width.getValue());
        ol.setHeight((String) height.getValue());

        ol.addComponent(getTestComponent());
        main.addComponent(panel);
        main.removeWindow(subwindow);
    }

    private void drawInSubwindow() {
        main.removeAllComponents();
        main.getContent().setSizeFull();
        VerticalLayout ol = new VerticalLayout();
        ol.setWidth((String) width.getValue());
        ol.setHeight((String) height.getValue());

        ol.addComponent(getTestComponent());
        subwindow.setContent(ol);
        main.addWindow(subwindow);
    }

    private void drawInMainWindow() {
        main.removeAllComponents();
        VerticalLayout ol = new VerticalLayout();
        main.setContent(ol);
        ol.setWidth((String) width.getValue());
        ol.setHeight((String) height.getValue());

        ol.addComponent(getTestComponent());
        main.removeWindow(subwindow);
    }

    private Component getTestComponent() {
        if (getTable) {
            Table testTable = TestForTablesInitialColumnWidthLogicRendering
                    .getTestTable(4, 50);
            testTable.setSizeFull();
            return testTable;
        } else {
            Label l = new Label("Label");
            l.setStyleName("no-padding");
            l.setSizeFull();
            return l;
        }
    }
}