1313use OCP \Files \File ;
1414use OCP \Files \FileInfo ;
1515use OCP \Files \Folder ;
16+ use OCP \Files \IFilenameValidator ;
17+ use OCP \Files \NotFoundException ;
1618use OCP \Files \NotPermittedException ;
1719
1820class NotesService {
1921 private MetaService $ metaService ;
2022 private SettingsService $ settings ;
2123 private NoteUtil $ noteUtil ;
24+ private IFilenameValidator $ filenameValidator ;
2225
2326 public function __construct (
2427 MetaService $ metaService ,
2528 SettingsService $ settings ,
2629 NoteUtil $ noteUtil ,
30+ IFilenameValidator $ filenameValidator ,
2731 ) {
2832 $ this ->metaService = $ metaService ;
2933 $ this ->settings = $ settings ;
3034 $ this ->noteUtil = $ noteUtil ;
35+ $ this ->filenameValidator = $ filenameValidator ;
3136 }
3237
3338 public function getAll (string $ userId , bool $ autoCreateNotesFolder = false ) : array {
@@ -223,8 +228,8 @@ private static function getFileById(string $customExtension, Folder $folder, int
223228 * @NoCSRFRequired
224229 * @return \OCP\Files\File
225230 */
226- public function getAttachment (string $ userId , int $ noteid , string $ path ) : File {
227- $ note = $ this ->get ($ userId , $ noteid );
231+ public function getAttachment (string $ userId , int $ noteId , string $ path ) : File {
232+ $ note = $ this ->get ($ userId , $ noteId );
228233 $ notesFolder = $ this ->getNotesFolder ($ userId );
229234 $ path = str_replace ('\\' , '/ ' , $ path ); // change windows style path
230235 $ p = explode ('/ ' , $ note ->getCategory ());
@@ -243,24 +248,24 @@ public function getAttachment(string $userId, int $noteid, string $path) : File
243248
244249 /**
245250 * @param $userId
246- * @param $noteid
251+ * @param $noteId
247252 * @param $fileDataArray
253+ *
254+ * @return array
248255 * @throws NotPermittedException
249256 * @throws ImageNotWritableException
250- * https://github.com/nextcloud/deck/blob/master/lib/Service/AttachmentService.php
257+ * @throws NotFoundException
258+ * @throws InvalidPathException
259+ * https://github.com/nextcloud/deck/blob/master/lib/Service/AttachmentService.php
251260 */
252- public function createImage (string $ userId , int $ noteid , $ fileDataArray ) {
253- $ note = $ this ->get ($ userId , $ noteid );
261+ public function createImage (string $ userId , int $ noteId , $ fileDataArray ) : array {
262+ $ note = $ this ->get ($ userId , $ noteId );
254263 $ notesFolder = $ this ->getNotesFolder ($ userId );
255- $ parent = $ this ->noteUtil ->getCategoryFolder ($ notesFolder , $ note ->getCategory ());
264+ $ parentFolder = $ this ->noteUtil ->getCategoryFolder ($ notesFolder , $ note ->getCategory ());
256265
257- // try to generate long id, if not available on system fall back to a shorter one
258- try {
259- $ filename = bin2hex (random_bytes (16 ));
260- } catch (\Exception $ e ) {
261- $ filename = uniqid ();
262- }
263- $ filename = $ filename . '. ' . explode ('. ' , $ fileDataArray ['name ' ])[1 ];
266+ $ saveDir = $ this ->getAttachmentDirectoryForNote ($ note , $ userId );
267+ $ fileName = self ::getUniqueFileName ($ saveDir , $ fileDataArray ['name ' ]);
268+ $ this ->filenameValidator ->validateFilename ($ fileName );
264269
265270 if ($ fileDataArray ['tmp_name ' ] === '' ) {
266271 throw new ImageNotWritableException ();
@@ -272,8 +277,61 @@ public function createImage(string $userId, int $noteid, $fileDataArray) {
272277 fclose ($ fp );
273278
274279 $ result = [];
275- $ result ['filename ' ] = $ filename ;
276- $ this -> noteUtil -> getRoot ()-> newFile ($ parent -> getPath () . ' / ' . $ filename , $ content );
280+ $ result ['filename ' ] = ' .attachments. ' . $ note -> getId () . ' / ' . $ fileName ;
281+ $ saveDir -> newFile ($ fileName , $ content );
277282 return $ result ;
278283 }
284+
285+ /**
286+ * Get unique file name in a directory. Add '(n)' suffix.
287+ *
288+ * @param Folder $dir
289+ * @param string $fileName
290+ *
291+ * @return string
292+ */
293+ public static function getUniqueFileName (Folder $ dir , string $ fileName ) : string {
294+ $ extension = pathinfo ($ fileName , PATHINFO_EXTENSION );
295+ $ counter = 1 ;
296+ $ uniqueFileName = $ fileName ;
297+ if ($ extension !== '' ) {
298+ while ($ dir ->nodeExists ($ uniqueFileName )) {
299+ $ counter ++;
300+ $ uniqueFileName = (string )preg_replace ('/\. ' . $ extension . '$/ ' , ' ( ' . $ counter . '). ' . $ extension , $ fileName );
301+ }
302+ } else {
303+ while ($ dir ->nodeExists ($ uniqueFileName )) {
304+ $ counter ++;
305+ $ uniqueFileName = (string )preg_replace ('/$/ ' , ' ( ' . $ counter . ') ' , $ fileName );
306+ }
307+ }
308+ return $ uniqueFileName ;
309+ }
310+
311+ /**
312+ * Get or create file--specific attachment folder
313+ *
314+ * @param Note $note
315+ * @param string $userid
316+ *
317+ * @return Folder
318+ * @throws NotFoundException
319+ * @throws NotPermittedException
320+ * @throws InvalidPathException
321+ */
322+ private function getAttachmentDirectoryForNote (Note $ note , string $ userId ) : Folder {
323+ $ notesFolder = $ this ->getNotesFolder ($ userId );
324+ $ parentFolder = $ this ->noteUtil ->getCategoryFolder ($ notesFolder , $ note ->getCategory ());
325+
326+ $ attachmentFolderName = '.attachments. ' . $ note ->getId ();
327+ if ($ parentFolder ->nodeExists ($ attachmentFolderName )) {
328+ $ attachmentFolder = $ parentFolder ->get ($ attachmentFolderName );
329+ if ($ attachmentFolder instanceof Folder) {
330+ return $ attachmentFolder ;
331+ }
332+ } else {
333+ return $ parentFolder ->newFolder ($ attachmentFolderName );
334+ }
335+ throw new NotFoundException ('Attachment dir for note ' . $ note ->getId () . ' was not found or could not be created. ' );
336+ }
279337}
0 commit comments