Les archives de FluxBB.fr
Vous n'êtes pas identifié(e).
Auteur : Winjet
Cette mod permet de rajouter un .htaccess lorsque vous tenterez d'aller dans la partie Administration de votre forum.
Elle peut ainsi protéger votre forum même si une personne malveillante a réussi à s'identifier sur votre compte Administrateur.
Celle-ci ne pourra pas accéder à la partie Administration.
Notes : # Le mot de passe doit être crypté au format MD5.
# Pour plus de sécurité, mettez un identifiant et un mot de passe différent
de celui de votre compte.
# Le nom du fichier proposé qui contient l'identifiant et le mot de passe
n'est qu'un exemple, vous pouvez sans aucun problème, changer son
nom et l'endroit où il se trouve sur votre serveur...
Pensez tout de même a effectuer les changements dans le fichier
common_admin.php
Winjet
Dernière modification par Winjet (04-11-2008 10:30:02)
Hors ligne
ça pourrai être utile pour plus de sécurité en effet.
Mais ça ne poserai pas un problème si quelqu'un télécharge le fichier admin_mdp.pwd ?
PS: Je crois que c'est préférable de hashé le mot de passe en sha1.
Dernière modification par gogogo3 (21-10-2008 21:40:18)
Hors ligne
Bonjour,
Il est tout-à-fait possible de :
- Utiliser un autre nom de fichier comme my5ty8pd7.pwd
- Mettre ce fichier autre part qu'à la racine, par exemple dans un dossier nommé y87rh4E5Ty
Bien sûr, il faudra modifier le script pour lui indiquer le bon chemin du fichier :
/y87rh4E5Ty/my5ty8pd7.pwd
Ce n'est pas parce que l'erreur se propage qu'elle devient vérité. Gandhi
Sont différents : ça et sa - est et ait - à et a - ce et se - mes et mais ou met - été et était - c'est et ces - ce-si et ceci
La vie sans musique est tout simplement une erreur, une fatigue, un exil. Friedrich Nietzsche
Hors ligne
Bonjour,
Otomatic a raison, on peut changer le nom du fichier et mettre ce qu'on veut à la place et il n'est pas obligé d'être à la racine du serveur non plus.
Le nom du fichier que j'ai mis dedans, c'était juste pour la démonstration.
Hors ligne
Otomatic a raison, on peut changer le nom du fichier et mettre ce qu'on veut à la place et il n'est pas obligé d'être à la racine du serveur non plus.
Le nom du fichier que j'ai mis dedans, c'était juste pour la démonstration.
Ok, merci pour cette mod Winjet 
PS: vaut mieux le préciser dans le readme.txt pour les gens qui ne feraient pas attention (tout comme moi).
Dernière modification par gogogo3 (22-10-2008 00:44:36)
Hors ligne
J'ai fais la modification seulement dans la discussion. Je ne vais pas de nouveau uploader l'archive juste pour cà. Mais, certainement dans la prochaine version... 
Dernière modification par Winjet (21-10-2008 22:02:03)
Hors ligne
quesqui pourrait changer dans la prochaine version ? 
Hors ligne
salut
j'ai bien tout suivi et j'ai tou fais mais j'ai une erreur qui est la suivante
Fatal error: Call to undefined function: authenticate() in ---------------------------------Forum/include/common_admin.php on line 36
j'ai la version 1.2.20
merci de m'aidé!
Dernière modification par YEX (02-11-2008 02:04:53)
Hors ligne
Salut Yex,
Il s'agit d'un bug, la mod faisait appel à une fonction qui n'existait pas.
J'ai corrigé le bug et uploadé de nouveau la mod.
++
Hors ligne
salut merci pour ta réponse
toujours la même erreur:
Parse error: syntax error, unexpected '}' in /............................./htdocs/include/common_admin.php on line 37
a la ligne 37 ya un " } "
Hors ligne
Salut,
Non là je suis sur qu'il n'y a pas d'erreur...
Au vue de l'erreur que tu as mis, ton navigateur a rencontré un caractère inattendu notamment celui-ci '}'.
Regarde bien dans le code que tu as rajouté dans common_admin.php s'il y a le même nombre de '{' et de '}'.
Tu peux aussi mettre le code. Mais là, je suis certain tu as un '}' qui traîne et qui n'a rien à faire là.
Ps : J'ai quand même réinstallé l'archive sur mon site pour voir, je n'obtiens aucune erreur...
++
Winjet
Hors ligne
C'est sa mon code
-------------------------------------------------------------------------------
// Make sure no one attempts to run this script "directly"
if (!defined('PUN'))
exit;
function authentification() {
header('WWW-Authenticate: Basic realm="punBB Administration"');
header('HTTP/1.0 401 Unauthorized');
echo "Vous devez entrer un identifiant et un mot de passe valide pour accéder à cette partie\n";
exit;
}
if (!isset($_SERVER['PHP_AUTH_USER'])) {
authentification()
}
else {
$auth = file("./admin_mdp.pwd");
list($user, $password) = split(":", trim($auth[0]));
if($_SERVER['PHP_AUTH_USER'] != $user || md5($_SERVER['PHP_AUTH_PW']) != $password) {
authentification();
}
}-------------------------------------------------------------------------------------------
le navigateur me di
Parse error: syntax error, unexpected '}' in /homepages/../............./htdocs/include/common_admin.php on line 37
Hors ligne
Salut,
C'est bien ce que je disais, supprime le dernier '}' indiqué dans le code ci-dessus que tu as mis.
En résumé, voilà le code correct
// Make sure no one attempts to run this script "directly"
if (!defined('PUN'))
exit;
function authentification() {
header('WWW-Authenticate: Basic realm="punBB Administration"');
header('HTTP/1.0 401 Unauthorized');
echo "Vous devez entrer un identifiant et un mot de passe valide pour accéder à cette partie\n";
exit;
}
if (!isset($_SERVER['PHP_AUTH_USER'])) {
authentification()
}
else {
$auth = file("./admin_mdp.pwd");
list($user, $password) = split(":", trim($auth[0]));
if($_SERVER['PHP_AUTH_USER'] != $user || md5($_SERVER['PHP_AUTH_PW']) != $password) {
authentification();
}++
Winjet
Hors ligne
Merci beaucoup pour tes réponse mais je enlevé le "}" et j'ai toujour la meme erreur donc j'arrête de tempêté !
merci encore!
Hors ligne
Bonsoir,
il manque un ";" dans cette partie :
if (!isset($_SERVER['PHP_AUTH_USER'])) {
authentification()
}comme ceci :
if (!isset($_SERVER['PHP_AUTH_USER'])) {
authentification();
}++
[img]http://blog.ludikreation.com/wp-content/ban_fofo_ludikreation_400.png[/img]
[img]http://www.chaodisiaque.com/files/123084651278515/1230938952-41022.png[/img]
[img]http://www.chaodisiaque.com/files/123084651278515/1230938980-40906.png[/img]
[img]http://blog.ludikreation.com/wp-content/themes/mag-style/images/ico.png[/img] MyReferencing
Hors ligne
bonjour,
voila un code pour crypter vos mot de passe.
créer un fichier "crypter.php" dans ce fichier, mettez le code :
<title>:: Cryptage de mot de passe ::..</title>
<p>
<?php
if (isset($_POST['login']) AND isset($_POST['pass']))
{
$login = $_POST['login'];
$pass_crypte = crypt($_POST['pass']); // On crypte le mot de passe
echo 'Ligne à copier dans le admin_mdp.pwd :<br />' . $login . ':' . $pass_crypte;
}
else // On n'a pas encore rempli le formulaire
{
?>
</p>
<p>Entrez votre login et votre mot de passe pour le crypter.</p>
<form method="post">
<p>
Login : <input type="text" name="login">
Mot de passe : <input type="text" name="pass">
<input type="submit" value="Crypter !">
</p>
</form>
<?php
}
?>Hors ligne
Bonsoir,
plusieur test la meme erreur que chez YEX 
je confirm ça bug 
Hors ligne
Salut Smed79,
Peux tu être un plus précis sur le bug car j'ai installé cette mod sur un forum après ton message et je n'ai aucun soucis tout fonctionne parfaitement...
Winjet
Hors ligne
Moi je fais tout bien et et je rentre le login choisi ainsi que le mdp mais a chaque fois il me dit de me re indentifier
Hors ligne
Bonjour Winijet
Je t'ai envoyé un mail avant de voir ce topic
Ton mod n'a pas l'air de fonctionner sur ovh
Ca renvoie bien la fenetre pour s'identifier mais login et mdp ne fonctionnent pas
je l'ai installé chez un autre hebergeur sans probleme donc je pense que ca vient d'une "spécificité ovh" je précise que c'est sur un mutu
http://forum.ovh.com/showthread.php?t=3275
j'ai vu ce topic sur le forum ovh apparememnt authentification nest pas possible ca viendrait de la ?
Hors ligne
ça pourrai être utile pour plus de sécurité en effet.
Mais ça ne poserai pas un problème si quelqu'un télécharge le fichier admin_mdp.pwd ?
PS: Je crois que c'est préférable de hashé le mot de passe en sha1.
vous pouvez faire chmod 111 votre fichier et il ne sera pas telecharger
Hors ligne
vous pouvez aussi copier tous les fichiers de l'administration dans un repertoir et vous mettez un parfeu en utilisent .htaccess et .htpasswds .en modifient dant tous les fichier define('PUN_ROOT', './'); par define('PUN_ROOT', '../');
code .htaccess
AuthType Basic
AuthName "stop"
AuthUserFile "/home/user/.htpasswds"
require valid-usercode .htpasswds
utilisateur:password_crypter_en_DES_ou_MD5pour crypter ou decypter md5
http://md5.arab4security.org/
Dernière modification par the_a_n_g (14-04-2009 23:00:26)
Hors ligne
Salut tous le monde 
Petite question je suis sur un serveur linux avec mon forum et le mod sais bien installé impecable se pendan j'aimerai méttre un htacces sur le forum en lui méme mais avec un simple htaccess je n'y arrive pas
toujours la méme érreure
500 forbiden 
Pourai ton adapté se mod la pour tous le forum et si oui comment ?
Merci mille fois pour votre aide 
Hors ligne
chez moi tout a l'air de marcher mais lorsque je rentre le mot de passe il ré-affiche la fenêtre et je ne peu pas accéder a la page.J'utilise 1.2.21.
common_admin.php (dans include)
<?php
/***********************************************************************
Copyright (C) 2002-2005 Rickard Andersson (rickard@punbb.org)
This file is part of PunBB.
PunBB is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
PunBB is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston,
MA 02111-1307 USA
************************************************************************/
// Make sure no one attempts to run this script "directly"
if (!defined('PUN'))
exit;
function authentification() {
header('WWW-Authenticate: Basic realm="punBB Administration"');
header('HTTP/1.0 401 Unauthorized');
echo "Vous devez entrer un identifiant et un mot de passe valide pour accéder à cette partie\n";
exit;
}
if (!isset($_SERVER['PHP_AUTH_USER'])) {
authentification();
}
else {
$auth = file("./admin_mdp.pwd");
list($user, $password) = split(":", trim($auth[0]));
if($_SERVER['PHP_AUTH_USER'] != $user || md5($_SERVER['PHP_AUTH_PW']) != $password) {
authentification();
}
}
//
// Display the admin navigation menu
//
function generate_admin_menu($page = '')
{
global $pun_config, $pun_user;
$is_admin = $pun_user['g_id'] == PUN_ADMIN ? true : false;
?>
<div id="adminconsole" class="block2col">
<div id="adminmenu" class="blockmenu">
<h2><span>Menu <?php echo ($is_admin) ? 'Admin' : 'Modérateur' ?></span></h2>
<div class="box">
<div class="inbox">
<ul>
<li<?php if ($page == 'index') echo ' class="isactive"'; ?>><a href="admin_index.php">Index</a></li>
<?php if ($is_admin): ?> <li<?php if ($page == 'categories') echo ' class="isactive"'; ?>><a href="admin_categories.php">Catégories</a></li>
<?php endif; ?><?php if ($is_admin): ?> <li<?php if ($page == 'forums') echo ' class="isactive"'; ?>><a href="admin_forums.php">Forums</a></li>
<?php endif; ?> <li<?php if ($page == 'users') echo ' class="isactive"'; ?>><a href="admin_users.php">Utilisateurs</a></li>
<?php if ($is_admin): ?> <li<?php if ($page == 'groups') echo ' class="isactive"'; ?>><a href="admin_groups.php">Groupes</a></li>
<?php endif; ?><?php if ($is_admin): ?> <li<?php if ($page == 'options') echo ' class="isactive"'; ?>><a href="admin_options.php">Options</a></li>
<?php endif; ?><?php if ($is_admin): ?> <li<?php if ($page == 'permissions') echo ' class="isactive"'; ?>><a href="admin_permissions.php">Permissions</a></li>
<?php endif; ?> <li<?php if ($page == 'censoring') echo ' class="isactive"'; ?>><a href="admin_censoring.php">Mots censurés</a></li>
<?php if ($is_admin): ?> <li<?php if ($page == 'ranks') echo ' class="isactive"'; ?>><a href="admin_ranks.php">Rangs utilisateurs</a></li>
<?php endif; ?><?php if ($is_admin || $pun_config['p_mod_ban_users'] == '1'): ?> <li<?php if ($page == 'bans') echo ' class="isactive"'; ?>><a href="admin_bans.php">Bannissements</a></li>
<?php endif; ?><?php if ($is_admin): ?> <li<?php if ($page == 'prune') echo ' class="isactive"'; ?>><a href="admin_prune.php">Élaguage</a></li>
<?php endif; ?><?php if ($is_admin): ?> <li<?php if ($page == 'maintenance') echo ' class="isactive"'; ?>><a href="admin_maintenance.php">Maintenance</a></li>
<?php endif; ?> <li<?php if ($page == 'reports') echo ' class="isactive"'; ?>><a href="admin_reports.php">Signalements</a></li>
</ul>
</div>
</div>
<?php
// See if there are any plugins
$plugins = array();
$d = dir(PUN_ROOT.'plugins');
while (($entry = $d->read()) !== false)
{
$prefix = substr($entry, 0, strpos($entry, '_'));
$suffix = substr($entry, strlen($entry) - 4);
if ($suffix == '.php' && ((!$is_admin && $prefix == 'AMP') || ($is_admin && ($prefix == 'AP' || $prefix == 'AMP'))))
$plugins[] = array(substr(substr($entry, strpos($entry, '_') + 1), 0, -4), $entry);
}
$d->close();
// Did we find any plugins?
if (!empty($plugins))
{
?>
<h2 class="block2"><span>Plugins</span></h2>
<div class="box">
<div class="inbox">
<ul>
<?php
while (list(, $cur_plugin) = @each($plugins))
echo "\t\t\t\t\t".'<li'.(($page == $cur_plugin[1]) ? ' class="isactive"' : '').'><a href="admin_loader.php?plugin='.$cur_plugin[1].'">'.str_replace('_', ' ', $cur_plugin[0]).'</a></li>'."\n";
?>
</ul>
</div>
</div>
<?php
}
?>
</div>
<?php
}
//
// Delete topics from $forum_id that are "older than" $prune_date (if $prune_sticky is 1, sticky topics will also be deleted)
//
function prune($forum_id, $prune_sticky, $prune_date)
{
global $db;
$extra_sql = ($prune_date != -1) ? ' AND last_post<'.$prune_date : '';
if (!$prune_sticky)
$extra_sql .= ' AND sticky=\'0\'';
// Fetch topics to prune
$result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE forum_id='.$forum_id.$extra_sql, true) or error('Unable to fetch topics', __FILE__, __LINE__, $db->error());
$topic_ids = '';
while ($row = $db->fetch_row($result))
$topic_ids .= (($topic_ids != '') ? ',' : '').$row[0];
if ($topic_ids != '')
{
// Fetch posts to prune
$result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id IN('.$topic_ids.')', true) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
$post_ids = '';
while ($row = $db->fetch_row($result))
$post_ids .= (($post_ids != '') ? ',' : '').$row[0];
if ($post_ids != '')
{
// Delete topics
$db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.$topic_ids.')') or error('Unable to prune topics', __FILE__, __LINE__, $db->error());
// Delete subscriptions
$db->query('DELETE FROM '.$db->prefix.'subscriptions WHERE topic_id IN('.$topic_ids.')') or error('Unable to prune subscriptions', __FILE__, __LINE__, $db->error());
// Delete posts
$db->query('DELETE FROM '.$db->prefix.'posts WHERE id IN('.$post_ids.')') or error('Unable to prune posts', __FILE__, __LINE__, $db->error());
// We removed a bunch of posts, so now we have to update the search index
require_once PUN_ROOT.'include/search_idx.php';
strip_search_index($post_ids);
}
}
}admin_mdp.pwd(a la racine)
utilisateur:mot_de_passe_crypter_en_md5merci de votre aide
Hors ligne
chez moi tout a l'air de marcher mais lorsque je rentre le mot de passe il ré-affiche la fenêtre et je ne peu pas accéder a la page.J'utilise 1.2.21.
common_admin.php (dans include)
<?php /*********************************************************************** Copyright (C) 2002-2005 Rickard Andersson (rickard@punbb.org) This file is part of PunBB. PunBB is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. PunBB is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ // Make sure no one attempts to run this script "directly" if (!defined('PUN')) exit; function authentification() { header('WWW-Authenticate: Basic realm="punBB Administration"'); header('HTTP/1.0 401 Unauthorized'); echo "Vous devez entrer un identifiant et un mot de passe valide pour accéder à cette partie\n"; exit; } if (!isset($_SERVER['PHP_AUTH_USER'])) { authentification(); } else { $auth = file("./admin_mdp.pwd"); list($user, $password) = split(":", trim($auth[0])); if($_SERVER['PHP_AUTH_USER'] != $user || md5($_SERVER['PHP_AUTH_PW']) != $password) { authentification(); } } // // Display the admin navigation menu // function generate_admin_menu($page = '') { global $pun_config, $pun_user; $is_admin = $pun_user['g_id'] == PUN_ADMIN ? true : false; ?> <div id="adminconsole" class="block2col"> <div id="adminmenu" class="blockmenu"> <h2><span>Menu <?php echo ($is_admin) ? 'Admin' : 'Modérateur' ?></span></h2> <div class="box"> <div class="inbox"> <ul> <li<?php if ($page == 'index') echo ' class="isactive"'; ?>><a href="admin_index.php">Index</a></li> <?php if ($is_admin): ?> <li<?php if ($page == 'categories') echo ' class="isactive"'; ?>><a href="admin_categories.php">Catégories</a></li> <?php endif; ?><?php if ($is_admin): ?> <li<?php if ($page == 'forums') echo ' class="isactive"'; ?>><a href="admin_forums.php">Forums</a></li> <?php endif; ?> <li<?php if ($page == 'users') echo ' class="isactive"'; ?>><a href="admin_users.php">Utilisateurs</a></li> <?php if ($is_admin): ?> <li<?php if ($page == 'groups') echo ' class="isactive"'; ?>><a href="admin_groups.php">Groupes</a></li> <?php endif; ?><?php if ($is_admin): ?> <li<?php if ($page == 'options') echo ' class="isactive"'; ?>><a href="admin_options.php">Options</a></li> <?php endif; ?><?php if ($is_admin): ?> <li<?php if ($page == 'permissions') echo ' class="isactive"'; ?>><a href="admin_permissions.php">Permissions</a></li> <?php endif; ?> <li<?php if ($page == 'censoring') echo ' class="isactive"'; ?>><a href="admin_censoring.php">Mots censurés</a></li> <?php if ($is_admin): ?> <li<?php if ($page == 'ranks') echo ' class="isactive"'; ?>><a href="admin_ranks.php">Rangs utilisateurs</a></li> <?php endif; ?><?php if ($is_admin || $pun_config['p_mod_ban_users'] == '1'): ?> <li<?php if ($page == 'bans') echo ' class="isactive"'; ?>><a href="admin_bans.php">Bannissements</a></li> <?php endif; ?><?php if ($is_admin): ?> <li<?php if ($page == 'prune') echo ' class="isactive"'; ?>><a href="admin_prune.php">Élaguage</a></li> <?php endif; ?><?php if ($is_admin): ?> <li<?php if ($page == 'maintenance') echo ' class="isactive"'; ?>><a href="admin_maintenance.php">Maintenance</a></li> <?php endif; ?> <li<?php if ($page == 'reports') echo ' class="isactive"'; ?>><a href="admin_reports.php">Signalements</a></li> </ul> </div> </div> <?php // See if there are any plugins $plugins = array(); $d = dir(PUN_ROOT.'plugins'); while (($entry = $d->read()) !== false) { $prefix = substr($entry, 0, strpos($entry, '_')); $suffix = substr($entry, strlen($entry) - 4); if ($suffix == '.php' && ((!$is_admin && $prefix == 'AMP') || ($is_admin && ($prefix == 'AP' || $prefix == 'AMP')))) $plugins[] = array(substr(substr($entry, strpos($entry, '_') + 1), 0, -4), $entry); } $d->close(); // Did we find any plugins? if (!empty($plugins)) { ?> <h2 class="block2"><span>Plugins</span></h2> <div class="box"> <div class="inbox"> <ul> <?php while (list(, $cur_plugin) = @each($plugins)) echo "\t\t\t\t\t".'<li'.(($page == $cur_plugin[1]) ? ' class="isactive"' : '').'><a href="admin_loader.php?plugin='.$cur_plugin[1].'">'.str_replace('_', ' ', $cur_plugin[0]).'</a></li>'."\n"; ?> </ul> </div> </div> <?php } ?> </div> <?php } // // Delete topics from $forum_id that are "older than" $prune_date (if $prune_sticky is 1, sticky topics will also be deleted) // function prune($forum_id, $prune_sticky, $prune_date) { global $db; $extra_sql = ($prune_date != -1) ? ' AND last_post<'.$prune_date : ''; if (!$prune_sticky) $extra_sql .= ' AND sticky=\'0\''; // Fetch topics to prune $result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE forum_id='.$forum_id.$extra_sql, true) or error('Unable to fetch topics', __FILE__, __LINE__, $db->error()); $topic_ids = ''; while ($row = $db->fetch_row($result)) $topic_ids .= (($topic_ids != '') ? ',' : '').$row[0]; if ($topic_ids != '') { // Fetch posts to prune $result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id IN('.$topic_ids.')', true) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error()); $post_ids = ''; while ($row = $db->fetch_row($result)) $post_ids .= (($post_ids != '') ? ',' : '').$row[0]; if ($post_ids != '') { // Delete topics $db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.$topic_ids.')') or error('Unable to prune topics', __FILE__, __LINE__, $db->error()); // Delete subscriptions $db->query('DELETE FROM '.$db->prefix.'subscriptions WHERE topic_id IN('.$topic_ids.')') or error('Unable to prune subscriptions', __FILE__, __LINE__, $db->error()); // Delete posts $db->query('DELETE FROM '.$db->prefix.'posts WHERE id IN('.$post_ids.')') or error('Unable to prune posts', __FILE__, __LINE__, $db->error()); // We removed a bunch of posts, so now we have to update the search index require_once PUN_ROOT.'include/search_idx.php'; strip_search_index($post_ids); } } }admin_mdp.pwd(a la racine)
utilisateur:mot_de_passe_crypter_en_md5merci de votre aide
meme erreur
Hors ligne