Issues (4069)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

modules/Documents/Document.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2 1
if(!defined('sugarEntry') || !sugarEntry)
3
	die('Not A Valid Entry Point');
4
/*********************************************************************************
5
 * SugarCRM Community Edition is a customer relationship management program developed by
6
 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
7
8
 * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd.
9
 * Copyright (C) 2011 - 2014 Salesagility Ltd.
10
 *
11
 * This program is free software; you can redistribute it and/or modify it under
12
 * the terms of the GNU Affero General Public License version 3 as published by the
13
 * Free Software Foundation with the addition of the following permission added
14
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
15
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
16
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
17
 *
18
 * This program is distributed in the hope that it will be useful, but WITHOUT
19
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
21
 * details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License along with
24
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
25
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26
 * 02110-1301 USA.
27
 *
28
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
29
 * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected].
30
 *
31
 * The interactive user interfaces in modified source and object code versions
32
 * of this program must display Appropriate Legal Notices, as required under
33
 * Section 5 of the GNU Affero General Public License version 3.
34
 *
35
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
36
 * these Appropriate Legal Notices must retain the display of the "Powered by
37
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
38
 * reasonably feasible for  technical reasons, the Appropriate Legal Notices must
39
 * display the words  "Powered by SugarCRM" and "Supercharged by SuiteCRM".
40
 ********************************************************************************/
41
42 1
require_once('include/SugarObjects/templates/file/File.php');
43
44
45
// User is used to store Forecast information.
46
class Document extends File {
47
48
	var $id;
49
	var $document_name;
50
	var $description;
51
	var $category_id;
52
	var $subcategory_id;
53
	var $status_id;
54
	var $status;
55
	var $created_by;
56
	var $date_entered;
57
	var $date_modified;
58
	var $modified_user_id;
59
    var $assigned_user_id;
60
	var $active_date;
61
	var $exp_date;
62
	var $document_revision_id;
63
	var $filename;
64
	var $doc_type;
65
66
	var $img_name;
67
	var $img_name_bare;
68
	var $related_doc_id;
69
	var $related_doc_name;
70
	var $related_doc_rev_id;
71
	var $related_doc_rev_number;
72
	var $is_template;
73
	var $template_type;
74
75
	//additional fields.
76
	var $revision;
77
	var $last_rev_create_date;
78
	var $last_rev_created_by;
79
	var $last_rev_created_name;
80
	var $file_url;
81
	var $file_url_noimage;
82
83
	var $table_name = "documents";
84
	var $object_name = "Document";
85
	var $user_preferences;
86
87
	var $encodeFields = Array ();
88
89
	// This is used to retrieve related fields from form posts.
90
	var $additional_column_fields = Array ('revision');
91
92
	var $new_schema = true;
93
	var $module_dir = 'Documents';
94
95
	var $relationship_fields = Array(
96
		'contract_id'=>'contracts',
97
	 );
98
99
100 22
	function __construct() {
101 22
		parent::__construct();
102 22
		$this->setupCustomFields('Documents'); //parameter is module name
103 22
		$this->disable_row_level_security = false;
104 22
	}
105
106
    /**
107
     * @deprecated deprecated since version 7.6, PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code, use __construct instead
108
     */
109
    function Document(){
110
        $deprecatedMessage = 'PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code';
111
        if(isset($GLOBALS['log'])) {
112
            $GLOBALS['log']->deprecated($deprecatedMessage);
113
        }
114
        else {
115
            trigger_error($deprecatedMessage, E_USER_DEPRECATED);
116
        }
117
        self::__construct();
118
    }
119
120
121 1
	function save($check_notify = false) {
122
123 1
        if (empty($this->doc_type)) {
124
			$this->doc_type = 'Sugar';
125
		}
126 1
        if (empty($this->id) || $this->new_with_id)
127
		{
128 1
            if (empty($this->id)) {
129 1
                $this->id = create_guid();
130 1
                $this->new_with_id = true;
131
            }
132
133 1
            if ( isset($_REQUEST) && isset($_REQUEST['duplicateSave']) && $_REQUEST['duplicateSave'] == true && isset($_REQUEST['filename_old_doctype']) ) {
134
                $this->doc_type = $_REQUEST['filename_old_doctype'];
135
                $isDuplicate = true;
136
            } else {
137 1
                $isDuplicate = false;
138
            }
139
140 1
            $Revision = new DocumentRevision();
141
            //save revision.
142 1
            $Revision->in_workflow = true;
143 1
            $Revision->not_use_rel_in_req = true;
144 1
            $Revision->new_rel_id = $this->id;
145 1
            $Revision->new_rel_relname = 'Documents';
146 1
            $Revision->change_log = translate('DEF_CREATE_LOG','Documents');
147 1
            $Revision->revision = $this->revision;
148 1
            $Revision->document_id = $this->id;
149 1
            $Revision->filename = $this->filename;
150
151 1
            if(isset($this->file_ext))
152
            {
153
            	$Revision->file_ext = $this->file_ext;
154
            }
155
156 1
            if(isset($this->file_mime_type))
157
            {
158
            	$Revision->file_mime_type = $this->file_mime_type;
159
            }
160
161 1
            $Revision->doc_type = $this->doc_type;
162 1
            if ( isset($this->doc_id) ) {
163
                $Revision->doc_id = $this->doc_id;
164
            }
165 1
            if ( isset($this->doc_url) ) {
166
                $Revision->doc_url = $this->doc_url;
167
            }
168
169 1
            $Revision->id = create_guid();
170 1
            $Revision->new_with_id = true;
171
172 1
            $createRevision = false;
173
            //Move file saved during populatefrompost to match the revision id rather than document id
174 1
            if (!empty($_FILES['filename_file'])) {
175
                rename("upload://{$this->id}", "upload://{$Revision->id}");
176
                $createRevision = true;
177 1
            } else if ( $isDuplicate && ( empty($this->doc_type) || $this->doc_type == 'Sugar' ) ) {
178
                // Looks like we need to duplicate a file, this is tricky
179
                $oldDocument = new Document();
180
                $oldDocument->retrieve($_REQUEST['duplicateId']);
181
                $old_name = "upload://{$oldDocument->document_revision_id}";
182
                $new_name = "upload://{$Revision->id}";
183
                $GLOBALS['log']->debug("Attempting to copy from $old_name to $new_name");
184
                copy($old_name, $new_name);
185
                $createRevision = true;
186
            }
187
188
            // For external documents, we just need to make sure we have a doc_id
189 1
            if ( !empty($this->doc_id) && $this->doc_type != 'Sugar' ) {
190
                $createRevision = true;
191
            }
192
193 1
            if ( $createRevision ) {
194
                $Revision->save();
195
                //update document with latest revision id
196
                $this->process_save_dates=false; //make sure that conversion does not happen again.
197
                $this->document_revision_id = $Revision->id;
198
            }
199
200
201
            //set relationship field values if contract_id is passed (via subpanel create)
202 1
            if (!empty($_POST['contract_id'])) {
203
                $save_revision['document_revision_id']=$this->document_revision_id;
204
                $this->load_relationship('contracts');
205
                $this->contracts->add($_POST['contract_id'],$save_revision);
206
            }
207
208 1
            if ((isset($_POST['load_signed_id']) and !empty($_POST['load_signed_id']))) {
209
                $query="update linked_documents set deleted=1 where id='".$_POST['load_signed_id']."'";
210
                $this->db->query($query);
211
            }
212
        }
213
214 1
		return parent :: save($check_notify);
215
	}
216 2
	function get_summary_text() {
217 2
		return "$this->document_name";
218
	}
219
220 1
	function is_authenticated() {
221 1
		return $this->authenticated;
222
	}
223
224 2
	function fill_in_additional_list_fields() {
225 2
		$this->fill_in_additional_detail_fields();
226 2
	}
227
228 4
	function fill_in_additional_detail_fields() {
229 4
		global $theme;
230 4
		global $current_language;
231 4
		global $timedate;
232 4
		global $locale;
233
234 4
		parent::fill_in_additional_detail_fields();
235
236 4
		$mod_strings = return_module_language($current_language, 'Documents');
237
238 4
        if (!empty($this->document_revision_id)) {
239
240
            $query = "SELECT users.first_name AS first_name, users.last_name AS last_name, document_revisions.date_entered AS rev_date,
241
            	 document_revisions.filename AS filename, document_revisions.revision AS revision,
242
            	 document_revisions.file_ext AS file_ext, document_revisions.file_mime_type AS file_mime_type
243
            	 FROM users, document_revisions
244
            	 WHERE users.id = document_revisions.created_by AND document_revisions.id = '$this->document_revision_id'";
245
246
            $result = $this->db->query($query);
247
            $row = $this->db->fetchByAssoc($result);
248
249
            //populate name
250
            if(isset($this->document_name))
251
            {
252
            	$this->name = $this->document_name;
253
            }
254
255
            if(isset($row['filename']))$this->filename = $row['filename'];
256
            //$this->latest_revision = $row['revision'];
257
            if(isset($row['revision']))$this->revision = $row['revision'];
258
259
            //image is selected based on the extension name <ext>_icon_inline, extension is stored in document_revisions.
260
            //if file is not found then default image file will be used.
261
            global $img_name;
262
            global $img_name_bare;
263
            if (!empty ($row['file_ext'])) {
264
                $img_name = SugarThemeRegistry::current()->getImageURL(strtolower($row['file_ext'])."_image_inline.gif");
265
                $img_name_bare = strtolower($row['file_ext'])."_image_inline";
266
            }
267
        }
268
269
		//set default file name.
270 4
		if (!empty ($img_name) && file_exists($img_name)) {
271
			$img_name = $img_name_bare;
272
		} else {
273 4
			$img_name = "def_image_inline"; //todo change the default image.
274
		}
275 4
		if($this->ACLAccess('DetailView')) {
276 4
			if(!empty($this->doc_type) && $this->doc_type != 'Sugar' && !empty($this->doc_url)) {
277
                $file_url= "<a href='".$this->doc_url."' target='_blank'>".SugarThemeRegistry::current()->getImage($this->doc_type.'_image_inline', 'border="0"',null,null,'.png',$mod_strings['LBL_LIST_VIEW_DOCUMENT'])."</a>";
278
			} else {
279 4
			    $file_url = "<a href='index.php?entryPoint=download&id={$this->document_revision_id}&type=Documents' target='_blank'>".SugarThemeRegistry::current()->getImage($img_name, 'border="0"', null,null,'.gif',$mod_strings['LBL_LIST_VIEW_DOCUMENT'])."</a>";
280
			}
281
282 4
    		$this->file_url = $file_url;
283 4
    		$this->file_url_noimage = "index.php?entryPoint=download&type=Documents&id={$this->document_revision_id}";
284
		}else{
285
            $this->file_url = "";
286
            $this->file_url_noimage = "";
287
		}
288
289
		//get last_rev_by user name.
290 4
		if (!empty ($row)) {
291
			$this->last_rev_created_name = $locale->getLocaleFormattedName($row['first_name'], $row['last_name']);
292
293
			$this->last_rev_create_date = $timedate->to_display_date_time($this->db->fromConvert($row['rev_date'], 'datetime'));
294
			$this->last_rev_mime_type = $row['file_mime_type'];
295
		}
296
297 4
		global $app_list_strings;
298 4
	    if(!empty($this->status_id)) {
299
	       //_pp($this->status_id);
300
	       $this->status = $app_list_strings['document_status_dom'][$this->status_id];
301
	    }
302 4
        if (!empty($this->related_doc_id)) {
303
            $this->related_doc_name = Document::get_document_name($this->related_doc_id);
304
            $this->related_doc_rev_number = DocumentRevision::get_document_revision_name($this->related_doc_rev_id);
305
        }
306 4
	}
307
308 1
	function list_view_parse_additional_sections(&$list_form/*, $xTemplateSection*/) {
309 1
		return $list_form;
310
	}
311
312 1
    function create_export_query($order_by, $where, $relate_link_join='')
313
    {
314 1
        $custom_join = $this->getCustomJoin(true, true, $where);
315 1
        $custom_join['join'] .= $relate_link_join;
316
		$query = "SELECT
317 1
						documents.*";
318 1
        $query .=  $custom_join['select'];
319 1
		$query .= " FROM documents ";
320 1
        $query .=  $custom_join['join'];
321
322 1
		$where_auto = " documents.deleted = 0";
323
324 1
		if ($where != "")
325 1
			$query .= " WHERE $where AND ".$where_auto;
326
		else
327 1
			$query .= " WHERE ".$where_auto;
328
329 1
		if ($order_by != "")
330 1
			$query .= " ORDER BY $order_by";
331
		else
332 1
			$query .= " ORDER BY documents.document_name";
333
334 1
		return $query;
335
	}
336
337 1
	function get_list_view_data() {
338 1
		global $current_language;
339 1
		$app_list_strings = return_app_list_strings_language($current_language);
340
341 1
		$document_fields = $this->get_list_view_array();
342
343 1
        $this->fill_in_additional_list_fields();
344
345
346 1
		$document_fields['FILENAME'] = $this->filename;
347 1
		$document_fields['FILE_URL'] = $this->file_url;
348 1
		$document_fields['FILE_URL_NOIMAGE'] = $this->file_url_noimage;
349 1
		$document_fields['LAST_REV_CREATED_BY'] = $this->last_rev_created_name;
350 1
		$document_fields['CATEGORY_ID'] = empty ($this->category_id) ? "" : $app_list_strings['document_category_dom'][$this->category_id];
351 1
		$document_fields['SUBCATEGORY_ID'] = empty ($this->subcategory_id) ? "" : $app_list_strings['document_subcategory_dom'][$this->subcategory_id];
352 1
        $document_fields['NAME'] = $this->document_name;
353 1
		$document_fields['DOCUMENT_NAME_JAVASCRIPT'] = $GLOBALS['db']->quote($document_fields['DOCUMENT_NAME']);
354 1
		return $document_fields;
355
	}
356
357
358
    /**
359
     * mark_relationships_deleted
360
     *
361
     * Override method from SugarBean to handle deleting relationships associated with a Document.  This method will
362
     * remove DocumentRevision relationships and then optionally delete Contracts depending on the version.
363
     *
364
     * @param $id String The record id of the Document instance
365
     */
366 2
	function mark_relationships_deleted($id)
367
    {
368 2
        $this->load_relationships('revisions');
0 ignored issues
show
The call to Document::load_relationships() has too many arguments starting with 'revisions'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
369 2
       	$revisions= $this->get_linked_beans('revisions','DocumentRevision');
370
371 2
       	if (!empty($revisions) && is_array($revisions)) {
372
       		foreach($revisions as $key=>$version) {
373
       			UploadFile::unlink_file($version->id,$version->filename);
374
       			//mark the version deleted.
375
       			$version->mark_deleted($version->id);
376
       		}
377
       	}
378
379 2
	}
380
381
382 22
	function bean_implements($interface) {
383
		switch ($interface) {
384 22
			case 'ACL' :
0 ignored issues
show
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
385 22
				return true;
386
		}
387 2
		return false;
388
	}
389
390
	//static function.
391 1
	function get_document_name($doc_id){
392 1
		if (empty($doc_id)) return null;
393
394 1
		$db = DBManagerFactory::getInstance();
395 1
		$query="select document_name from documents where id='$doc_id'  and deleted=0";
396 1
		$result=$db->query($query);
397 1
		if (!empty($result)) {
398 1
			$row=$db->fetchByAssoc($result);
399 1
			if (!empty($row)) {
400 1
				return $row['document_name'];
401
			}
402
		}
403 1
		return null;
404
	}
405
}
406
407 1
require_once('modules/Documents/DocumentExternalApiDropDown.php');
408
409