1 /********************************************************
2 Copyright (C) 2007-2013 Hewlett-Packard Development Company, L.P.
3 Copyright (C) 2015-2019 Siemens AG
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 version 2 as published by the Free Software Foundation.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 ********************************************************/
19 /**
20 * \file
21 * \brief local function of delagent
22 *
23 * delagent: Remove an upload from the DB and repository
24 *
25 */
26 #include "delagent.h"
27
28 int Verbose = 0;
29 int Test = 0;
30 PGconn* pgConn = NULL; // the connection to Database
31
32 /**
33 * \brief If verbose is on, print to stdout
34 * \param format printf format to use for printing
35 * \param ... Data to be printed
36 * \return Number of characters printed
37 */
printfInCaseOfVerbosity(const char * format,...)38 int printfInCaseOfVerbosity (const char *format, ...)
39 {
40 va_list arg;
41 int done = 0;
42
43 if (Verbose)
44 {
45 va_start (arg, format);
46 done = vprintf(format, arg);
47 va_end (arg);
48 }
49 return done;
50 }
51
52 /**
53 * \brief simple wrapper which includes PQexec and fo_checkPQcommand
54 * \param desc description for the SQL command, else NULL
55 * \param SQL SQL command executed
56 * \param file source file name
57 * \param line source line number
58 * \return PQexec query result
59 * \see PQexec()
60 */
PQexecCheck(const char * desc,char * SQL,char * file,const int line)61 PGresult * PQexecCheck(const char *desc, char *SQL, char *file, const int line)
62 {
63 PGresult *result;
64
65 if(desc == NULL)
66 {
67 printfInCaseOfVerbosity("# %s:%i: %s\n", file, line, SQL);
68 }
69 else
70 {
71 printfInCaseOfVerbosity("# %s:%i: %s (%s)\n", file, line, desc, SQL);
72 }
73
74 result = PQexec(pgConn, SQL);
75 if (fo_checkPQcommand(pgConn, result, SQL, file, line))
76 {
77 exitNow(-1);
78 }
79 return result;
80 }
81
82 /**
83 * \brief Execute SQL query and create the result
84 * \see PQexecCheck()
85 */
PQexecCheckClear(const char * desc,char * SQL,char * file,const int line)86 void PQexecCheckClear(const char *desc, char *SQL, char *file, const int line)
87 {
88 PGresult *result;
89 result = PQexecCheck(desc, SQL, file, line);
90 PQclear(result);
91 }
92
93 /**
94 * \brief if this account is valid
95 *
96 * \param[in] user user name
97 * \param[in] password password
98 * \param[out] userId will be set to the id of the user
99 * \param[out] userPerm will be set to the permission level of the user
100 *
101 * \return 1: invalid;
102 * 0: yes, valid;
103 * -1: failure
104 */
authentication(char * user,char * password,int * userId,int * userPerm)105 int authentication(char *user, char *password, int *userId, int *userPerm)
106 {
107 if (NULL == user || NULL == password)
108 {
109 return 1;
110 }
111 char SQL[MAXSQL] = {0};
112 PGresult *result;
113 char user_seed[myBUFSIZ] = {0};
114 char pass_hash_valid[41] = {0};
115 unsigned char pass_hash_actual_raw[21] = {0};
116 char pass_hash_actual[41] = {0};
117
118 /** get user_seed, user_pass on one specified user */
119 snprintf(SQL,MAXSQL,"SELECT user_seed, user_pass, user_perm, user_pk from users where user_name=$1;");
120 const char *values[1] = {user};
121 int lengths[1] = {strlen(user)};
122 int binary[1] = {0};
123 result = PQexecParams(pgConn, SQL, 1, NULL, values, lengths, binary, 0);
124 if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__))
125 {
126 return -1;
127 }
128 if (!PQntuples(result))
129 {
130 return 1;
131 }
132 strcpy(user_seed, PQgetvalue(result, 0, 0));
133 strcpy(pass_hash_valid, PQgetvalue(result, 0, 1));
134 *userPerm = atoi(PQgetvalue(result, 0, 2));
135 *userId = atoi(PQgetvalue(result, 0, 3));
136 PQclear(result);
137 if (user_seed[0] && pass_hash_valid[0])
138 {
139 strcat(user_seed, password); // get the hash code on seed+pass
140 gcry_md_hash_buffer(GCRY_MD_SHA1, pass_hash_actual_raw, user_seed,
141 strlen(user_seed));
142 }
143 else
144 {
145 return -1;
146 }
147 int i = 0;
148 char temp[256] = {0};
149 for (i = 0; i < 20; i++)
150 {
151 snprintf(temp, 256, "%02x", pass_hash_actual_raw[i]);
152 strcat(pass_hash_actual, temp);
153 }
154 return (strcmp(pass_hash_valid, pass_hash_actual) == 0) ? 0 : 1;
155 }
156
157 /**
158 * \brief check if the upload can be deleted, that is the user have
159 * the permission to delete this upload
160 *
161 * \param uploadId upload id
162 * \param user_name user name
163 *
164 * \return 0: yes, you have the needed permissions;
165 * 1: no;
166 * -1: failure;
167 * -2: does not exist
168 */
check_permission_upload(int wanted_permissions,long uploadId,int userId,int userPerm)169 int check_permission_upload(int wanted_permissions, long uploadId, int userId, int userPerm)
170 {
171 int perms = getEffectivePermissionOnUpload(pgConn, uploadId, userId, userPerm);
172 if (perms > 0)
173 {
174 if (perms < wanted_permissions)
175 {
176 return 1;
177 }
178 else
179 {
180 return 0;
181 }
182 }
183 else if (perms == 0)
184 {
185 return 1;
186 }
187 return perms;
188 }
189
190 /**
191 * \brief check if the user has read permission on the given upload
192 * \param uploadId
193 * \param userId
194 * \param userPerm Permission requested by user
195 * \return 0: yes, you have the needed permissions;
196 * 1: no;
197 * -1: failure;
198 * -2: does not exist
199 */
check_read_permission_upload(long uploadId,int userId,int userPerm)200 int check_read_permission_upload(long uploadId, int userId, int userPerm)
201 {
202 return check_permission_upload(PERM_READ, uploadId, userId, userPerm);
203 }
204
205 /**
206 * \brief check if the user has read permission on the given upload
207 * \param uploadId
208 * \param userId
209 * \param userPerm Permission requested by user
210 * \return 0: yes, you have the needed permissions;
211 * 1: no;
212 * -1: failure;
213 * -2: does not exist
214 */
check_write_permission_upload(long uploadId,int userId,int userPerm)215 int check_write_permission_upload(long uploadId, int userId, int userPerm)
216 {
217 return check_permission_upload(PERM_WRITE, uploadId, userId, userPerm);
218 }
219
220 /**
221 * \brief check if the upload can be deleted, that is the user have
222 * the permission to delete this upload
223 *
224 * \param uploadId upload id
225 * \param user_name user name
226 * \param userPerm Permission requested by user
227 *
228 * \return 0: yes, can be deleted;
229 * 1: can not be deleted;
230 * -1: failure;
231 */
check_write_permission_folder(long folder_id,int userId,int userPerm)232 int check_write_permission_folder(long folder_id, int userId, int userPerm)
233 {
234 char SQL[MAXSQL];
235 PGresult *result;
236 int count = 0;
237
238 if (userPerm < PERM_WRITE)
239 {
240 return 1; // can not be deleted
241 }
242
243 snprintf(SQL,MAXSQL,"SELECT count(*) FROM folder JOIN users ON (users.user_pk = folder.user_fk OR users.user_perm = 10) WHERE folder_pk = %ld AND users.user_pk = %d;",folder_id,userId);
244 result = PQexec(pgConn, SQL);
245 if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__))
246 {
247 return -1;
248 }
249 count = atol(PQgetvalue(result,0,0));
250 if(count == 0)
251 {
252 return 1; // can not be deleted
253 }
254 return 0; // can be deleted
255 }
256
257 /**
258 * \brief check if the license can be deleted, that is the user have
259 * the permission to delete this license
260 *
261 * \param license_id license id
262 * \param userPerm Permission requested by user
263 *
264 * \return 0: yes, can be deleted;
265 * 1: can not be deleted;
266 */
check_write_permission_license(long license_id,int userPerm)267 int check_write_permission_license(long license_id, int userPerm)
268 {
269 if (userPerm != PERM_ADMIN)
270 {
271 printfInCaseOfVerbosity("only admin is allowed to delete licenses\n");
272 return 0; // can not be deleted
273 }
274 return 1; // can be deleted
275 }
276
277 /**
278 * \brief Given an upload ID, delete it.
279 *
280 * \param uploadId the upload id
281 * \param userId
282 * \param userPerm permission level the user has
283 *
284 * \return 0: yes, can is deleted;
285 * 1: can not be deleted;
286 * -1: failure;
287 * -2: does not exist
288 */
deleteUpload(long uploadId,int userId,int userPerm)289 int deleteUpload (long uploadId, int userId, int userPerm)
290 {
291 char *S;
292 int Row,maxRow;
293 char tempTable[256];
294 PGresult *result, *pfileResult;
295 char SQL[MAXSQL], desc[myBUFSIZ];
296
297 int permission_upload = check_write_permission_upload(uploadId, userId, userPerm);
298 if(0 != permission_upload) {
299 return permission_upload;
300 }
301
302 snprintf(tempTable,sizeof(tempTable),"delup_%ld_pfile",uploadId);
303 snprintf(SQL,MAXSQL,"DROP TABLE IF EXISTS %s;",tempTable);
304 PQexecCheckClear(NULL, SQL, __FILE__, __LINE__);
305
306 snprintf(desc, myBUFSIZ, "Deleting upload %ld",uploadId);
307 PQexecCheckClear(desc, "SET statement_timeout = 0;", __FILE__, __LINE__);
308 PQexecCheckClear(NULL, "BEGIN;", __FILE__, __LINE__);
309
310 /* Delete everything that impacts the UI */
311 if (!Test) {
312 /* The UI depends on uploadtree and folders for navigation.
313 Delete them now to block timeouts from the UI. */
314 PQexecCheckClear(NULL, "COMMIT;", __FILE__, __LINE__);
315 }
316
317 /* Begin complicated stuff */
318 /* Get the list of pfiles to delete */
319 /* These are all pfiles in the upload_fk that only appear once. */
320 snprintf(SQL,MAXSQL,"SELECT DISTINCT pfile_pk,pfile_sha1 || '.' || pfile_md5 || '.' || pfile_size AS pfile INTO %s FROM uploadtree INNER JOIN pfile ON upload_fk = %ld AND pfile_fk = pfile_pk;",tempTable,uploadId);
321 PQexecCheckClear("Getting list of pfiles to delete", SQL, __FILE__, __LINE__);
322
323 /* Remove pfiles which are reused by other uploads */
324 snprintf(SQL, MAXSQL, "DELETE FROM %s WHERE pfile_pk IN (SELECT pfile_pk FROM %s INNER JOIN uploadtree ON pfile_pk = pfile_fk WHERE upload_fk != %ld)", tempTable, tempTable, uploadId);
325 PQexecCheckClear(NULL, SQL, __FILE__, __LINE__);
326
327 if (Verbose) {
328 snprintf(SQL,MAXSQL,"SELECT COUNT(*) FROM %s;",tempTable);
329 result = PQexec(pgConn, SQL);
330 if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__)) {
331 return -1;
332 }
333 printf("# Created pfile table %s with %ld entries\n", tempTable, atol(PQgetvalue(result,0,0)));
334 PQclear(result);
335 }
336
337 /* Now to delete the actual pfiles from the repository before remove the DB. */
338 /* Get the file listing -- needed for deleting pfiles from the repository. */
339 snprintf(SQL,MAXSQL,"SELECT pfile FROM %s ORDER BY pfile_pk;",tempTable);
340 pfileResult = PQexec(pgConn, SQL);
341 if (fo_checkPQresult(pgConn, pfileResult, SQL, __FILE__, __LINE__)) {
342 return -1;
343 }
344
345 if (Test <= 1) {
346 maxRow = PQntuples(pfileResult);
347 for(Row=0; Row<maxRow; Row++) {
348 S = PQgetvalue(pfileResult,Row,0); /* sha1.md5.len */
349 if (fo_RepExist("files",S)) {
350 if (Test) {
351 printf("TEST: Delete %s %s\n","files",S);
352 } else {
353 fo_RepRemove("files",S);
354 }
355 }
356 if (fo_RepExist("gold",S)) {
357 if (Test) {
358 printf("TEST: Delete %s %s\n","gold",S);
359 } else {
360 fo_RepRemove("gold",S);
361 }
362 }
363 fo_scheduler_heart(1);
364 }
365 }
366 PQclear(pfileResult);
367
368 /*
369 This begins the slow part that locks the DB.
370 The problem is, we don't want to lock a critical row,
371 otherwise the scheduler will lock and/or fail.
372 */
373 if (!Test) {
374 PQexecCheckClear(NULL, "BEGIN;", __FILE__, __LINE__);
375 }
376 /* Delete the upload from the folder-contents table */
377 snprintf(SQL,MAXSQL,"DELETE FROM foldercontents WHERE (foldercontents_mode & 2) != 0 AND child_id = %ld;",uploadId);
378 PQexecCheckClear("Deleting foldercontents", SQL, __FILE__, __LINE__);
379
380 /* Deleting the actual upload contents*/
381 /* Delete the bucket_container record as it can't be cascade delete with upload table */
382 snprintf(SQL,MAXSQL,"DELETE FROM bucket_container USING uploadtree WHERE uploadtree_fk = uploadtree_pk AND upload_fk = %ld;",uploadId);
383 PQexecCheckClear("Deleting bucket_container", SQL, __FILE__, __LINE__);
384
385 /* Delete the tag_uploadtree record as it can't be cascade delete with upload table */
386 snprintf(SQL,MAXSQL,"DELETE FROM tag_uploadtree USING uploadtree WHERE uploadtree_fk = uploadtree_pk AND upload_fk = %ld;",uploadId);
387 PQexecCheckClear("Deleting tag_uploadtree", SQL, __FILE__, __LINE__);
388
389 char uploadtree_tablename[1000];
390 snprintf(SQL,MAXSQL,"SELECT uploadtree_tablename FROM upload WHERE upload_pk = %ld;",uploadId);
391 result = PQexec(pgConn, SQL);
392 if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__)) {
393 return -1;
394 }
395 if (PQntuples(result)) {
396 strcpy(uploadtree_tablename, PQgetvalue(result, 0, 0));
397 PQclear(result);
398 }
399
400 printfInCaseOfVerbosity("Deleting local license decisions for upload %ld\n",
401 uploadId);
402 /* delete from clearing_event table. */
403 snprintf(SQL, MAXSQL, "WITH alld AS ("
404 "SELECT *, ROW_NUMBER() OVER "
405 "(PARTITION BY clearing_event_pk ORDER BY scope DESC) rnum "
406 "FROM clearing_event ce "
407 "INNER JOIN clearing_decision_event cde "
408 "ON cde.clearing_event_fk = ce.clearing_event_pk "
409 "INNER JOIN clearing_decision cd "
410 "ON cd.clearing_decision_pk = cde.clearing_decision_fk "
411 "AND cd.uploadtree_fk IN "
412 "(SELECT uploadtree_pk FROM %s WHERE upload_fk = %ld)) "
413 "DELETE FROM clearing_event ce USING alld AS ad "
414 "WHERE ad.rnum = 1 AND ad.scope = 0 " // Make sure not to delete global decisions
415 "AND ce.clearing_event_pk = ad.clearing_event_pk;",
416 uploadtree_tablename, uploadId);
417 PQexecCheckClear("Deleting from clearing_event", SQL, __FILE__, __LINE__);
418
419 /* delete from clearing_decision_event table. */
420 snprintf(SQL, MAXSQL, "DELETE FROM clearing_decision_event AS cde "
421 "USING clearing_decision AS cd "
422 "WHERE cd.scope = 0 " // Make sure not to delete global decisions
423 "AND cd.uploadtree_fk IN "
424 "(SELECT uploadtree_pk FROM %s WHERE upload_fk = %ld) "
425 "AND cd.clearing_decision_pk = cde.clearing_decision_fk;",
426 uploadtree_tablename, uploadId);
427 PQexecCheckClear("Deleting from clearing_decision_event", SQL, __FILE__, __LINE__);
428
429 /* delete from clearing_decision table. */
430 snprintf(SQL, MAXSQL, "DELETE FROM clearing_decision "
431 "WHERE scope = 0 AND uploadtree_fk IN "
432 "(SELECT uploadtree_pk FROM %s WHERE upload_fk = %ld);",
433 uploadtree_tablename, uploadId);
434 PQexecCheckClear("Deleting from clearing_decision", SQL, __FILE__, __LINE__);
435
436 /* delete from license_ref_bulk table. */
437 snprintf(SQL, MAXSQL, "DELETE FROM license_ref_bulk "
438 "WHERE uploadtree_fk IN "
439 "(SELECT uploadtree_pk FROM %s WHERE upload_fk = %ld);",
440 uploadtree_tablename, uploadId);
441 PQexecCheckClear("Deleting from license_ref_bulk", SQL, __FILE__, __LINE__);
442
443 /* delete from uploadtree table. */
444 snprintf(SQL, MAXSQL, "DELETE FROM %s WHERE upload_fk = %ld;",
445 uploadtree_tablename, uploadId);
446 PQexecCheckClear("Deleting from uploadtree", SQL, __FILE__, __LINE__);
447
448 /* Delete uploadtree_nnn table */
449 if (strcasecmp(uploadtree_tablename,"uploadtree_a")) {
450 snprintf(SQL,MAXSQL,"DROP TABLE %s;", uploadtree_tablename);
451 PQexecCheckClear(NULL, SQL, __FILE__, __LINE__);
452 }
453
454 /* delete from pfile is SLOW due to constraint checking. Do it separately. */
455 snprintf(SQL,MAXSQL,"DELETE FROM pfile USING %s WHERE pfile.pfile_pk = %s.pfile_pk;",tempTable,tempTable);
456 PQexecCheckClear("Deleting from pfile", SQL, __FILE__, __LINE__);
457
458 snprintf(SQL,MAXSQL,"DROP TABLE %s;",tempTable);
459 PQexecCheckClear(NULL, SQL, __FILE__, __LINE__);
460
461 /* Mark upload deleted in upload table */
462 snprintf(SQL,MAXSQL,"UPDATE upload SET expire_action = 'd', "
463 "expire_date = now(), pfile_fk = NULL WHERE upload_pk = %ld;", uploadId);
464 PQexecCheckClear("Marking upload as deleted", SQL, __FILE__, __LINE__);
465
466 PQexecCheckClear(NULL, "SET statement_timeout = 120000;", __FILE__, __LINE__);
467
468 printfInCaseOfVerbosity("Deleted upload %ld from DB, now doing repository.\n",uploadId);
469
470 if (Test) {
471 PQexecCheckClear(NULL, "ROLLBACK;", __FILE__, __LINE__);
472 } else {
473 PQexecCheckClear(NULL, "COMMIT;", __FILE__, __LINE__);
474 }
475
476 printfInCaseOfVerbosity("Deleted upload %ld\n",uploadId);
477
478 return 0; /* success */
479 } /* deleteUpload() */
480
481 /**
482 * \brief remove link between parent and (child,mode) if there are other parents
483 *
484 * \param child id of the child to be unlinked
485 * \param parent id of the parent to unlink from
486 * \param mode 1<<0 child is folder_fk, 1<<1 child is upload_fk, 1<<2 child is an uploadtree_fk
487 * \param userPerm permission level the user has
488 *
489 * \return 0: successfully deleted link (other link existed);
490 * 1: was not able to delete the link (no other link to this upload existed);
491 * -1: failure
492 * \todo add permission checks
493 */
unlinkContent(long child,long parent,int mode,int userId,int userPerm)494 int unlinkContent (long child, long parent, int mode, int userId, int userPerm)
495 {
496 int cnt, cntUpload;
497 char SQL[MAXSQL];
498 PGresult *result;
499
500 if(mode == 1){
501 snprintf(SQL,MAXSQL,"SELECT COUNT(DISTINCT parent_fk) FROM foldercontents WHERE foldercontents_mode=%d AND child_id=%ld",mode,child);
502 }
503 else{
504 snprintf(SQL,MAXSQL,"SELECT COUNT(parent_fk) FROM foldercontents WHERE foldercontents_mode=%d AND"
505 " child_id in (SELECT upload_pk FROM folderlist WHERE pfile_fk="
506 "(SELECT pfile_fk FROM folderlist WHERE upload_pk=%ld limit 1))",
507 mode,child);
508 }
509 result = PQexec(pgConn, SQL);
510 if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__))
511 {
512 return -1;
513 }
514 cnt = atoi(PQgetvalue(result,0,0));
515 PQclear(result);
516 if(cnt>1 && !Test)
517 {
518 if(mode == 2){
519 snprintf(SQL,MAXSQL,"SELECT COUNT(DISTINCT parent_fk) FROM foldercontents WHERE foldercontents_mode=1 AND child_id=%ld",parent);
520 result = PQexec(pgConn, SQL);
521 if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__))
522 {
523 return -1;
524 }
525 cntUpload = atoi(PQgetvalue(result,0,0));
526 PQclear(result);
527 if(cntUpload > 1){ // check for copied/duplicate folder
528 return 0;
529 }
530 }
531 snprintf(SQL,MAXSQL,"DELETE FROM foldercontents WHERE foldercontents_mode=%d AND child_id =%ld AND parent_fk=%ld",mode,child,parent);
532 PQexecCheckClear(NULL, SQL, __FILE__, __LINE__);
533 return 0;
534 }
535 return 1;
536 }
537
538 /**
539 * \brief Draw folder tree.
540 *
541 * if DelFlag is set, then all child uploads are
542 * deleted and the folders are deleted.
543 *
544 * \param Parent the parent folder id
545 * \param Depth
546 * \param row grandparent (used to unlink if multiple grandparents)
547 * \param DelFlag 0=no del, 1=del if unique parent, 2=del unconditional
548 * \param userId
549 * \param userPerm permission level the user has
550 *
551 * \return 0: success;
552 * 1: fail;
553 * -1: failure
554 *
555 */
listFoldersRecurse(long Parent,int Depth,long Row,int DelFlag,int userId,int userPerm)556 int listFoldersRecurse (long Parent, int Depth, long Row, int DelFlag, int userId, int userPerm)
557 {
558 int r, i, rc, maxRow;
559 int count, resultUploadCount;
560 long Fid;
561 char *Desc;
562 char SQL[MAXSQL], SQLUpload[MAXSQL];
563 char SQLFolder[MAXSQLFolder];
564 PGresult *result, *resultUpload, *resultFolder;
565
566 rc = check_write_permission_folder(Parent, userId, userPerm);
567 if(rc < 0)
568 {
569 return rc;
570 }
571 if(DelFlag && rc > 0){
572 return 1;
573 }
574
575 snprintf(SQLFolder, MAXSQLFolder,"SELECT COUNT(*) FROM folderlist WHERE folder_pk=%ld",Parent);
576 resultFolder = PQexec(pgConn, SQLFolder);
577 count= atoi(PQgetvalue(resultFolder,0,0));
578 PQclear(resultFolder);
579
580 /* Find all folders with this parent and recurse, but don't show uploads, if they also exist in other directories */
581 snprintf(SQL,MAXSQL,"SELECT folder_pk,foldercontents_mode,name,description,upload_pk,pfile_fk FROM folderlist WHERE parent=%ld"
582 " ORDER BY name,parent,folder_pk ", Parent);
583 result = PQexec(pgConn, SQL);
584 if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__))
585 {
586 return -1;
587 }
588 maxRow = PQntuples(result);
589 for(r=0; r < maxRow; r++)
590 {
591 if (atol(PQgetvalue(result,r,0)) == Parent)
592 {
593 continue;
594 }
595
596 Fid = atol(PQgetvalue(result,r,0));
597 if (Fid != 0)
598 {
599 if (!DelFlag)
600 {
601 for(i=0; i<Depth; i++)
602 {
603 fputs(" ",stdout);
604 }
605 printf("%4ld :: %s",Fid,PQgetvalue(result,r,2));
606 Desc = PQgetvalue(result,r,3);
607 if (Desc && Desc[0])
608 {
609 printf(" (%s)",Desc);
610 }
611 printf("\n");
612 }
613 rc = listFoldersRecurse(Fid,Depth+1,Parent,DelFlag,userId,userPerm);
614 if (rc < 0)
615 {
616 if (DelFlag)
617 {
618 printf("Deleting the folder failed.");
619 }
620 return 1;
621 }
622 }
623 else
624 {
625 if (DelFlag==1 && unlinkContent(Parent,Row,1,userId,userPerm)==0)
626 {
627 continue;
628 }
629 if (rc < 0)
630 {
631 return rc;
632 }
633 if (DelFlag)
634 {
635 snprintf(SQLUpload, MAXSQL,"SELECT COUNT(*) FROM folderlist WHERE pfile_fk=%ld", atol(PQgetvalue(result,r,5)));
636 resultUpload = PQexec(pgConn, SQLUpload);
637 resultUploadCount = atoi(PQgetvalue(resultUpload,0,0));
638 if(count < 2 && resultUploadCount < 2)
639 {
640 rc = deleteUpload(atol(PQgetvalue(result,r,4)),userId, userPerm);
641 if (rc < 0)
642 {
643 return rc;
644 }
645 if (rc != 0)
646 {
647 printf("Deleting the folder failed since it contains uploads you can't delete.");
648 return rc;
649 }
650 }
651 else{
652 rc = unlinkContent(atol(PQgetvalue(result,r,4)),Parent,2,userId,userPerm);
653 if(rc < 0){
654 return rc;
655 }
656 }
657 }
658 else
659 {
660 rc = check_read_permission_upload(atol(PQgetvalue(result,r,4)),userId,userPerm);
661 if (rc < 0)
662 {
663 return rc;
664 }
665 if (rc == 0)
666 {
667 for(i=0; i<Depth; i++)
668 {
669 fputs(" ",stdout);
670 }
671 printf("%4s :: Contains: %s\n","--",PQgetvalue(result,r,2));
672 }
673 }
674 }
675 }
676 PQclear(result);
677
678 switch(Parent)
679 {
680 case 1: /* skip default parent */
681 if (DelFlag != 0)
682 {
683 printf("INFO: Default folder not deleted.\n");
684 }
685 break;
686 case 0: /* it's an upload */
687 break;
688 default: /* it's a folder */
689 if (DelFlag == 0)
690 {
691 break;
692 }
693 printf("INFO: folder id=%ld will be deleted with flag %d\n",Parent,DelFlag);
694 if (DelFlag==1)
695 {
696 rc = unlinkContent(Parent,Row,1,userId,userPerm);
697 if (rc == 0)
698 {
699 break;
700 }
701 if (rc < 0)
702 {
703 return rc;
704 }
705 }
706 if(Row > 0)
707 snprintf(SQL,MAXSQL,"DELETE FROM foldercontents WHERE foldercontents_mode=1 AND parent_fk=%ld AND child_id=%ld",Row,Parent);
708 else
709 snprintf(SQL,MAXSQL,"DELETE FROM foldercontents WHERE foldercontents_mode=1 AND child_id=%ld",Parent);
710 if (Test)
711 {
712 printf("TEST: %s\n",SQL);
713 }
714 else
715 {
716 PQexecCheckClear(NULL, SQL, __FILE__, __LINE__);
717 }
718 if(Row > 0)
719 snprintf(SQL,MAXSQL,"DELETE FROM folder f USING foldercontents fc WHERE f.folder_pk = fc.child_id AND fc.parent_fk='%ld' AND f.folder_pk = '%ld';",Row,Parent);
720 else
721 snprintf(SQL,MAXSQL,"DELETE FROM folder WHERE folder_pk = '%ld';",Parent);
722 if (Test)
723 {
724 printf("TEST: %s\n",SQL);
725 }
726 else
727 {
728 PQexecCheckClear(NULL, SQL, __FILE__, __LINE__);
729 }
730 } /* switch() */
731
732 return 0; /* success */
733 } /* listFoldersRecurse() */
734
735 /**
736 * \brief Given a PGresult, find detached folders
737 * \param result PGresult from a query
738 * \param userId
739 * \param userPerm permission level the user has
740 * \return 0: success;
741 * 1: fail;
742 * -1: failure
743 */
listFoldersFindDetatchedFolders(PGresult * result,int userId,int userPerm)744 int listFoldersFindDetatchedFolders(PGresult *result, int userId, int userPerm)
745 {
746 int DetachFlag=0;
747 int i,j;
748 int maxRow = PQntuples(result);
749 long Fid; /* folder ids */
750 int Match;
751 char *Desc;
752 int rc;
753
754 /* Find detached folders */
755 for(i=0; i < maxRow; i++)
756 {
757 Fid = atol(PQgetvalue(result,i,1));
758 if (Fid == 1)
759 {
760 continue; /* skip default parent */
761 }
762 Match=0;
763 for(j=0; (j<maxRow) && !Match; j++)
764 {
765 if ((i!=j) && (atol(PQgetvalue(result,j,0)) == Fid)) Match=1;
766 }
767 if (!Match && !atol(PQgetvalue(result,i,4)))
768 {
769 if (!DetachFlag)
770 {
771 printf("# Unlinked folders\n");
772 DetachFlag=1;
773 }
774 printf("%4ld :: %s",Fid,PQgetvalue(result,i,2));
775 Desc = PQgetvalue(result,i,3);
776 if (Desc && Desc[0])
777 {
778 printf(" (%s)",Desc);
779 }
780 printf("\n");
781 rc = listFoldersRecurse(Fid,1,i,0,userId,userPerm);
782 if (rc < 0)
783 {
784 return rc;
785 }
786 }
787 }
788 return 0;
789 }
790
791 /**
792 * \brief Given a PGresult, find detached uploads
793 * \param result PGresult from a query
794 * \param userId
795 * \param userPerm permission level the user has
796 * \return 0: success
797 */
listFoldersFindDetatchedUploads(PGresult * result,int userId,int userPerm)798 int listFoldersFindDetatchedUploads(PGresult *result, int userId, int userPerm)
799 {
800 int DetachFlag=0;
801 int i,j;
802 int maxRow = PQntuples(result);
803 long Fid; /* folder ids */
804 int Match;
805 char *Desc;
806 /* Find detached uploads */
807 for(i=0; i < maxRow; i++)
808 {
809 Fid = atol(PQgetvalue(result,i,1));
810 if (Fid == 1)
811 {
812 continue; /* skip default parent */
813 }
814 Match=0;
815 for(j=0; (j<maxRow) && !Match; j++)
816 {
817 if ((i!=j) && (atol(PQgetvalue(result,j,0)) == Fid)) Match=1;
818 }
819 if (!Match && atol(PQgetvalue(result,i,4)))
820 {
821 if (!DetachFlag)
822 {
823 printf("# Unlinked uploads (uploads without folders)\n");
824 DetachFlag=1;
825 }
826 printf("%4s",PQgetvalue(result,i,4));
827 printf(" :: %s",PQgetvalue(result,i,2));
828 Desc = PQgetvalue(result,i,3);
829 if (Desc && Desc[0])
830 {
831 printf(" (%s)",Desc);
832 }
833 printf("\n");
834 }
835 }
836 return 0;
837 }
838
839 /**
840 * \brief Given a user id, find detached folders and uploads
841 * \param userId
842 * \param userPerm permission level the user has
843 * \return 0: success;
844 * 1: fail;
845 * -1: failure
846 */
listFoldersFindDetatched(int userId,int userPerm)847 int listFoldersFindDetatched(int userId, int userPerm)
848 {
849 char SQL[MAXSQL];
850 PGresult *result;
851 int rc;
852
853 snprintf(SQL,MAXSQL,"SELECT folder_pk,parent,name,description,upload_pk FROM folderlist ORDER BY name,parent,folder_pk;");
854 result = PQexec(pgConn, SQL);
855 if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__))
856 {
857 return -1;
858 }
859 rc = listFoldersFindDetatchedFolders(result, userId, userPerm);
860 if (rc < 0 )
861 {
862 PQclear(result);
863 return rc;
864 }
865 rc = listFoldersFindDetatchedUploads(result, userId, userPerm);
866 PQclear(result);
867 if (rc < 0 )
868 {
869 return rc;
870 }
871 return 0;
872 }
873
874 /**
875 * \brief List every folder.
876 * \param userId
877 * \param userPerm permission level the user has
878 */
listFolders(int userId,int userPerm)879 int listFolders (int userId, int userPerm)
880 {
881 char SQL[MAXSQL];
882 PGresult *result;
883 int rc;
884
885 if(userPerm == 0){
886 printf("you do not have the permsssion to view the folder list.\n");
887 return 1;
888 }
889
890 printf("# Folders\n");
891 snprintf(SQL,MAXSQL,"SELECT folder_name from folder where folder_pk =1;");
892 result = PQexec(pgConn, SQL);
893 if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__))
894 {
895 return -1;
896 }
897
898 printf("%4d :: %s\n", 1, PQgetvalue(result,0,0));
899 PQclear(result);
900
901 rc = listFoldersRecurse(1,1,-1,0,userId,userPerm);
902 if (rc < 0)
903 {
904 return rc;
905 }
906
907 rc = listFoldersFindDetatched(userId, userPerm);
908 if (rc < 0)
909 {
910 return rc;
911 }
912 return 0;
913 } /* listFolders() */
914
915 /**
916 * \brief List every upload ID.
917 *
918 * \param userId user id
919 * \param userPerm permission level the user has
920 * \return 0 on success; -1 on failure
921 */
listUploads(int userId,int userPerm)922 int listUploads (int userId, int userPerm)
923 {
924 int Row,maxRow;
925 long NewPid;
926 PGresult *result;
927 int rc;
928 char *SQL = "SELECT upload_pk,upload_desc,upload_filename FROM upload ORDER BY upload_pk;";
929 printf("# Uploads\n");
930 result = PQexec(pgConn, SQL);
931 if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__))
932 {
933 exitNow(-1);
934 }
935
936 /* list each value */
937 maxRow = PQntuples(result);
938 for(Row=0; Row < maxRow; Row++)
939 {
940 NewPid = atol(PQgetvalue(result,Row,0));
941 rc = check_read_permission_upload(NewPid, userId, userPerm);
942 if (rc < 0)
943 {
944 PQclear(result);
945 return rc;
946 }
947 if (NewPid >= 0 && (userPerm == PERM_ADMIN || rc == 0))
948 {
949 char *S;
950 printf("%ld :: %s",NewPid,PQgetvalue(result,Row,2));
951 S = PQgetvalue(result,Row,1);
952 if (S && S[0]) printf(" (%s)",S);
953 printf("\n");
954 }
955 }
956 PQclear(result);
957 return 0;
958 } /* listUploads() */
959
960 /**
961 * \brief recursively delete a folder
962 *
963 * Given a folder ID, delete it AND recursively delete everything below it!
964 * This includes upload deletion!
965 *
966 * \param cFolder the folder id to delete
967 * \param pFolder parent of the current folder
968 * \param userId
969 * \param userPerm permission level the user has
970 *
971 * \return 0: success;
972 * 1: fail
973 * -1: failure
974 *
975 **/
deleteFolder(long cFolder,long pFolder,int userId,int userPerm)976 int deleteFolder(long cFolder, long pFolder, int userId, int userPerm)
977 {
978 if(pFolder == 0) pFolder= -1 ;
979 return listFoldersRecurse(cFolder, 0,pFolder,2,userId,userPerm);
980 } /* deleteFolder() */
981
982 /**********************************************************************/
983
984 /**
985 * \brief Parse parameters
986 *
987 * Read Parameter from scheduler.
988 * Process line elements.
989 *
990 * \param Parm the parameter string
991 * \param userId
992 * \param userPerm permission level the user has
993 *
994 * \return 0: yes, can is deleted;
995 * 1: can not be deleted;
996 * -1: failure;
997 * -2: does not exist
998 *
999 **/
readAndProcessParameter(char * Parm,int userId,int userPerm)1000 int readAndProcessParameter (char *Parm, int userId, int userPerm)
1001 {
1002 char *L;
1003 int rc=0; /* assume no data */
1004 int Type=0; /* 0=undefined; 1=delete; 2=list */
1005 int Target=0; /* 0=undefined; 1=upload; 2=license; 3=folder */
1006 const char s[2] = " ";
1007 char *token;
1008 char a[15];
1009 long fd[2];
1010 int i = 0, len = 0;
1011
1012 if (!Parm)
1013 {
1014 return(-1);
1015 }
1016 if (Verbose > 1) fprintf(stderr,"DEBUG: Line='%s'\n",Parm);
1017
1018 /* process the string. */
1019 L = Parm;
1020 while(isspace(L[0])) L++;
1021
1022 /** Get the type of command: delete or list **/
1023 if (!strncasecmp(L,"DELETE",6) && isspace(L[6]))
1024 {
1025 Type=1; /* delete */
1026 L+=6;
1027 }
1028 else if (!strncasecmp(L,"LIST",4) && isspace(L[4]))
1029 {
1030 Type=2; /* list */
1031 L+=4;
1032 }
1033 while(isspace(L[0])) L++;
1034 /** Get the target **/
1035 if (!strncasecmp(L,"UPLOAD",6) && (isspace(L[6]) || !L[6]))
1036 {
1037 Target=1; /* upload */
1038 L+=6;
1039 }
1040 else if (!strncasecmp(L,"LICENSE",7) && (isspace(L[7]) || !L[7]))
1041 {
1042 Target=2; /* license */
1043 L+=7;
1044 }
1045 else if (!strncasecmp(L,"FOLDER",6) && (isspace(L[6]) || !L[6]))
1046 {
1047 Target=3; /* folder */
1048 L+=6;
1049 }
1050
1051 len = strlen(L);
1052 memcpy(a, L,len);
1053 token = strtok(a, s);
1054
1055 while( token != NULL )
1056 {
1057 fd[i] = atol(token);
1058 token = strtok(NULL, s);
1059 i++;
1060 }
1061
1062 /* Handle the request */
1063 if ((Type==1) && (Target==1))
1064 {
1065 rc = deleteUpload(fd[0], userId, userPerm);
1066 }
1067 else if ((Type==1) && (Target==3))
1068 {
1069 rc = deleteFolder(fd[1],fd[0], userId, userPerm);
1070 }
1071 else if (((Type==2) && (Target==1)) || ((Type==2) && (Target==2)))
1072 {
1073 rc = listUploads(0, PERM_ADMIN);
1074 }
1075 else if ((Type==2) && (Target==3))
1076 {
1077 rc = listFolders(userId, userPerm);
1078 }
1079 else
1080 {
1081 LOG_ERROR("Unknown command: '%s'\n",Parm);
1082 }
1083
1084 return rc;
1085 } /* readAndProcessParameter() */
1086
1087 /**
1088 * \brief process the jobs from scheduler
1089 *
1090 * -# Read the jobs from the scheduler using fo_scheduler_next().
1091 * -# Get the permission level of the current user.
1092 * -# Parse the parameters and process
1093 * \see fo_scheduler_next()
1094 * \see readAndProcessParameter()
1095 */
doSchedulerTasks()1096 void doSchedulerTasks()
1097 {
1098 char *Parm = NULL;
1099 char SQL[MAXSQL];
1100 PGresult *result;
1101 int userId = -1;
1102 int userPerm = -1;
1103
1104 while(fo_scheduler_next())
1105 {
1106 Parm = fo_scheduler_current();
1107 userId = fo_scheduler_userID();
1108
1109 /* get perm level of user */
1110 snprintf(SQL,MAXSQL,"SELECT user_perm FROM users WHERE user_pk='%d';", userId);
1111 result = PQexec(pgConn, SQL);
1112 if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__) || !PQntuples(result))
1113 {
1114 exitNow(0);
1115 }
1116 userPerm = atoi(PQgetvalue(result, 0, 0));
1117 PQclear(result);
1118
1119 int returnCode = readAndProcessParameter(Parm, userId, userPerm);
1120 if (returnCode != 0)
1121 {
1122 /* Loglevel is to high, but scheduler expects FATAL log message before exit */
1123 LOG_FATAL("Due to permission problems, the delagent was not able to list or delete the requested objects or they did not exist.");
1124 exitNow(returnCode);
1125 }
1126 }
1127 }
1128 /**
1129 * @brief Exit function. This does all cleanup and should be used
1130 * instead of calling exit() or main() return.
1131 *
1132 * @param ExitVal Exit value
1133 * @returns void Calls exit()
1134 */
exitNow(int exitVal)1135 void exitNow(int exitVal)
1136 {
1137 if (pgConn) PQfinish(pgConn);
1138
1139 if (exitVal) LOG_ERROR("Exiting with status %d", exitVal);
1140
1141 fo_scheduler_disconnect(exitVal);
1142 exit(exitVal);
1143 } /* exitNow() */
1144