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