[fr] [en] PickyPaste For Picky People: Robust (uses Zerobin) AND easy-to-use


(Pour qu'on puisse vous répondre)

(Le message est détruit après lecture)
Votre message


Gagnez encore plus de temps en utilisant notre snippet

Glissez-déposez ce lien ( PickyPaste ) dans vos favoris.

Vous n'avez plus qu'à cliquer sur ce lien pour partager le lien vers la page où vous vous trouvez !
Une fenêtre PickyPaste s'ouvrira automatiquement en pré-remplissant le message avec le lien de la page où vous étiez.

Paramètres avancés

Ne modifiez les valeurs ci-dessous que si vous savez ce que vous faites !!



(Incompatible avec Lecture unique)
Voir le code source
Cacher le code source
<?php error_reporting(-1);

# PickyPaste 1.0
# > For Picky People (aka I want it robust AND easy-to-use)

# @author: JeromeJ (webmaster of http://www.olissea.com/ )
# @licence: DoWhatTheFuckYouWantAsLongAsYouGiveMeSomeCredits(HighlyAppreciated)

# Based on Zerobin's encryption code

# NOTE: - Best used with the snippet
#        - Best used in offline-mode

# DESIGN BY iamyog and nabellaleen

# TODO:
# - Allow multiple recipient?
# - Create a local version of the HTML client, configurable through an optional external config file (an .ini file ?)
# - Create two versions: A standalone PHP and a client/server version.
#     -> The client/server version could simple have a client sending data to the standalone PHP version.
#    -> Additionnaly, for the people who DONT want people being able to use the standalone PHP version, there could be a light PHP version (no form, only the 'mailServer')
# - What if they don't want to send it by email? Wouldn't it be easier for them if they had only one tool to use? -> Make it modulable!

#################### CONFIG ####################################

mb_internal_encoding('UTF-8');

global 
$PP_config;  # Just in case PP would be embarked in a scope.

# Set those as you prefer
define('PP_DEFAULT_LANG''fr');
define('PP_DEFAULT_EXPI''1month');  # Can be never, 5min, 10min, 1hour, 1day, 1week, 1month or 1year

$PP_config = array(
    
'langs' => array('fr''en'),  # Available langs
    
'lang' => PP_DEFAULT_LANG,
    
'dateFormat' => array(
        
'fr' => array(
            
'short' => 'd/m/Y à H\hi',
            
'long' => 'd M Y à H\hi',
        ),
        
'en' => array(
            
'short' => 'm-d-Y \a\t H:i',
            
'long' => 'M d \'Y \a\t H:i'
        
),
    ),
    
'expiration' => array(
        
'fr' => array(
            
'5min' => '5 minutes',
            
'10min' => '10 minutes',
            
'1hour' => '1 heure',
            
'1day' => '1 jour',
            
'1week' => '1 semaine',
            
'1month' => '1 mois',
            
'1year' => '1 ans',
            
'never' => 'Jamais',
        ),
        
'en' => array(
            
'5min' => '5 minutes',
            
'10min' => '10 minutes',
            
'1hour' => '1 hour',
            
'1day' => '1 day',
            
'1week' => '1 week',
            
'1month' => '1 month',
            
'1year' => '1 year',
            
'never' => 'Never',
        )
    ),
    
'texts' => array(
        
'recipient' => array(
            
'fr' => 'Destinataire',
            
'en' => 'Recipient',
        ),
        
        
'email_address' => array(
            
'fr' => 'Adresse email',
            
'en' => 'Email address',
        ),
        
        
'your_email' => array(
            
'fr' => 'Votre email',
            
'en' => 'Your email',
        ),
        
        
'optional' => array(
            
'fr' => 'Facultatif',
            
'en' => 'Optional',
        ),
        
        
'answerable' => array(
            
'fr' => 'Pour qu\'on puisse vous répondre',
            
'en' => 'So you can be answered',
        ),
        
        
'expiration' => array(
            
'fr' => 'Expiration',
            
'en' => 'Expiration',
        ),
        
        
'burn-after-reading' => array(
            
'fr' => 'Lecture unique',
            
'en' => 'Burn after reading',
        ),
        
        
'bar-explanations' => array(
            
'fr' => 'Le message est détruit après lecture',
            
'en' => 'Message is destroyed after reading',
        ),
        
        
'your_message' => array(
            
'fr' => 'Votre message',
            
'en' => 'Your message',
        ),

        
'save-even-more-time' => array(
            
'fr' => <<<EOF
            <h3>Gagnez encore plus de temps en utilisant notre snippet</h3>
            
            Glissez-déposez ce lien ( <a class="snippet" href="javascript:void(window.open('http://www.olissea.com/PP/PP.php#'+window.location.href, '_blank'))">PickyPaste</a> ) dans vos favoris.<br /><br />
            
            Vous n'avez plus qu'à cliquer sur ce lien pour partager le lien vers la page où vous vous trouvez !<br />
            Une fenêtre PickyPaste s'ouvrira automatiquement en pré-remplissant le message avec le lien de la page où vous étiez.
EOF
,
            
'en' => <<<EOF
            <h3>Save even more time by using our snippet</h3>
            
            Drag-and-drop this link ( <a class="snippet" href="javascript:void(window.open('http://www.olissea.com/PP/PP.php#'+window.location.href, '_blank'))">PickyPaste</a> ) in your bookmarks.<br /><br />
            
            Now you only need to click on this link to share the link you were currently on before clicking it!<br />
            A PickyPaste window will open and automatically fill the message content with the link of the page you were on.
EOF
,
        ),

        
'advanced-parameters' => array(
            
'fr' => <<<EOF
            <h3>Paramètres avancés</h3>
            
            <em>Ne modifiez les valeurs ci-dessous que si vous savez ce que vous faites !!</em><br /><br />
            
            <label for="zerobinServer">Serveur Zerobin</label> <input id="zerobinServer" name="zerobinServer" type="text" value="http://sebsauvage.net/paste/" size="50" /><br />
            <label for="mailServer">Serveur Mail</label> <input id="mailServer" type="text" size="50" value="{emailServer}" /> <br />
            <label for="opendiscussion">Discussion ouverte</label> <input id="opendiscussion" name="opendiscussion" type="checkbox" value="1" /> (Incompatible avec <strong>Lecture unique</strong>)<br />
            <label for="syntaxcoloring" style="clear: both">Colorisation syntaxique</label> <input id="syntaxcoloring" name="syntaxcoloring" type="checkbox" value="1" />
            
            <input id="data" name="data" type="hidden" />
            <input id="randomkey" name="randomkey" type="hidden" />

EOF
,
            
'en' => <<<EOF
            <h3>Advanced parameters</h3>
            
            <em>Don't modify anything below unless you know what you are doing!!</em><br /><br />
            
            <label for="zerobinServer">Zerobin Server</label> <input id="zerobinServer" name="zerobinServer" type="text" value="http://sebsauvage.net/paste/" size="50" /><br />
            <label for="mailServer">Email Server</label> <input id="mailServer" type="text" size="50" value="{emailServer}" /> <br />
            <label for="opendiscussion">Open discussion</label> <input id="opendiscussion" name="opendiscussion" type="checkbox" value="1" /> (Not compatible with <strong>Burn after reading</strong>)<br />
            <label for="syntaxcoloring" style="clear: both">Syntax coloring</label> <input id="syntaxcoloring" name="syntaxcoloring" type="checkbox" value="1" />
            
            <input id="data" name="data" type="hidden" />
            <input id="randomkey" name="randomkey" type="hidden" />

EOF
,
        ),

        
'show-source' => array(
            
'fr' => 'Voir le code source',
            
'en' => 'Show the source code',
        ),

        
'hide-source' => array(
            
'fr' => 'Cacher le code source',
            
'en' => 'Hide the source code',
        ),
        
        
'willExpireOn' => array(
            
'fr' => 'Ce message expirera le {}',
            
'en' => 'This paste will expire on {}',
        ),  
        
'willExpireOn - short' => array(
            
'fr' => 'Expire le {}',
            
'en' => 'Expires on {}',
        ),

        
'secured-message' => array(
            
'fr' => 'Message sécurisé',
            
'en' => 'Secured message',
        ),

        
'from' => array(
            
'fr' => 'de ',
            
'en' => 'from ',
        ),

        
'anonymous' => array(
            
'fr' => 'anonyme',
            
'en' => 'anonymous',
        ),

        
'email-sent-with-success' => array(
            
'fr' => 'Mail envoyé avec succès !<br /><br />'.
                
'Vous pouvez dès à présent fermer cette page.<br /><br />',
            
'en' => 'Email sent with success!<br /><br />'.
                
'You can now close this page.<br /><br />'
        
),
        
        
'burnafterreading' => array(
            
'fr' => <<<EOF
Ce message est à <strong>lecture unique</strong>. Ce qui signifie que vous ne pourrez l'ouvrir qu'une seule fois.<br />
Par conséquent, si vous voulez pouvoir revoir son contenu, n'oubliez pas de le sauvegarder manuellement d'une façon ou d'une autre.<br /><br />
<strong>Attention,</strong> si vous ne parvenez pas à ouvrir ce message alors qu'il n'est pas censé être déjà expiré (voir date d'expiration ci dessus, s'il en avait une), veuillez considérez ce qui suit :
<ol>
    <li>Se peut-il que vous ayez déjà ouvert ce message (ou quelqu'un d'autre ayant accès à votre messagerie) ? Dans ce cas, vous n'êtes pas censé pouvoir le réouvrir.</li>
    <li>Le cas échéant, et si le message n'était pas censé être expiré, cela peut vouloir dire que le réseau que vous utilisez actuellement est <strong>corrompu</strong> !!<br />
    Merci de nous prévenir si cela vous arrive en nous précisant quel service mail vous utilisez et si cela s'est déjà produit auparavant. <a href="http://www.olissea.com/contact.php">Me contacter</a> (N'oubliez pas de préciser le motif !!) (Tout abus est punissable)</li>
</ol><br />
EOF
,
            
'en' => <<<EOF
This is a <strong>burn-after-reading</strong> message. It means it can be open once only.<br />
Thus, if you desire being able to read it again, don't forget to save it manually one way or another.<br /><br />
<strong>Be warned,</strong> if you can't open this message although it isn't supposed to be expired yet (check the expiration date above, if there's one) then please consider the following:
<ol>
    <li>May have you already opened this message (or someone else having access to your inbox)? In which case, you are not meant being able to open it again.</li>
    <li>Else, and if the message wasn't supposed to be expired, this could mean that the network you are currently using might be <strong>corrupted</strong>!! Be careful!!<br />
    Thanks to warn us if it keeps happening and don't forget to precise us which webmail you and your recipient were using when it happened. <a href="http://www.olissea.com/contact.php">Contact me</a> (Don't forget to include the context) (Abuse is punishable)</li>
</ol><br />
EOF
,
        ),
        
        
'mailContent' => array(
            
'fr' => <<<EOF
Bonjour/soir,<br /><br />
Vous avez reçu un message de la part de <strong>{from}</strong>.<br /><br />
Afin de protéger votre vie privée, le message en question a été chiffré via <a href="http://www.olissea.com/PP/PP.php">PickyPaste</a> (service facilitant l'envoie de mail sécurisé via <a href="http://sebsauvage.net/paste/">Zerobin</a>).<br /><br />
Pour visionner le message, merci de cliquer sur ce lien : <a href="{link}">{link}</a>{reply}<br />
{expiration}<br />
{burnafterreading}
<strong>Note :</strong> Toutes les applications et intermédiaires utilisés pour chiffrer et transmettre ce message sont des applications open-sources : Rien ne vous oblige à avoir confiance en nos applications naïvement, vous pouvez vérifier leur fonctionnement en détail ou même implémenter vos propres instances.
EOF
,
            
'en' => <<<EOF
Hello,<br /><br />
You have received a message from <strong>{from}</strong>.<br /><br />
In order to protect your privacy, this message has been encrypted using <a href="http://www.olissea.com/PP/PP.php">PickyPaste</a> (service facilitating the sending of secured messages via <a href="http://sebsauvage.net/paste/">Zerobin</a>).<br /><br />
To read this message, please click on this link : <a href="{link}">{link}</a>{reply}<br />
{expiration}<br />
{burnafterreading}
<strong>Note:</strong> All the applications used to encrypt this message or transmit it, are open-sources applications: It means you don't have to give us your trust blindly, you can check how it works step by step or even implementing your own instances of it.
EOF
,
        ),

        
'replyTo' => array(
            
'fr' => ' (<a href="http://www.olissea.com/PP/PP.php?mail={email}">Répondre via PickyPaste</a>)',
            
'en' => ' (<a href="http://www.olissea.com/PP/PP.php?mail={email}">Reply using PickyPaste</a>)',
        ),
        
        
'directLink' => array(
            
'fr' => 'Lien direct vers votre paste',
            
'en' => 'Direct link to your paste',
        ),
        
'directLinkInhibited' => array(
            
'fr' => 'Étant donné que votre paste est en lecture unique, l\'ouvrir équivaudrait à le supprimer.',
            
'en' => 'Since your paste is set to be read only once, opening it would equal to delete it.',
        ),
        
'deleteLink' => array(
            
'fr' => 'Lien pour détruire votre message manuellement',
            
'en' => 'Link to delete your message manually',
        ),
        
        
'error' => array(
            
'fr' => 'Erreur !!',
            
'en' => 'Error!!',
        ),
        
'errors' => array(
            
'PHPVersionTooLow' => array(
                
'fr' => 'PickyPaste nécessite au moins PHP {}.',
                
'en' => 'PickyPaste requires at lest PHP {}.',
            ),
            
'decryptingKeyMissing' => array(
                
'fr' => 'La clé de décryptage n\'a pas été reçue !!',
                
'en' => 'The decrypting key is missing!!',
            ),
            
'recipientMissing' => array(
                
'fr' => 'Vous devez spécifiez un destinataire !!',
                
'en' => 'You have to specify the recipient!!',
            ),
            
'invalidZerobinAddr' => array(
                
'fr' => 'Adresse du serveur Zerobin invalide !!',
                
'en' => 'Invalid Zerobin\'s address!!',
            ),
            
'zerobinServNotAnswering' => array(
                
'fr' => 'Serveur Zerobin injoignable !!<br /><br />Veuillez réessayer plus tard ou en choisir un autre le cas échéant.',
                
'en' => 'Zerobin Server isn\'t answering!!<br /><br />Please try again later or choose another one if it keeps failing.',
            ),
            
'pasteNotCreated - zerobinServUnhappy' => array(
                
'fr' => 'Impossible de créer votre past !! (Erreur de protocole avec le serveur Zerobin)',
                
'en' => 'Failed to create your paste!! (Protocol error with the Zerobin server)',
            ),
            
'pasteNotCreated - error' => array(
                
'fr' => 'Impossible de créer votre past : {}',
                
'en' => 'Failed to create your paste: {}',
            ),
            
'invalidRecipient' => array(
                
'fr' => 'Un ou plusieurs des destinataire est invalide !',
                
'en' => 'One or more of the recipients is invalid!',
            ),
            
'emailDeliveryFailed' => array(
                
'fr' => 'L\'envoie du mail a échoué !!<br /><br />Voici le lien vers votre paste <a href="{link}">{link}</a> (Procédure manuelle : clic droit sur le lien, copier l\'adresse du lien puis coller là dans votre email)',
                
'en' => 'The email failed to be delivered!!<br /><br />Here is the direct link to your paste <a href="{link}">{link}</a> (Manual procedure: right click on the link, Copy Link Location then paste it in your email)'
            
),
        ),
    ),
);

$months = array(
    
'fr' => array(=> 'janvier''février''mars''avril''mai''juin''juillet''août''septembre''octobre''novembre''décembre'),
    
'en' => array(=> 'January''February''March''April''May''June''July''August''September''October''November''December'),
);





















#### HAMMER TIME: Don't edit what's below except if you know what you're doing! #######

################# ACTUAL CODE ##################################

# 5.2: filter_var, DateTime class 
define('PP_REQUIRED_PHP_VERSION''5.2.0');

# 5.3: namespaces
# define('PP_REQUIRED_PHP_VERSION', '5.3.0'); # I'd like to eventually switch to 5.3, be warned.

session_start('PP');

/********** <LANG> **********/
if(isset($_GET['set_lang']) && in_array($_GET['set_lang'], $PP_config['langs']))
{
    
$_SESSION['PP']['lang'] = $_GET['set_lang'];
    
$PP_config['lang'] = $_GET['set_lang'];
    
    
# Reflections about automating the selection of the lang (in French - feel free to translate it ☺)
    #
    # 1) Via les infos données au navigateur -> Si on le fait, ça sera en BONUS je pense … ou … ou alors je peux prendre mon script déjà existant
    #
    #    ['HTTP_ACCEPT_LANGUAGE'] => 'fr, en'    # Mon code est basé là dessus, mais ça fonctionne pas toujours :/ par défaut c'est "FR" mais beaucoup d'anglais se sont plains que le site s'affichait en français (au lieu de passer en anglais auto)
    #
    #    ['GEOIP_COUNTRY_CODE'] => 'BE'            # Nécessiterait un long tableau de correspondance location -> lang # Ou une lib qui propose déjà ça ?
    #    ['GEOIP_COUNTRY_NAME'] => 'Belgium'        # Pareil. Donc mauvais idée je trouve (Puis, pas vraiment nécessaire)
    #
    # 2) Via l'IP: Non. Compliqué ou nécessite un service externe (et on va pas donner l'ip comme ça ☺ non mais !)
    #
    
    # Idea: Let the user the possibility to config how cookies are stored (or not at all) via GET args only (and not implement it to the interface)
    
$path substr($_SERVER['SCRIPT_URI'], 0strrpos($_SERVER['SCRIPT_URI'], '/'));
    
$domain ''# TODO: Restrict to the host where PP is host?
    
$https = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on');
    
$httponly false# Allow JavaScript to access it. This isn't sensitive data.
    
    
setcookie('PP_lang'$PP_config['lang'], time()+3600*24*365$path$domain$https$httponly);
}
else if(isset(
$_SESSION['PP']['lang']) && in_array($_SESSION['PP']['lang'], $PP_config['langs'])) $PP_config['lang'] = $_SESSION['PP']['lang'];
else if(isset(
$_COOKIE['PP_lang']) && in_array($_COOKIE['PP_lang'], $PP_config['langs'])) $PP_config['lang'] = $_COOKIE['PP_lang'];

if(!
in_array($PP_config['lang'], $PP_config['langs'])) $PP_config['lang'] = PP_DEFAULT_LANG;
/********* </LANG> ***********/

function PP_htmlspecialchars($str)
{
    
# Sometimes htmlspecialschars seems to return an empty string, let's implement it ourselves. (Only the default behaviour as we only use this one)
    
    
return str_replace(array('&''<''>''"'), array('&amp;''&lt;''&gt;''&quot;'), $str);
}

# Return a message from its name into the language chosen in the config (by default)
function PP_msg($msg$context=null$lang=null)
{
    global 
$PP_config;
    
    if(
$lang === null$lang $PP_config['lang'];
    
    if(
$context !== null)
    {
        
# echo '<pre>',print_r(array($msg, $context, $PP_config)),'</pre>';
        
if(!isset($PP_config['texts'][$context][$msg])) return '{{'.PP_htmlspecialchars($msg).'|text_not_found}}';
        if(!isset(
$PP_config['texts'][$context][$msg][$lang])) return '{{'.PP_htmlspecialchars($msg).'|lang_not_avaible}}';
        
        return 
$PP_config['texts'][$context][$msg][$lang];
    }
    
    if(!isset(
$PP_config['texts'][$msg])) return '{{'.PP_htmlspecialchars($msg).'|text_not_found}}';
    if(!isset(
$PP_config['texts'][$msg][$lang])) return '{{'.PP_htmlspecialchars($msg).'|lang_not_avaible}}';
    
    return 
$PP_config['texts'][$msg][$lang];
}

function 
PP_error($error$exit=true)
{
    global 
$PP_config;
    
    echo 
'<!DOCTYPE html>',
        
'<html lang="',PP_htmlspecialchars($PP_config['lang']),'">',
            
'<head>',
                
'<title>PickyPaste - ',PP_msg('error'),'</title>',
                
'<link type="text/css" rel="stylesheet" href="css/pickypaste.css" />',
                
'<meta charset="UTF-8">',
                
'<meta http-equiv="content-type" content="text/html; charset=utf-8" />',
            
'</head>',
            
'<body>',
                
'<span class="error">',PP_msg($error'errors'),'</span>',
            
'</body',
        
'</html>';
    
    if(
$exit) exit;
}

# PP_format function (better than sprintf)
function PP_f($msg$vars)
{
    
$vars = (array)$vars;
    
    
$msg preg_replace_callback('#\{\}#', function($r){
        static 
$i 0;
        return 
'{'.($i++).'}';
    }, 
$msg);
    
    return 
str_replace(
        
array_map(function($k) {
            return 
'{'.$k.'}';
        }, 
array_keys($vars)),
        
        
array_values($vars),
        
        
$msg
    
);
}

/*********** </TOOLS> **************/

if(version_compare(PHP_VERSIONPP_REQUIRED_PHP_VERSION) < 0) exit(PP_f(PP_error('PHPVersionTooLow'false), PP_htmlspecialchars(PP_REQUIRED_PHP_VERSION)));

if(!empty(
$_POST))
{
    if(!isset(
$_POST['randomkey'])) PP_error('decryptingKeyMissing');
    
    if(!isset(
$_POST['to'])) PP_error('recipientMissing');
    else
    {
        
# Olissea Plugin # TODO: Upgrade it!
        
if(in_array(strtolower($_POST['to']), array('jeromej')))
        {
            require 
$_SERVER['DOCUMENT_ROOT'].'/inc/init.php';
            
            
$_POST $_POST_HTML;  # Removing auto-protection from Olissea
            
            # TODO: Add a hash verification to allow private access to username->email (Actually, this is to add Piiu to the list but not making it public) (a "password" would be required)
            
            
list($_POST['to']) = $pdo->query('SELECT email FROM mb WHERE f_username=? LIMIT 1', array($_POST['to']), 'row');
        }
        else
        {        
            
/*echo '<pre>',
                    'TO: ',PP_htmlspecialchars($_POST['to']),"\n",
                    'VALID: ',(preg_match('/
                        \s*
                        (?:[^<]*<[^>]+>|[^@\s]+@[^.]+\.[^,;]+) # Match an email add
                        (?:
                            \s*[,;]\s*
                            (?:[^<]*<[^>]+>|[^@\s]+@[^.]+\.[^,;]+) # Same
                        )*
                    /xiU', $_POST['to']) ? 'TRUE':'FALSE'),
                '</pre>'; */
            
            
$_POST['to'] = preg_split('#[,;]#'$_POST['to']);
            
            
# echo PP_htmlspecialchars(print_r($_POST['to'], true));
            
            
foreach($_POST['to'] AS $k => $email)
            {
                if(
trim($email) === '') unset($_POST['to'][$k]);
                else if(!
preg_match('#^\s*(?:[^<]*<[^>]+>|[^@\s]+@[^.]+\.[^,;]+)\s*$#'$email)) PP_error('invalidRecipient');
            }
            
            
$nb_recipients count($_POST['to']);
            
            
$_POST['to'] = implode(', '$_POST['to']);
        }
    }
    
    if(isset(
$_POST['from']) && $_POST['from']) $from PP_htmlspecialchars($_POST['from']);
    else 
$from PP_msg('anonymous');
    
    
$zerobinServer = (isset($_POST['zerobinServer']) ? $_POST['zerobinServer']:$_SERVER['SCRIPT_URI']);
    
    
$data = array('data''expire''burnafterreading''opendiscussion''syntaxcoloring');
    
    
$postdata = array();
    foreach(
$data AS $k) if(isset($_POST[$k])) $postdata[$k] = $_POST[$k];
    
    if(isset(
$postdata['data'])) $postdata['data'] = str_replace('\\'''$postdata['data']);
    
    
$opts = array('http' =>
        array(
            
'method'  => 'POST',
            
'header'  => 'Content-type: application/x-www-form-urlencoded',
            
'content' => http_build_query($postdata)
        )
    );
    
    
$context stream_context_create($opts);
    
    
set_error_handler(function($errno$errstr){throw new Exception($errno);});
    
    try
    {
        if(!
filter_var($zerobinServerFILTER_VALIDATE_URL)) throw new Exception('BAD URL');
        
        
$result file_get_contents($zerobinServerfalse$context);
    }
    catch(
Exception $e)
    {
        if(
$e->getMessage() == 'BAD URL'PP_error('invalidZerobinAddr');
        else 
PP_error('zerobinServNotAnswering');
        
        
restore_error_handler();
        
        exit();
    }
    
    
restore_error_handler();
    
    
$result json_decode($resulttrue);
    
    if(
$result === null || !isset($result['status']) || ($result['status'] == && !isset($result['message'])) || $result['status'] != || !isset($result['id']) || !is_string($result['id'])) PP_error('pasteNotCreated - zerobinServUnhappy');
    
    if(
$result['status'] == 1) exit(PP_f(PP_error('pasteNotCreated - error'false), PP_htmlspecialchars($result['message'])));
    
    
$expirationTable = array(
        
'5min'    => '+5 min',
        
'10min'    => '+10 min',
        
'1hour'    => '+1 hour',
        
'1day'    => '+1 day',
        
'1week'    => '+7 day',  # To match how Zerobin works
        
'1month'=> '+30 day',  # To match how Zerobin works
        
'1year'    => '+365 day',  # To match how Zerobin works
    
);
    
    
$expiration = new DateTime();
    
    
$expire = (isset($_POST['expire']) ? $_POST['expire']:'1month');
    
    if(
$expire == 'never'$expiration '';
    else
    {
        if(isset(
$expirationTable[$expire])) $expiration->modify($expirationTable[$expire]);
        else 
$expiration->modify($expirationTable['1month']);
        
        
$date $expiration;
        
        
$expiration PP_f(PP_msg('willExpireOn'), str_replace($months['en'], $months['fr'], $date->format($PP_config['dateFormat'][$PP_config['lang']]['long'])));
        
$expiration_abbr PP_f(PP_msg('willExpireOn - short'), $date->format($PP_config['dateFormat'][$PP_config['lang']]['short']));
        
        
$expiration .= '.<br />';
    }
    
    if(isset(
$_POST['burnafterreading']) && $_POST['burnafterreading'] == '1'$burnafterreading PP_msg('burnafterreading');
    else 
$burnafterreading '';
    
    
$link PP_htmlspecialchars($zerobinServer).'?'.PP_htmlspecialchars($result['id']).'#'.PP_htmlspecialchars($_POST['randomkey']);

    
$reply = ($from == PP_msg('anonymous') || strpos($from'@') === false '':PP_f(PP_msg('replyTo'), array('email' => PP_htmlspecialchars($from))));
    
    
$mailContent PP_f(PP_msg('mailContent'), compact('from''link''expiration''burnafterreading''reply'));
    
    
# Used this technic to get the title to be displayed in UTF-8 as well: http://bitprison.net/php_mail_utf-8_subject_and_message
    
if(!mail($_POST['to'],
        
'=?UTF-8?B?'.base64_encode(PP_msg('secured-message').' '.($from == PP_msg('anonymous') ? PP_msg('anonymous'):PP_msg('from').PP_htmlspecialchars($from)).' #'.substr(PP_htmlspecialchars($result['id']), 05).($expiration ' ('.$expiration_abbr.')':'')).'?=',
        
$mailContent,
        
"From: PickyPaste<no-reply@".(preg_replace('#^www\.#'''$_SERVER['SERVER_NAME'])).">\r\n".
        
"Content-type: text/html; charset=utf-8".
        (
filter_var($fromFILTER_VALIDATE_EMAIL) ? "\r\nReply-To: ".$from:'')))
    {
        exit(
PP_f(PP_error('emailDeliveryFailed'false), compact('link'))); 
    }
    
    echo 
'<html>',
            
'<head>',
                
'<title>PickyPaste</title>',
                
'<link type="text/css" rel="stylesheet" href="css/pickypaste.css" />',
                
'<meta charset="UTF-8">',
                
'<meta http-equiv="content-type" content="text/html; charset=utf-8" />',
                
# Note: Ne fonctionne pas sous Firefox.
                # Note 2: À réfléchir, idéalement peut-être ne pas fermer afin de pouvoir donner le lien de suppression du paste (ou l'envoyer à l'email de l'envoyeur ?)
                # '<script type="text/javascript">window.close();</script>', # TODO: Mettre un timer de 5sec # Mmh, tiens, j'ai plus revérifier si ça fonctionne ça ou pas, t'façon si je close automatiquement l'onglet, j'peux pas donner le lien qui permet de supprimer le paste ... (à moins de faire un truc dans le genre 0bin :) enregistrer les pastes localement ^^ ça pourrait être sympa :D If you know what I mean
            
'</head>',
            
'<body>',
                
PP_msg('email-sent-with-success');
            
            if(
$burnafterreading) echo '<del>';
            
            echo 
'(<a href="',PP_htmlspecialchars($link),'">',PP_msg('directLink'),'</a>)';
            
            if(
$burnafterreading) echo '</del> -> <em>',PP_msg('directLinkInhibited'),'</em>';
                
            echo 
'<br />',
                
                (isset(
$result['deletetoken']) ? '(<a href="'.PP_htmlspecialchars($zerobinServer).'?pasteid='.$result['id'].'&deletetoken='.$result['deletetoken'].'">'.PP_msg('deleteLink').'</a>)':''),
            
'</body>',
        
'</html>';
    
    exit;
}
?>
<!DOCTYPE html>
<html lang="fr">
    <head>
        <title>PickyPaste</title>
        <link type="text/css" rel="stylesheet" href="css/pickypaste.css" />
        <meta charset="UTF-8">
        <script src="js/sjcl.js"></script>
        <script src="js/base64.js"></script>
        <script src="js/rawdeflate.js"></script>
        <script src="js/pickypaste.js"></script>
    </head>
    <body>
        <h1 id="title">
            <!-- Pour les icones pour changer de langue, j'avais pensé à ça -->
            <!-- (Bon je mettrais le CSS où il faut après :p) -->
            <!-- (Et j'embedderais les images qu'il faut avec l'archive ^^) -->
            <!-- Hmm par contre, pour changer de langue, … j'peux pas vraiment faire une requête AJAX, right ??  -->
            <!-- Du coup, ce que je pourrais faire c'est au moins un ptit popup js de confirmation qu'on est prêt à quitter la page :) -->
            <!-- Car je pourrais même pas dire d'envoyer le formulaire (sans le valider et le reremplir auto pour l'user) vu que le message doit être crypté AVANT de quitter le nav, see ? -->
            
            <!-- EDIT: À moins de chiffrer les données et rediriger vers la même page avec la demande de la nouvelle langue + la clé de décryption via l'ancre (donc jamais envoyé au site non plus) puis déchiffrer à la réception x) compliqué à implémenter mais ça serait plus KISS pour l'user tout en respectant sa vie privée

            -> Le serveur "mail" reçoit la clé lui, n'oublions pas :/ (fck)
            
            -->
            
            <!-- Note: j'évitais de mixer l'HTML et le PHP car je pensais que ça faciliterait la confection de la version offline :) Mais je pense (espère) que pour la version offline un simple Ctrl+S pourrait "peut-être" fonctionner -->
            
            <!-- -> Le serveur "mail" reçoit la clé lui, n'oublions pas :/ (fck) -->
            
            <script type="text/javascript">
                var texts = {
                    'fr': 'Attention, vous allez fermer la page actuelle avec un message entamé\n\nÊtes-vous sûr ?',
                    'en': 'Caution, you\'re going to shut the current page with a started message.\n\nAre you sure?'
                }
                
                function warn(lang)
                {
                    if(document.getElementById('message').value  != '')
                    {
                        var msg = texts[lang];
                        
                        if(lang != 'en') msg += '\n\n--------------\n\n' + texts["en"];

                        return confirm(msg);
                    }
                    
                    return true;
                }

                function toggleSourceCode(){
                    document.getElementById("sourceOn").classList.toggle('hide');
                    document.getElementById("sourceOff").classList.toggle('hide');
                    
                    window.location = '#sourceOn';
                    
                    return false;
                }  
            </script>
            
            <span id="langBar">
                <a href="?set_lang=fr" onclick="return warn('fr')" style="text-decoration: none"><!-- My webserver seemed to absolutely refuse to send the updated version of the CSS, so here, some inline CSS. -->
                    <img alt="[fr]" src="img/fr.png" title="French" width="16px" height="11px" />
                </a> 
                <a href="?set_lang=en" onclick="return warn('en')">
                    <img alt="[en]" src="img/en.png" title="English" width="16px" hieght="11px" />
                </a>
            </span>
            <span class="title_P">P</span>icky<span class="title_P">P</span>aste 
            <span id="subtitle">For Picky People: Robust (uses <a href="http://sebsauvage.net/paste/">Zerobin</a>) AND easy-to-use</span>
        </h1>
        
        <noscript>
            <strong style="color: red">
                /!\ Your JavaScript seems disabled, DON'T USE THIS APPLICATION. It's not secured without JavaScript /!\<br />
                /!\ Votre JavaScript semble désactivé, N'UTILISEZ PAS CETTE APPLICATION. Ce n'est pas sécurisé sans JavaScript /!\
            </strong>
            <div> </div>
        </noscript>
        
        <form action="javascript:;" id="ppForm" method="post" onSubmit="return prepareForm()">
            <label for="f_to"><?php echo PP_msg('recipient'); ?></label> <input autofocus id="f_to" name="to"  placeholder="<?php echo PP_msg('email_address'); ?>" required type="text" value="<?php echo PP_htmlspecialchars(isset($_GET['mail']) ? $_GET['mail']:''); ?>" /><br /><!--  Changed type="email" to type="text" to allow Olissea plugin. -->
            <label for="f_from"><?php echo PP_msg('your_email'); ?></label> <input id="f_from" name="from" placeholder="<?php echo PP_msg('optional'); ?>" type="text" /> (<?php echo PP_msg('answerable'); ?>)<br />
            <label for="pasteExpiration"><?php echo PP_msg('expiration'); ?></label> <select id="pasteExpiration" name="expire" required>
                <option value="5min"><?php echo $PP_config['expiration'][$PP_config['lang']]['5min']; ?></option>
                <option value="10min"><?php echo $PP_config['expiration'][$PP_config['lang']]['10min']; ?></option>
                <option value="1hour"><?php echo $PP_config['expiration'][$PP_config['lang']]['1hour']; ?></option>
                <option value="1day"><?php echo $PP_config['expiration'][$PP_config['lang']]['1day']; ?></option>
                <option value="1week"><?php echo $PP_config['expiration'][$PP_config['lang']]['1week']; ?></option>
                <option value="1month" selected="selected"><?php echo $PP_config['expiration'][$PP_config['lang']]['1month']; ?></option>
                <option value="1year"><?php echo $PP_config['expiration'][$PP_config['lang']]['1year']; ?></option>
                <option value="never"><?php echo $PP_config['expiration'][$PP_config['lang']]['never']; ?></option>
            </select><br />
            <label for="burnafterreading" title="Burn After Reading"><?php echo PP_msg('burn-after-reading');?></label> <input checked="checked" id="burnafterreading" name="burnafterreading" type="checkbox" value="1" /> (<?php echo PP_msg('bar-explanations'); ?>)<br />
            
            <div id="status"></div>
            
            <span class="label"><?php echo PP_msg('your_message'); ?></span><br />
            <textarea id="message" cols="70" rows="20" required></textarea><br /><br />
            <input type="submit" />
            
<?php echo PP_msg('save-even-more-time'); ?>
            
            <!-- <h3>Gagnez encore plus de sécurité</h3>
            
            Enregistrer simplement cette page ( <span class="key">Ctrl</span>+<span class="key">S</span> ) sur votre ordinateur et utilisez la plutôt que celle-ci.<br /><br />
            
            Cela permettra de vous protéger au cas où notre site serait corrompu, cela vous protègera également contre les attaques de types Man-In-The-Middle. -->
            
            <hr />
            
<?php echo PP_f(PP_msg('advanced-parameters'), array('emailServer' => PP_htmlspecialchars($_SERVER['SCRIPT_URI']))); ?>
        </form>
<?php

echo     '<div id="sourceOff"',(isset($_GET['showSource']) ? ' class="hide"':''),'>',
            
'<a href="?showSource#source" onclick="return toggleSourceCode()">',PP_msg('show-source'),'</a>',
        
'</div>',
        
'<div id="sourceOn"',(isset($_GET['showSource']) ? '':' class="hide"'),'>',
            
'<a href="?" onclick="return toggleSourceCode()">',PP_msg('hide-source'),'</a>',
            
'<div id="sourcecode">';

highlight_file(__FILE__);

echo         
'</div>',
        
'</div>';
?>
        <script type="text/javascript">
            document.getElementById('message').value = window.location.hash.substring(1) // Get it then…
            if(window.location.hash) window.location.hash = '' // Clean it (to avoid that someone share his private URL accidently when sharing the PickyPaste address)
        </script>
    </body>
</html>