Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
<?php
class Post_Model extends TinyMVC_Model
{
public $post = NULL;
public $files = NULL;
function __construct()
{
parent::__construct();
$this->post = &$_POST;
$this->files = &$_FILES;
/* Do not evaluate anchor here cause it may harm POST query
related to theme switching.
Until there is no model for all POST processing in one place. */
$anchor = '';
if (! empty($_POST) ) {
$notices = array();
$this->msg('$_POST:');
$this->msg('<pre>'.var_export($_POST,1).'</pre>');
/* building arrays for checked posts and files */
$cboxes = array_keys($_POST, 'on', true);
$posts_ids = array();
$files_ids = array();
for ($i=0; $i<count($cboxes); $i++) {
$probably_id = preg_replace('/^cbox_post_([0-9]+)$/','$1',$cboxes[$i]);
if ($probably_id !== NULL && $probably_id !== $cboxes[$i])
$posts_ids[] = $probably_id;
$probably_id = preg_replace('/^cbox_file_([0-9]+)$/','$1',$cboxes[$i]);
if ($probably_id !== NULL && $probably_id !== $cboxes[$i])
$files_ids[] = $probably_id;
}
/* bad proxy */
if ( !empty($_SERVER['HTTP_X_FORWARDED_FOR']) )
$notices[] = 'bad_proxy';
/* false cookie */
elseif (! tmvc::instance()->controller->user->had_valid_cookie )
$notices[] = 'no_cookie';
/* delete_post */
elseif( isset($_POST['delete_post']) ) {
if ( !empty($posts_ids) ) {
for ($i=0; $i<count($posts_ids); $i++) {
$comment_data = $this->db->query_one('select session_id,date
from det_comments where id=?',array($posts_ids[$i]) );
if ( !empty($comment_data) ) {
if ($comment_data['session_id'] ==
tmvc::instance()->controller->user->session_id
||
tmvc::instance()->controller->user->is_admin) {
if ( (time() - (integer)$comment_data['date']) < 180
||
tmvc::instance()->controller->user->is_admin) {
$this->db->where('id',$posts_ids[$i]);
$this->db->delete('det_comments');
$anchor='';
}
else
$notices[] = 'too_late';
}
else
$notices = 'cannot_delete';
}
else
$notices[] = 'no_such_id';
}
}
else
$notices[] = 'invalid_id';
}
/* approve post */
elseif ( isset($_POST['approve_post']) &&
tmvc::instance()->controller->user->is_admin === true ) {
if ( !empty($posts_ids) ) {
for ($i=0; $i<count($posts_ids); $i++) {
$comment_data = $this->db->query_one(
'select approved from det_comments where id=?',
array($posts_ids[$i]) );
if ($comment_data['approved'] == 0) {
$this->msg("Strarting the approval process for the comment #{$posts_ids[$i]}");
$this->db->where('id',$posts_ids[$i]);
$this->db->update('det_comments',array('approved' => 1));
$comment_data = $this->db->query_one(
'select session_id from det_comments where id=?',
array($posts_ids[$i]));
$this->msg("Session id is… ‘{$comment_data['session_id']}’",1);
$session_data = $this->db->query_one(
'select posts_approved,trusted from det_sessions where session_id=?',
array($comment_data['session_id']) );
$this->msg("This session is… ".(($session_data['trusted'] ===true)?'trusted':'untrusted'),1);
$this->msg("The session has {$session_data['posts_approved']} posts approved before.",1);
$this->msg(var_export($session_data['posts_approved'],1),1);
if ($session_data['posts_approved']+1 >=
tmvc::instance()->config['trusted_user_barrier'])
$session_data['trusted'] = true;
$this->db->where('session_id',$comment_data['session_id']);
$this->db->update('det_sessions',
array('posts_approved' =>
$session_data['posts_approved']+1,
'trusted' =>
$session_data['trusted'] ));
}
}
}
else
$notices[] = 'invalid_id';
}
/* ban files */
elseif ( isset($_POST['ban_files']) &&
tmvc::instance()->controller->user->is_admin === true ) {
if ( !empty($files_ids) ) {
for ($i=0; $i<count($files_ids); $i++) {
$this->db->where('id',$files_ids[$i]);
$this->db->update('det_files',array('type' => 'banned') );
}
}
else
$notices[] = 'invalid_id';
}
/* new_post */ /* Always last! */
elseif ( !empty($_POST['new_post']) || !empty($_FILES) ) {
/* This call’s only purpose is to check and reset captcha level.
There can be a gap in time between the page was given to browser
and the time this POST request has come to the server,
this is when captcha’s level may changed (multiple clients with
one session_id cookie should increase the level, and post from
any other session_id but user’s will drop the level to 0
and thus all this can be tracked). */
tmvc::instance()->controller->comments->build_tree('on_SIds_only');
/* This has only purpose to provide anchor, but do we really need it
to be set to last comment of the tree by default? We are in
POST precoessing structure now, and if the result will be positive,
i.e. a new comment will be added, its number will appear in the
anchor, and in case of any trouble anchor will be set
to the notices box,*/
/* if ( !empty(tmvc::instance()->controller->comments->tree) ) */
/* $anchor = '#post_'. */
/* tmvc::instance()->controller->comments->tree */
/* [count(tmvc::instance()->controller->comments->tree)-1]['id']; */
/* Validating captcha till the ELSE statement */
if ( ! (
tmvc::instance()->controller->captcha->prepare_post_data()
&&
tmvc::instance()->controller->captcha->get_saved_data()
) )
header('Location: '.
tmvc::instance()->controller->page->url_wo_args.
'?no_such_captcha', true, 303);
elseif (tmvc::instance()->controller->captcha->saved_level >
tmvc::instance()->controller->captcha->level
&&
!tmvc::instance()->controller->user->can_post_wo_captcha)
header('Location: '.
tmvc::instance()->controller->page->url_wo_args.
'?captcha_level_has_changed', true, 303);
elseif (! (tmvc::instance()->controller->captcha->is_valid()
||
tmvc::instance()->controller->user->can_post_wo_captcha) )
header('Location: '.
tmvc::instance()->controller->page->url_wo_args.
'?no_such_captcha', true, 303);
else {
/* process the request */
if ( isset($_POST['new_post']) ) {
/* isset and not ‘not empty’ because of we rely upon non-empty
new post body OR some valid uploaded files. As we can’t
start from both let’s suppose we have at least new_post set
to something */
$_POST['name'] =
( (isset($_POST['name']))?
htmlspecialchars( substr( $_POST['name'],0,88),
NULL,
'UTF-8')
:'');
$_POST['new_post'] =
/* htmlspecialchars( */ $_POST['new_post']/* , */
/* NULL, */
/* 'UTF-8') */;
$new_comment = array(
'session_id' =>
tmvc::instance()->controller->user->session_id,
'page' =>
tmvc::instance()->controller->page->section_slash_file,
'name' => $_POST['name'],
'body' => $_POST['new_post'],
'date' => time(),
'approved' => tmvc::instance()->controller->user->is_trusted?1:0,
);
$this->db->insert('det_comments', $new_comment );
$new_comment_id = $this->db->last_insert_id();
/* Files */
/* This flag will be used to know if any files from $_FILES were properly
added to post, and if none of ’em were AND comment body is empty,
treat the whole post as empty and delete it from DB. */
$successful_file = false;
if (!empty($_FILES)) {
$this->msg('$_FILES:');
$this->msg('<pre>'.var_export($_FILES,1).'</pre>');
$this->msg('Processing uploaded files…');
foreach ($_FILES as $filename => $params_array) {
$this->msg('$_FILES['.$filename.']:',1);
/* if ( !empty($_POST[$filename.'_hash']) ) { */
/* $id = $this->check_hash($_POST[$filename.'_hash']);
if ( $id !== FALSE )
$file_exist = true;
else
Create new empty record in det_files with currernt timestamp.
Send HTTP <not added> or something code.
Then in AJAX send another POST request containing
the file’s body and id of the empty file record in det_files.
If uploaded file’s last mod. time is higher
than timestamp of the empty record in database by
not more than 20min, move that uploaded file to permanent
folder, and update the record.
}
if (!empty($_POST['sent_after'])) // by AJAX
check the $_FILES[] for a new file
*/
if ( preg_match('/file[1-5]/',$filename) === 1 ) {
if ( $_FILES[$filename]['error'] === 0 ) {
if ( is_uploaded_file($_FILES[$filename]['tmp_name']) ) {
/* check hash */
/* if md5 of the file exist in the database, take data from
db
*/
$tmpfile_md5 = explode(" ",
exec('md5sum '.
$_FILES[$filename]['tmp_name']));
$file_data = $this->db->query_one(
'select id from det_files where hash=?',
array($tmpfile_md5[0]));
$_FILES[$filename]['id'] =
( ! empty($file_data) )?
$file_data['id']
:NULL;
if ($_FILES[$filename]['id'] === NULL) {
/* We need to add a new file */
$this->msg('No hash found. This is a new file.',2);
/* Define mime type */
$_FILES[$filename]['mimetype'] =
exec('mimetype --output-format %m '.
$_FILES[$filename]['tmp_name']);
$this->msg("MIME type defined as… {$_FILES[$filename]['mimetype']}",2);
/* Trying to keep original name */
$name_data = pathinfo($_FILES[$filename]['name']);
$name = $name_data['filename'];
$ext = $name_data['extension'];
$suffix = '';
$this->msg('Trying the following name (name, suffix, extension):'.
"‘{$name}’, ‘{$suffix}’, ‘{$ext}’.",2);
while (file_exists(DET_FILES.'/'.$name.(string)$suffix.'.'.$ext)) {
if ($suffix == '')
$suffix = 1;
else
$suffix++;
}
$this->msg('Will write with the following name (name, suffix, extension):'.
"‘{$name}’, ‘{$suffix}’, ‘{$ext}’.",2);
$file_path = DET_FILES.'/'.$name.(string)$suffix.'.'.$ext;
move_uploaded_file($_FILES[$filename]['tmp_name'],
$file_path);
/* Creating a thumbnail */
$mime_archives = array('application/octet-stream',
'application/x-archive',
'application/x-bzip',
'application/x-bzip-compressed-tar',
'application/x-bzpdf',
'application/x-compressed-tar',
'application/x-gzip',
'application/x-lrzip',
'application/x-lrzip-compressed-tar',
'application/x-lzip',
'application/x-lzma-compressed-tar',
'application/x-rar',
'application/x-tar',
'application/x-tarz',
'application/x-xz-compressed-tar',
'application/zip');
$mimecat = preg_replace('|^([^/]+)/.*|','$1',
$_FILES[$filename]['mimetype']);
$this->msg("MIME category is… {$mimecat}.",2);
if ($mimecat === 'image') {
exec('convert -define jpeg:size=500x500 "'.$file_path.
'" -auto-orient -thumbnail 200x200 -unsharp 0x.5 "'.
DET_THUMBS.'/'.$name.$suffix.'.jpg"');
$_FILES[$filename]['type'] = 'image';
}
elseif ( $mimecat === 'text') {
$_FILES[$filename]['type'] = 'text';
}
elseif ($_FILES[$filename]['mimetype'] === 'application/pdf' ) {
$_FILES[$filename]['type'] = 'pdf';
}
elseif ( in_array($_FILES[$filename]['mimetype'],
$mime_archives) ) {
$_FILES[$filename]['type'] = 'archive';
}
elseif ($mimecat === 'audio') {
$_FILES[$filename]['type'] = 'audio';
}
else {
/* PHP error can’t be 9 as of v5.4 */
$notices[] = array($filename,
'9',
$_FILES[$filename]['name']);
/* Drop processing of this file and process the next one */
$this->msg('Defined file type was not allowed and processing stopped.',2);
break;
}
$this->msg("File type is defined as… {$_FILES[$filename]['type']}.",2);
/* add file to DB */
$this->db->insert('det_files',
array(
'name' => $name.(string)$suffix.'.'.$ext,
'hash' => $tmpfile_md5[0],
'type' => $_FILES[$filename]['type']
));
$_FILES[$filename]['id'] = $this->db->last_insert_id();
} /* Eo if $_FILES[$filename]['id'] === NULL */
else
$this->msg("Hash match for id={$_FILES[$filename]['id']} in det_files. This file was uploaded earlier.",2);
/* Eo update record with file */
$this->db->where('id',$new_comment_id);
$this->db->update('det_comments', array(
$filename =>
$_FILES[$filename]['id'] ));
$successful_file = true;
} /* truly uploaded file */
else {
$notices[] = 'incorrect_query';
$this->msg('Something strange happened or break-in attempt. File wasn’t uploaded.',2);
}
} /* if uploaded without errors */
else {
/* If a file was not uploaded and no name was specified,
it doesn’t seem to be like an actual error, but that
no file was given and form was just sent with an empty
fileX field. */
if ( $_FILES[$filename]['error'] == '4' &&
$_FILES[$filename]['name'] == '' )
break;
$notices[] = array($filename,
$_FILES[$filename]['error'],
$_FILES[$filename]['name']);
if (array_key_exists($_FILES[$filename]['error'],
tmvc::instance()->controller->informer->file_upload_notices))
$this->msg('An error occured: '.
tmvc::instance()->controller->informer->file_upload_notices
[$_FILES[$filename]['error']],
2);
}
/* } if POST[$filename.'_hash'] */
}
else { /* incorrect file name passed through HTML form */
$notices[] = 'incorrect_query';
$this->msg('Invalid file name. Valid are: file[1…'.tmvc::instance()->config['max_files_for_upload'].'].',2);
}
} /* foreach */
} /* if !empty($_FILES) */
$this->msg($successful_file?'There are file coming with new post.'
:'There is no file coming with new post.');
$this->msg('Comment body is… '.
(empty($new_comment['body'])?'empty':'not_empty').'.');
/* No files + no text = empty comment. Should be deleted */
if (!$successful_file && empty($new_comment['body'])) {
$this->db->where('id',$new_comment_id);
$this->db->delete('det_comments');
$this->msg('Uploaded comment was considered empty and was deleted.');
$notices[]= 'nothing_to_post';
}
else {
$anchor = '#post_'.$new_comment_id;
}
}
}
}
/* All the processing of $_POST is finished, now just collecting messages
and redirecting to page with a proper anchor (empty|post_#|notices). */
// $this->msg(var_export($notices,1));
$anchor =
empty($notices)?
$anchor
:'#notices';
for ($i=0; $i<count($notices); $i++) {
if ( is_array($notices[$i]) ) {
setcookie($notices[$i][0].'_err', $notices[$i][1],
time()+3600, '/', $_SERVER['HTTP_HOST']);
setcookie($notices[$i][0].'_name', $notices[$i][2],
time()+3600, '/', $_SERVER['HTTP_HOST']);
}
else
setcookie($notices[$i],'t',time()+3600,'/',$_SERVER['HTTP_HOST']);
}
if ( DET_DEBUG && DET_FORCE_303_REDIRECT || !DET_DEBUG )
header('Location: '.
tmvc::instance()->controller->page->url_wo_args.$anchor,
true, 303);
} /* Eof if !empty($_POST) */
/* Now we need to check, if the page is going to do a redirect,
and if so, return and do not generate captcha, parse comment tree
or something. Great time economy. */
$headers = headers_list();
$location_header_found = false;
foreach ($headers as $hdr)
if (stripos($hdr, 'Location') !== false) {
$location_header_found = true;
break;
}
if ($location_header_found === true)
exit;
}
function msg($string,$lvl=0) {
tmvc::instance()->controller->debug->msg($string,$lvl);
}
function notice($string) {
tmvc::instance()->controller->informer->notice($string);
}
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.