Just to support rproffitt's, on Ubuntu 16.04 it redirects to 127::1:

ยป ping -c 3                                                                                       
PING ( 56(84) bytes of data.
64 bytes from icmp_seq=1 ttl=64 time=0.055 ms
64 bytes from icmp_seq=2 ttl=64 time=0.029 ms
64 bytes from icmp_seq=3 ttl=64 time=0.048 ms

--- ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.029/0.044/0.055/0.011 ms

On Mac OS it fails:

$ ping -c 3
PING ( 56 data bytes
ping: sendto: No route to host
ping: sendto: No route to host
Request timeout for icmp_seq 0
ping: sendto: No route to host
Request timeout for icmp_seq 1
--- ping statistics ---
3 packets transmitted, 0 packets received, 100.0% packet loss
Votes + Comments
Thanks for the tests.

I just saw your reply, I'm glad you solved!

// Old answer

as far $permissions is defined by the input and defaulting to the database and not like an array:

$permissions = ((isset($_POST['permissions']) && $_POST['permissions'] != '')?sanitize($_POST['permissions']):$User['permissions']);

you can hardcode the options in the select tag, and then just compare which is set:

<select name="permissions">
    <option value="editor" <?php echo (0 == strcasecmp($permissions, 'editor')) ? ' selected="selected"' : ''; ?>>Editor</option>
    <option value="admin,editor" <?php echo (0 == strcasecmp($permissions, 'admin,editor')) ? ' selected="selected"' : ''; ?>>Admin,Editor</option>

For the sanitazation you could also write:

$permissions = filter_input(INPUT_POST, 'permissions', FILTER_SANITIZE_STRING) ? : $User['permissions'];

filter_input() will fail to FALSE or NULL if the filter fails or the input is not set, in both cases will fallback to $User['permission'], instead, it will return a string on success.

I usually manage selects through two functions:

if ( ! function_exists('_form_select'))
     * Create <select>
     * $array_options format:
     *  'option value' => 'text'
     * @param  string $name
     * @param  string $label
     * @param  array  $array_options
     * @param  string $selected
     * @return string
    function _form_select($name, $label, $array_options, $selected = FALSE)
        $template = '
        <label for="%1$s">%2$s</label>
        <select name="%1$s" id="%1$s">

        $options = '';

        foreach($array_options as $key => $value)
            $options .= _form_options($key, $value, $selected);        

        return sprintf($template, $name, $label, $options);

if ( ! function_exists('_form_options'))
     * Create <option>
     * @param  string  $key
     * @param  string  $value
     * @param  boolean $selected
     * @return string
    function _form_options($key, $value, $selected ...


you are trying to iterate the same result set two times: on line 28 and 106. On line 28 you get the first row, so if the query returns only one, you don't get anything when you call mysqli_fetch_assoc() again on line 106. You can use $User otherwise, if you need to loop again, insert data_seek() at line 105:

mysqli_data_seek($userResults, 0);

This will rewind the result set. Documentation:



if you don't want to allow duplicates then set a unique constraint to the brand column, and then use INSERT IGNORE ..., INSERT ... ON DUPLICATE KEY UPDATE ... or a regular update query. An example:

create table `brands` (
  `id` int unsigned auto_increment primary key,
  `brand` varchar(100) unique not null
) engine = innodb;

insert into `brands` (`brand`) values('sony'),('canon'),('nikon'),('fuji'),('pentax'),('zeiss');

> select * from `brands` order by `id`;
|   id | brand   |
|    1 | sony    |
|    2 | canon   |
|    3 | nikon   |
|    4 | fuji    |
|    5 | pentax  |
|    6 | zeiss   |
6 rows in set
Time: 0.003s

Now, if you try a regular insert, you get an error for duplicated entry:

> insert into `brands` (`brand`) values('Canon');
(1062, "Duplicate entry 'Canon' for key 'brand'")

If instead you use the INSERT ... ON DUPLICATE KEY UPDATE ... the existing row gets updated and your script can continue:

> insert into `brands` (`brand`) values('Canon') on duplicate key update `brand` = 'Canon';
> select * from `brands` order by `id`;

|   id | brand   |
|    1 | sony    |
|    2 | Canon   |
|    3 | nikon   |
|    4 | fuji    |
|    5 | pentax  |
|    6 | zeiss   |
6 rows in set
Time: 0.003s

What can happen? If in the edit form you select Canon id, and in the input field you write Zeiss, with this setup ...

Votes + Comments


that's a JSONP response, so in order to process this through PHP you need to remove the callback function that wraps the JSON data, for example:

callback({JSON DATA});

At this point you can remove it from the string:


$jsonp = 'callback({"name": "micheal"});';
$callb = 'callback'; // to remove

$s = substr($jsonp, mb_strlen($callb) + 1); // +1 to include the opening `(`
$s = substr($s, 0, -2); // -2 to remove `);`

print_r(json_decode($s, true));

Now, most JSON servers allows the client to define a callback in the requesting link:


So you receive:

foo({JSON DATA});

This allows you to write a more robust solution, as it's should not affect your script if they change their default callback function. See also if the server allows to get other formats, like simple JSON or XML.


However internalImageUpload() writes only to the database.

Instead, it's in:

imagejpeg($im, $upload_path.$filename);

I overlooked that, and yes by defining a second parameter you would save the resource loaded into $im. However you cannot inject (as far as I know) the contents from $_POST['image'] to $im.

Try something like this:


$uid      = $_POST['uid'];
$token    = $_POST['token'];
$group_id = $_POST['group_id'];
$needle   = $_POST['image'];
$haystack = 'data:image/png;base64,';
$png_blob = substr($needle, mb_stripos($needle, $haystack) + mb_strlen($haystack));

$upload_path = '../' . UPLOAD_PATH;
$filename    = time() . $uid . '.jpg';

// save to image folder
file_put_contents($upload_path . $filename, base64_decode($png_blob));

// save to database
internalImageUpload($uid, $filename, $group_id, FALSE);

$imageID       = internalGetUploadImage($uid, $filename);
$fullImagePath = BASE_URL . UPLOAD_PATH . $filename;

echo "<img src='".$fullImagePath."'  class='webcam_preview' id='".$imageID[0]->id."'/>";

Just make sure the path is correct, that ../ in $upload_path makes me nervous :D as it would always be relative to the link in the frontend side and to the system path in the backend side.


The point is that you cannot use $_POST['image'] that way. See the definition of imagecreatefrompng():

resource imagecreatefrompng ( string $filename )

It means it expects a string to define the filename, not the contents. Something that would work would be:

$im = imagecreatefrompng('file.png');

And from here you create a resource that will be saved into file.png, you cannot import the value of $_POST['image'] into this resource. The value in $_POST['image'] is a base64 encoded string, which once decoded is a binary blob.

Hence, you don't need that code to save the input.

For more details, take this part:

$image = $_POST['image'];
$filter_image = str_replace("data:image/png;base64,", "", $image);
// input is in format 1,2,3...|1,2,3...|...
if($filter_image == $invalid)
    $im = "";
    echo "false";
    $im = imagecreatetruecolor(320, 240);
    foreach (explode("|", $_POST['image']) as $y => $csv) {
        foreach (explode(";", $csv) as $x => $color) {
            imagesetpixel($im, $x, $y, $color);

the comment says the expected format is: 1,2,3...|1,2,3...|... but it is not like this, it is something like:


Which, decoded with my post.php script, produces a 10x10 cyan PNG image. Call it a.png.

The IF statement: if($filter_image == $invalid) is trying to compare an hardcoded blank blob to what is received by $_POST, to make sure it's not an empty snapshot. This can easily fail because the PNG specification allows to set a tIME value everytime the file is modified (or created), in practice some softwares as Gimp and in some cases ImageMagick, will add it and even ...


Okay, I got it to work, the JS function will export the canvas contents to PNG:


so, what you get in$_POST['image'] is a base64 encoded blob. All you need to do is to remove the data:image/png;base64, part, as you were doing, decode the remaining string and save it to a file, at basic:

$needle      = $_POST['image'];
$haystack    = 'data:image/png;base64,';
$png_blob    = substr($needle, mb_stripos($needle, $haystack) + mb_strlen($haystack));
$destination = __DIR__ . '/image.png';

file_put_contents($destination, base64_decode($png_blob));

So if $needle is data:image/png;base64,AAA..., $png_blob will be AAA. You don't need the GD functions unless you want to test if the resulting file is really a PNG and not a script.

Full test:

<!DOCTYPE html>

    <video id="player" width="480px" height="240px" autoplay="true"></video>
    <button id="takeSnap" class="startbutton messageButton">Take Snap</button>

    <div id="webcam">
        <input type="hidden" id="uploadvalues">
        <canvas id="snapshot"></canvas>
        <div id="webcam_preview"></div>

    <h3>Reload to see latest snapshot</h3>
    <img src="image.png">

    <script type="text/javascript" src="https://unpkg.com/jquery@3.2.1"></script>
    <script type="text/javascript">

        var captureButton = document.getElementById('takeSnap');
        var snapshot      = document.getElementById('snapshot');
        var video         = document.getElementById('player');

        // @see https://www.kirupa.com/html5/accessing_your_webcam_in_html5.htm

        navigator.getUserMedia  = navigator.getUserMedia
                               || navigator.webkitGetUserMedia
                               || navigator.mozGetUserMedia
                               || navigator.msGetUserMedia
                               || navigator.oGetUserMedia;

        if (navigator.getUserMedia)       
            navigator.getUserMedia({video: true}, handleVideo, videoError);

        function handleVideo(stream) {
            video.src = window.URL.createObjectURL(stream);

        function videoError(e) {
            // do something

        captureButton.addEventListener('click', function(e)
            var context = snapshot.getContext('2d');

            // Draw the video frame to the canvas.
            context.drawImage(player, 0, 0, snapshot.width, snapshot.height);

            //start webcam upload
            var webcamURL = 'post.php';

            $.post(webcamURL, {type: 'data', image: snapshot.toDataURL('image/png')}, function(data) {
                        var values = $('#uploadvalues').val();


                        var X = $('.webcam_preview').attr('id');

                        if ($.trim(values).length > 0)
                            var Z = X + ',' + values; ...
Votes + Comments
Nice work.
Going the extra mile, again :) +1

Hmm, what kind of input do you expect in $_POST['image']?

Because imagecreatefromjpeg() expects a string to be used like a file name. You are submitting $_POST['image'] instead, which it appears to be a base64 encoded string, and from the previous PHP code, it seems it should be a list of PNG image blobs.

At line 20 you have:

$filter_image = str_replace("data:image/png;base64,", "", $image);

But in the loop you refer again to $_POST['image'] so when you explode by the pipe and semi-colon chars, in practice you end up with:

$x[] = 'data:image/png';
$x[] = 'base64,HEX_STRING';

i.e. two strings that cannot be decoded by base64_decode() which, by the side, is not used in your code.

The imagesetpixel() function, instead expects an integer for the $color argument... and here I get lost because I don't understand anymore what should be the contents of $_POST['image'].


I got a message back from the provider and it's indeed not possible to edit the php,ini file because it's not only used by mine hosting package also by others. That kind of sucks!

Indeed, it sucks :| Not considering that they could use pools to provide separated resources and configuration files for each client.

I will ask if I can create a custom php.ini file in my own dcucment root to override settings.

Either that or my first suggestion: through prepended scripts, which should work for directives that can be applied at runtime, see:


Do you mean with a dedicated interface a thing like cPanel?

Yes. Sometimes you can edit configuration files only through these forms. You can, also, try to write a custom php.ini file and save it into the document root, success however depends on hosting configuration: if it is allowed then it will override the defaults.



Are you using PHP-FPM? In such cases the PHP engine can be located into another server and accessed through an IP address. The address is configured in the web server config files and the php.ini file is in the remote server. You can probably use ini_set() by including a script in top of the others. Through .htaccess this is done like this:

php_value auto_prepend_file "/path/to/iniset.php"

Otherwise in PHP:

require "/path/to/iniset.php";

I would also check with hosting documentation to see if you can set the directives through a dedicated interface.



if you can edit the main php.ini file, change the option to on:


then reload the phpinfo page to see if it applied. The location of the file is defined in the Configuration File (php.ini) Path of the phpinfo view. If you cannot edit the main php.ini, you can try to create a new php.ini file in the document root. And just add the options you want to change. The Loaded Configuration File field of the phpinfo view should show if the new file is loaded.

Note: some times, the configuration (of PHP or of the web server) does not allow to override the settings through custom php.ini files, so you may need to contact your hosting support to make changes.

For more info, see the HOST and PATH directives:



you are missing a comma between these two columns, in the update statement:

work_carry = '$work_carry' demage_found = '$demage_found'

Then edit_customer_detail is not set by the form which, however, is okay if this is set by a previous step and carried through GET.


Yes, it's the same on tcpiputils: https://www.tcpiputils.com/browse/domain/stacychristine.com

And if you see the result of the WHOIS you can see who is the real registrant:

Registrant Name: CCA, Lt webPHOTOMaster, Betcha Private Registration

Which is:

It seems they developed the website, registered the godaddy DNS management for your client, then saved the website into Netsuite. If your client does not have the credentials to enter in Netsuite, then she can only ask to webPHOTOMaster support. I have some doubts you would be able to access that account. It will more probable that you could only point the DNS somewhere else, from godaddy panel, and start the website from zero. Good luck! :)

Besides: the hosting in use is powered by ASP.NET, not PHP:

> http head stacychristine.com                                                                   

HTTP/1.1 301 Moved Permanently
Cache-Control: private
Connection: keep-alive
Date: Tue, 30 May 2017 14:32:14 GMT
Location: http://www.stacychristine.com
Server: akka-http/10.0.0-100-netsuite-02
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET

It seems godaddy was used to register and manage the DNS. Enter into the domain management page and see where it points. From those IPs you should be able to find the hosting company. The same can be done through tools like dig and whois:

> dig daniweb.com ANY

daniweb.com.        299 IN  A

> whois

Organization:   SoftLayer Technologies Inc. (SOFTL)

or through a service like:

for example, if you search daniweb.com you get a summary with the hosting company name:

Domain daniweb.com is listed in the top million list of Alexa ... This domain is hosted by SoftLayer Technologies Inc. (AS36351)...

Which is http://www.softlayer.com/