Skip navigation

Category Archives: work

A simple PHP script to display which data filtering functions are available with your version. When used properly these can help protect against certain web based attacks such as XSS, and helps perform simple input validation.

<?php ob_start();

$filters = '<h1>Data Filters</h1><table><tr>'
    . '<td><strong>Filter ID</strong></td>'
    . '<td><strong>Filter Name</strong></td></tr>';

foreach(filter_list() as $id =>$filter) {
    $filters .= '<tr><td>' . $filter . '</td><td>' . filter_id($filter) . '</td></tr>';
}
$filters .= '</table>';
echo $filters;
ob_flush();
?>

Ran into this during an upgrade of a personal media server upgrade. I have a user who requires a low bandwidth stream for her music, as some of her library is in flac this becomes problematic. Previously I had used the flac library in conjunction with lame to transcode the flac file to mp3 upon request, cached it locally during the push and then discard the data. Now I’m using ffmpeg, which uses many of the same libraries though makes configuration easier. On Fedora however, one of the SELinux contexts on a child library in incorrect, causing Ampache to report:

ffmpeg: error while loading shared libraries: /lib/swresample.so.0: cannot restore segment prot after reloc: Permission Denied

That just smelled like an SElinux denial, so a little research led me to a solution -a very similar solution I had come to during an mySQL compile last week, so I’m writing this one down.

sudo chcon -t textrel_shlib_t /lib/swresample.so.0

Fixed. This should be rolled into a policy so that it survives a re-label.

I need to be able to relay mail over SMTP from a sendmail instance on Fedora, through Google Mail servers. This is how I did it.

(Make sure you have the m4, sendmail, and sendmail-cf package installed.)

In the client auth config (/etc/mail/auth/client-info):

AuthInfo:smtp.gmail.com "U:root" "I:myusername@gmail.com" "P:password" "M:PLAIN"
AuthInfo:smtp.gmail.com:587 "U:root" "I:myusername@gmail.com" "P:password" "M:PLAIN"

Create the client info database file:

sudo makemap -r hash client-info.db
sudo chmod 600 /etc/mail/auth/client-info
sudo chmod 600 /etc/mail/auth/client-info.db

UPDATE: The above stopped working for me as of Fedora 19, the following is what I had to use to update the client-info.db:

cd /etc/mail/auth
sudo makemap -v hash client-info.db < client-info
sudo chmod 600 /etc/mail/auth/client-info
sudo chmod 600 /etc/mail/auth/client-info.db

The M4 configuration:

Feature	FEATURE(`authinfo',`hash /etc/mail/auth/client-info.db')dnl	
Define	define(`SMART_HOST',`smtp.gmail.com')dnl	
Define	define(`RELAY_MAILER_ARGS', `TCP $h 587')dnl	
Define	define(`ESMTP_MAILER_ARGS', `TCP $h 587')dnl	
Define	define(`CERT_DIR', `/etc/mail/ssl')dnl	
Define	define(`confCACERT_PATH', `CERT_DIR')dnl	
Define	define(`confCACERT', `CERT_DIR/ca-bundle.crt')dnl	
Define	define(`confCRL', `CERT_DIR/ca-bundle.crt')dnl	
Define	define(`confSERVER_CERT', `CERT_DIR/sendmail.pem')dnl	
Define	define(`confSERVER_KEY', `CERT_DIR/sendmail.pem')dnl	
Define	define(`confCLIENT_CERT', `CERT_DIR/sendmail.pem')dnl	
Define	define(`confCLIENT_KEY', `CERT_DIR/sendmail.pem')dnl	
Define	define(`confAUTH_MECHANISMS', `EXTERNAL GSSAPI DIGEST-MD5 CRAM-MD5 LOGIN PLAIN') TRUST_AUTH_MECH(`EXTERNAL DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')dnl

Rebuild the config:

m4 sendmail.mc > sendmail.cf

If you run into SELinux denials, check your sendmail flags with:
sestatus -b | grep -i sendmail
If you see 'http_can_sendmail' is off, issue setsebool -P httpd_can_sendmail 1

I find myself searching for this often enough to make a note of it.

sudo find . -type f -exec chmod 644 {} \; && find . -type d -exec chmod 755 {} \;

or

sudo find /path/to/docroot/. -type f -exec chmod 644 {} \; && find . -type d -exec chmod 755 {} \;

For SETUID/SETGID conscious configurations:

find /path/to/docroot -type d -exec chmod 2775 {} \; && find /path/to/docroot -type f -exec chmod 0664 {} \;

The error message:
The mobile features adapter must have a "path" config parameter defined

When using Zend Framework and doing any sort of browser detection for mobile devices, this error may pop up. It’s an error in the Zend Documentation, the page The Browscap UserAgent Features Adapter gives the config line of:

resources.useragent.mobile.features.classname = "Zend_Http_UserAgent_Device_Features_Browscap"

This is wrong, the classpath is incorrect. The correct path is:

resources.useragent.mobile.features.classname = "Zend_Http_UserAgent_Features_Adapter_Browscap"

This goes in application/config/application.ini

SELinux was complaining about ‘duplicate declaration in module X’ after a recent package update. This is the result of stale policies not being flushed, so let’s flush them all and rebuild.

rm -f /etc/selinux/targeted/modules/active/modules/*.pp
semodule -B

Hopefully this will be fixed in an upcoming release.

The previous version works well enough, but it’s fairly inefficient memory-wise and uses a rather strange method of moving data around using streams within the class. So, now we have this one:

/*
 * A Simple PDF Stamper using PDFBox
 *
 * July, 2013
 * @author jack
 */
package simplestamper;

import java.awt.Color;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Properties;
import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.exceptions.CryptographyException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType1Font;

/**
 *
 * @author jack
 */
public class SimpleStamper {

    private static int numPages;
    private static Properties config = new Properties();
    private static String configFile;
    private static String outputFN = null;
    private static String inputFN = null;
    private static String stampString = null;
    private static String fontFamily = null;
    private static Float fontSize = null;
    private static Integer textRot = null;
    private static Color color = null;
    private static Boolean invertY = false;

    /**
     * Our Constructor
     */
    public SimpleStamper() {
        super();
    }

    /**
     * The main class. It's what plants crave.
     *
     * @param args the command line arguments args[0] -> The config file args[1]
     * -> The PDF document to be stamped args[2] -> The string to stamp
     * (optional, falls back to ss.text in the config file) args[3] -> The
     * desired name and path of the stamped PDF (optional, also defined in
     * config file as ss.outputFN)
     */
    public static void main(String[] args) throws IOException, CryptographyException, COSVisitorException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {

        // as if we were outside...
        SimpleStamper app = new SimpleStamper();

        // if we don't have any parameters...
        if (args.length == 0) {
            usage();
        } else {

            // the config file path
            configFile = args[0];

            // load the config
            loadConfig(configFile, args);

        }
        // stamp
        app.stamp();
    }

    /**
     * Loads the configuration data.
     * Note: If we add many more config values, this function should be revised as a fetching routine.
     *
     * @param configFile
     * @param args
     * @throws NoSuchFieldException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static void loadConfig(String configFile, String[] args) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {

        // the input stream
        InputStream is;

        try {
            is = new FileInputStream(configFile);
            
            try {
                // try to load the config file
                config.load(is);
                is.close();
            } catch (IOException e) {
                System.out.println(e);
            }
        } catch (IOException e) {
            System.out.println(e);
        }

        // the input PDF filename/path
        if (args.length > 1) {
            inputFN = args[1];
        } else {
            // make sure we have an input filename
            try {
                inputFN = config.getProperty("ss.inputFN");
            } catch (NullPointerException e) {
                System.err.println("You must specify an input filename as the second argument, or in the properties file. (ss.inputFN)");
            }

        }

        // the string to be stamped
        if (args.length > 2) {
            stampString = args[2];
        } else {
            // make sure we have a string to stamp
            try {
                stampString = config.getProperty("ss.text");
            } catch (NullPointerException e) {
                System.err.println("You must specify a string to stamp as the third argument, or in the properties file. (ss.text)");
            }

        }

        // the output PDF filename/path
        if (args.length > 3) {
            outputFN = args[3];
        } else {
            // make sure we have an output filename
            try {
                outputFN = config.getProperty("ss.outputFN");
            } catch (NullPointerException e) {
                System.err.println("You must specify an output filename as the fourth argument, or in the properties file. (ss.outputFN)");
            }

        }

        // make sure we have a font from the prop file
        try {
            fontFamily = config.getProperty("ss.fontFamily");
        } catch (NullPointerException e) {
            System.err.println("You must specify a font in the properties file. (ss.fontFamily)");
        }

        // make sure we have a font size from the prop file
        try {
            fontSize = Float.parseFloat(config.getProperty("ss.fontSize"));
        } catch (NullPointerException e) {
            System.err.println("You must specify a font size in the properties file. (ss.fontSize)");
        }

        // text rotation
        // make sure we have a font size from the prop file
        try {
            textRot = Integer.parseInt(config.getProperty("ss.rotation"));
        } catch (NullPointerException e) {
            System.err.println("You must specify a rotation in the properties file. (ss.rotation)");
        }

        // text color. if not set in the properties file we default to black
        try {
            Field field = Color.class.getField(config.getProperty("ss.fontColor"));
            color = (Color) field.get(null);
        } catch (NullPointerException e) {
            System.err.println("You must specify a font color in the properties file. (ss.fontColor)");
        }

        // the Y value inversion bool. Do we calc yVal from the top or bottom of the page?
        invertY = "true".equals(config.getProperty("ss.invertY")) ? true : false;
    }

    /**
     * The Stamping function. Where the magic lives.
     *
     * @throws IOException
     * @throws COSVisitorException
     */
    public void stamp() throws IOException, COSVisitorException {

        // the document
        PDDocument doc = null;

        try {
            // load the incoming document into a PDDcoument
            doc = PDDocument.load(inputFN);

            // make a list array of all the pages
            List allPages = doc.getDocumentCatalog().getAllPages();

            // Create a new font object selecting one of the PDF base fonts
            PDFont font = PDType1Font.getStandardFont(fontFamily);

            // the x/y coords
            float xVal = Float.parseFloat(config.getProperty("ss.xVal"));
            float yVal = Float.parseFloat(config.getProperty("ss.yVal"));

            // for every page in the incoming doc, stamp
            for (int i = 0; i < allPages.size(); i++) {
                // create an empty page and a geo object to use for calcs
                PDPage page = (PDPage) allPages.get(i);
                PDRectangle pageSize = page.findMediaBox();

                // are we inverting the y axis?
                if (invertY) {
                    yVal = pageSize.getHeight() - yVal;
                }

                // calculate the width of the string according to the font
                float stringWidth = font.getStringWidth(stampString) * fontSize / 1000f;

                // determine the rotation stuff. Is the the loaded page in landscape mode? (for axis and string dims)
                int pageRot = page.findRotation();
                boolean pageRotated = pageRot == 90 || pageRot == 270;

                // are we rotating the text?
                boolean textRotated = textRot != 0 || textRot != 360;

                // calc the diff of rotations so the text stamps
                int totalRot = pageRot - textRot;

                // calc the page dimensions
                float pageWidth = pageRotated ? pageSize.getHeight() : pageSize.getWidth();
                float pageHeight = pageRotated ? pageSize.getWidth() : pageSize.getHeight();

                // determine the axis of rotation
                double centeredXPosition = pageRotated ? pageHeight / 2f : (pageWidth - stringWidth) / 2f;
                double centeredYPosition = pageRotated ? (pageWidth - stringWidth) / 2f : pageHeight / 2f;

                // append the content to the existing stream
                PDPageContentStream contentStream = new PDPageContentStream(doc, page, true, true, true);
                contentStream.beginText();

                // set font and font size
                contentStream.setFont(font, fontSize);

                // set the stroke (text) color
                contentStream.setNonStrokingColor(color);

                // if we are rotating, do it
                if (pageRotated) {

                    // rotate the text according to the calculations above
                    contentStream.setTextRotation(Math.toRadians(totalRot), centeredXPosition, centeredYPosition);

                } else if (textRotated) {

                    // rotate the text according to the calculations above
                    contentStream.setTextRotation(Math.toRadians(textRot), xVal, yVal);

                } else {

                    // no rotate, just move it.
                    contentStream.setTextTranslation(xVal, yVal);
                }

                // stamp the damned text already
                contentStream.drawString(stampString);

                // close and clean up
                contentStream.endText();
                contentStream.close();
            }

            doc.save(outputFN);

        } finally { // you know, PHP 5.5 now has a finally construct...

            // if the document isnt closed, do so
            if (doc != null) {
                doc.close();
            }

        }
    }

    /**
     * Attempts to provide basic usage info.
     */
    private static void usage() {
        System.err.println("usage: java -jar simplestamper \"PdfToStamp.pdf\" \"The text to stamp, in parenthesis.\" \"Output.pdf\"");
    }
}

And the config file:

# the properties for each event

#The text to be stamped.
ss.text=This is the text to be stamped.
#The x value of the stamped text, measured in pixels with the origin at bottom-left.
ss.xVal=100
#The y value of the stamped text, measured in pixels with the origin at bottom-left.
ss.yVal=200
# The counterclockwise rotation of the text in degrees (converted to radians in the app)
ss.rotation=0
#The name/path of the input file
ss.inputFN=pdf.pdf
# the filename of the stamped pdf
ss.outputFN=output.pdf

# FONT OPTIONS:
# more info: http://pdfbox.apache.org/apidocs/org/apache/pdfbox/pdmodel/font/PDType1Font.html
#
# Helvetica-BoldOblique
# Times-Italic
# ZapfDingbats
# Symbol
# Helvetica-Oblique
# Courier
# Helvetica-Bold
# Helvetica
# Courier-Oblique
# Times-BoldItalic
# Courier-Bold
# Times-Roman
# Times-Bold
# Courier-BoldOblique
ss.fontFamily=Helvetica
ss.fontSize=18
#
# The font color.
# Examples: blue, red, green, black, white, yelllow, cyan, gray, lightGray
# More Info: http://docs.oracle.com/javase/7/docs/api/java/awt/Color.html
ss.fontColor=black

# Operational Vars
#
# Do we want to calc text placement from the top (true) or bottom (false) of the page?
# (The PDF spec. dictates that we calc all y values starting from the bottom, this is why top = true)
ss.invertY=true

PDFbox is a fairly robust library for manipulating and extracting information from PDF documents. This is a short and simple Java class that utilizes PDFbox to stamp/watermark a PDF from the command line, be it a bash script or exec call. There isn’t much in the way of existing user documentation for this type of action using PDFbox, so I’m leaving it here in case it’s useful.

/*
 * A Simple PDF Stamper using PDFBox
 *
 * July, 2013
 * @author jack
 */
package simplestamper;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import org.apache.pdfbox.Overlay;
import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.exceptions.CryptographyException;
import org.apache.pdfbox.exceptions.InvalidPasswordException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType1Font;

/**
 *
 * @author jack
 */
public class SimpleStamper {

    private static PDDocument pdf;
    private static int numPages;
    private static Properties config = new Properties();
    private static String configFile;
    private static String outputFN = null;
    private static String inputFN = null;
    private static String stampString = null;
    private static String fontFamily = null;
    private static Float fontSize = null;

    /**
     * The main class. It's what plants crave.
     *
     * @param args the command line arguments
     *      args[0] -> The config file
     *      args[1] -> The PDF document to be stamped
     *      args[2] -> The string to stamp (optional, falls back to ss.text in the config file)
     *      args[3] -> The desired name and path of the stamped PDF (optional, also defined in config file as ss.outputFN)
     */
    public static void main(String[] args) throws IOException, CryptographyException, COSVisitorException {

        // if we don't have any parameters...
        if (args.length == 0) {
            usage();
        } else {

            // the config file path
            configFile = args[0];

            // load the config
            loadConfig(configFile, args);

        }
        // stamp
        stampPdf();
    }

    /**
     * Loads the configuration data.
     *
     * @param configFile
     */
    public static void loadConfig(String configFile, String[] args) {

        // the input stream
        InputStream is;

        try {

            is = new FileInputStream(configFile);

            try {
                // try to load the config file
                config.load(is);
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // the input PDF filename/path
        if (args.length > 1) {
            inputFN = args[1];
        }else{
            inputFN = config.getProperty("ss.inputFN");
        }

        // the string to be stamped
        if(args.length > 2){
            stampString = args[2];
        }else{
            stampString = config.getProperty("ss.text");
        }

        // the output PDF filename/path
        if(args.length > 3){
            outputFN = args[3];
        }else{
            outputFN = config.getProperty("ss.outputFN");
        }

        // make sure we have a font from the prop file
        try{
            fontFamily = config.getProperty("ss.fontFamily");
        } catch (NullPointerException e){
            System.err.println("You must specify a font in the properties file.");
        }

        // make sure we have a font size from the prop file
        try{
            fontSize = Float.parseFloat(config.getProperty("ss.fontSize"));
        } catch (NullPointerException e){
            System.err.println("You must specify a font size in the properties file.");
        }

    }

    /**
     * Coordinate the stamping procedure.
     *
     */
    public static void stampPdf() throws IOException, CryptographyException, COSVisitorException {

        // load the PDF to be stamped
        pdf = PDDocument.load(inputFN);

        if (pdf.isEncrypted()) {
            try {
                // try to open the encrypted PDF
                pdf.decrypt("");

            } catch (InvalidPasswordException e) {

                // This error message dictates that the document is encrypted and we have no password
                System.err.println("The document is encrypted or otherwise has prohibitive security settings..");
                System.exit(1);
            }
        }
        // create the overlay page with the text to be stamped
        PDDocument overlayDoc = createOverlay(stampString);

        // update the number of pages we have in the incoming pdf
        numPages = pdf.getPageCount();

        // do the overlay
        doOverlay(pdf, overlayDoc);

        // close
        pdf.close();
        overlayDoc.close();
    }

    /**
     * Creates the overlay PDF.
     *
     * @param text
     * @return PDDocument
     * @throws IOException
     * @throws COSVisitorException
     */
    public static PDDocument createOverlay(String text) throws IOException, COSVisitorException {

        // Create a document and add a page to it
        PDDocument document = new PDDocument();
        PDPage page = new PDPage();
        document.addPage(page);

        // the x/y coords
        Float xVal = Float.parseFloat(config.getProperty("ss.xVal"));
        Float yVal = Float.parseFloat(config.getProperty("ss.yVal"));

        // Create a new font object selecting one of the PDF base fonts
        PDFont font = PDType1Font.getStandardFont(fontFamily);

        // Start a new content stream which will "hold" the to be created content
        PDPageContentStream contentStream = new PDPageContentStream(document, page);

        // Create the text and position it
        contentStream.beginText();
        contentStream.setFont(font, fontSize);
        contentStream.moveTextPositionByAmount(xVal, yVal);
        contentStream.drawString(text);
        contentStream.endText();

        // Make sure that the content stream is closed:
        contentStream.close();

        //return the string doc
        return document;
    }

    /**
     * Performs the overlay of the two documents.
     *
     * @param basePDF
     * @param overlayDoc
     * @throws IOException
     * @throws COSVisitorException
     */
    private static void doOverlay(PDDocument basePDF, PDDocument overlayDoc) throws IOException, COSVisitorException {

        PDDocumentCatalog docCatalog = basePDF.getDocumentCatalog();

        // get the pages of the pdf
        List pages = docCatalog.getAllPages();
        Iterator pageIter = pages.iterator();

        while (pageIter.hasNext()) {
            PDPage page = (PDPage) pageIter.next();
        }

        Overlay overlay = new Overlay();
        PDDocument output = overlay.overlay(overlayDoc, basePDF);
        //overlay.overlay(overlayDoc, basePDF);

        // save the new doc to the file name specified in the config
        output.save(outputFN);

        // close, close
        overlayDoc.close();
        basePDF.close();
    }

    /**
     * Attempts to provide basic usage information.
     */
    private static void usage() {
        System.err.println("usage: java -jar simplestamper \"PdfToStamp.pdf\" \"The text to stamp, in parenthesis.\" \"Output.pdf\"");
    }
}

SVN is like a girl with huge tits and a pet Rhino, lots of fun most of the time but that damned Rhino keeps breaking all your shit. One of the first things to check when you notice peculiar behavior in your SVN workflow is to make sure your SVN client and the SVN server are running compatible version. I found this useful, so I’m making a note of it here.

To get the version of the SVN server:

wget -S --no-check-certificate --user='jack' password='thepassword' --spider 'https://svn.jackson-brain.com/trunk/branch' 2>&1 | sed -n '/SVN/s/.*\(SVN[0-9\/\.]*\).*/\1/p';

The client:

svn --version

Unless you have custom compiled or otherwise installed and configured the Zend OPcache extension for PHP, or are running PHP >= 5.5 in which case the extension is enabled by default.

This is simple, yet handy if you are developing within an environment where you want to test code with and without OPcache. In the .htaccess file of the directory containing the PHP you do not want cached, add the line:

php_flag opcache.enable Off

No need to restart Apache. To determine the state of OP caching within this directory, examine the output of phpinfo() executed from within that directory. Look for the section titled ‘Zend OPcache’, if it’s enabled you’ll get a good deal of stats on the items in the cache, if it’s not it will simple say ‘Disabled’.

Troubleshooting:

  1. Check .htaccess override setting in httpd.conf