<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Stuporglue.org</title>
	
	<link>http://stuporglue.org</link>
	<description>Programming, Rambling and More!</description>
	<lastBuildDate>Mon, 28 May 2012 14:15:43 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/stuporglue" /><feedburner:info uri="stuporglue" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><geo:lat>45.095674</geo:lat><geo:long>-93.253361</geo:long><item>
		<title>Drinking In Brazil (A 1998 Journal Entry)</title>
		<link>http://feedproxy.google.com/~r/stuporglue/~3/LRDszWjwdDs/</link>
		<comments>http://stuporglue.org/drinking-in-brazil-a-1998-journal-entry/#comments</comments>
		<pubDate>Mon, 28 May 2012 14:15:43 +0000</pubDate>
		<dc:creator>stuporglue</dc:creator>
				<category><![CDATA[Brazil]]></category>
		<category><![CDATA[Rotary Youth Exchange]]></category>
		<category><![CDATA[beer]]></category>
		<category><![CDATA[drinking]]></category>

		<guid isPermaLink="false">http://stuporglue.org/?p=1357</guid>
		<description><![CDATA[This is a journal entry from 1998 through the eyes of a 16 year old small-town Michigan boy. Despite my naïveté, I did my best to record what I saw and experienced so others would know what it was like. &#8230; <a href="http://stuporglue.org/drinking-in-brazil-a-1998-journal-entry/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>This is a journal entry from 1998 through the eyes of a 16 year old small-town Michigan boy. Despite my naïveté, I did my best to record what I saw and experienced so others would know what it was like. Try to interpret anything you read here through that lens and realize that it may not completely match reality.</p>
<h2>Drinking (Entry 1)</h2>
<p>Drinking here is very different. Anyone can buy beer. It&#8217;s weird because there are vending machines where you can buy it, just like treats or pop. Drinking is more like  pop than something &#8220;special&#8221;. It seems that they think at lunch&#8221;oh, a beer might be good, or coke?&#8221;</p>
<p>The appropriate age to drink seems to be around 12-13 to start and at 15-16 just drink w/ the adults. For example,  we were at a club and my host sister wanted a drink (not a whole can) of beer, but her mom said no; but later on the way home my host mom offered her a cup of wine.</p>
<p>When someone gets a beer they offer everyone in the group a drink and if I say no, it&#8217;s no big deal. It&#8217;s just &#8220;ok you&#8217;re not thirsty&#8221;. If I say I don&#8217;t drink then they look at me funny for a second and then say ok. It&#8217;s no big deal and they don&#8217;t pressure me to drink.</p>
<h2>Drinking at Carnival (Entry 2)</h2>
<p>Brazilians know how to handle their beer? I have never seen so many drunks in my life. At Carnival everyone (almost) was drinking and drinking and&#8230;</p>
<h2>All You Can Drink Night At the Club (Entry 3)</h2>
<p>One night the disco had a special. Buy 1 beer for R$5.00 and trade your empty can for a new one, as many times as you want. So everyone gets plastered right away and then slows down. But what happens when you slow down? Your beer gets warm. And hey! If you can get a free beer with an empty can, why not dump your beer on the dance floor and&#8230;</p>
<p>There were 1/2 inch deep puddles everywhere</p>
<h2>2012 Update</h2>
<p>Not every 12-13 year old in Brazil is being given drinks on the side by their parents, but it happens a lot more than in the US. Not every 15-16 year old in Brazil gets plastered at parties and at the clubs, but the number drunk teenagers I hung out with back then only gets more disturbing the older I get. When I say <em>drunk</em> I&#8217;m not talking about just a slight buzz after a can, I really mean altered-state, crying on the floor drunk.</p>
<p>Part of the blessing and curse of being an exchange student is that the most outgoing people approach you first and invite you to join their circle of friends. It seems logical that there would be a correlation between being outgoing and doing social activities such as going to parties, clubs and bars as a group where drinking occurs.</p>
<p>I love the group of friends I made in Brazil, but at the same time I wish I would&#8217;ve had more friends who didn&#8217;t drink, or at least more friends who drank responsibly. It&#8217;s completely possible that if I had found a different groups of friends my perception would be that everyone in Brazil was a responsible drinker.</p>
<p>If you go to Brazil as a teetotaler like me or someone who loves getting really drunk, you will find plenty of people to be accepting of your stance on drinking.</p>

<p><a href="http://feedads.g.doubleclick.net/~a/GUXPwlFpwgeOH4GJE6gGW5A5Y8g/0/da"><img src="http://feedads.g.doubleclick.net/~a/GUXPwlFpwgeOH4GJE6gGW5A5Y8g/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/GUXPwlFpwgeOH4GJE6gGW5A5Y8g/1/da"><img src="http://feedads.g.doubleclick.net/~a/GUXPwlFpwgeOH4GJE6gGW5A5Y8g/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/stuporglue/~4/LRDszWjwdDs" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stuporglue.org/drinking-in-brazil-a-1998-journal-entry/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://stuporglue.org/drinking-in-brazil-a-1998-journal-entry/</feedburner:origLink></item>
		<item>
		<title>Pizza in Brazil (A 1998 Journal Entry)</title>
		<link>http://feedproxy.google.com/~r/stuporglue/~3/4MO-bMKxH28/</link>
		<comments>http://stuporglue.org/pizza-in-brazil-a-1998-journal-entry/#comments</comments>
		<pubDate>Fri, 25 May 2012 16:09:09 +0000</pubDate>
		<dc:creator>stuporglue</dc:creator>
				<category><![CDATA[Brazil]]></category>

		<guid isPermaLink="false">http://stuporglue.org/?p=1341</guid>
		<description><![CDATA[This is a journal entry from 1998 through the eyes of a 16 year old small-town Michigan boy. Despite my naïveté, I did my best to record what I saw and experienced so others would know what it was like. &#8230; <a href="http://stuporglue.org/pizza-in-brazil-a-1998-journal-entry/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>This is a journal entry from 1998 through the eyes of a 16 year old small-town Michigan boy. Despite my naïveté, I did my best to record what I saw and experienced so others would know what it was like. Try to interpret anything you read here through that lens and realize that it may not completely match reality.</p>
<h2>Pizza</h2>
<p>I can&#8217;t say for all pizza, but it is very different, but very good.</p>
<p><strong></strong>No tomato sauce, cream cheese instead, but not the same as Phillidelphia.</p>
<p><strong></strong>A typical pizza has shredded chicken, mozzarella cheese, a few tomato slices and a few black olives.</p>
<h2>2012 Update</h2>
<p>I&#8217;m not sure why that entry on Pizza was so short. I love Brazilian pizza, so here&#8217;s some more info.</p>
<p>Most pizzas don&#8217;t have much tomato sauce, if they do it&#8217;s generally a very thin layer. Pizzas are nearly always thin-crust and very often wood fired.</p>
<p>Most pizza places deliver, frequently by motorcycle. The motorcycles have fiberglass or metal hot boxes bolted on the back and zip around the city delivering deliciousness to your doorstep.</p>
<p>Some of my favorite Brazilian pizzas include:</p>
<p>Frango com Catupiry &#8212; Shredded chicken and Catupiry cheese. Catupiry is a creamy cheese with the uncookced consistency of corn starch mixed with water. It&#8217;s got a creamy, delicious, salty, taste and goes really well with the chicken.</p>
<p>Frango com Milho Verde &#8212; Shredded chicken and corn. Milho verde is like sweet corn, but not sweet &#8212; just large fresh kernels.</p>
<p>Gorgonzola &#8212; You can get gorgonzola cheese in the US, but I&#8217;ve never seen it on a pizza here (except at my house!). It&#8217;s got a strong flavor and goes well on a pizza with thinly sliced onions.</p>
<p>Margherita &#8212; A traditional Italian pizza with just buffalo mozzerella, basil and tomato. When wood fired it&#8217;s deliciously simple.</p>
<p>Desert pizzas are also prominent, but I&#8217;ve never liked desert pizza. I would rather have real pizza for desert than desert pizza.</p>
<h3>Pizza Rodizios</h3>
<p>A Rodizio is a brazilian steak house where the servers carry various cuts of meat to your table and you eat from each type until you&#8217;re stuffed. If you&#8217;re lucky you&#8217;ll also run into a pizza rodizio where they bring all sorts of types of pizza by your table and you eat till you&#8217;re stuffed. The pizza at the pizza rodizios aren&#8217;t usually as high of quality, but you get to try a lot of flavors and it&#8217;s lots of fun.</p>

<p><a href="http://feedads.g.doubleclick.net/~a/9JNoZJuKYNrD2BO5zx1-j_-72oY/0/da"><img src="http://feedads.g.doubleclick.net/~a/9JNoZJuKYNrD2BO5zx1-j_-72oY/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/9JNoZJuKYNrD2BO5zx1-j_-72oY/1/da"><img src="http://feedads.g.doubleclick.net/~a/9JNoZJuKYNrD2BO5zx1-j_-72oY/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/stuporglue/~4/4MO-bMKxH28" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stuporglue.org/pizza-in-brazil-a-1998-journal-entry/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://stuporglue.org/pizza-in-brazil-a-1998-journal-entry/</feedburner:origLink></item>
		<item>
		<title>Toilets and Showers in Brazil (A 1998 Journal Entry)</title>
		<link>http://feedproxy.google.com/~r/stuporglue/~3/PoRbW8-7dxM/</link>
		<comments>http://stuporglue.org/toilets-and-showers-in-brazil-a-1998-journal-entry/#comments</comments>
		<pubDate>Thu, 24 May 2012 05:12:20 +0000</pubDate>
		<dc:creator>stuporglue</dc:creator>
				<category><![CDATA[Brazil]]></category>
		<category><![CDATA[Rotary Youth Exchange]]></category>
		<category><![CDATA[showers]]></category>
		<category><![CDATA[toilets]]></category>

		<guid isPermaLink="false">http://stuporglue.org/?p=1338</guid>
		<description><![CDATA[This is a journal entry from 1998 through the eyes of a 16 year old small-town Michigan boy. Despite my naïveté, I did my best to record what I saw and experienced so others would know what it was like. &#8230; <a href="http://stuporglue.org/toilets-and-showers-in-brazil-a-1998-journal-entry/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>This is a journal entry from 1998 through the eyes of a 16 year old small-town Michigan boy. Despite my naïveté, I did my best to record what I saw and experienced so others would know what it was like. Try to interpret anything you read here through that lens and realize that it may not completely match reality.</p>
<h2>Toilets</h2>
<p>No toilet seats! But the hole is smaller so its no problem. By most house toilets, but not public restrooms, there is a bidet.</p>
<p>It&#8217;s true, water really does go backwards down the pot. But, 2 things: 1) I think I&#8217;m not far enough south to get the full effect and 2) the way the water is pressurized out it spashes at the bottom so there&#8217;s no nice neat smooth spin.</p>
<p>It just so happens I have a high-tec toilet; it accepts TP. Many many exchange students say no, don&#8217;t put toilet paper in the toilet, but I asked an I can.</p>
<h2>Showers</h2>
<p>In most places in Brazil there isn&#8217;t heated house water. So showers work a little different. Basically as the cold water comes out, an electronic heater in the shower head heats it. What at the books said is:</p>
<ol>
<li> Turn water on</li>
<li>Put sandals on to protect yourself from shocks</li>
<li>Turn on heater</li>
<li>Shower</li>
<li>Turn off heater</li>
<li>Turn off water</li>
</ol>
<p>If you use the heater without water you can short out the room or building&#8230;or house.</p>
<p>Most showers in the USA come sideways from the wall. The ones I&#8217;ve seen here come out and drop the water straight down.</p>
<h2>2012 Update</h2>
<p>I wanted to believe that the water went down counterclockwise. If it did though, <a title="Coriolis Forces on Draining Water" href="http://www.snopes.com/science/coriolis.asp" target="_blank">it wasn&#8217;t because of where I was relative to the equator</a>.</p>
<p>Lots of toilets in Brazil do have toilet seats, but lots don&#8217;t. The nicer the bathroom is, the more likely that there will be a seat.</p>
<p>When I went back to Brazil as a missionary I was in Sao Paulo and I never encountered a toilet that wouldn&#8217;t take toilet paper in real buildings in the city. The only places I found those was in places where DIY plumbing left the toilets with extremely low water pressure &#8212; mainly in favelas and in small self-built rural homes.</p>
<p>One other thing I didn&#8217;t note back in 1998 is that toilets in Brazil come in way more colors than in the USA. Black, red, blue, yellow, marbled&#8230;lots of colors. You can find colored toilets in the US, but they&#8217;re not nearly as common as in Brazil.</p>
<p>When I went to Brazil as an exchange student I must have caught the tail end of a transition from manual-on shower heads to automatic-on showerheads. The books I read before leaving gave lots of warnings about turning on the water first and avoiding shocks. I only ran into a couple of places where that was an issue.</p>
<p>For the most part the showerheads now have water sensors that turn the heater on and off automatically when the water comes on. As long as it has been installed and grounded correctly you won&#8217;t get any shocks either. There was only one shower I used where you could feel the electric current running through the faucet handle, but only if you had a hangnail.</p>

<p><a href="http://feedads.g.doubleclick.net/~a/gXS95ufUDrh-a4WCSsNz3C8vL6w/0/da"><img src="http://feedads.g.doubleclick.net/~a/gXS95ufUDrh-a4WCSsNz3C8vL6w/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/gXS95ufUDrh-a4WCSsNz3C8vL6w/1/da"><img src="http://feedads.g.doubleclick.net/~a/gXS95ufUDrh-a4WCSsNz3C8vL6w/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/stuporglue/~4/PoRbW8-7dxM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stuporglue.org/toilets-and-showers-in-brazil-a-1998-journal-entry/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://stuporglue.org/toilets-and-showers-in-brazil-a-1998-journal-entry/</feedburner:origLink></item>
		<item>
		<title>Describe Sacramento, Minas Gerais, Brazil (A 1998 Journal Entry)</title>
		<link>http://feedproxy.google.com/~r/stuporglue/~3/k9Y562ZBiJQ/</link>
		<comments>http://stuporglue.org/describe-sacramento-minas-gerais-brazil-a-1998-journal-entry/#comments</comments>
		<pubDate>Mon, 21 May 2012 05:11:00 +0000</pubDate>
		<dc:creator>stuporglue</dc:creator>
				<category><![CDATA[Brazil]]></category>
		<category><![CDATA[Rotary Youth Exchange]]></category>

		<guid isPermaLink="false">http://stuporglue.org/?p=1335</guid>
		<description><![CDATA[This is a journal entry from 1998 through the eyes of a 16 year old small-town Michigan boy. Despite my naïveté, I did my best to record what I saw and experienced so others would know what it was like. &#8230; <a href="http://stuporglue.org/describe-sacramento-minas-gerais-brazil-a-1998-journal-entry/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>This is a journal entry from 1998 through the eyes of a 16 year old small-town Michigan boy. Despite my naïveté, I did my best to record what I saw and experienced so others would know what it was like. Try to interpret anything you read here through that lens and realize that it may not completely match reality.</p>
<h2>Describe Sacramento, Minas Gerais, Brazil</h2>
<p>Well&#8230;First the roofs. Almost every roof is either red clay shingles or corrugated metal or plastic. The building show a strong trend towards cement, stucco and tiles. The average building is 2-3 stories tall. The sidewalks are stone with stones about 1 foot or 1/2 foot across and wide. The roads are organized cobblestone. Each rock looks the same color and is cut almost the same size. In the middle of the road every 10-15 feet is a tree, some kind of palm.</p>
<p>The highways however are paved and much smoother than Ironwoods.</p>
<p>Driving laws are different. I think there isn&#8217;t as much respect for the centerline, speed limit or pedestrians.</p>
<h2>2012 Update</h2>
<p>I now know that the roads weren&#8217;t cobblestone, they used <a title="Sett Paving at Wikipedia" href="http://en.wikipedia.org/wiki/Sett_%28paving%29" target="_blank">Sett Paving</a>. If I had spent any time at all in California before going to Brazil, I would have described the architecture as Spanish Mission Style.</p>

<p><a href="http://feedads.g.doubleclick.net/~a/mT7AMSHciVbraI9D9f2VAlx3wZA/0/da"><img src="http://feedads.g.doubleclick.net/~a/mT7AMSHciVbraI9D9f2VAlx3wZA/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/mT7AMSHciVbraI9D9f2VAlx3wZA/1/da"><img src="http://feedads.g.doubleclick.net/~a/mT7AMSHciVbraI9D9f2VAlx3wZA/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/stuporglue/~4/k9Y562ZBiJQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stuporglue.org/describe-sacramento-minas-gerais-brazil-a-1998-journal-entry/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://stuporglue.org/describe-sacramento-minas-gerais-brazil-a-1998-journal-entry/</feedburner:origLink></item>
		<item>
		<title>I’m Looking for a Microfilm Digitization Quote</title>
		<link>http://feedproxy.google.com/~r/stuporglue/~3/FEZGDPBZ6dQ/</link>
		<comments>http://stuporglue.org/im-looking-for-a-microfilm-digitization-quote/#comments</comments>
		<pubDate>Tue, 15 May 2012 16:58:51 +0000</pubDate>
		<dc:creator>stuporglue</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Digitization]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Something Interesting]]></category>

		<guid isPermaLink="false">http://stuporglue.org/?p=1327</guid>
		<description><![CDATA[I&#8217;m looking for a microfilm digitization quote. If you or someone you know provides microfilm digitization, please have them send me a quote. I&#8217;ve got 113 reels of microfilm I&#8217;d like to digitize and I&#8217;m looking for a ballpark estimate &#8230; <a href="http://stuporglue.org/im-looking-for-a-microfilm-digitization-quote/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m looking for a microfilm digitization quote. If you or someone you know provides microfilm digitization, please have them send me a quote.</p>
<p>I&#8217;ve got 113 reels of microfilm I&#8217;d like to digitize and I&#8217;m looking for a ballpark estimate for the project.</p>
<p>Here&#8217;s the info I know, please let me know if you need anything else:</p>
<ul>
<li>There are an average of 600 images per reel (about 67,800 images)</li>
<li>I&#8217;d like to scan at 300 dpi, 8bit grayscale lossless images (tiff? png?)</li>
<li>I have the copyright on the images on these reels</li>
<li>The reels are lightly used duplicates of the master reels. The master reels are unfortunately unavailable</li>
<li>The images are all scanned newspapers</li>
<li>I don&#8217;t need any OCR done</li>
<li>The only metadata I need is which reel each of the images came from eg. One directory per reel with incremental file names would be just fine.</li>
</ul>
<p>Project Background: I would like to put 128 years of <a title="Iron County Miner" href="http://ironcountyminer.com/" target="_blank">Iron County Miner</a> newspaper archives online. They would be freely available (no subscription or account required) and there&#8217;s no plan to make money from them. Since there&#8217;s no revenue expected I&#8217;m looking for ways to reduce costs while still putting something out there to benefit genealogists and historians.</p>
<p>The master rolls are held by the Wisconsin Historical Society who wants nearly $10,000 ($0.145/image) for the project or $80 per reel to send us fresh copies of the reels. From their perspective, I think that&#8217;s probably fair; they aren&#8217;t in the digitization business and they probably aren&#8217;t set up to do this sort of project in a streamlined manner. They also can&#8217;t amortize their digitization equipment costs across so many clients as a commercial company can.</p>
<p>For me though, $10,000 means that I can&#8217;t pursue this project right now.</p>
<p>Most digitization companies I have contacted have been reluctant to provide even a ballpark quote without seeing test reels, and I understand that that is a factor. Right now though, I just need a gauge to determine if this project is viable. If $10,000 is the real cost for this sort of project it will have to wait till I&#8217;m rich, but if I can get a cheaper quote I hope to make it happen this summer.</p>
<h2>Pre-Announcing NewspaperCMS</h2>
<p>I have been working on a CMS (Content Management System) called NewspaperCMS, to host the scanned images with and to make them easily navigable. It is licensed under the GPLv2 so anybody needing to host newspaper archives can use it.</p>
<p>Here&#8217;s its page on Google Code: <a title="NewspaperCMS" href="http://code.google.com/p/newspapercms/" target="_blank">http://code.google.com/p/newspapercms/</a></p>
<p>I would classify it as in late Alpha or early Beta stages right now. I&#8217;ll do an official post on it as it matures and as I get a publicly accessible test site set up. As a teaser, features include:</p>
<ul>
<li>Browse collection by microfilm, newspaper or date
<ul>
<li>Drill down within those categories by newspaper, issue, year or month</li>
</ul>
</li>
<li>Access-driven generation of midsized images. No need to generate 60,000 midsized images ahead of time.</li>
<li>Valid HTML5/CSS3</li>
<li>HTML5/Canvas based client-side image viewer. The user can zoom, rotate, invert, sharpen and change the contrast of the image (uses the <a title="Pixastic HTML5 canvas image processing" href="http://www.pixastic.com/" target="_blank">http://www.pixastic.com/</a>JavaScript libraries)
<ul>
<li>Falls back to a static image if they don&#8217;t have Canvas or JavaScript support</li>
</ul>
</li>
<li>Built in search engine</li>
<li>Support for the <a title="Tesseract OCR" href="http://code.google.com/p/tesseract-ocr/" target="_blank">tesseract</a> OCR engine</li>
</ul>
<p>As I said, it&#8217;s still in development, but if you need something like it, you can play with it now. It&#8217;s at the point where more development doesn&#8217;t make sense until I know I can get the microfilms scanned.</p>

<p><a href="http://feedads.g.doubleclick.net/~a/GPwX9mQ1rd_3WtgS-3vhZNIknEY/0/da"><img src="http://feedads.g.doubleclick.net/~a/GPwX9mQ1rd_3WtgS-3vhZNIknEY/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/GPwX9mQ1rd_3WtgS-3vhZNIknEY/1/da"><img src="http://feedads.g.doubleclick.net/~a/GPwX9mQ1rd_3WtgS-3vhZNIknEY/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/stuporglue/~4/FEZGDPBZ6dQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stuporglue.org/im-looking-for-a-microfilm-digitization-quote/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://stuporglue.org/im-looking-for-a-microfilm-digitization-quote/</feedburner:origLink></item>
		<item>
		<title>Create a Side-to-Side Draggable HTML5 Canvas in a Div</title>
		<link>http://feedproxy.google.com/~r/stuporglue/~3/yCSP1L2MwKw/</link>
		<comments>http://stuporglue.org/create-a-side-to-side-draggable-html5-canvas-in-a-div/#comments</comments>
		<pubDate>Tue, 08 May 2012 20:02:00 +0000</pubDate>
		<dc:creator>stuporglue</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>

		<guid isPermaLink="false">http://stuporglue.org/?p=1324</guid>
		<description><![CDATA[I have been playing with Pixastic and a little bit of HTML5 canvas image manipulation for a site I&#8217;m working on. I load an image into an HTML5 canvas and let the user do some basic manipulation, including zooming in &#8230; <a href="http://stuporglue.org/create-a-side-to-side-draggable-html5-canvas-in-a-div/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I have been playing with <a title="Pixastic HTML5 canvas image processing" href="http://www.pixastic.com/">Pixastic</a> and a little bit of HTML5 canvas image manipulation for a site I&#8217;m working on. I load an image into an HTML5 canvas and let the user do some basic manipulation, including zooming in on the image.</p>
<p>Zooming in on the image quickly causes the canvas to outgrow my browser window. The div around the canvas is set to use <em>overflow: auto</em> so that the growing canvas doesn&#8217;t disrupt the rest of the page flow.</p>
<pre>&lt;div id="<a>pageimgdiv</a>" style="max-width: 100%; overflow: auto;"&gt;
   &lt;canvas&gt;
      Your browser doesn't support HTML5 Canvas
   &lt;/canvas&gt;
&lt;/div&gt;</pre>
<p>The overflowed div gains horizontal scrollbars (but not vertical ones, since there&#8217;s no max-height in my case). Unfortunately many people, including myself, don&#8217;t have horizontal scrolling configured for their mouse which means scrolling down to the scrollbar, moving over, then scrolling back up.</p>
<h2>JQuery To The Rescue</h2>
<p>I was able to use JQuery and the scrollLeft() function to make the canvas dragable within the div. The canvas itself doesn&#8217;t change sizes or pan (which would require a second canvas used as a buffer, I think). Instead we get the mouse position and current scrollLeft setting when the mouse is clicked, and then scroll more as they move the mouse, until they release the mouse or until they leave the wrapper div.</p>
<p>I&#8217;m using JQuery 1.7.2.</p>
<pre>$(document).ready(function(){
    $('#pageimgdiv').on(
    {
	mousedown: function(clicke){
	    origX = clicke.pageX + $('#pageimgdiv').scrollLeft();
	    $('#pageimgdiv').on(
	    {
		mousemove : function(e){
		    curX = e.pageX + $('#pageimgdiv').scrollLeft();
		    var diff = (origX - curX);
		    var newpos = $('#pageimgdiv').scrollLeft() + diff;
		    if(newpos &gt; ($('canvas').width() - $('#pageimgdiv').width())){
			newpos = ($('canvas').width() - $('#pageimgdiv').width());
		    }
		    if(newpos &lt; 0){
			newpos = 0;
		    }
		    $('#pageimgdiv').scrollLeft(newpos);
		}
	    }
	    );
	},
	mouseleave: function(){
	    $('#pageimgdiv').off('mousemove');
	},
	mouseup: function(){
	    $('#pageimgdiv').off('mousemove');
	},
	click: function(){
	    $('#pageimgdiv').off('mousemove');
	}
    }
    );
});</pre>
<h2>Embrace and Extend</h2>
<p>This code only scrolls horizontally. You could easily extend it to use scrollTop() and enable vertical scrolling as well.</p>

<p><a href="http://feedads.g.doubleclick.net/~a/BTISzy_Goo8wNZmGf1I0zXyzzL4/0/da"><img src="http://feedads.g.doubleclick.net/~a/BTISzy_Goo8wNZmGf1I0zXyzzL4/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/BTISzy_Goo8wNZmGf1I0zXyzzL4/1/da"><img src="http://feedads.g.doubleclick.net/~a/BTISzy_Goo8wNZmGf1I0zXyzzL4/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/stuporglue/~4/yCSP1L2MwKw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stuporglue.org/create-a-side-to-side-draggable-html5-canvas-in-a-div/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://stuporglue.org/create-a-side-to-side-draggable-html5-canvas-in-a-div/</feedburner:origLink></item>
		<item>
		<title>Setting up Xdebug with NetBeans on Windows, with a Remote Apache Server</title>
		<link>http://feedproxy.google.com/~r/stuporglue/~3/0VMvFqKetyw/</link>
		<comments>http://stuporglue.org/setting-up-xdebug-with-netbeans-on-windows-with-a-remote-apache-server/#comments</comments>
		<pubDate>Wed, 02 May 2012 01:11:06 +0000</pubDate>
		<dc:creator>stuporglue</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Something Interesting]]></category>
		<category><![CDATA[debugging]]></category>
		<category><![CDATA[netbeans]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[putty]]></category>
		<category><![CDATA[ssh]]></category>
		<category><![CDATA[ssh tunnel]]></category>
		<category><![CDATA[xdebug]]></category>

		<guid isPermaLink="false">http://stuporglue.org/?p=1304</guid>
		<description><![CDATA[I fought with Xdebug and NetBeans enough to necessitate a post about it, if only so I don&#8217;t forget. Most Xdebug/NetBeans tutorial assume that you&#8217;re doing development on your local machine. That&#8217;s a fine setup, but not what I was &#8230; <a href="http://stuporglue.org/setting-up-xdebug-with-netbeans-on-windows-with-a-remote-apache-server/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I fought with Xdebug and NetBeans enough to necessitate a post about it, if only so I don&#8217;t forget.</p>
<p>Most Xdebug/NetBeans tutorial assume that you&#8217;re doing development on your local machine. That&#8217;s a fine setup, but not what I was needed for this project.</p>
<h2>Environment</h2>
<p>Server: A typical Linux server &#8212; Debian, Apache2 and PHP.</p>
<p>Debugger: <a title="Xdebug" href="http://xdebug.org/" target="_blank">Xdebug</a></p>
<p>Client: Firefox on Windows, etc.</p>
<p>IDE: <a title="NetBeans for PHP" href="http://netbeans.org/features/php/" target="_blank">NetBeans</a> 7.1.1</p>
<p>Other Tools: <a title="PuTTY" href="http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html" target="_blank">PuTTY</a></p>
<h2>Setting Up Xdebug</h2>
<p>Install xdebug with the command:</p>
<pre>pecl install xdebug</pre>
<p>The final lines of output should say something like:</p>
<pre>Build process completed successfully
Installing '/usr/lib/php5/20090626/xdebug.so'
install ok: channel://pecl.php.net/xdebug-2.2.</pre>
<p>Take note of the install path, <strong>/usr/lib/php5/20090626/xdebug.so</strong>, in this case. Now add the following to your PHP configuration. I created a new .ini file at</p>
<pre>/etc/php5/conf.d/xdebug.ini</pre>
<p>Its contents should be as follows (change the zend_extension to match the install path found above):</p>
<pre>[xdebug]
zend_extension = "/usr/lib/php5/20090626/xdebug.so"

xdebug.remote_enable = on

; Most users won't want autostart. More on this later.
; xdebug.remote_autostart = on
xdebug.remote_autostart = off
xdebug.remote_handler = dbgp
xdebug.remote_port = 9000
xdebug.remote_server = localhost
xdebug.remote_mode = req

; Most users won't want a hard coded idekey. More on this later.
; xdebug.idekey = netbeans-xdebug
output_buffering = off

xdebug.remote_log = "/var/log/xdebug.log"</pre>
<p>Restart Apache to complete the installation.</p>
<p>xdebug.remote_server = localhost. Localhost? Wait a second, I thought this was for working with a remote server? Yes, but xdebug needs to be able to connect to your computer. The easy option is to have xdebug connect to the server&#8217;s localhost, then use PuTTY to create an SSH tunnel so that NetBeans can listen on your computer&#8217;s localhost.</p>
<p>The harder option is to configure your home or office router to forward port 9000 to you, be sure to never change your IP address, and open port 9000 on your Windows firewall. You could use <em>xdebug.remote_connect_back</em> so that xdebug would connect to whichever IP made the web request, but then someone who isn&#8217;t you could access your code.</p>
<p>In my opinion, the SSH tunneling is the cleanest option. You can use it anywhere that you have SSH access, xdebug access is restricted to those who have SSH access and you don&#8217;t have to worry about your IP address changing.</p>
<h2>Creating an SSH Tunnel for Xdebug</h2>
<p>Using <a title="PuTTY" href="http://www.chiark.greenend.org.uk/~sgtatham/putty/">PuTTY.exe</a> create and save a new SSH session which connects to your server. In this saved session configure a tunnel. Set the source port to 9000, the destination to localhost:9000, and choose the Remove and Auto radio buttons. Click the Add button to add that port forwarding configuration, then save that session to use every time you want to use xdebug.</p>
<figure id="attachment_1305" aria-labelledby="figcaption_attachment_1305" class="wp-caption aligncenter" style="width: 476px"><a href="http://stuporglue.org/setting-up-xdebug-with-netbeans-on-windows-with-a-remote-apache-server/putty_tunnel/" rel="attachment wp-att-1305"><img class="size-full wp-image-1305" title="PuTTY xdebug SSH tunnel" src="http://stuporglue.org/wp-content/uploads/2012/05/putty_tunnel.png" alt="PuTTY xdebug SSH tunnel" width="466" height="451" /></a><figcaption id="figcaption_attachment_1305" class="wp-caption-text">PuTTY xdebug SSH tunnel</figcaption></figure>
<p>Go ahead and connect to that saved PuTTY session now. Once you&#8217;re connected to your server you should be able to run netstat to verify that it&#8217;s working correctly. It should show something like this:</p>
<pre>netstat -a -n | grep 9000
tcp        0      0 127.0.0.1:9000          0.0.0.0:*               LISTEN
tcp6       0      0 ::1:9000                :::*                    LISTEN</pre>
<h2>Setting up NetBeans</h2>
<p>Set up your NetBeans project like you usually would. Verify that the PHP Debugging Settings (In the Tools-&gt;Options menu) are set to debugger port 9000, and the Session ID of netbeans-xdebug. I had problems with the <em>Watches and Balloon Evaluation</em> options. YMMV.</p>
<figure id="attachment_1306" aria-labelledby="figcaption_attachment_1306" class="wp-caption aligncenter" style="width: 580px"><a href="http://stuporglue.org/setting-up-xdebug-with-netbeans-on-windows-with-a-remote-apache-server/netbeans_debugging/" rel="attachment wp-att-1306"><img class="size-medium wp-image-1306" title="NetBeans Debugging Settings" src="http://stuporglue.org/wp-content/uploads/2012/05/NetBeans_Debugging-570x422.png" alt="NetBeans Debugging Settings" width="570" height="422" /></a><figcaption id="figcaption_attachment_1306" class="wp-caption-text">NetBeans Debugging Settings</figcaption></figure>
<p>In your project properties (Right click your project, click Properties) edit the Run Configuration&#8217;s Advanced properties. Select &#8220;Do Not Open Web Browser&#8221;.</p>
<h3>A Note on Path Mappings</h3>
<p>Based on the tutorials I read, most users don&#8217;t seem to need Path Mappings. NetBeans seems to figure out the path based on the upload directory, the URL and the Web Root settings.  That didn&#8217;t work for me<strong>. I needed Path Mapping because I had a symlink on the server</strong>.</p>
<p>PHP (and so Xdebug) dereferences symlinks and NetBeans needs the mapping between the dereferenced path on the server and the local sources directory.</p>
<p>This is a <a title="Xdebug does not follow Symlinks" href="http://bugs.xdebug.org/view.php?id=432" target="_blank">known issue</a>, but not very well known. Symptoms that you need to  use mapping include NetBeans not breaking in symlinked files, NetBeans not opening the file or not opening the correct file when Xdebug connection is made. Or it may work only if you use &#8220;Debug File&#8221; instead of &#8220;Debug Project&#8221;.</p>
<p><a href="http://stuporglue.org/setting-up-xdebug-with-netbeans-on-windows-with-a-remote-apache-server/netbeans_mapping/" rel="attachment wp-att-1307"><img class="aligncenter size-medium wp-image-1307" title="NetBeans Run Configuration Advanced Properties" src="http://stuporglue.org/wp-content/uploads/2012/05/Netbeans_mapping-570x409.png" alt="" width="570" height="409" /></a></p>
<h2> Debugging With Xdebug and NetBeans</h2>
<p>Here&#8217;s where you have some options. The manual way to active Xdebug is to append XDEBUG_SESSION_START=netbeans-xdebug to your query string when you request a page in the browser. With the current setup, this should work. Go ahead and test it.</p>
<p>Choose Debug -&gt; Debug Project from the NetBeans menu. Then, in your browser, go to http://example.com/path/to/page.php??XDEBUG_SESSION_START=netbeans-xdebug (replacing the URL with your own, of course).</p>
<p>If that doesn&#8217;t work then something isn&#8217;t set up correctly. It might be NetBeans, it might be Xdebug, it might be the SSH tunnel. Figure it out, get it fixed, then keep reading (you can leave comments here, and I&#8217;ll help as I can. Google is pretty helpful too).</p>
<p>If that does work, then GREAT!</p>
<p>Now you have some options.</p>
<h3>Browser Extensions</h3>
<p>Firefox and Chrome both have Xdebug extensions that set the XDEBUG_SESSION_START parameter in the http headers. This makes it so you don&#8217;t have to type it yourself.  <a title="Firefox Easy Xdebug" href="https://addons.mozilla.org/en-US/firefox/addon/easy-xdebug/" target="_blank">Yay Firefox</a>. <a title="Chrome Xdebug Helper" href="https://chrome.google.com/webstore/detail/eadndfjplgieldjbigjakmdgkmoaaaoc" target="_blank">Yay Chrome</a>.</p>
<h3>Always Auto-start Xdebug</h3>
<p>If you are testing embedded webkit, or from mobile devices, or something else that&#8217;s not a normal browser, then appending XDEBUG_SESSION_START is going to be difficult. This is where I send you back to your xdebug.ini file to change some settings. If you need to debug requests from these sorts of devices, then you&#8217;re going to want to edit xdebug.ini and set:</p>
<pre>xdebug.remote_autostart = on
xdebug.idekey = netbeans-xdebug</pre>
<p>This will cause Xdebug to attempt to connect on port 9000 on every request. The idekey setting is so that NetBeans will know that the connection is for it.</p>
<h2>Other PHP Debugging Tools</h2>
<p>If you are looking for good PHP debugging tools, you may also want to try out <a title="KCacheGrind" href="http://kcachegrind.sourceforge.net/html/Home.html" target="_blank">KCacheGrind</a> which will profile your code giving you an idea what&#8217;s taking up time and memory. <a title="HipHop-PHP" href="https://github.com/facebook/hiphop-php/wiki/" target="_blank">HipHop-PHP</a>, a tool from Facebook of all places, compiles PHP into C++. In the process it spits out all sorts of helpful errors and notices that will help you find errors in your code.</p>
<p>That concludes one more NetBeans Xdebug tutorial that will hopefully get you that much closer to doing some serious PHP debugging. Happy coding!</p>

<p><a href="http://feedads.g.doubleclick.net/~a/pQTppQdU6Lv7A-_LGOwv7dfmnBw/0/da"><img src="http://feedads.g.doubleclick.net/~a/pQTppQdU6Lv7A-_LGOwv7dfmnBw/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/pQTppQdU6Lv7A-_LGOwv7dfmnBw/1/da"><img src="http://feedads.g.doubleclick.net/~a/pQTppQdU6Lv7A-_LGOwv7dfmnBw/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/stuporglue/~4/0VMvFqKetyw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stuporglue.org/setting-up-xdebug-with-netbeans-on-windows-with-a-remote-apache-server/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://stuporglue.org/setting-up-xdebug-with-netbeans-on-windows-with-a-remote-apache-server/</feedburner:origLink></item>
		<item>
		<title>Add Trailing Slashes Without Hardcoding The Domain</title>
		<link>http://feedproxy.google.com/~r/stuporglue/~3/Djkf_JpPCLA/</link>
		<comments>http://stuporglue.org/add-trailing-slashes-without-hardcoding-the-domain/#comments</comments>
		<pubDate>Thu, 26 Apr 2012 16:28:24 +0000</pubDate>
		<dc:creator>stuporglue</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[mod_rewrite]]></category>

		<guid isPermaLink="false">http://stuporglue.org/?p=1300</guid>
		<description><![CDATA[There are lots of tutorials showing how to add trailing slashes to a URL with mod_rewrite, but 99.9% of them hard code the domain. You might want to always append a trailing slash if you depend on it in $_SERVER['REDIRECT_URL'] &#8230; <a href="http://stuporglue.org/add-trailing-slashes-without-hardcoding-the-domain/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>There are lots of tutorials showing how to add trailing slashes to a URL with mod_rewrite, but 99.9% of them hard code the domain.</p>
<p>You might want to always append a trailing slash if you depend on it in $_SERVER['REDIRECT_URL'] or if you want to ensure that content is only available via a single URL for search engines etc.</p>
<p>Here&#8217;s the Apache mod_rewrite code:</p>
<pre>&lt;IfModule mod_rewrite.c&gt;
RewriteEngine On

# Always append a trailing slash
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !/$
RewriteRule . %{REQUEST_URI}/ [R=301,L]

&lt;/IfModule&gt;</pre>

<p><a href="http://feedads.g.doubleclick.net/~a/rvvXr1MzrUOQrj64lJzC0iwnfQU/0/da"><img src="http://feedads.g.doubleclick.net/~a/rvvXr1MzrUOQrj64lJzC0iwnfQU/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/rvvXr1MzrUOQrj64lJzC0iwnfQU/1/da"><img src="http://feedads.g.doubleclick.net/~a/rvvXr1MzrUOQrj64lJzC0iwnfQU/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/stuporglue/~4/Djkf_JpPCLA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stuporglue.org/add-trailing-slashes-without-hardcoding-the-domain/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://stuporglue.org/add-trailing-slashes-without-hardcoding-the-domain/</feedburner:origLink></item>
		<item>
		<title>Announcing Todayish In History</title>
		<link>http://feedproxy.google.com/~r/stuporglue/~3/I0eag67ZOEM/</link>
		<comments>http://stuporglue.org/announcing-todayish-in-history/#comments</comments>
		<pubDate>Mon, 16 Apr 2012 14:57:48 +0000</pubDate>
		<dc:creator>stuporglue</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Something Interesting]]></category>
		<category><![CDATA[Todayish In History Plugin]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[today in history.]]></category>
		<category><![CDATA[todayish in history]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://stuporglue.org/?p=1096</guid>
		<description><![CDATA[My other blog is a garden blog and so old posts have a high likelihood of becoming relevant again each and every year around the same time. I wanted a way to display a link to the blog post written &#8230; <a href="http://stuporglue.org/announcing-todayish-in-history/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>My <a title="The Fridley Farmer" href="http://fridleyfarmer.com/" target="_blank">other blog is a garden blog</a> and so old posts have a high likelihood of becoming relevant again each and every year around the same time. I wanted a way to display a link to the blog post written nearest to the current date from each previous year. After not finding a plugin that did what I wanted, I wrote my own.</p>
<p>The plugin is named Todayish because if there isn&#8217;t a blog post from today&#8217;s<br />
date in previous years it will use the blog post which is the least<br />
number of days away from todays date.</p>
<p>Todayish in History provides a function for use in themes as well as a<br />
widget for use in your sidebar.</p>
<h2>Todayish In History&#8217;s Homepage</h2>
<p>Todayish In History has a permanent home at <a title="Todayish In History" href="http://stuporglue.org/todayish-in-history/">http://stuporglue.org/todayish-in-history/</a>.</p>
<p>Hopefully this proves to be a useful plugin for someone. Please send me any feedback. You can use the comments, the contact page, or email me at stuporglue@gmail.com. <a title="Todayish In History" href="http://stuporglue.org/todayish-in-history/"><br />
</a></p>
<h2>Screenshots</h2>
<p>&nbsp;</p>

<a href='http://stuporglue.org/announcing-todayish-in-history/screenshot-1-2/' title='Todayish in a theme'><img width="150" height="54" src="http://stuporglue.org/wp-content/uploads/2012/04/screenshot-1-150x54.png" class="attachment-thumbnail" alt="Todayish in a theme" title="Todayish in a theme" /></a>
<a href='http://stuporglue.org/announcing-todayish-in-history/screenshot-2/' title='Todayish in a theme, expanded'><img width="150" height="44" src="http://stuporglue.org/wp-content/uploads/2012/04/screenshot-2-150x44.png" class="attachment-thumbnail" alt="Todayish in a theme, expanded" title="Todayish in a theme, expanded" /></a>
<a href='http://stuporglue.org/announcing-todayish-in-history/screenshot-3/' title='Todayish as a widget in vertical mode'><img width="150" height="115" src="http://stuporglue.org/wp-content/uploads/2012/04/screenshot-3-150x115.png" class="attachment-thumbnail" alt="Todayish as a widget in vertical mode" title="Todayish as a widget in vertical mode" /></a>
<a href='http://stuporglue.org/announcing-todayish-in-history/screenshot-4/' title='Todayish as a widget in horizontal mode'><img width="150" height="112" src="http://stuporglue.org/wp-content/uploads/2012/04/screenshot-4-150x112.png" class="attachment-thumbnail" alt="Todayish as a widget in horizontal mode" title="Todayish as a widget in horizontal mode" /></a>
<a href='http://stuporglue.org/announcing-todayish-in-history/screenshot-5/' title='Todayish widget options'><img width="136" height="150" src="http://stuporglue.org/wp-content/uploads/2012/04/screenshot-5-136x150.png" class="attachment-thumbnail" alt="Todayish widget options" title="Todayish widget options" /></a>

<h2>Demo</h2>
<p>You can see Todayish In History in action at the top of every page over at<a title="The Fridley Farmer" href="http://fridleyfarmer.com/" target="_blank"> The Fridley Farmer</a>.</p>
<h2></h2>

<p><a href="http://feedads.g.doubleclick.net/~a/0hLg_n46ApX0JiIzNjGKwJhr9k4/0/da"><img src="http://feedads.g.doubleclick.net/~a/0hLg_n46ApX0JiIzNjGKwJhr9k4/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/0hLg_n46ApX0JiIzNjGKwJhr9k4/1/da"><img src="http://feedads.g.doubleclick.net/~a/0hLg_n46ApX0JiIzNjGKwJhr9k4/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/stuporglue/~4/I0eag67ZOEM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stuporglue.org/announcing-todayish-in-history/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://stuporglue.org/announcing-todayish-in-history/</feedburner:origLink></item>
		<item>
		<title>mailReader.php — Parse E-mail and Save Attachments PHP, Version 2</title>
		<link>http://feedproxy.google.com/~r/stuporglue/~3/rGf6Vr2GHEk/</link>
		<comments>http://stuporglue.org/mailreader-php-parse-e-mail-and-save-attachments-php-version-2/#comments</comments>
		<pubDate>Sat, 14 Apr 2012 06:59:57 +0000</pubDate>
		<dc:creator>stuporglue</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Something Interesting]]></category>
		<category><![CDATA[attachments]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[mailReader.php]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://stuporglue.org/?p=1249</guid>
		<description><![CDATA[One of most popular pages of all time is Recieve E-mail and Save Attachments with a PHP script. What was meant to be a quick hack that was only ever tested with Gmail ended up generating lots of support requests. &#8230; <a href="http://stuporglue.org/mailreader-php-parse-e-mail-and-save-attachments-php-version-2/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>One of most popular pages of all time is <a title="Receive Email and Save Attachments With a PHP Script" href="http://stuporglue.org/recieve-e-mail-and-save-attachments-with-a-php-script/">Recieve E-mail and Save Attachments with a PHP script</a>. What was meant to be a quick hack that was only ever tested with Gmail ended up generating lots of support requests.</p>
<p>At first I suggested that people needed more robust email parsing use a dedicated library. But no one seemed to want to do the coding for that, so I ended up writing a new version which uses the <a title="Mail::mimeDecode" href="http://pear.php.net/package/Mail_mimeDecode" target="_blank">PEAR mimeDecode.php</a> library to do the parsing.</p>
<p>Without further ado, here&#8217;s mailReader.php!</p>
<h2>E-mail Processing Script Features:</h2>
<ol>
<li>Saves the e-mail sender, subject and body to a database</li>
<li>Saves any attachments as files and creates an entry for those files in the database, associated with the e-mail info in #1</li>
<li>Sends  a response back to the sender telling them what files were received and their file sizes</li>
<li>Checks a list of allowed senders to make sure we only take files from specified addresses.</li>
</ol>
<h2>Database Setup:</h2>
<p>If you&#8217;re going to use the database features, you&#8217;ll need a database. Here&#8217;s the SQL to create an identical setup to the one I have:</p>
<pre>-- Here's my DB structureCREATE TABLE IF NOT EXISTS `emails` (
  `id` int(255) NOT NULL AUTO_INCREMENT,
  `from` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `subject` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `body` text COLLATE utf8_unicode_ci NOT NULL,
  `date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;

CREATE TABLE IF NOT EXISTS `files` (
  `id` int(255) NOT NULL AUTO_INCREMENT,
  `email_id` int(255) NOT NULL,
  `filename` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `size` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
  `mime` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;</pre>
<h2>Security</h2>
<p>Make sure that your upload directory is out of your webroot. If someone emails you a malicious PHP script (eg. Virus.php) and can access it via the web, they could infect your server or your visitors. Many servers are configured to automatically treat .pl and .cgi as CGI scripts and run them as well. You do not want to create a way for untrusted users to upload files to your webroot!</p>
<p>With the file names in the database you can use <a title="PHP's Readfile" href="http://php.net/manual/en/function.readfile.php" target="_blank">Readfile</a> to pass files down to users.</p>
<h2>The Script: mailReader.php</h2>
<p><a title="mailReader.txt" href="http://stuporglue.org/downloads/mailReader.txt" target="_blank">Download it here</a>.</p>
<pre>#!/usr/bin/php -q
&lt;?php
//  Use -q so that php doesn't print out the HTTP headers

/*
 * mailReader.php
 *
 * Recieve mail and attachments with PHP
 *
 * Usage:
 * This script expects to recieve raw emails via STDIN.
 *
 * Configure your mail server to pipe emails to this script. (See
 * http://stuporglue.org/add-an-email-address-that-forwards-to-a-script/
 * for instructions).  Make this script executable, and edit the
 * configuration options to suit your needs. Change permissions
 * of the directories so that the user executing the script (probably the
 * mail user) will have write permission to the file upload directory.
 *
 * By default the script is configured to save pdf, zip, jpg, png and gif files.
 * Edit the switch statements around line 200 to change this.
 *
 * Requirements:
 * You will need mimeDecode.php from http://pear.php.net/package/Mail_mimeDecode/
 * I used version 1.5.5
 *
 * Copyright 2012, Michael Moore
 * Licensed under the same terms as PHP itself. You are free to use this script
 * for personal or commercial projects. Use at your own risk. No guarantees or
 * warranties.
 *
 * Contact:
 * &lt;stuporglue@gmail.com&gt;
 * http://stuporglue.org
 *
 * Support:
 * Limited free support available in the comments on the webpage for this script
 * or via email. Contracted support available for specific projects.
 * http://stuporglue.org/mailreader-php-parse-e-mail-and-save-attachments-php-version-2/
 *
 * Thanks:
 * Many thanks to forahobby of www.360-hq.com for testing this script and helping me find
 * the initial bugs.
 * Thanks to Craig Hopson of twitterrooms.co.uk for help tracking down an iOS email handling bug.
 */

global $save_directory,$saved_files,$debug,$body;

/*
 *
 * 	Configuration Options
 *
 */

// What's the max # of seconds to try to process an email?
$max_time_limit = 600; 

// A safe place for files WITH TRAILING SLASH
// Malicious users could upload a php or executable file,
// so keep this out of your web root
$save_directory = "/a/safe/save/directory/";

// Allowed senders is now just the email part of the sender (no name part)
$allowed_senders = Array(
    'myemail@example.com',
    'whatever@example.com',
); 

// Send confirmation e-mail back to sender?
$send_email = FALSE; 

// Save e-mail message and file list to DB?
$save_msg_to_db = FALSE; 

// Configure your MySQL database connection here
$db_host = 'localhost';
$db_un = 'db_un';
$db_pass = 'db_pass';
$db_name = 'db_name';

$debug = FALSE;

/*
 *
 * 	End of Configuration Options
 *
 */

//Anything printed to STDOUT will be sent back to the sender as an error!
//error_reporting(-1);
//ini_set("display_errors", 1);

// Initialize the other global, set PHP options, load email library
$saved_files = Array();
set_time_limit($max_time_limit);
ini_set('max_execution_time',$max_time_limit);
require_once('mimeDecode.php');

// Some functions we'll use
function formatBytes($bytes, $precision = 2) {
    $units = array('B', 'KB', 'MB', 'GB', 'TB');

    $bytes = max($bytes, 0);
    $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
    $pow = min($pow, count($units) - 1);

    $bytes /= pow(1024, $pow);

    return round($bytes, $precision) . ' ' . $units[$pow];
} 

// Find a happy place! Find a happy place!
function saveFile($filename,$contents,$mimeType){
    global $save_directory,$saved_files,$debug;
    $filename = preg_replace('/[^a-zA-Z0-9_-]/','_',$filename);

    $unlocked_and_unique = FALSE;
    while(!$unlocked_and_unique){
	// Find unique
	$name = time() . "_" . $filename;
	while(file_exists($save_directory . $name)) {
	    $name = time() . "_" . $filename;
	}

	// Attempt to lock
	$outfile = fopen($save_directory.$name,'w');
	if(flock($outfile,LOCK_EX)){
	    $unlocked_and_unique = TRUE;
	}else{
	    flock($outfile,LOCK_UN);
	    fclose($outfile);
	}
    }

    fwrite($outfile,$contents);
    fclose($outfile);

    // This is for readability for the return e-mail and in the DB
    $saved_files[$name] = Array(
	'size' =&gt; formatBytes(filesize($save_directory.$name)),
	'mime' =&gt; $mimeType
    );
}

function decodePart($body_part){
    global $body,$debug;
    if(array_key_exists('name',$body_part-&gt;ctype_parameters)){ // everyone else I've tried
	$filename = $body_part-&gt;ctype_parameters['name'];
    }else if($body_part-&gt;ctype_parameters &amp;&amp; array_key_exists('filename',$body_part-&gt;ctype_parameters)){ // hotmail
	$filename = $body_part-&gt;ctype_parameters['filename'];
    }else{
	$filename = "file";
    }

    if($debug){
	print "Found body part type {$body_part-&gt;ctype_primary}/{$body_part-&gt;ctype_secondary}\n";
    }

    $mimeType = "{$body_part-&gt;ctype_primary}/{$body_part-&gt;ctype_secondary}"; 

    switch($body_part-&gt;ctype_primary){
    case 'text':
	switch($body_part-&gt;ctype_secondary){
	case 'plain':
	    $body = $body_part-&gt;body; // If there are multiple text/plain parts, we will only get the last one.
	    break;
	}
	break;
    case 'application':
	switch ($body_part-&gt;ctype_secondary){
	case 'pdf': // save these file types
	case 'zip':
	case 'octet-stream':
	    saveFile($filename,$body_part-&gt;body,$mimeType);
	    break;
	default:
	    // anything else (exe, rar, etc.) will faill into this hole and die
	    break;
	}
	break;
    case 'image':
	switch($body_part-&gt;ctype_secondary){
	case 'jpeg': // Save these image types
	case 'png':
	case 'gif':
	    saveFile($filename,$body_part-&gt;body,$mimeType);
	    break;
	default:
	    break;
	}
	break;
    case 'multipart':
	if(is_array($body_part-&gt;parts)){
	    foreach($body_part-&gt;parts as $ix =&gt; $sub_part){
		decodePart($sub_part);
	    }
	}
	break;
    default:
	// anything else isn't handled
	break;
    }
}

//
// Actual email handling starts here!
// 

// Process the e-mail from stdin
$fd = fopen('php://stdin','r');
$raw = '';
while(!feof($fd)){ $raw .= fread($fd,1024); }

// Uncomment this for debugging.
// Then you can do
// cat /my/saved/file.raw | ./mailReader.php
// for testing
//file_put_contents("$save_directory/" . time() . "_email.raw",$raw);

// Now decode it!
// http://pear.php.net/manual/en/package.mail.mail-mimedecode.decode.php
$decoder = new Mail_mimeDecode($raw);
$decoded = $decoder-&gt;decode(
    Array(
	'decode_headers' =&gt; TRUE,
	'include_bodies' =&gt; TRUE,
	'decode_bodies' =&gt; TRUE,
    )
);

// Set $from_email and check if it's allowed
$from = $decoded-&gt;headers['from'];
$from_email = preg_replace('/.*&lt;(.*)&gt;.*/',"$1",$from);
if(!in_array($from_email,$allowed_senders)){
    die("$from_email not an allowed sender");
}

// Set the $subject
$subject = $decoded-&gt;headers['subject'];

// Find the email body, and any attachments
// $body_part-&gt;ctype_primary and $body_part-&gt;ctype_secondary make up the mime type eg. text/plain or text/html
if(is_array($decoded-&gt;parts)){
    foreach($decoded-&gt;parts as $idx =&gt; $body_part){
	decodePart($body_part);
    }
}

// $from_email, $subject and $body should be set now. $saved_files should have
// the files we captured

// Put the results in the database if needed
if($save_msg_to_db){
    mysql_connect($db_host,$db_un,$db_pass);
    mysql_select_db($db_name);

    $q = "INSERT INTO `emails` (`from`,`subject`,`body`) VALUES ('" .
	mysql_real_escape_string($from_email) . "','" .
	mysql_real_escape_string($subject) . "','" .
	mysql_real_escape_string($body) . "')";

    mysql_query($q) or die(mysql_error());

    if(count($saved_files) &gt; 0){
	$id = mysql_insert_id();
	$q = "INSERT INTO `files` (`email_id`,`filename`,`size`,`mime`) VALUES ";
	$filesar = Array();
	foreach($saved_files as $f =&gt; $data){
	    $filesar[] = "('$id','" .
		mysql_real_escape_string($f) . "','" .
		mysql_real_escape_string($data['size']) . "','" .
		mysql_real_escape_string($data['mime']) . "')";
	}
	$q .= implode(', ',$filesar);
	mysql_query($q) or die(mysql_error());
    }
}

// Send response e-mail if needed
if($send_email &amp;&amp; $from_email != ""){
    $to = $from_email;
    $newmsg = "Thanks! I just uploaded the following ";
    $newmsg .= "files to your storage:\n\n";
    $newmsg .= "Filename -- Size\n";
    foreach($saved_files as $f =&gt; $s){
	$newmsg .= "$f -- $s\n";
    }
    $newmsg .= "\nI hope everything looks right. If not,";
    $newmsg .=  "please send me an e-mail!\n";

    mail($to,$subject,$newmsg);
}

if($debug){
    print "From : $from_email\n";
    print "Subject : $subject\n";
    print "Body : $body\n";
    print "Saved Files : \n";
    print_r($saved_files);
}</pre>
<h2>Thanks</h2>
<p>Many thanks to <a title="www.360-hq.com" href="www.360-hq.com" target="_blank">forahobby</a> for testing this script and helping me squash a bunch of little bugs. Thanks to <a title="Twitter Rooms" href="http://twitterrooms.co.uk/" target="_blank">Craig Hopson</a> for his help finding a problem handling emails from iOS devices.</p>

<p><a href="http://feedads.g.doubleclick.net/~a/l55kQls1laC_3ytUzFgaszGyJCc/0/da"><img src="http://feedads.g.doubleclick.net/~a/l55kQls1laC_3ytUzFgaszGyJCc/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/l55kQls1laC_3ytUzFgaszGyJCc/1/da"><img src="http://feedads.g.doubleclick.net/~a/l55kQls1laC_3ytUzFgaszGyJCc/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/stuporglue/~4/rGf6Vr2GHEk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stuporglue.org/mailreader-php-parse-e-mail-and-save-attachments-php-version-2/feed/</wfw:commentRss>
		<slash:comments>23</slash:comments>
		<feedburner:origLink>http://stuporglue.org/mailreader-php-parse-e-mail-and-save-attachments-php-version-2/</feedburner:origLink></item>
	</channel>
</rss>

