Je bent niet ingelogd.

  • |

#1 08-May-2010 12:41:58

warp
Administrator

Deeplinken van foto's "mankeren"

Op een website staan de nodige (mooie) foto's.
Bezoekers kunnen in hun forumbericht een link naar een van de foto's op "mijn" site.
Niet als clickable link, maar zodanig dat de foto rechtstreeks ingebed in hun bericht wordt getoond.
Dan lijkt het alsof die foto van henzelf is, terwijl hij in werkelijkheid steeds van mijn site opgeroepen wordt zonder dat de lezer dat meteen ziet.
Deze techniek heet "deep-linking".
Het weglaten van bronvermelding ergert en daarom kun je er een (klein) stokje voor steken.

Het probleem valt uiteen in een paar stappen:

  • hoe kan een Apache webserver zien dat vanuit een vreemde webpagina (d.w.z. vanuit een pagina die niet van zijn eigen site is) een foto/image wordt opgevraagd.

  • hoe trigger je in zo'n geval een actie die iets anders doet dan braaf de gevraagde foto serveren.

  • hoe schilder je "on the fly" een stringetje tekst in een foto.

Stappen 1 en 2 pak je aan met de "mod_rewrite" van Apache, en stap 3 kan met PHP en z'n grafische GD-library.

In de httpd.conf van Apache controleer je dat 'mod_rewrite' wordt geladen (dat is i.h.a. default) en voeg je, in de "container" van de virtual host website, de volgende regels toe. De nummers in de kantlijn horen er niet bij; die zijn voor de uitleg die volgt:

01 RewriteEngine on
02 RewriteLog /xxxx/logs/rewrite_log
03 RewriteLogLevel 0

04 RewriteCond %{HTTP_REFERER} !^http://www.mijnsite.nl/.* [NC]
05 RewriteRule .*\.jpe?g$|.*\.gif$|.*\.png$
                 /rewriteimage.php?URI=%{REQUEST_URI} [NC,last]

  • In regel 01 schakelen we het rewrite-module aan.
    Dat moet expliciet want het is een setting die je nooit "erft" uit een hoger configuratie-niveau.
    Met dat module kun je binnenkomende URL's bekijken, en onder voorwaarden wijzigen/herschrijven.
    Dat luistert nogal nauwkeurig, dus het heeft zin om debug-logging aan te zetten (02, 03).

  • Regel 02 vertelt hoe de logfile moet heten.
    De 'xxxx' in regel 02 moet in je UNIX-servermachine een absolute padnaam vormen, en gebruiker 'apache' moet in die logfile kunnen schrijven.
    Je kiest typisch dezelfde directory als waar ook de access-logfile en de error-logfile staan (zoek naar AccessLog en ErrorLog regels). Rewrite-logging is erg duur.

  • Regel 03 zet deze logging effectief uit. Om het tijdens het testen (tijdelijk!!) aan te zetten moet je het level-getal opkrikken, tot maximaal 9.

  • Regel 04 bekijkt de 'HTTP_REFERER' string. Dat is de aanvragende website; we willen weten of dat 'onze eigen' website is.
    Zo'n regel bestaat uit 3 of 4 delen: (deel a) keyword 'RewriteCond' zegt dat een voorwaarde getest moet worden. (deel b) %{HTTP_REFERER} benoemt het slachtoffer: de REFERER-string uit het binnengekomen HTTP-request. Dit deel (b) van de regel kent zeer veel verschillende mogelijkheden; de Apache-mod_rewrite documentatie is hierbij onontbeerlijk. Deel (c) is een reguliere expressie, Perl-stijl, die we op deel (b) willen loslaten. Dat levert dan wel of geen match op. Maar Apache heeft als extra mogelijkheid een ! voor de expressie, die je gebruikt als je juist niet een match zoekt. Dat doen wij dus.
    Deel (d), tussen [] haken, is een optioneel aanhangel waarin je een reeks "vlaggen" kunt zetten; ook hier weer veel mogelijkheden. In ons geval gebruiken we vlag [NC] (regexp-match NotCase sensitive).

  • Dan komen we op regel 05; die moet je als één lange regel schrijven.
    Deze RewriteRule is de work-horse, die het echte werk doet.
    En ook zo'n RewriteRule heeft weer 3 of 4 delen. Het keyword RewriteRule (deel a) spreekt voor zich.
    Maar je moet wel weten dat een RewriteRule alleen maar dienst doet als alle RewriteCond statements die er onmiddellijk aan vooraf gaan een 'true' hebben gescoord.

Delen (b) en (c) van een RewriteRule vormen een search+replace. (b) is een Perl-regexp die moet matchen op een deel van de gevraagde filenaam, (c) is de replace-string. Optioneel deel (d), tussen [], bevat weer vlaggetjes.

De reguliere-expressie in ons deel (b) ziet er wat ingewikkeld uit. Je moet 'm even in drie stukken knippen bij de |-tekens. Dan krijg je drie aparte expressies; de | vormt een OR van die drie. De eerste van de drie is .*\.jpe?g$ en betekent stukje voor stukje: .* pakt een willekeurig (longest match) beginstuk, daarachter \. een letterlijke punt, daarachter de letters jp, dan betekent e? optioneel een letter e, dan een letter g, en tenslotte betekent $ het einde van de string. We zoeken dus naar filenaam-extensies .jpg en .jpeg (en de vlag [NC] had je natuurlijk ook weer gezien). De andere twee van de drie expressies zoeken vergelijkbaar naar .gif en naar .png extensies.

Deel (c) in de regel is de replace: we vervangen de opgevraagde filenaam door de vast gekozen filenaam /rewriteimage.php (een PHP-programma dat we hierna bespreken; de naam hebben we zelf verzonnen) en met een '?' koppelen we daar de oorspronkelijke gevraagde REQUEST_URI (de naam van de foto-file) als z.g. HTTP-GET-parameter aan vast. De vlag '[last]' betekent: verder geen andere rewrite-regels meer toepassen; eindbestemming bereikt. Default gedrag is dat de hele rewrite-matching met de gewijzigde filenaam opnieuw begint, en dat zou met onze criteria een oneindige loop opleveren.

Nog even wat fijnproeverij over het verschil tussen een URI (I=identifier) en een URL (L=locator): http://www.mijnsite.nl/aap/noot/mies.jpg is een URL, en aap/noot/mies.jpg is een URI.

We zijn een heel eind gevorderd: als vanuit een vreemde website een .jpg of .jpeg of .gif of .png bestand van onze website wordt opgeroepen, start Apache ons PHP-programma /rewriteimage.php en geeft als GET-parameter daar de opgevraagde bestandsnaam aan door. Alle data die ons PHP-programma op zijn stdout-kanaal naar buiten pompt, gaan terechtkomen op het bordje van de browsende klant die op de gevraagde foto staat te wachten.

Nu bekijken we dat /rewriteimage.php -programma.

<?php
01   $uri = $_GET['URI'];
02   $ext = substr($uri, 1+strrpos($uri, '.'));

03   switch(strtolower($ext)) {
04   case 'jpeg': case 'jpg':
05       $inputfunction  = 'ImageCreateFromJPEG';
06       $outputfunction = 'ImageJPEG';
07       header('Content-Type: image/jpeg');
08       break;
09   case 'gif':
10       $inputfunction  = 'ImageCreateFromGIF';
11       $outputfunction = 'ImageGIF';
12       header('Content-Type: image/gif');
13       break;
14   case 'png':
15       $inputfunction  = 'ImageCreateFromPNG';
16       $outputfunction = 'ImagePNG';
17       header('Content-Type: image/png');
18       break;
19   default:
20       exit(1);
21   }

22   $handle = $inputfunction($_SERVER['DOCUMENT_ROOT'].'/'.$uri);
23   $xsize = ImageSx($handle);
24   $ysize = ImageSy($handle);

25   if (($xsize >= 190) && ($ysize >= 40)) {
26       $white = ImageColorAllocate($handle, 0xFE, 0xFE, 0xFE);
27       $black = ImageColorAllocate($handle, 0x01, 0x01, 0x01);
28       $font = 'LucidaSansDemiBold';
29       $text = 'from: www.mijnsite.nl';
30       ImageTTFText($handle, 10, 0, 2, $ysize-2, $black, $font, $text);
31       ImageTTFText($handle, 10, 0, 3, $ysize-2, $white, $font, $text);
32   }

33   $outputfunction($handle);
    ?>

  • Regel 01 haalt de GET-parameter met de URI-string op.
    Dat is dus de naam van het foto-bestand dat door de browsende klant is opgevraagd; via de Apache-rewrite hadden we die naam laten doorgeven.

  • Regel 02 peutert de extensie van die filenaam apart.

  • De extensie gebruiken we in regels 03 t.m. 21 om twee functienamen te vormen, afhankelijk van het type image waar het om gaat.
    Ook sturen we (07,12,17) alvast de verplichte http-header naar de browser toe.

  • Regel 22 haalt het gevraagde foto-bestand van disk.
    Merk op dat we de document-root van de website voor de URI-naam moeten plakken.
    Als uw website erg ingewikkelde filenamen gebruikt, dan is dit statement mogelijk iets te simpel.

  • Regels 23 en 24 kijken hoe groot de ingelezen foto is.

  • Regel 25 voorkomt dat we in al te kleine plaatjes nog tekst erbij proberen te frutten.
    De getallen corresponderen ongeveer met de afmeting van de tekst die we gaan schrijven, plus een randje eromheen.

  • Regels 26 en 27 definiëren de kleuren wit en zwart.
    We kiezen 0xFE in plaats van 0xFF, en 0x01 in plaats van 0x00 om net naast de "echte" kleuren wit resp. zwart te gaan zitten.
    De reden bespreken we hieronder.

  • Regels 28 en 29 spreken voor zich; het genoemde font moet natuurlijk wel in TTF-formaat op de server beschikbaar zijn voor PHP.

  • In regel 30 schrijven we onze tekst in zwarte letters in de foto.

  • Regel 31 doet dat nogmaals in witte letters, en ietsje verschoven.

  • Regel 33 perst het resultaat naar buiten, naar de browsende klant. Klaar.....

Het doel is bereikt: als een foto in mijn eigen webpagina verschijnt is hij ongemarkeerd, maar verschijnt hij deep-linked in "andermans" pagina dan staat mijn bronvermelding in de foto geschreven.

In GIF en PNG plaatjes kan een bepaalde kleur tot "transparant" zijn verklaard, en vaak wordt "echt" zwart (0x000000) of "echt" wit (0xFFFFFF) gekozen omdat dat de oorspronkelijke achtergrondkleur was.
Door met een eigen kleurdefinities daar net naast te gaan zitten (regels 26/27) minimaliseren we de kans dat onze zwarte of witte tekstcomponent (regels 30/31) in het plaatje transparant wordt gemaakt.

Merk op dat onze aanpak je niet beschermt tegen iemand die een kopie van je foto op zijn eigen webserver plaatst, en 'm vanaf daar serveert.
Dat kun je niet tegenhouden, zelfs niet met een simpel JavaScriptje dat een klik van de rechter muisknop afvangt en een "Foei, mag niet" pop-up display.
Je kunt gestolen kopieën alleen maar achteraf proberen op te sporen.

Offline

Bulletin Board voettekst