Creating a plugin for WordPress is relatively simple and getting information back into your blog post or page. Take a look at the following video to cover the basic steps of creating a plugin. Once you understand how to create a basic plugin, we will dive into creating a plugin to query a mySQL Database.
Below Plugin lists how to make a simple DB query plugin. Below is also a 2 hour video of the process of creating the plugin thru trial and error
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
<?php /* * Plugin Name: A Product Query * Description: List contents of mySQL Record and Print to your Blog * Version: 1.0 * Author: Steven * Author URI: https://healthyfixation.com */ // Query SQL DB for Record of Product function product_query($atts, $content=null) { include_once('db_config.php'); // Grab Content FROM product DB // Open Connection to DB $conn = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, product); $pid = $atts['pid']; // Image or URL, Rating $field = $atts['field']; // Query Single Record based on ID, that is passed in the shortcode $sql = "SELECT * FROM product where id='$pid'"; // $sql = "SELECT * FROM product where category4_hidden IS NULL ORDER BY datecreated DESC LIMIT 9"; $result = $conn->query($sql); while($post = $result->fetch_assoc()){ if("$field" === "image") { // Example: return '<img title="'.$title.'" src="http://chart.apis.google.com/chart?cht='.$chart_type.''.$attributes.'" alt="'.$title.'" />'; $URL = $post['url']; $return = '<a target="_blank" href="' . $URL . '"><img width=100 src="' . $post['largeimage'] . '" alt=test /></a>'; } elseif("$field" === "url") { // [maxbutton id="1" text="Click Me" url="http://yoururl" window="new" nofollow="true"] // Example: $return_string = '<a href="'.get_permalink().'">'.get_the_title().'</a>'; //$return = '[maxbutton id="1" text="Click Me" url="' . $post['url'] . '" window="new" nofollow="true"]'; // WORKS return do_shortcode("[maxbutton id=1 text='Click Me' url='$URL' window='new' nofollow='true']"); $URL = $post['url']; $return = do_shortcode("[maxbutton id=1 text='Click Me' url='$URL' window='new' nofollow='true']"); } else { return $return; } } $conn->close(); return $return; } add_shortcode('ProductQuery', 'product_query'); ?> |
Script is just the basic DB configuration so you don’t have to store your user and password in your main script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php define ('DB_USER', "root"); define ('DB_PASSWORD', "XXXX"); define ('DB_DATABASE', "product"); define ('DB_HOST', "localhost"); $mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_DATABASE); ?> |
You may also be interested in the Amazon API Post or the PHP Email Form
I would love to hear from you and if I have missed anything, let me know and I will add it.
I would appreciate you linking back to my blog if you found any of these articles useful.
All credits for the code go to these guys at ItSolutionStuff.com for the code. The code is good, but it has an issue if you scroll too fast with the browser, it gets confused and duplicates lines which makes things pretty messy quickly. I found a fix on Stack Flow which corrects the problem, so I am going to post the new code to use. Most of the code is the same except for the AJAX stuff, there is a bunch of changes we need to make.
You can see the scroll in action here CoolHipThings.com
1 2 3 4 5 6 |
CREATE TABLE IF NOT EXISTS `posts` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `description` text COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=16 ; |
db_config.PHP
1 2 3 4 5 6 7 8 9 |
<?php define (DB_USER, "root"); define (DB_PASSWORD, "root"); define (DB_DATABASE, "sole"); define (DB_HOST, "localhost"); $mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_DATABASE); ?> |
index.PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<!DOCTYPE html> <html> <head> <title>PHP infinite scroll pagination</title> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.js"></script> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> <style type="text/css"> .ajax-load{ background: #e1e1e1; padding: 10px 0px; width: 100%; } </style> </head> <body> <div class="container"> <h2 class="text-center">PHP infinite scroll pagination</h2> <br/> <div class="col-md-12" id="post-data"> <?php require('db_config.php'); $sql = "SELECT * FROM posts ORDER BY id DESC LIMIT 8"; $result = $mysqli->query($sql); ?> <?php include('data.php'); ?> </div> </div> <div class="ajax-load text-center" style="display:none"> <p><img src="http://demo.itsolutionstuff.com/plugin/loader.gif">Loading More post</p> </div> |
Notice all the loaded = statements. Also the if statement had to be updated. You can read the original StackOverFlow Article here
index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
<script type="text/javascript"> loaded = true; // Stop it from repeating itself $(window).scroll(function() { //if($(window).scrollTop() + $(window).height() >= $(document).height()) { if(($(window).scrollTop() == $(document).height() - $(window).height()) && loaded) { var last_id = $(".post-id:last").attr("id"); loadMoreData(last_id); } }); function loadMoreData(last_id){ loaded = false; // Stop it from repeating itself $.ajax( { url: '/php/loadMoreData.php?last_id=' + last_id, type: "get", beforeSend: function() { $('.ajax-load').show(); } }) .done(function(data) { $('.ajax-load').hide(); $("#post-data").append(data); loaded = true; // Stop it from repeating itself }) .fail(function(jqXHR, ajaxOptions, thrownError) { alert('Loading...'); loaded = true; }); } </script> |
data.PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php while($post = $result->fetch_assoc()){ ?> <div class="post-id" id="<?php echo $post['id']; ?>"> <h3><a href=""><?php echo $post['title']; ?></a></h3> <p><?php echo $post['description']; ?></p> <div class="text-right"> <button class="btn btn-success">Read More</button> </div> <hr style="margin-top:5px;"> </div> <?php } ?> |
loadMoreData.PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php require('db_config.php'); $sql = "SELECT * FROM posts WHERE id < '".$_GET['last_id']."' ORDER BY id DESC LIMIT 8"; $result = $mysqli->query($sql); $json = include('data.php'); // echo json_encode($json); ?> |
You may also be interested in the Amazon API Post or the PHP Email Form
I would love to hear from you and if I have missed anything, let me know and I will add it.
I would appreciate you linking back to my blog if you found any of these articles useful.
Create a small pop window with a youtube video with minimum code requirements.
If you want to see it in action, click here and then click on the Watch Video Link
Please this code in between your html header tag. Original Post can be found here on StackOverflow
1 2 3 4 5 |
<script> function popupYT(id) { window.open('https://www.youtube.com/embed/'+id, 'popup', config='height=375,width=450') } </script> |
1 2 3 |
<a href="#play" onclick="popupYT('2cxqZiWyW3g')">Video 1</a> <a href="#play" onclick="popupYT('607RMNoJfl4')">Video 2</a> |
I made slight changes to the code as I wanted to be able to input a youtube url. This doesn’t seem to work with the shortened version of the youtube URL
1 2 3 4 5 6 7 8 9 10 |
<!-- Play Video in Pop UP --> <script> function popupYT(id) { window.open(id, 'popup', config='height=375,width=450') } </script> <!-- Javascript in index.php to make this work --> <a href="#play" onclick="popupYT('<?php echo $post['video']; ?>?autoplay=1')"><img height="50" width="150" src="http://coolhipthings.com/img/video.jpg"></a> |
My $post variable would equal a field from my DB, notice I have added the embed into the URL
https://www.youtube.com/embed/OZ92BgOqq28
You may also be interested in the Amazon API Post or the AWS Cloud as Personal WebServer Post
I would love to hear from you and if I have missed anything, let me know and I will add it.
I would appreciate you linking back to my blog if you found any of these articles useful.
I am going to show you how to use the script below to get a price using the Amazon API, the easiest way. I appreciate you linking back to my website if you find this useful.
Here is a great script to grab the prices from the Amazon. I found this well-written script by this SITE. What I found is that it printed out the raw API information which I couldn’t use.
Then I found this website and a post from which helped explain how to use it.
The just of it is to use the following call to get the price for the product you provide from the $response = getAmazonPrice(“com”, $asin);
1 2 3 4 5 6 7 8 |
require_once PATH_ROOT."amazon.php"; $asin = $CustomFields->fieldValue('jr_asin',$entry); // Region code and Product ASIN $response = getAmazonPrice("com", $asin); if ($response) { return $response['price']; } return false; |
I think take the price and update my database tables daily with the Amazon prices. I am not sure if the prices have to be done in real time but figured this would just slow down my website, so I thought updating maybe once or twice a day would be sufficient.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
$response = getAmazonPrice("com", "B00KQPGRRE"); function getAmazonPrice($region, $asin) { $xml = aws_signed_request($region, array( "Operation" => "ItemLookup", "ItemId" => $asin, "IncludeReviewsSummary" => False, "ResponseGroup" => "Medium,OfferSummary", )); $item = $xml->Items->Item; $title = htmlentities((string) $item->ItemAttributes->Title); $url = htmlentities((string) $item->DetailPageURL); $image = htmlentities((string) $item->MediumImage->URL); $price = htmlentities((string) $item->OfferSummary->LowestNewPrice->Amount); $code = htmlentities((string) $item->OfferSummary->LowestNewPrice->CurrencyCode); $qty = htmlentities((string) $item->OfferSummary->TotalNew); if ($qty !== "0") { $response = array( "code" => $code, "price" => number_format((float) ($price / 100), 2, '.', ''), "image" => $image, "url" => $url, "title" => $title ); } return $response; } function getPage($url) { $curl = curl_init($url); curl_setopt($curl, CURLOPT_FAILONERROR, true); curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); $html = curl_exec($curl); curl_close($curl); return $html; } function aws_signed_request($region, $params) { $public_key = "PUBLIC_KEY"; $private_key = "PRIVATE_KEY"; $method = "GET"; $host = "ecs.amazonaws." . $region; $host = "webservices.amazon." . $region; $uri = "/onca/xml"; $params["Service"] = "AWSECommerceService"; $params["AssociateTag"] = "affiliate-20"; // Put your Affiliate Code here $params["AWSAccessKeyId"] = $public_key; $params["Timestamp"] = gmdate("Y-m-d\TH:i:s\Z"); $params["Version"] = "2011-08-01"; ksort($params); $canonicalized_query = array(); foreach ($params as $param => $value) { $param = str_replace("%7E", "~", rawurlencode($param)); $value = str_replace("%7E", "~", rawurlencode($value)); $canonicalized_query[] = $param . "=" . $value; } $canonicalized_query = implode("&", $canonicalized_query); $string_to_sign = $method . "\n" . $host . "\n" . $uri . "\n" . $canonicalized_query; $signature = base64_encode(hash_hmac("sha256", $string_to_sign, $private_key, True)); $signature = str_replace("%7E", "~", rawurlencode($signature)); $request = "http://" . $host . $uri . "?" . $canonicalized_query . "&Signature=" . $signature; $response = getPage($request); var_dump($response); $pxml = @simplexml_load_string($response); if ($pxml === False) { return False;// no xml } else { return $pxml; } } ?> |
Populating your mySQL Database with the Prices which you can query later, I run this nightly, you may find this useful.
I just run it on the command line, php -f price.php. You don’t need the html.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
<html> <body> <?php include('global.php'); require('db_config.php'); // Select ROWS where ASID is not NULL. Can also use %813 or just '' $sql = "SELECT * FROM product where asin NOT LIKE '% %'"; // Open 1st connection to DB to get ASIN's $result = $mysqli->query($sql); // Open a 2nd connection to DB to update it with new prices //while ($row = mysql_fetch_array($result)) while($row = $result->fetch_assoc()){ $asin = $row["asin"]; $response = getAmazonPrice("com", "$asin"); sleep(1); echo $row["id"].", Name ".$row["name"].", Price ".$row["price"] ; Writelog($row['id'],$row["price"],$response["price"]); $new_price = $response['price']; if($new_price === '0.00') { $new_price = NULL; } $row_id = $row['id']; $sql_update = "UPDATE product set price='$new_price',pricelastupdate=now() where id='$row_id'"; Writelog($sql_update, $row_id, $new_price); Writelog("Update DB with ASIN", $row_id, $new_price); $conn = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_DATABASE); $conn->query($sql_update); $conn->close(); } echo "<br><br>Price:" . $response['price'] . "<br>"; echo "<br><br>Code:" . $response['code'] . "<br>"; #if ($response) { # return $response['price']; #} #return false; # ?> </body> </html> |
I would love to hear from you and if I have missed anything, let me know and I will add it.
I would appreciate you linking back to my blog if you found any of these articles useful.
You may also be interested in the Email Form Post.
If you one of those guys struggling to get the PHP Mail to work like it should, take a look at this form. I have used a PHP/HTML Form and perl to send the actual mail. I couldn’t get the php mail to work, no matter what I did to the php.ini. I had no issues sending mail from the command line, just not from a php file. I decided to use perl to send the actual mail which worked just fine, although I shouldn’t have had to go to perl to send the mail. I personally think the PHP Mail implementation is seriously broken.
I got the original form here, by Pavel Malos, it’s worth checking it out as it may work for you. I like the look of the form with the curved boxes vs the general boring boxes.
You going to need the following to make this work
What the final contact form will look like, you can play around with the background colour.
This is the final PHP/HTML code that displays the form, make sure you grab the css file as well or use the link below in your code and don’t forget to get the cgi-lib.pl. You can see this form in action at coolhipthings.com
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
<html> <body> <head> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css"> <style> body{ background:#333; font-family:Arial; } </style> </head> <table align="center" width=50% border=1> <tr> <td> <form role="form" method="post" action="http://coolhipthings.com/php/mail.cgi"> <div class="form-group"> <label for="name" class="col-sm-2 control-label">Name</label> <div class="col-sm-10"> <input type="text" class="form-control" id="name" name="name" placeholder="First & Last Name" value="<?php echo htmlspecialchars($_POST['name']); ?>"> <?php echo "<p class='text-danger'>$errName</p>";?> </div> </div> <div class="form-group"> <label for="email" class="col-sm-2 control-label">Email</label> <div class="col-sm-10"> <input type="email" class="form-control" id="email" name="email" placeholder="[email protected]" value="<?php echo htmlspecialchars($_POST['email']); ?>"> <?php echo "<p class='text-danger'>$errEmail</p>";?> </div> </div> <div class="form-group"> <label for="message" class="col-sm-2 control-label">Message</label> <div class="col-sm-10"> <textarea class="form-control" rows="4" name="message"><?php echo htmlspecialchars($_POST['message']);?></textarea> <?php echo "<p class='text-danger'>$errMessage</p>";?> </div> </div> <div class="form-group"> <label for="human" class="col-sm-2 control-label">2 + 3 = ?</label> <div class="col-sm-10"> <input type="text" class="form-control" id="human" name="human" placeholder="Your Answer"> <?php echo "<p class='text-danger'>$errHuman</p>";?> </div> </div> <div class="form-group"> <div class="col-sm-10 col-sm-offset-2"> <input id="submit" name="submit" type="submit" value="Send" class="btn btn-primary"> </div> </div> <div class="form-group"> <div class="col-sm-10 col-sm-offset-2"> <?php echo $result; ?> </div> </div> </form> </td> </td> </table> </body> </html> |
This is my mail.cgi I pulled from my scripts and you can test it from the command line by running perl mail.cgi and it should run with no errors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#!/usr/bin/perl push(@INC, "."); require "cgi-lib.pl"; print &PrintHeader; &ReadParse(*input); $subject = 'SQ Contact Form'; $message = "Name: $input{name} \nEmail: $input{email}\n\nMessage:\n$input{message}"; open(MAIL, "|/usr/sbin/sendmail -t"); # Email Header print MAIL "To: $to\n"; print MAIL "From: $from\n"; print MAIL "Subject: $subject\n\n"; # Email Body print MAIL $message; close(MAIL); print "Email Sent Successfully\n"; $url="http://coolhipthings.com"; print "<META HTTP-EQUIV=refresh CONTENT=\"1;URL=$url\">\n"; |
This is an old but reliable cgi library, I searched and doesn’t seem to be readily available on the internet. There are probably some other libraries you can use instead that are more modern, but I have been using this one for over 10 years and know it works and is solid.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 |
# Perl Routines to Manipulate CGI input # Steven E. Brenner / [email protected] # $Id: cgi-lib.pl,v 2.12 1996/06/19 13:46:01 brenner Exp $ # # Copyright (c) 1996 Steven E. Brenner # Unpublished work. # Permission granted to use and modify this library so long as the # copyright above is maintained, modifications are documented, and # credit is given for any use of the library. # # Thanks are due to many people for reporting bugs and suggestions # especially Meng Weng Wong, Maki Watanabe, Bo Frese Rasmussen, # Andrew Dalke, Mark-Jason Dominus, Dave Dittrich, Jason Mathews # For more information, see: # http://www.bio.cam.ac.uk/cgi-lib/ $cgi_lib'version = sprintf("%d.%02d", q$Revision: 2.12 $ =~ /(\d+)\.(\d+)/); # Parameters affecting cgi-lib behavior # User-configurable parameters affecting file upload. $cgi_lib'maxdata = 131072; # maximum bytes to accept via POST - 2^17 $cgi_lib'writefiles = 0; # directory to which to write files, or # 0 if files should not be written $cgi_lib'filepre = "cgi-lib"; # Prefix of file names, in directory above # Do not change the following parameters unless you have special reasons $cgi_lib'bufsize = 8192; # default buffer size when reading multipart $cgi_lib'maxbound = 100; # maximum boundary length to be encounterd $cgi_lib'headerout = 0; # indicates whether the header has been printed # ReadParse # Reads in GET or POST data, converts it to unescaped text, and puts # key/value pairs in %in, using "\0" to separate multiple selections # Returns >0 if there was input, 0 if there was no input # undef indicates some failure. # Now that cgi scripts can be put in the normal file space, it is useful # to combine both the form and the script in one place. If no parameters # are given (i.e., ReadParse returns FALSE), then a form could be output. # If a reference to a hash is given, then the data will be stored in that # hash, but the data from $in and @in will become inaccessable. # If a variable-glob (e.g., *cgi_input) is the first parameter to ReadParse, # information is stored there, rather than in $in, @in, and %in. # Second, third, and fourth parameters fill associative arrays analagous to # %in with data relevant to file uploads. # If no method is given, the script will process both command-line arguments # of the form: name=value and any text that is in $ENV{'QUERY_STRING'} # This is intended to aid debugging and may be changed in future releases sub ReadParse { local (*in) = shift if @_; # CGI input local (*incfn, # Client's filename (may not be provided) *inct, # Client's content-type (may not be provided) *insfn) = @_; # Server's filename (for spooled files) local ($len, $type, $meth, $errflag, $cmdflag, $perlwarn, $got); # Disable warnings as this code deliberately uses local and environment # variables which are preset to undef (i.e., not explicitly initialized) $perlwarn = $^W; $^W = 0; binmode(STDIN); # we need these for DOS-based systems binmode(STDOUT); # and they shouldn't hurt anything else binmode(STDERR); # Get several useful env variables $type = $ENV{'CONTENT_TYPE'}; $len = $ENV{'CONTENT_LENGTH'}; $meth = $ENV{'REQUEST_METHOD'}; if ($len > $cgi_lib'maxdata) { #' &CgiDie("cgi-lib.pl: Request to receive too much data: $len bytes\n"); } if (!defined $meth || $meth eq '' || $meth eq 'GET' || $type eq 'application/x-www-form-urlencoded') { local ($key, $val, $i); # Read in text if (!defined $meth || $meth eq '') { $in = $ENV{'QUERY_STRING'}; $cmdflag = 1; # also use command-line options } elsif($meth eq 'GET' || $meth eq 'HEAD') { $in = $ENV{'QUERY_STRING'}; } elsif ($meth eq 'POST') { if (($got = read(STDIN, $in, $len) != $len)) {$errflag="Short Read: wanted $len, got $got\n"}; } else { &CgiDie("cgi-lib.pl: Unknown request method: $meth\n"); } @in = split(/[&;]/,$in); push(@in, @ARGV) if $cmdflag; # add command-line parameters foreach $i (0 .. $#in) { # Convert plus to space $in[$i] =~ s/\+/ /g; # Split into key and value. ($key, $val) = split(/=/,$in[$i],2); # splits on the first =. # Convert %XX from hex numbers to alphanumeric $key =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge; $val =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge; # Associate key and value $in{$key} .= "\0" if (defined($in{$key})); # \0 is the multiple separator $in{$key} .= $val; } } elsif ($ENV{'CONTENT_TYPE'} =~ m#^multipart/form-data#) { # for efficiency, compile multipart code only if needed $errflag = !(eval <<'END_MULTIPART'); local ($buf, $boundary, $head, @heads, $cd, $ct, $fname, $ctype, $blen); local ($bpos, $lpos, $left, $amt, $fn, $ser); local ($bufsize, $maxbound, $writefiles) = ($cgi_lib'bufsize, $cgi_lib'maxbound, $cgi_lib'writefiles); # The following lines exist solely to eliminate spurious warning messages $buf = ''; ($boundary) = $type =~ /boundary="([^"]+)"/; #"; # find boundary ($boundary) = $type =~ /boundary=(\S+)/ unless $boundary; &CgiDie ("Boundary not provided: probably a bug in your server") unless $boundary; $boundary = "--" . $boundary; $blen = length ($boundary); if ($ENV{'REQUEST_METHOD'} ne 'POST') { &CgiDie("Invalid request method for multipart/form-data: $meth\n"); } if ($writefiles) { local($me); stat ($writefiles); $writefiles = "/tmp" unless -d _ && -r _ && -w _; # ($me) = $0 =~ m#([^/]*)$#; $writefiles .= "/$cgi_lib'filepre"; } # read in the data and split into parts: # put headers in @in and data in %in # General algorithm: # There are two dividers: the border and the '\r\n\r\n' between # header and body. Iterate between searching for these # Retain a buffer of size(bufsize+maxbound); the latter part is # to ensure that dividers don't get lost by wrapping between two bufs # Look for a divider in the current batch. If not found, then # save all of bufsize, move the maxbound extra buffer to the front of # the buffer, and read in a new bufsize bytes. If a divider is found, # save everything up to the divider. Then empty the buffer of everything # up to the end of the divider. Refill buffer to bufsize+maxbound # Note slightly odd organization. Code before BODY: really goes with # code following HEAD:, but is put first to 'pre-fill' buffers. BODY: # is placed before HEAD: because we first need to discard any 'preface,' # which would be analagous to a body without a preceeding head. $left = $len; PART: # find each part of the multi-part while reading data while (1) { die [email protected] if $errflag; $amt = ($left > $bufsize+$maxbound-length($buf) ? $bufsize+$maxbound-length($buf): $left); $errflag = (($got = read(STDIN, $buf, $amt, length($buf))) != $amt); die "Short Read: wanted $amt, got $got\n" if $errflag; $left -= $amt; $in{$name} .= "\0" if defined $in{$name}; $in{$name} .= $fn if $fn; $name=~/([-\w]+)/; # This allows $insfn{$name} to be untainted if (defined $1) { $insfn{$1} .= "\0" if defined $insfn{$1}; $insfn{$1} .= $fn if $fn; } BODY: while (($bpos = index($buf, $boundary)) == -1) { die [email protected] if $errflag; if ($name) { # if no $name, then it's the prologue -- discard if ($fn) { print FILE substr($buf, 0, $bufsize); } else { $in{$name} .= substr($buf, 0, $bufsize); } } $buf = substr($buf, $bufsize); $amt = ($left > $bufsize ? $bufsize : $left); #$maxbound==length($buf); $errflag = (($got = read(STDIN, $buf, $amt, $maxbound)) != $amt); die "Short Read: wanted $amt, got $got\n" if $errflag; $left -= $amt; } if (defined $name) { # if no $name, then it's the prologue -- discard if ($fn) { print FILE substr($buf, 0, $bpos-2); } else { $in {$name} .= substr($buf, 0, $bpos-2); } # kill last \r\n } close (FILE); last PART if substr($buf, $bpos + $blen, 4) eq "--\r\n"; substr($buf, 0, $bpos+$blen+2) = ''; $amt = ($left > $bufsize+$maxbound-length($buf) ? $bufsize+$maxbound-length($buf) : $left); $errflag = (($got = read(STDIN, $buf, $amt, length($buf))) != $amt); die "Short Read: wanted $amt, got $got\n" if $errflag; $left -= $amt; undef $head; undef $fn; HEAD: while (($lpos = index($buf, "\r\n\r\n")) == -1) { die [email protected] if $errflag; $head .= substr($buf, 0, $bufsize); $buf = substr($buf, $bufsize); $amt = ($left > $bufsize ? $bufsize : $left); #$maxbound==length($buf); $errflag = (($got = read(STDIN, $buf, $amt, $maxbound)) != $amt); die "Short Read: wanted $amt, got $got\n" if $errflag; $left -= $amt; } $head .= substr($buf, 0, $lpos+2); push (@in, $head); @heads = split("\r\n", $head); ($cd) = grep (/^\s*Content-Disposition:/i, @heads); ($ct) = grep (/^\s*Content-Type:/i, @heads); ($name) = $cd =~ /\bname="([^"]+)"/i; #"; ($name) = $cd =~ /\bname=([^\s:;]+)/i unless defined $name; ($fname) = $cd =~ /\bfilename="([^"]*)"/i; #"; # filename can be null-str ($fname) = $cd =~ /\bfilename=([^\s:;]+)/i unless defined $fname; $incfn{$name} .= (defined $in{$name} ? "\0" : "") . $fname; ($ctype) = $ct =~ /^\s*Content-type:\s*"([^"]+)"/i; #"; ($ctype) = $ct =~ /^\s*Content-Type:\s*([^\s:;]+)/i unless defined $ctype; $inct{$name} .= (defined $in{$name} ? "\0" : "") . $ctype; if ($writefiles && defined $fname) { $ser++; $fn = $writefiles . ".$$.$ser"; open (FILE, ">$fn") || &CgiDie("Couldn't open $fn\n"); binmode (FILE); # write files accurately } substr($buf, 0, $lpos+4) = ''; undef $fname; undef $ctype; } 1; END_MULTIPART if ($errflag) { local ($errmsg, $value); $errmsg = $@ || $errflag; foreach $value (values %insfn) { unlink(split("\0",$value)); } &CgiDie($errmsg); } else { # everything's ok. } } else { &CgiDie("cgi-lib.pl: Unknown Content-type: $ENV{'CONTENT_TYPE'}\n"); } # no-ops to avoid warnings $insfn = $insfn; $incfn = $incfn; $inct = $inct; $^W = $perlwarn; return ($errflag ? undef : scalar(@in)); } # PrintHeader # Returns the magic line which tells WWW that we're an HTML document sub PrintHeader { return "Content-type: text/html\n\n"; } # HtmlTop # Returns the <head> of a document and the beginning of the body # with the title and a body <h1> header as specified by the parameter sub HtmlTop { local ($title) = @_; return <<END_OF_TEXT; <html> <head> <title>$title</title> </head> <body> <h1>$title</h1> END_OF_TEXT } # HtmlBot # Returns the </body>, </html> codes for the bottom of every HTML page sub HtmlBot { return "</body>\n</html>\n"; } # SplitParam # Splits a multi-valued parameter into a list of the constituent parameters sub SplitParam { local ($param) = @_; local (@params) = split ("\0", $param); return (wantarray ? @params : $params[0]); } # MethGet # Return true if this cgi call was using the GET request, false otherwise sub MethGet { return (defined $ENV{'REQUEST_METHOD'} && $ENV{'REQUEST_METHOD'} eq "GET"); } # MethPost # Return true if this cgi call was using the POST request, false otherwise sub MethPost { return (defined $ENV{'REQUEST_METHOD'} && $ENV{'REQUEST_METHOD'} eq "POST"); } # MyBaseUrl # Returns the base URL to the script (i.e., no extra path or query string) sub MyBaseUrl { local ($ret, $perlwarn); $perlwarn = $^W; $^W = 0; $ret = 'http://' . $ENV{'SERVER_NAME'} . ($ENV{'SERVER_PORT'} != 80 ? ":$ENV{'SERVER_PORT'}" : '') . $ENV{'SCRIPT_NAME'}; $^W = $perlwarn; return $ret; } # MyFullUrl # Returns the full URL to the script (i.e., with extra path or query string) sub MyFullUrl { local ($ret, $perlwarn); $perlwarn = $^W; $^W = 0; $ret = 'http://' . $ENV{'SERVER_NAME'} . ($ENV{'SERVER_PORT'} != 80 ? ":$ENV{'SERVER_PORT'}" : '') . $ENV{'SCRIPT_NAME'} . $ENV{'PATH_INFO'} . (length ($ENV{'QUERY_STRING'}) ? "?$ENV{'QUERY_STRING'}" : ''); $^W = $perlwarn; return $ret; } # MyURL # Returns the base URL to the script (i.e., no extra path or query string) # This is obsolete and will be removed in later versions sub MyURL { return &MyBaseUrl; } # CgiError # Prints out an error message which which containes appropriate headers, # markup, etcetera. # Parameters: # If no parameters, gives a generic error message # Otherwise, the first parameter will be the title and the rest will # be given as different paragraphs of the body sub CgiError { local (@msg) = @_; local ($i,$name); if (!@msg) { $name = &MyFullUrl; @msg = ("Error: script $name encountered fatal error\n"); }; if (!$cgi_lib'headerout) { #') print &PrintHeader; print "<html>\n<head>\n<title>$msg[0]</title>\n</head>\n<body>\n"; } print "<h1>$msg[0]</h1>\n"; foreach $i (1 .. $#msg) { print "<p>$msg[$i]</p>\n"; } $cgi_lib'headerout++; } # CgiDie # Identical to CgiError, but also quits with the passed error message. sub CgiDie { local (@msg) = @_; &CgiError (@msg); die @msg; } # PrintVariables # Nicely formats variables. Three calling options: # A non-null associative array - prints the items in that array # A type-glob - prints the items in the associated assoc array # nothing - defaults to use %in # Typical use: &PrintVariables() sub PrintVariables { local (*in) = @_ if @_ == 1; local (%in) = @_ if @_ > 1; local ($out, $key, $output); $output = "\n<dl compact>\n"; foreach $key (sort keys(%in)) { foreach (split("\0", $in{$key})) { ($out = $_) =~ s/\n/<br>\n/g; $output .= "<dt><b>$key</b>\n <dd>:<i>$out</i>:<br>\n"; } } $output .= "</dl>\n"; return $output; } # PrintEnv # Nicely formats all environment variables and returns HTML string sub PrintEnv { &PrintVariables(*ENV); } # The following lines exist only to avoid warning messages $cgi_lib'writefiles = $cgi_lib'writefiles; $cgi_lib'bufsize = $cgi_lib'bufsize ; $cgi_lib'maxbound = $cgi_lib'maxbound; $cgi_lib'version = $cgi_lib'version; $cgi_lib'filepre = $cgi_lib'filepre; 1; #return true |
I would love to hear from you and if I have missed anything, let me know and I will add it.
You might also want to take a look at my review of creating a Personal Webserver on AWS Cloud.