Mots-clé : modules apache

Serveur Apache et mod_rewrite : mémo / astuces / hints / tips

Voici mes notes de règles de ré-écriture. Principalement des expressions régulières dont j’ai souvent besoin et que je n’ai pas en tête :

Problème :
Apache ajoute automatiquement des slashes / si c’est un répertoire.
Solution :
DirectorySlash Off
Risques de sécurite si ça n’est pas bien géré.

Problème :
Chercher une variable dans les paramètres _GET, y compris en plein milieu (les solutions en général ne sont valides que si la variable est au début dans le _GET)
Solution :
RewriteCond %{QUERY_STRING} (^|&)nomvariable=valeur(&|$)

Problème :
Prendre en compte toute une séquence de caractères sauf le slash
Solution :
RewriteRule ^/([^/]+)/?$ page.php?page=$1 [L]
ou encore prendre tout sauf le slash et le point :
RewriteRule ^/([^/\.]+)/?$ page.php?page=$1 [L]

Librairie Apr : tutoriel 5

5. Gestion des fichiers

Quand on veut des opérations dans un fichier, il faut en premier lieu appeler apr_file_open(). Voilà la déclaration :

/* extrait de apr_file_io.h */
APR_DECLARE(apr_status_t) apr_file_open(apr_file_t **newf,
    const char *fname, apr_int32_t flag,
    apr_fileperms_t perm, apr_pool_t *pool);

Le type du premier argument est apr_file_t**, qui est un argument résultat. Plus précisément, il stocke l’objet apr_file_nouvellement crée lors de l’appel à apr_file_open(). Le second argument est le répertoire + nom du fichier. Le troisième est une composition de flags, définis dans apr_file_io.h. Le quatrième argument est le type de permission qu’on accorde sur ce fichier, mais cela ne fonctionne que lors de la création de fichier. Les flags sont définis dans apr_file_info.h. Par exemple, si vous voulez créer un fichier dont les permissions d’accès sont 0600, c-à-d. lecture-écriture uniquement autorisée par le possesseur du fichier, il vous faudra spécifier APR_UREAD|APR_UWRITE. Vous utiliserez le plus couramment APR_OS_DEFAULT comme permission. Le cinquième et dernier argument est le pool mémoire à utiliser. Précisons pour le lecteur occasionnel que le pool mémoire aura été crée auparavant via apr_pool_create().
Après qu’on ait ouvert le fichier, on peut l’utiliser pour d’autres fonctions de la librairie apr. On peut trouver ces fonctions dans apr_file_io.h. Les fonctions les plus basiques sont apr_file_read() et apr_file_write(). Comme vous l’imaginez, apr_file_read() donne la possibilité de lire quelque chose du fichier et apr_file_write() d’écrire quelque chose dedans.Voilà les déclarations :

/* extrait de apr_file_io.h */
APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile,
    void *buf, apr_size_t *nbytes);
APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile,
    const void *buf, apr_size_t *nbytes);

Le troisième argument des deux fonctions est un argument en entrée mais aussi en sortie (= résultat). En entrée, il sert à spécifier la longueur de la valeur, et on a en sortie le retour du nombre d’octets résultant de l’opération. Pour simplifier, apr_file_read() renvoie le nombre d’octets lus, et apr_file_write() renvoie le nombre d’octets écrits. Voici un code d’exemple.

/* pseudo-code expliquant apr_file_write() */
strcpy(outbuf, "123456789");
apr_size_t outlen = strlen(outbuf);
rv = apr_file_write(fp, outbuf, &outlen);
printf("apr_file_write() rv = %d, nb octets = %d\n", rv, outlen);

Dans ce cas, avant d’appeler apr_file_write(), la variable ‘outlen’ vaut 9. En passant &outlen à apr_file_write(), on dit à la librairie qu’il faut écrire 9 octets. Au retour de apr_file_write(), la valeur de ‘outlen’ est remplie de ce qui a été effectivement écrit. Habituellement c’est 9, surtout si c’est un fichier local. En théorie, la valeur pourrait être plus petite (si le disque est plein par exemple).
Il faut toujours appeler apr_file_close() pour fermer le fichier. Il est possible aussi de le fermer automatiquement en détruisant le pool mémoire par lequel il a été crée, mais je préfère fermer mes fichiers de manière explicite. Cela n’engage que moi.

REMARQUE : Il y a quelques incompatibilités entre les différentes versions de la librairie libapr. Le troisième argument de apr_file_open() a comme préfixe APR_FOPEN_ depuis libapr-1.1.0, alors que ce n’était pas le cas avant. Il faut donc utiliser APR_FOPEN_CREATE au lieu de APR_CREATE. Consultez apr_file_io.h pour voir ce qu’il vous faut réellement utiliser. De la même façon, le quatrième argument de apr_file_open() a le préfixe APR_FPROT_ depuis la libapr-1.1.0.

REMARQUE : Il y a un problème de portabilité sur le séparateur des répertoires/noms de fichiers. Unix(POSIX) utilise le slash (‘/’), et les systèmes Microsoft utilisent le backslash (‘\’) en tant que séparateur. Si vous voulez écrire une application qui tourne sur les deux système, je vous conseille de transformer toujours les séparateur en slash (‘/’) car les systèmes Microsoft l’acceptent quand même.

REMARQUE : Faites attention lors du l’utilisation de apr_file_gets(). Faire un appel à cette fonction sans le paramètre APR_BUFFERED dégrade sérieusement les performances de l’opération. Cela s’explique par le fait que apr_file_gets() appelle apr_file_read(), et, sans ce paramètre, il y aura un appel pour chaque octet à lire. Souvenez vous bien qu’il vous faut ouvrir le fichier avec le flag APR_BUFFERED si vous voulez utiliser apr_file_gets().

Je vous recommande d’utiliser toujours APR_BUFFERED sauf dans ces cas :

  • Quand vous « mmap »-ez le fichier (cela génère une erreur du fichier à « mmapp-er ») ;
  • Aucune lecture/écriture (p.ex. un fichier destiné à être uniquement un verrou) ;
  • Vous êtes sûr que vous lisez ou écrivez avec un buffer suffisamment gros.

REMARQUE : Si vous ouvrez un fichier avec le flag APR_BUFFERED et que vous appelez par la suite apr_file_trunc(), n’oubliez pas d’appeler apr_file_flush() avant apr_file_trunc(). Sinon vous allez perdre des informations.

REMARQUE: Si vous ouvrez un fichier avec le flag APR_BUFFERED, et que le fichier est partagé par plusieurs threads, il vous faut obligatoirement préciser APR_XTHREAD. Malheureusement, ce flag a quelques revers de médailles sur les systèmes d’exploitation Windows. Par expérience, je vous conseillerai d’éviter d’utiliser APR_XTHREAD sur les systèmes d’exploitation Windows.

Il est possible d’avoir des informations sur le fichier telles que la date de création, de modification, le possesseur du fichier, les permissions, etc. Ces informations sont dans la structure apr_finfo_t, que vous trouverez décrite dans apr_file_info.h. Il y a deux fonctions que fournit la librairie libapr :

/* extrait de apr_file_io.h */
APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo,
    apr_int32_t wanted, apr_file_t *thefile);
/* extrait de apr_file_info.h */
APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo,
    const char *fname, apr_int32_t wanted, apr_pool_t *pool);

Il faut un objet apr_file_t pour la fonction apr_file_info_get(), et un nom de fichier pour apr_stat(). Si on a déjà ouvert ce fichier et qu’on a donc un objet apr_file_t crée, c’est plus pratique de se servir de apr_file_info_get(). Sinon, il faut appeler apr_stat(). A l’inverse de beaucoup d’autres types, apr_finfo_t est un type complet. Plutôt que d’appeler une fonction de la librairie pour créer l’objet, il faut allouer une structure apr_finfo_t explicitement. Typiquement, c’est alloué sur la pile locale, parce qu’on n’a souvent besoin besoin que d’un seul attribut, comme la taille du fichier, ou sa date de création, et rien de plus. Notez que quelquefois des informations telles que apr_finfo_t::fname, sont allouées dans le pool mémoire que vous passez en paramètre. Faites attention, c’est parfois la cause de problèmes de fuites mémoire. Regardez l’exemple à finfo-sample.c pour bien comprendre le fonctionnement.

Il y a quelques fonctions de gestion des fichiers qui fonctionnent en se basant sur un nom de fichiers. Par exemple, apr_file_remove() et apr_file_copy(). Vous lez trouverez dans apr_file_io.h et apr_file_info.h.

REMARQUE : Quelques fonctions de la librairie aprlib prennent en paramètre des argument initialisés par des flags que l’on peut combiner (« bit-wised flags ») pour récupérer les attributs des fichiers. Ces fonctions sont apr_dir_read(), apr_stat(), apr_lstat(), et apr_file_info_get(). Notez que selon la valeur que vous donnez à l’argument, ce dernier peut ne pas exister sur le système d’exploitation, et dans ce cas, renvoyer la valeur d’erreur APR_INCOMPLETE.

Librairie Apr : tutoriels 1 et 2

1. Disponibilité du tutoriel au complet

Regardez le tutoriel au complet, en Anglais, ici.
Vous trouverez tout ce tutoriel séparé en plusieurs pages ici.

2. Un squelette de code pour libapr

Je pense que c’est une bonne idée d’écrire un « squelette de code » au début, lorsque vous commencez à apprendre une nouvelle librairie ou un nouveau framework. Un ‘squelette de code’ est le plus petit morceau de code possible que vous pouvez compiler et exécuter (malgré le fait qu’il ne fasse rien de vraiment utile).
Heureusement, le squelette de code que l’on va faire pour la libapr est beaucoup plus simple que la plupart des frameworks modernes. Voyons apr-skeleton.c. On appelle apr_initialize() pour ouvrir la librairie, et apr_terminate() pour la fermer. C’est tout. Comme vous pouvez l’imaginer, le code ne fait rien du tout.
libapr n’est pas une librairie de type framework. Pour être plus précis, libapr ne vous aide pas du tout à concevoir la structure de votre code programme. Il y a des avantages et des inconvénients. L’avantage le plus important est que l’utilisation de libapr est très facile à utiliser et à intégrer dans du code existant. L’inconvénient principal est que vous devez concevoir la structure complète du code lorsque vous utilisez libapr.
Ici, nous avons tout de même quelques règles et style concernant libapr :

  • la règle de nommage est très simple et claire ;
  • les types de données opaques sont souvent utilisés (p.ex. les types incomplets) ;
  • la plupart des retours de fonctions sont apr_status_t et si besoin est, certaines variables en paramètre sont aussi des arguments résultats ;
  • règles de pool mémoire.

On peut voir les styles et les règles directement par cet exemple :

/* extrait de mp-sample.c */
apr_status_t rv;
apr_pool_t *mp;
rv = apr_pool_create(&mp, NULL);

Voici l’explication du code : ici ne regardez que la notation. Vous voyez le préfixe apr_. Ce dernier indique que le symbole fait partie de l’espace de nommage de la librairie libapr. Le suffixe _t précise que le nom concerné est un type.
apr_pool_t est un type opaque. Cela signifie que la structure de ce type n’est pas publique. En utilisant une terminologie de style OO (Object Oriented), toutes les variables membres sont privées. Vous ne pouvez pas y accéder directement. Vous ne pouvez pas les voir dans les fichiers « .h » non plus. La seule chose que vous puissiez faire pour ce type est d’appeler une API qui l’utilise, telle que apr_foo_bar() par exemple. Plus important encore, vous ne pouvez créer une instance de ce type dynamiquement. Vous êtes obligé de passer par une fonction d’allocation de la librairie. Seule libapr sait comment construire et détruire ce genre d’objets.
Comme vous pouvez le voir, le type de retour de la fonction apr_pool_create() est apr_status_t. apr_status_t est soit un code indiquant un status soir un code d’erreur. apr_status_t is est utilisé couramment en tant que type de valeur de retour par la plupart des fonctions de l’APIs. De la même façon, on peut recevoir des résultats de fonction via des arguments. De tels arguments sont appelés arguments-résultat. Il y a beaucoup d’arguments-résultat dans le monde de la libapr.
En général, si vous voyez le type apr_foo_t, vous verrez des fonctions apr_foo_bar(), qui ont un lien avec le type apr_foo_t. Le code qui suit est un pseudo code courant :

/* pseudo code of libapr. Vérif. des erreur omise */

apr_status_t rv;
apr_foo_t *foo;

/* créer un objet @foo selon @args : */
rv = apr_foo_create(&foo, args...);

/* faire quelque chose avec @foo : */
rv = apr_foo_do_something(foo, args...); /*  */

/* détruire l'objet @foo. */
apr_foo_destroy(foo);

/* NB : Parfois la destruction d'un objet est
   faite implicitement lorsque le pool mémoire
   associé est détruit. */

Dix règles d'or pour les filtres de sortie.

Le contenu en Anglais non traduit se trouve ici.

Voilà les règles que doivent suivre à la lettre les filtres de sortie :

  1. Les filtres de sortie ne devraient pas passer de brigades vides le long de la chaine de sortie, mais devraient être tolérantes sur l’arrivée de brigades vides.
  2. Les filtres de sortie doivent passer tous les seaux de métadonnées (metadata buckets) le long de la chaine de sortie ; les seaux de vidages (FLUSH buckets) devraient être respectés en passant tous les seaux en attentes le long de la chaine de sortie.
  3. Les filtres de sortie devraient ignorer tous les seaux qui suivent un seaux de fin de fichier (EOS buckets).
  4. Les filtres de sortie doivent traiter une quantité fixe de données, à la fois, afin de s’assurer que la consommation mémoire n’est pas proportionelle à la taille du contenu qui est filtré.
  5. Les filtres de sortie devraient ignorer les types de seaux, et doivent être capables de traiter des seaux de type inconnu.
  6. Après avoir appelé ap_pass_brigade pour faire suivre une brigade le long de la chaine de filtres, les filtres de sortie devraient appeler apr_brigade_cleanup pour s’assurer que la brigade est vide avant de s’en resservir ; les filtres ne devraient jamais utiliser apr_brigade_destroy pour « détruire » des brigades.
  7. Les filtres de sortie doivent mettre de côté les seaux qui sont destinés à être gardés plus longtemps que la durée du filtrage.
  8. Les filtres de sortie ne doivent pas ignorer la valeur de retour de ap_pass_brigade, et doivent renvoyer les erreurs appropriées en retour, à la chaine de filtres.
  9. Les filtres de sortie doivent seulement créer un nombre fixe de brigades de seaux pour chaque réponse, plutôt que une par invocation.
  10. Les filtres de sortie devraient en tout premier lieu essayer des lectures non bloquantes sur chaque seau, et envoyer un buffer de vidage (FLUSH bucket) le long de la chaine de filtres si la lecture doit être faite à nouveau, et de manière bloquante, avant de recommencer une lecture bloquante.

Sachant que la règle 9 n’est pas très claire pour moi, je vous la relivre en Anglais :
Output filters must only create a fixed number of bucket brigades for each response, rather than one per invocation.

Apache : notes pour les filtres

Voilà ce que j’ai mis longtemps à comprendre :

Lorsqu’un client envoie quelque chose à httpd, le serveur Web commence à créer des seaux qu’il remplit au fur et à mesure : et quand un seau est plein, il en remplit un autre etc jusqu’à ce qu’il ait un nombre de seaux suffisants. Ces seaux sont rangés dans une brigade. Une brigade de seaux. Une bucket brigade, en Anglais. Et là, il les fait passer au premier filtre qu’il a sur sa liste. Ce filtre peut à son tour, ajouter d’autres seaux, en enlever, ou même, tout supprimer et créer toute une série d’autres seaux. Toujours est-il que quand on développe un filtre, il faut toujours avoir en tête plusieurs choses :

  • Il va toujours y avoir une série de seaux qu’il va falloir parcourir, ce ne sera jamais un seul seau simple et seul. La boucle de parcours sera toujours de la forme :
    /* on demande à lire une série de seaux : */
    rv = ap_get_brigade(f->next, bb, AP_MODE_READBYTES, APR_BLOCK_READ, readbytes);
    /* si erreur, la gérer, sinon boucle pour lire seau après seau : */
    for (b = APR_BRIGADE_FIRST(bb);
         b != APR_BRIGADE_SENTINEL(bb);
         b = nextb) {
        nextb = APR_BUCKET_NEXT(b);
        ...
        traitement de ce qu'on a reçu
        ...
    }
  • Le serveur Apache gère une chaine de filtres, qui se suivent dans un ordre qu’il est impossible à connaitre du point de vue d’un filtre. En résumé, on ne sait jamais, quand on programme un filtre, quels sont les filtres qui ont été appelés avant le nôtre, ou quels sont les filtres le seront après. Bon ok. Il y a quand même une possibilité de dire grossièrement où on veut que le filtre soit, dans la chaine. En réalité il est possible de préciser l’un cinq niveaux grossiers parmi :
    1. APR_HOOK_REALLY_FIRST
    2. APR_HOOK_FIRST
    3. APR_HOOK_MIDDLE
    4. APR_HOOK_LAST
    5. APR_HOOK_REALLY_LAST

    Revenons à notre brigade, pleine de seaux, mais dont on ne sait pas exactement ce qu’elle contient, mais on le suppose. Lorsque celle-ci arrive et que notre filtre est appelé, on ne sait que grossièrement où est placé le filtre dans la chaine, donc il se peut que la brigade arrive avec des données en plus qui proviennent d’autres filtres mais qui ne nous concernent pas (entre autres des données personnelles qui vont d’un filtre à l’autre, des méta-données, en Anglais METADATA). C’est pour cela qu’il faut toujours s’assurer que ce ne sont pas des métadonnées qui ne nous concernent pas : c’est la macro APR_BUCKET_IS_METADATA :
    /* on demande à lire une série de seaux : */
    rv = ap_get_brigade(f->next, bb, AP_MODE_READBYTES, APR_BLOCK_READ, readbytes);
    /* si erreur, la gérer, sinon boucle pour lire seau après seau : */
    for (b = APR_BRIGADE_FIRST(bb);
         b != APR_BRIGADE_SENTINEL(bb);
         b = nextb) {
        nextb = APR_BUCKET_NEXT(b);
        if ( ! APR_BUCKET_IS_METADATA(b) ) {
            ...
            traitement de ce qu'on a reçu
            ...
        }
    }

Directive directory

<Directory [répertoire]> et </Directory> sont utilisées pour encadrer un groupe d’instructions qui vont être appliquées sur le répertoire et les sous-répertoires de ce répertoire.

On peut mettre n’importe quelle instruction tant que c’est une instruction « pour répertoire ». Dans tous les cas, [répertoire] est :

  • soit le nom complet du répertoire concerné (sans le slash final) ;
  • soit une expression régulière simple (que des ?, * ou []) (je n’entrerai pas en détail ici).

Exemple :

<Directory "/var/www/cgi-bin">
  AllowOverride None
  Options None
  Order allow,deny
  Allow from all
</Directory>

AllowOverride : All|None|directive-type [directive-type] ..

On précise quelles sont les directives autorisées dans le fichier .htaccess.

Voici en fonction de ce que l’on met, le résultat :

  • None : Les fichiers .htaccess sont complètement ignorés
  • All : Toutes les directives possibles dans un contexte .htaccess sont prises en compte
  • AuthConfig : Autorise les directives d’autorisation (!) à des utilisateurs (AuthDBMGroupFile, AuthDBMUserFile, AuthGroupFile, AuthName, AuthType, AuthUserFile, Require, etc.).
  • FileInfo : Autorise les directives qui controllent les types des documents (DefaultType, ErrorDocument, ForceType, LanguagePriority, SetHandler, SetInputFilter, SetOutputFilter, et les directives du mod_mime Add* et Remove*, etc.), données méta de document (Header, RequestHeader, SetEnvIf, SetEnvIfNoCase, BrowserMatch, CookieExpires, CookieDomain, CookieStyle, CookieTracking, CookieName), directives mod_rewrite RewriteEngine, RewriteOptions, RewriteBase, RewriteCond, RewriteRule) et Action du module mod_actions.
  • Indexes : Autorise les directives qui controllent l’indexation de répertoire (AddDescription, AddIcon, AddIconByEncoding, AddIconByType, DefaultIcon, DirectoryIndex, FancyIndexing, HeaderName, IndexIgnore, IndexOptions, ReadmeName, etc.).
  • Limit : Autorise les directives qui controllent les accès « hôtes » (Allow, Deny et Order).
  • Options[=Option,...] : Autorise les directives qui controllent des particularités des répertoires (Options et XBitHack).

Order : Allow,Deny / Deny,Allow

La directive Order, lorsqu’elle est accompagnée des directives Allow et Deny, sert pour un système de contrôle en 3 passes.

  1. On passe au travers d’une des deux directives (Allow ou Deny)
  2. On passe ensuite l’autre directive (Deny ou Allow)
  3. Enfin on applique la 3ème passe pour toutes les requêtes qui n’ont correspondu ni à la première ni à la seconde

Notez bien que toutes les directives Allow et Deny sont appliquées, à l’inverse d’un pare-feu où, dès qu’il y a correspondance, on s’arrête. La troisième passe aussi est appliquée. En conséquence, l’ordre dans lequel les lignes de configuration apparaissent importe peu : toutes les directives Allow sont mises en un seul groupe, et toutes les directives Deny dans un autre. Les ordres possibles sont :

  1. Allow,Deny
    1. Toutes les directives Allow sont appliquées. Au moins l’une doit correspondre sinon la requête est rejetée.
    2. Ensuite, toutes les directives Deny sont appliquées. Si l’une d’elles correspond, la requête est rejetée.
    3. Enfin, toutes les requêtes qui ne sont validées ni par Allow ni par Deny sont considérées comme Deny
  2. Deny,Allow
    1. Toutes les directives Deny sont appliquées. Si l’une d’elles correspond, la requête est rejetée à moins qu’à la passe suivante, il y ait correspondance.
    2. Toutes les directives Allow sont appliquées.
    3. Toutes les requêtes qui ne sont validées ni par Allow ni par Deny sont considérées comme Allow
Validation : Résultat sur Allow,Deny Résultat sur Deny,Allow
Seul Allow valide Requête autorisée Requête autorisée
Seul Deny valide Requête refusée Requête refusée
Allow et Deny pas valides Seconde directive appliquée = refusée Seconde directive appliquée = autorisée
A la fois Allow & Deny valides Dernier contrôle valide appliqué = refusée Dernier contrôle valide appliqué = autorisée

Allow from all|host|env=env-variable [host|env=env-variable] ...

La directive Allow spécifie qui peut accéder à une zone du serveur.

L’accès peut être contrôlé par nom d’hôte, adresse IP, plage d’adresses IP ou encore d’autres caractéristiques de la requête du client qui ont été capturées dans des variables d’environnement.

Le premier argument est toujours from. Les arguments suivants peuvent prendre 3 formes différentes :

  • Si Allow from all est spécifié, alors tout le monde est autorisé, dans le cadre de la directive Allow, c’est à dire qu’il faudra prendre en compte la directive Deny et l’ordre spécifié par Order, mais ceci est expliqué plus haut.
  • Pour autoriser l’accès au serveur à des hôtes ou à des groupes d’hôtes on peut le faire en utilisant différents formats qui sont décrits en détail (en Anglais) ici.
  • Le dernier format possible est celui qui se base sur les variables d’environnement. C’est aussi expliqué en détail (en Anglais) ici.

Deny from all|host|env=env-variable [host|env=env-variable] ...

La directive Deny spécifie qui peut accéder à une zone du serveur.

L’accès peut être contrôlé par nom d’hôte, adresse IP, plage d’adresses IP ou encore d’autres caractéristiques de la requête du client qui ont été capturées dans des variables d’environnement.

Les arguments sont identiques à ceux décrit pour la directive Allow.

Pour plus de détails, vous pouvez le lire en Anglais ici.

Options [+|-]option [[+|-]option] ...

La directive Option décrit quelles sont les possibilités offertes au serveur pour un répertoire donné.

Option peut être mis à :

  • None : dans ce cas, aucune des possibilités supplémentaires n’est activée
  • All : toutes les possibilités supplémentaires sont actives sauf MultiViews. C’est la configuration par défaut.
  • ExecCGI : L’éxécution des scripts CGI (en utilisant le module mod_cgi) est autorisée. Le serveur suivra les liens symboliques de ce répertoire. Malgré le fait que le serveur suivra les liens symboliques de ce répertoire, cela ne change pas le nom du répertoire utilisé pour appliquer les directives dans les sections <Directory>.
    Notez que cette option est ignorée si elle est dans une section <Location> (mais c’est un peu hors sujet ici).
  • Includes : les inclusions côté serveur (en utilisant le module mod_include) sont autorisées.
  • IncludesNOEXEC : les inclusions côté serveur (en utilisant le module mod_include) sont autorisées, mais les commandes #exec et #exec cgi sont interdites.
    Il est toujours possible d’inclure en utilisant #include, des scripts virtuels CGI à partir de répertoires ScriptAliased (mais c’est un peu hors sujet ici).
  • Indexes : Si l’URL de la requête correspond à un répertoire, et qu’il n’y a aucun DirectoryIndex (par exemple ni de index.html, ni de index.php) dans ce répertoire, alors le module mod_autoindex renverra un listing formatté du répertoire.
  • MultiViews : Les contenus negotiés "MultiViews" sont autorisés en utilisant le module mod_negotiation.
  • SymLinksIfOwnerMatch : Le serveur ne suivra les liens symboliques que si l’id de l’utilisateur qui possède le répertoire – ou fichier – destination est le même que celui du lien.
    Notez que cette option est ignorée si elle est dans une section <Location> (mais c’est un peu hors sujet ici).

Normalement, si on applique plusieurs Options dans un répertoire, c’est la directive qui est la plus spécifique au répertoire qui est appliquée. Les autres Options sont ignorées. Les autres Options ne sont pas fusionnées.

Néanmoins, si, dans une directive Options, toutes les options qui suivent sont précédées par un signe + ou -, les deux Options sont fusionnées. Dans ce cas, n’importe quelle option précédée par un + est ajoutée aux options actives, et n’importe quelle option précédée par un – est retirée des options actives.

Regardez l’exemple (en Anglais) ici.

Notez bien encore une fois que si on ne précise pas la directive Options, la valeur All est appliquée.

Le module mod_rewrite

Apache se souvient des modèles, ou groupes passés dans les règles de réécriture, et on peut les rappeler grâce à la directive $N, avec (0 <= N <= 9).

Apache se souvient aussi des derniers ordres passés dans les conditions de réécriture, et on peut les rappeler grâce à la directive %N, avec (0 <= N <= 9).

Un exemple concret :

  1. RewriteCond %{HTTP_HOST} ^(www\.)+mamemeamoi\.(com|fr) [NC]
  2. RewriteCond %{REQUEST_URI} ^(.*)\.php$ [NC]
  3. RewriteRule ^/(.*) /$1?IDP=mameme [PT,QSA,S=4]

Traduit en clair, cela signifie :

  1. Si on vient ici à partir d’une adresse telles que www.mamemeamoi.com ou encore www.mamemeamoi.fr voire mamemeamoi.fr alors c’est bon on continue les conditions pour la règle à venir
  2. Si on demande à voir une page qui se termine par php alors c’est bon on continue les conditions pour la règle à venir
  3. Si on est arrivé jusqu’ici c’est que toutes les conditions ont été validées => on applique la règle qui est, traduite en Français :
    1. ^/(.*) /$1?IDP=mameme : on ajoute à l’URI, quelle qu’elle soit, les paramètres IDP=mameme ;
    2. Directive [PT] : on applique cette URI en interne de manière à faire croire à tout ce qui suit, y compris une fois que les règles de réécriture sont terminées, que c’est vraiment cette adresse qui a été demandée, donc les autres modules agiront aussi en conséquence ;
    3. Directive [QSA] : sur l’URI appliquée, on y fait suivre les paramètres qui se trouvaient après le ?, les paramètres _GET en php. Par exemple si on a demandé /t.php?i=3, on se retrouve avec l’URI finale /t.php?i=3&IDP=meme, alors que sans la directive QSA, on se retrouve avec l’URL finale /t.php?IDP=meme ;
    4. Directive [S=4] : on demande de sauter les 4 règles de réécriture qui suivent, et on appliquera celles qu’il y a après (s’il y en a).

Maintenant, voyons si on avait changé la règle de réécriture par :

  1. RewriteCond %{HTTP_HOST} ^(www\.)+mamemeamoi\.(com|fr) [NC]
  2. RewriteCond %{REQUEST_URI} ^(.*)\.php$ [NC]
  3. RewriteRule ^/(.*) /%1%2/$1

Regardez bien : il y a le sigle %1 mais aussi le sigle $1. En réalité le %1 est remplacé par ce qu’il y a dans les parenthèses rencontrées dans la condition de réécriture. Et en partant de la première condition ! A l’inverse, le $1 est remplacé par ce qu’il y a dans les parenthèses rencontrées dans la règle de réécriture. Ainsi si on demande l’URI http://www.mamemeamoi.com/popo.php, elle sera transformée en http://www.com/popo.php, et si on demande l’URI http://mamemeamoi.com/popo.php, elle sera transformée en http://com/popo.php. Ce qui ne sert à rien d’autre que de montrer un exemple. Mais imaginez ce que vous pouvez faire !