Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 345
0.00% covered (danger)
0.00%
0 / 30
CRAP
n/a
0 / 0
zeroBSCRM_returnMimeTypes
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
2
jpcrm_return_ext_for_mimetype
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
zeroBS_acceptableFileTypeListStr
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
90
zeroBS_acceptableFileTypeListArr
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
30
zeroBS_acceptableFileTypeMIMEArr
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
132
jpcrm_acceptable_filetype_mime_array_from_contacts
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
42
jpcrm_acceptable_file_type_list_str_for_contact
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
90
zeroBS_removeFile
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
342
zeroBSCRM_privatiseUploadedFile
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
56
zeroBSCRM_privatisedDirCheck
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
jpcrm_get_hash_for_object
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
jpcrm_storage_dir_info
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
wf_jpcrm_storage_dir_path
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
jpcrm_storage_fonts_dir_path
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
jpcrm_storage_dir_info_for_object
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
30
jpcrm_storage_dir_info_for_contact
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
jpcrm_storage_dir_info_for_company
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
jpcrm_storage_dir_info_for_invoices
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
jpcrm_storage_dir_info_for_quotes
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
jpcrm_save_admin_upload_to_folder
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
42
zeroBSCRM_privatisedDirCheckWorks
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
30
zeroBSCRM_fileSlots_getFileSlots
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
72
zeroBSCRM_fileslots_fileSlot
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
42
zeroBSCRM_fileslots_allSlots
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
zeroBSCRM_fileslots_fileInSlot
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
zeroBSCRM_fileslots_addToSlot
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
zeroBSCRM_fileslots_clearFileSlot
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
zeroBSCRM_files_baseName
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
jpcrm_get_hashed_filename
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
jpcrm_file_check_mime_extension
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
240
1<?php
2/*
3 * Jetpack CRM
4 * https://jetpackcrm.com
5 * V1.20
6 *
7 * Copyright 2020 Automattic
8 *
9 * Date: 01/11/16
10 */
11
12defined( 'ZEROBSCRM_PATH' ) || exit( 0 );
13
14/*
15======================================================
16    Acceptible Mime Types
17    ====================================================== */
18
19    #} A list of applicable Mimetypes for file uploads
20function zeroBSCRM_returnMimeTypes() {
21    return array(
22        'pdf'  => array( 'application/pdf' ),
23        'doc'  => array( 'application/msword' ),
24        'docx' => array( 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ),
25        'ppt'  => array( 'application/vnd.ms-powerpointtd>' ),
26        'pptx' => array( 'application/vnd.openxmlformats-officedocument.presentationml.presentation' ),
27        'xls'  => array( 'application/vnd.ms-excel' ),
28        'xlsx' => array( 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ),
29        'csv'  => array( 'text/csv' ),
30        'png'  => array( 'image/png' ),
31        'jpg'  => array( 'image/jpeg' ),
32        'jpeg' => array( 'image/jpeg' ),
33        'gif'  => array( 'image/gif' ),
34        'mp3'  => array( 'audio/mpeg' ),
35        'txt'  => array( 'text/plain' ),
36        'zip'  => array( 'application/zip', 'application/x-compressed-zip' ),
37        'mp4'  => array( 'video/mp4' ),
38                                            # plus 'any'
39    );
40}
41
42    /*
43     * Returns the extension for the provided mimetype, false otherwise.
44     */
45function jpcrm_return_ext_for_mimetype( $mimetype ) {
46    global $zbs;
47    $all_types = $zbs->acceptable_mime_types;
48
49    foreach ( $all_types as $extension => $ext_mimetypes ) {
50        foreach ( $ext_mimetypes as $this_mimetype ) {
51            if ( $this_mimetype === $mimetype ) {
52                return $extension;
53            }
54        }
55    }
56
57    return false;
58}
59
60/*
61======================================================
62    / Acceptible Mime Types
63    ====================================================== */
64
65/*
66======================================================
67    File Upload Related Funcs
68    ====================================================== */
69
70    // str e.g. .pdf, .xls
71function zeroBS_acceptableFileTypeListStr() {
72
73    $ret = '';
74
75    global $zbs;
76
77    #} Retrieve settings
78    $settings = $zbs->settings->getAll();
79
80    if ( isset( $settings['filetypesupload'] ) ) {
81
82        if ( isset( $settings['filetypesupload']['all'] ) && $settings['filetypesupload']['all'] == 1 ) {
83
84            $ret = __( 'All File Types', 'zero-bs-crm' );
85
86        } else {
87
88            foreach ( $settings['filetypesupload'] as $filetype => $enabled ) {
89
90                if ( isset( $settings['filetypesupload'][ $filetype ] ) && $enabled == 1 ) {
91
92                    if ( ! empty( $ret ) ) {
93                        $ret .= ', ';
94                    }
95
96                    $ret .= '.' . $filetype;
97
98                }
99            }
100        }
101    }
102
103    if ( empty( $ret ) ) {
104        $ret = 'No Uploads Allowed';
105    }
106
107    return $ret;
108}
109
110function zeroBS_acceptableFileTypeListArr() {
111
112    $ret = array();
113
114    global $zbs;
115
116    #} Retrieve settings
117    $settings = $zbs->settings->getAll();
118
119    if ( isset( $settings['filetypesupload'] ) ) {
120        foreach ( $settings['filetypesupload'] as $filetype => $enabled ) {
121
122            if ( isset( $settings['filetypesupload'][ $filetype ] ) && $enabled == 1 ) {
123                $ret[] = '.' . $filetype;
124            }
125        }
126    }
127
128        return $ret;
129}
130
131function zeroBS_acceptableFileTypeMIMEArr() {
132
133    $ret = array();
134
135    global $zbs;
136
137    #} Retrieve settings
138    $settings = $zbs->settings->getAll();
139
140    // if all, pass that
141    if ( isset( $settings['filetypesupload'] ) && isset( $settings['filetypesupload']['all'] ) && $settings['filetypesupload']['all'] == 1 ) {
142
143        return array( 'all' => 1 );
144
145    }
146    if ( isset( $settings['filetypesupload'] ) ) {
147        if ( isset( $settings['filetypesupload']['all'] ) && $settings['filetypesupload']['all'] == 1 ) {
148            // add all
149            foreach ( $settings['filetypesupload'] as $filetype => $enabled ) {
150                $ret = array_merge( $ret, $zbs->acceptable_mime_types[ $filetype ] );
151            }
152        } else {
153            // individual
154            foreach ( $settings['filetypesupload'] as $filetype => $enabled ) {
155                if ( isset( $settings['filetypesupload'][ $filetype ] ) && $enabled == 1 ) {
156                    $ret = array_merge( $ret, $zbs->acceptable_mime_types[ $filetype ] );
157                }
158            }
159        }
160    }
161
162    return $ret;
163}
164
165    /**
166     * Returns an array with all the mime types accepted for uploads from
167     * contacts.
168     */
169function jpcrm_acceptable_filetype_mime_array_from_contacts() {
170    global $zbs;
171
172    $ret      = array();
173    $settings = $zbs->settings->getAll();
174    if ( isset( $settings['filetypesupload'] ) ) {
175        foreach ( $settings['filetypesupload'] as $filetype => $enabled ) {
176            if ( $enabled == 1
177                && isset( $settings['filetypesupload'][ $filetype ] )
178                && isset( $zbs->acceptable_mime_types[ $filetype ] )
179            ) {
180                $ret = array_merge( $ret, $zbs->acceptable_mime_types[ $filetype ] );
181            }
182        }
183    }
184
185    return $ret;
186}
187
188function jpcrm_acceptable_file_type_list_str_for_contact() {
189    $ret = '';
190
191    global $zbs;
192
193    $settings = $zbs->settings->getAll();
194
195    if ( isset( $settings['filetypesupload'] ) && is_array( $settings['filetypesupload'] ) ) {
196        foreach ( $settings['filetypesupload'] as $filetype => $enabled ) {
197            if ( isset( $settings['filetypesupload'][ $filetype ] ) && $enabled == 1 && $filetype !== 'all' ) {
198                if ( ! empty( $ret ) ) {
199                    $ret .= ', ';
200                }
201                $ret .= '.' . $filetype;
202            }
203        }
204    }
205    if ( empty( $ret ) ) {
206        $ret = 'No Uploads Allowed';
207    }
208
209    return $ret;
210}
211
212    #} removes a link to file (quote, invoice, other)
213    // not always customer id... sometimes inv/co etc.
214function zeroBS_removeFile( $objectID = -1, $fileType = '', $fileURL = '' ) {
215
216    if ( current_user_can( 'admin_zerobs_customers' ) ) {   // only admin can do this too (extra security layer)
217
218        global $zbs;
219
220        if ( $objectID !== -1 && ! empty( $fileURL ) ) {
221
222            /*
223                centralised into zeroBSCRM_files_getFiles
224            switch ($fileType){
225
226                case 'customer':
227
228                    $filesArrayKey = 'zbs_customer_files';
229
230                    break;
231                case 'quotes':
232
233                    $filesArrayKey = 'zbs_customer_quotes';
234
235                    break;
236                case 'invoices':
237
238                    $filesArrayKey = 'zbs_customer_invoices';
239
240                    break;
241            } */
242
243            #} good?
244            // zeroBSCRM_files_getFiles if (isset($filesArrayKey)){
245            if ( in_array( $fileType, array( 'customer', 'quotes', 'invoices', 'company' ) ) ) {
246
247                #} First remove list reference:
248
249                    #} any change?
250                    $changeFlag  = false;
251                $fileObjToDelete = false;
252
253                    #} Load files arr
254                    $filesList = zeroBSCRM_files_getFiles( $fileType, $objectID );
255
256                if ( is_array( $filesList ) && count( $filesList ) > 0 ) {
257
258                    #} defs
259                    $ret = array();
260
261                    #} Cycle through and remove any with this url - lame, but works for now
262                    foreach ( $filesList as $fileObj ) {
263
264                        if ( $fileObj['url'] != $fileURL ) {
265                            $ret[] = $fileObj;
266                        } else {
267                            $fileObjToDelete = $fileObj;
268                            $changeFlag      = true;
269
270                            // also, if the removed file(s) are logged in any slots, clear the slot :)
271                            $slot = zeroBSCRM_fileslots_fileSlot( $fileObj['file'], $objectID, ZBS_TYPE_CONTACT );
272                            if ( $slot !== false && ! empty( $slot ) ) {
273                                zeroBSCRM_fileslots_clearFileSlot( $slot, $objectID, ZBS_TYPE_CONTACT );
274                            }
275                        }
276                    }
277
278                    if ( $changeFlag ) {
279                        zeroBSCRM_files_updateFiles( $fileType, $objectID, $ret );
280                    }
281                } #} else w/e
282
283                    #} Then delete actual file ...
284                if ( $changeFlag && isset( $fileObjToDelete ) && isset( $fileObjToDelete['file'] ) ) {
285
286                    #} Brutal
287                    #} #recyclingbin
288                    if ( file_exists( $fileObjToDelete['file'] ) ) {
289
290                        #} Delete
291                        unlink( $fileObjToDelete['file'] );
292
293                        #} Check if deleted:
294                        if ( file_exists( $fileObjToDelete['file'] ) ) {
295
296                            // try and be more forceful:
297                            chmod( $fileObjToDelete['file'], 0777 );
298                            unlink( realpath( $fileObjToDelete['file'] ) );
299
300                            if ( file_exists( $fileObjToDelete['file'] ) ) {
301
302                                // tone down perms, at least
303                                chmod( $fileObjToDelete['file'], 0644 );
304
305                                // add message
306                                return __( 'Could not delete file from server:', 'zero-bs-crm' ) . ' ' . $fileObjToDelete['file'];
307
308                            }
309                        }
310                    }
311                }
312
313                    return true;
314
315            }
316        }
317    } #} / can manage options
318
319    return false;
320}
321
322/*
323======================================================
324        File Upload related funcs
325    ====================================================== */
326
327function zeroBSCRM_privatiseUploadedFile( $fromPath = '', $filename = '' ) {
328
329    #} Check dir created
330    $currentUploadDirObj = zeroBSCRM_privatisedDirCheck();
331    if ( is_array( $currentUploadDirObj ) && isset( $currentUploadDirObj['path'] ) ) {
332            $currentUploadDir = $currentUploadDirObj['path'];
333            $currentUploadURL = $currentUploadDirObj['url'];
334    } else {
335        $currentUploadDir = false;
336        $currentUploadURL = false;
337    }
338
339    if ( ! empty( $currentUploadDir ) ) {
340
341            // generate a safe name + check no file existing
342            // this is TEMP code to be rewritten on formally secure file sys WH
343            $filePreHash = md5( $filename . time() );
344            // actually limit to first 16 chars is plenty
345            $filePreHash   = substr( $filePreHash, 0, 16 );
346            $finalFileName = $filePreHash . '-' . $filename;
347            $finalFilePath = $currentUploadDir . '/' . $finalFileName;
348
349            // check exists, deal with (unlikely) dupe names
350            $c = 1;
351        while ( file_exists( $finalFilePath ) && $c < 50 ) {
352
353            // remake
354            $finalFileName = $filePreHash . '-' . $c . '-' . $filename;
355            $finalFilePath = $currentUploadDir . '/' . $finalFileName;
356
357            // let it roll + retest
358            ++$c;
359        }
360
361        if ( rename( $fromPath . '/' . $filename, $finalFilePath ) ) {
362
363            // moved :)
364
365            // check perms?
366            /*
367                https://developer.wordpress.org/reference/functions/wp_upload_bits/
368            // Set correct file permissions
369            $stat = @ stat( dirname( $new_file ) );
370            $perms = $stat['mode'] & 0007777;
371            $perms = $perms & 0000666;
372            @ chmod( $new_file, $perms );
373            */
374
375            $endPath = $finalFilePath;
376            // this url is temp, it should be fed via php later.
377            $endURL = $currentUploadURL . '/' . $finalFileName;
378
379            // the caller-func needs to remove/change data/meta :)
380            return array(
381                'file' => $endPath,
382                'url'  => $endURL,
383            );
384
385        } else {
386
387            // failed to move
388            return false;
389        }
390    }
391
392            return false; // couldn't - no dir to move to :)
393}
394
395function zeroBSCRM_privatisedDirCheck( $echo = false ) {
396    $storage_dir_info = jpcrm_storage_dir_info();
397
398    if ( $storage_dir_info === false ) {
399        return false;
400    }
401
402    $is_dir_created = jpcrm_create_and_secure_dir_from_external_access( $storage_dir_info['path'], false );
403
404    if ( $is_dir_created === false ) {
405        return false;
406    }
407
408    return $storage_dir_info;
409}
410
411function jpcrm_get_hash_for_object( $object_id, $object_hash_string ) {
412    global $zbs;
413    $zbs->load_encryption();
414    return $zbs->encryption->hash( $object_id . $zbs->encryption->get_encryption_key( $object_hash_string ) );
415}
416
417/*
418 * Returns the 'dir info' for the storage folder.
419 * dir info =
420 * [
421 *      'path' => 'path for the physical file',
422 *      'url'  => 'public facing url'
423 * ]
424 *
425 */
426function jpcrm_storage_dir_info() {
427    $uploads_dir      = WP_CONTENT_DIR;
428    $uploads_url      = content_url();
429    $private_dir_name = 'jpcrm-storage';
430
431    if ( ! empty( $uploads_dir ) && ! empty( $uploads_url ) ) {
432        $full_dir_path = $uploads_dir . '/' . $private_dir_name;
433        $full_url      = $uploads_url . '/' . $private_dir_name;
434        return array(
435            'path' => $full_dir_path,
436            'url'  => $full_url,
437        );
438    }
439
440    return false;
441}
442
443/**
444 * Returns the 'dir info' for the JPCRM storage folder.
445 *
446 * @return false|string
447 */
448function wf_jpcrm_storage_dir_path() {
449    $root_storage_info = jpcrm_storage_dir_info();
450
451    if ( ! $root_storage_info ) {
452        return false;
453    }
454
455    return $root_storage_info['path'];
456}
457
458/*
459 * Returns the 'dir info' for the fonts folder.
460 */
461function jpcrm_storage_fonts_dir_path() {
462    $root_storage_info = jpcrm_storage_dir_info();
463
464    if ( ! $root_storage_info ) {
465        return false;
466    }
467
468    $fonts_dir = $root_storage_info['path'] . '/fonts/';
469
470    // Create and secure fonts dir as needed
471    if ( ! jpcrm_create_and_secure_dir_from_external_access( $fonts_dir ) || ! is_dir( $fonts_dir ) ) {
472        return false;
473    }
474
475    return $fonts_dir;
476}
477
478/*
479 * Returns the 'dir info' for the provided generic object. This directory should
480 * be used to store all files associated to this object (e.g. contact files,
481 * company files, invoices...).
482 *
483 * dir info =
484 * [
485 *   'subfolder_1' =>
486 *     [
487 *        'path' => 'path for the physical file',
488 *        'url'  => 'public facing url'
489 *      ],
490 *   'subfolder_2' =>
491 *     [
492 *        'path' => 'path for the physical file',
493 *        'url'  => 'public facing url'
494 *      ],
495 *    .
496 *    .
497 *    .
498 * ]
499 *
500 */
501function jpcrm_storage_dir_info_for_object( $object_id, $object_hash_string, $object_parent_folder, $subfolder_list ) {
502    global $zbs;
503
504    $root_storage_info = jpcrm_storage_dir_info();
505
506    if ( $root_storage_info === false ) {
507        return false;
508    }
509
510    $parent_storage_info = array(
511        'path' => $root_storage_info['path'] . '/' . $object_parent_folder,
512        'url'  => $root_storage_info['url'] . '/' . $object_parent_folder,
513    );
514
515    if ( ! jpcrm_create_and_secure_dir_from_external_access( $parent_storage_info['path'], false ) ) {
516        return false;
517    }
518
519    $object_unique_hash   = jpcrm_get_hash_for_object( $object_id, $object_hash_string );
520    $parent_relative_path = sprintf( '/%s-%s/', $object_id, $object_unique_hash );
521    $parent_full_path     = $parent_storage_info['path'] . $parent_relative_path;
522
523    if ( ! jpcrm_create_and_secure_dir_from_external_access( $parent_full_path, false ) ) {
524        return false;
525    }
526
527    $object_dir_info = array();
528
529    foreach ( $subfolder_list as $subfolder ) {
530        $object_dir_info[ $subfolder ] = array(
531            'path' => $parent_full_path . $subfolder,
532            'url'  => $parent_storage_info['url'] . $parent_relative_path . $subfolder,
533        );
534    }
535
536    return $object_dir_info;
537}
538
539/*
540 * Returns the 'dir info' for the provided contact with the subfolders 'avatar',
541 * and 'files'. The definition of 'dir info' can be found in the function
542 * 'jpcrm_storage_dir_info_for_object'.
543 *
544 */
545function jpcrm_storage_dir_info_for_contact( $contact_id ) {
546
547    return jpcrm_storage_dir_info_for_object(
548        $contact_id,
549        'contact_hash',
550        'contacts',
551        array( 'avatar', 'files' )
552    );
553}
554
555/*
556 * Returns the 'dir info' for the provided company with the subfolder 'files'.
557 * The definition of 'dir info' can be found in the function 'jpcrm_storage_dir_info_for_object'.
558 *
559 */
560function jpcrm_storage_dir_info_for_company( $company_id ) {
561
562    return jpcrm_storage_dir_info_for_object(
563        $company_id,
564        'company_hash',
565        'companies',
566        array( 'files' )
567    );
568}
569
570/*
571 * Returns the 'dir info' for the provided invoice with the subfolder 'files'.
572 * The definition of 'dir info' can be found in the function 'jpcrm_storage_dir_info_for_object'.
573 *
574 */
575function jpcrm_storage_dir_info_for_invoices( $invoice_id ) {
576
577    return jpcrm_storage_dir_info_for_object(
578        $invoice_id,
579        'invoice_hash',
580        'invoices',
581        array( 'files' )
582    );
583}
584
585/*
586 * Returns the 'dir info' for the provided quote with the subfolder 'files'.
587 * The definition of 'dir info' can be found in the function 'jpcrm_storage_dir_info_for_object'.
588 *
589 */
590function jpcrm_storage_dir_info_for_quotes( $quote_id ) {
591
592    return jpcrm_storage_dir_info_for_object(
593        $quote_id,
594        'quote_hash',
595        'quotes',
596        array( 'files' )
597    );
598}
599/*
600 * Saves a file uploaded by an admin from $_FILES[ $param_name ] to the folder
601 * $target_dir_info['path'] and returns an array( 'error' => something ) in the
602 * case of errors and an
603 * array( 'file' => file_path, 'url' => file_url, 'priv' => boolean) in the case
604 * of success.
605 */
606function jpcrm_save_admin_upload_to_folder( $param_name, $target_dir_info ) {
607    $upload = wp_upload_bits( $_FILES[ $param_name ]['name'], null, file_get_contents( $_FILES[ $param_name ]['tmp_name'] ) );
608
609    if ( isset( $upload['error'] ) && $upload['error'] != 0 ) {
610        return $upload;
611    }
612    // change this to return a custom error in the future if needed
613    if ( $target_dir_info === false ) {
614        return $upload;
615    }
616
617    global $zbs;
618    $zbs->load_encryption();
619
620    $upload_path          = $target_dir_info['path'];
621    $upload_folder_exists = jpcrm_create_and_secure_dir_from_external_access( $upload_path, false );
622    if ( $upload_folder_exists ) {
623        $upload_filename = sanitize_file_name(
624            sprintf(
625                '%s-%s',
626                $zbs->encryption->get_rand_hex( 16 ),
627                $_FILES[ $param_name ]['name'] // for admins we are accepting "filename.ext" as provided by $_FILES
628            )
629        );
630
631        if ( move_uploaded_file( $_FILES[ $param_name ]['tmp_name'], $upload_path . '/' . $upload_filename ) ) {
632            $upload['file'] = $upload_path . '/' . $upload_filename;
633            $upload['url']  = $target_dir_info['url'] . '/' . $upload_filename;
634            // add this extra identifier if in privatised sys
635            $upload['priv'] = true;
636        }
637    }
638
639    return $upload;
640}
641
642// 2.95.5+ we also add a subdir for 'work' (this is used by CPP when making thumbs, for example)
643function zeroBSCRM_privatisedDirCheckWorks( $echo = false ) {
644    $uploads_dir      = WP_CONTENT_DIR;
645    $uploads_url      = content_url();
646    $private_dir_name = 'jpcrm-storage/tmp';
647
648    if ( ! empty( $uploads_dir ) && ! empty( $uploads_url ) ) {
649        $full_dir_path = $uploads_dir . '/' . $private_dir_name;
650        $full_url      = $uploads_url . '/' . $private_dir_name;
651
652        // check existence
653        if ( ! file_exists( $full_dir_path ) ) {
654
655            // doesn't exist, attempt to create
656            mkdir( $full_dir_path, 0755, true );
657            // force perms?
658            chmod( $full_dir_path, 0755 );
659
660        }
661
662        if ( is_dir( $full_dir_path ) ) {
663            jpcrm_create_and_secure_dir_from_external_access( $full_dir_path );
664            return array(
665                'path' => $full_dir_path,
666                'url'  => $full_url,
667            );
668        }
669    }
670
671    return false;
672}
673
674/*
675======================================================
676    / File Upload related funcs
677    ====================================================== */
678
679/*
680======================================================
681    File Slots helpers
682    ====================================================== */
683
684function zeroBSCRM_fileSlots_getFileSlots( $objType = 1 ) {
685
686    global $zbs;
687
688    $fileSlots = array();
689
690    $settings = zeroBSCRM_getSetting( 'customfields' );
691    $cfbInd   = 1;
692
693    switch ( $objType ) {
694
695        case 1:
696            if ( isset( $settings['customersfiles'] ) && is_array( $settings['customersfiles'] ) && count( $settings['customersfiles'] ) > 0 ) {
697
698                foreach ( $settings['customersfiles'] as $cfb ) {
699
700                    $cfbName = '';
701                    if ( isset( $cfb[0] ) ) {
702                        $cfbName = $cfb[0];
703                    }
704                    $key = $zbs->DAL->makeSlug( $cfbName ); // $cfbInd
705                    if ( ! empty( $key ) ) {
706                        $fileSlots[] = array(
707                            'key'  => $key,
708                            'name' => $cfbName,
709                        );
710                        ++$cfbInd;
711                    }
712                }
713            }
714
715            break;
716
717    }
718
719    return $fileSlots;
720}
721
722    // returns the slot (if assigned) of a given file
723function zeroBSCRM_fileslots_fileSlot( $file = '', $objID = -1, $objType = 1 ) {
724
725    // get all slotted files for contact/obj
726
727    if ( $objID > 0 && ! empty( $file ) ) {
728
729            global $zbs;
730            $fileSlots = zeroBSCRM_fileslots_allSlots( $objID, $objType );
731            // cycle through
732        if ( count( $fileSlots ) > 0 ) {
733
734            foreach ( $fileSlots as $fsKey => $fsFile ) {
735
736                if ( $fsFile == $file ) {
737                    return $fsKey;
738                }
739            }
740        }
741    }
742    return false;
743}
744
745    // returns all slots (if assigned) of a given obj(contact)
746function zeroBSCRM_fileslots_allSlots( $objID = -1, $objType = 1 ) {
747
748    if ( $objID > 0 ) {
749
750            global $zbs;
751            $fileSlots = zeroBSCRM_fileSlots_getFileSlots( ZBS_TYPE_CONTACT );
752            $ret       = array();
753        if ( count( $fileSlots ) > 0 ) {
754
755            foreach ( $fileSlots as $fs ) {
756
757                    $ret[ $fs['key'] ] = zeroBSCRM_fileslots_fileInSlot( $fs['key'], $objID, $objType );
758
759            }
760        }
761            return $ret;
762
763    }
764    return false;
765}
766
767    // returns a file for a slot
768function zeroBSCRM_fileslots_fileInSlot( $fileSlot = '', $objID = -1, $objType = 1 ) {
769
770    if ( $objID > 0 ) {
771
772            global $zbs;
773
774            return $zbs->DAL->meta( $objType, $objID, 'cfile_' . $fileSlot );
775
776    }
777    return false;
778}
779
780    // adds a file to a slot
781function zeroBSCRM_fileslots_addToSlot( $fileSlot = '', $file = '', $objID = -1, $objType = 1, $overrite = false ) {
782
783    if ( $objID > 0 ) {
784
785            // echo '<br>zeroBSCRM_fileslots_addToSlot '.$fileSlot.' '.$file.' '.$objID.' ext:'.zeroBSCRM_fileslots_fileInSlot($fileSlot,$objID).'!';
786
787            global $zbs;
788
789            // check existing?
790        if ( ! $overrite ) {
791            $existingFile = zeroBSCRM_fileslots_fileInSlot( $fileSlot, $objID );
792            if ( ! empty( $existingFile ) ) {
793                return false;
794            }
795        } else {
796
797            // overrite... so remove any if present before..
798            zeroBSCRM_fileslots_clearFileSlot( $fileSlot, $objID, $objType );
799        }
800
801            // DAL2 add via meta (for now)
802            $zbs->DAL->updateMeta( $objType, $objID, 'cfile_' . $fileSlot, $file );
803            return true;
804
805    }
806
807    return false;
808}
809
810function zeroBSCRM_fileslots_clearFileSlot( $fileSlot = '', $objID = -1, $objType = 1 ) {
811
812    if ( $objID > 0 ) {
813
814            global $zbs;
815            return $zbs->DAL->deleteMeta(
816                array(
817                    'objtype' => $objType,
818                    'objid'   => $objID,
819                    'key'     => 'cfile_' . $fileSlot,
820                )
821            );
822
823    }
824
825    return false;
826}
827
828function zeroBSCRM_files_baseName( $filePath = '', $privateRepo = false ) {
829
830    $file = '';
831    if ( ! empty( $filePath ) ) {
832
833            $file = basename( $filePath );
834        if ( $privateRepo ) {
835            $file = substr( $file, strpos( $file, '-' ) + 1 );
836        }
837    }
838
839    return $file;
840}
841
842/*
843======================================================
844    / File Slots helpers
845    ====================================================== */
846
847// gives an hashed filename+salt that is generally suitable for filesystems
848function jpcrm_get_hashed_filename( $filename, $suffix = '' ) {
849    global $zbs;
850    $zbs->load_encryption();
851    $salt            = $zbs->encryption->get_encryption_key( 'filename' );
852    $hashed_filename = $zbs->encryption->hash( $filename . $salt ) . $suffix;
853    return $hashed_filename;
854}
855
856/**
857 * Checks legitimacy (in so far as practicable) of an uploaded file
858 * With default 'setting' params, checks against the core functions:
859 * `zeroBS_acceptableFileTypeMIMEArr()` and `zeroBS_acceptableFileTypeListArr()`
860 * (Which reflect the core setting)
861 *
862 * @param $FILE
863 * @param $check_file_extension string|array - if string, can be single, e.g. `.pdf`, if array, can be multiple strings
864 * @param $check_mime_type string|array - if string, can be single, e.g. `application/pdf`, if array, can be multiple strings
865 */
866function jpcrm_file_check_mime_extension( $FILE, $check_file_extension = 'setting', $check_mime_type = 'setting' ) {
867
868        // expects $_FILES type array (e.g. pass $_FILES['zbsc_file_attachment'])
869    if ( ! is_array( $FILE ) || ! isset( $FILE['name'] ) || ! isset( $FILE['type'] ) || ! isset( $FILE['tmp_name'] ) ) {
870        return false;
871    }
872
873        // check file extension
874
875        // retrieve settings, or prepare an array of acceptable file extensions from passed string/array
876    if ( $check_file_extension == 'setting' ) {
877        $check_file_extension = zeroBS_acceptableFileTypeListArr();
878    } elseif ( $check_file_extension != 'setting' && ! is_array( $check_file_extension ) ) {
879        $check_file_extension = array( $check_file_extension );
880    }
881
882        // check actual extension
883    if ( ! in_array( '.' . pathinfo( $FILE['name'], PATHINFO_EXTENSION ), $check_file_extension ) ) {
884        return false;
885    }
886
887        // check mime
888
889        // retrieve settings, or prepare an array of acceptable file extensions from passed string/array
890    if ( $check_mime_type == 'setting' ) {
891        $check_mime_type = zeroBS_acceptableFileTypeMIMEArr();
892    } elseif ( $check_mime_type != 'setting' && ! is_array( $check_mime_type ) ) {
893        $check_mime_type = array( $check_mime_type );
894    }
895
896        // catch 'all' legacy solution which (perhaps dangerously) sidesteps this check
897        // will do mime check if legacy 'all' is empty, which includes false, 0, and !isset()
898    if ( empty( $check_mime_type['all'] ) ) {
899        // check actual mime type
900        if ( ! in_array( $FILE['type'], $check_mime_type ) ) {
901            return false;
902        }
903
904        // also check the mime type directly inferred from the uploaded file
905        // note: we don't check this type against $FILE['type'] because it
906        // doesn't really matter if they are different but both accepted types
907        $tmp_file_type = jpcrm_get_mimetype( $FILE['tmp_name'] ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
908
909        if ( ! in_array( $tmp_file_type, $check_mime_type ) ) {
910            return false;
911        }
912    }
913
914        return true;
915}