Hachage d’un fichier avec Indy

J’aime bien utiliser la bibliothèque Crypto++ pour calculer le hachage d’un fichier. Par contre, des fois il m’arrive de vouloir utiliser cette fonctionnalité sans rien télécharger de plus. Heureusement Indy, qui est inclus dans RAD Studio, nous permet de faire cela facilement.

Dans cet article, on verra comment faire un CRC32, MD5 et SHA1.

En premier, on ajoute les fichiers d’en-têtes:

#include <IdHashCRC.hpp>
#include <IdHashMessageDigest.hpp>
#include <IdHashSHA.hpp>

Pour le CRC32 on ajoute cette méthode:

String __fastcall TForm1::GetHashCRC32(const String AFileName)
{
    String Result;

    TIdHashCRC32* LCrc32 = NULL;
    System::Classes::TFileStream* LFileStream = NULL;
    try
    {
        LCrc32 = new TIdHashCRC32();
        LFileStream = new System::Classes::TFileStream(AFileName,
            fmOpenRead | fmShareDenyWrite);
        const unsigned __int64 LVal = LCrc32->HashValue(LFileStream);
        Result = System::Sysutils::IntToHex(LVal, 8);
    }
    __finally
    {
        delete LFileStream;
        delete LCrc32;
    }

    return Result;
}

Pour le MD5 on ajoute cette méthode:

String __fastcall TForm1::GetHashMD5(const String AFileName)
{
    String Result;

    TIdHashMessageDigest5* LMd5 = NULL;
    System::Classes::TFileStream* LFileStream = NULL;
    try
    {
        LMd5 = new TIdHashMessageDigest5();
        LFileStream = new System::Classes::TFileStream(AFileName,
            fmOpenRead | fmShareDenyWrite);
        Result = LMd5->HashStreamAsHex(LFileStream);
    }
    __finally
    {
        delete LFileStream;
        delete LMd5;
    }

    return Result;
}

Pour le SHA1 on ajoute cette méthode:

String __fastcall TForm1::GetHashSHA1(const String AFileName)
{
    String Result;

    TIdHashSHA1* LSha1 = NULL;
    System::Classes::TFileStream* LFileStream = NULL;
    try
    {
        LSha1 = new TIdHashSHA1();
        LFileStream = new System::Classes::TFileStream(AFileName,
            fmOpenRead | fmShareDenyWrite);
        Result = LSha1->HashStreamAsHex(LFileStream);
    }
    __finally
    {
        delete LFileStream;
        delete LSha1;
    }

    return Result;
}

Il est à noter que Indy supporte aussi ces classes:

  • TIdHashSHA224
  • TIdHashSHA256
  • TIdHashSHA384
  • TIdHashSHA512

Pour tester le code, on peut ajouter un contrôle TMemo et mettre le code suivant dans le constructeur.

const String LFileName = "C:\\monfichier.xyz"; // Le fichier doit exister
Memo1->Lines->Add("CRC32: " + GetHashCRC32(LFileName));
Memo1->Lines->Add("MD5  : " + GetHashMD5(LFileName));
Memo1->Lines->Add("SHA1 : " + GetHashSHA1(LFileName));

Si on veut être certain que le résultat est valide, on peut vérifier avec un logiciel comme HashTab:

HashTab

Compression de fichiers avec FWZip

Dans un article précédent j’avais parlé de la classe TZipFile qui permet de faire de la compression de fichiers. Elle est très pratique. Par contre, il y a une fonctionnalité manquante qui est importante pour moi. Il s’agit de l’ajout d’un mot de passe à l’archive. C’est une limitation qui est connue d’Embarcadero depuis C++Builder XE2. Malheureusement il n’y a pas encore de solution qui a été apportée.

Je suis tombé par hasard sur une bibliothèque qui supporte cette fonctionnalité. Il s’agit de FWZip de Александр (Rouse_) Багель. Elle est codée en Pascal Objet et fonctionne sur Win32 et Win64 dans des projets VCL et FireMonkey. Elle peut être téléchargée sur GitHub.

C’est très simple à utiliser. Il faut d’abord ajouter les fichiers suivants à votre projet:

  • FWZipConsts.pas
  • FWZipCrc32.pas
  • FWZipCrypt.pas
  • FWZipReader.pas
  • FWZipStream.pas
  • FWZipWriter.pas
  • FWZipZLib.pas

The tadalafil for sale cheap pharmaceutical market has been loaded with diverse choices of medicinal drugs which have been proving to be extremely helpful for getting recovered from the severe adverse reactions. To buy steroids in UK you should visit anabolicmuscles.com. anabolicmuscles.com have become most cialis buy online learningworksca.org trusted and acknowledge steroid supplier in UK since 2002. You’re safe now… you’re safe now… » Whoa! Sounds familiar, eh? Are you that overprotective over your smartphone-specifically, your iPhone? Hello! Who wouldn’t? Considering how much iPhone costs these days; one cannot simply afford cheap sildenafil an iPhone. Why? According to his repeated clinical research based on a large quantity of patients with chronic prostatitis, doctor Miller best levitra price makes progress in curing this disease by applying a new method.

On ajoute ensuite le fichier d’en-tête et la bibliothèque nécessaire.

#include <FWZipWriter.hpp>
#pragma comment(lib, "Shlwapi") // Pour PathCanonicalizeW / PathCanonicalizeA

Pour écrire une archive, on utilise ce code:

    TFWZipWriter* Zip = new TFWZipWriter(false,
        Fwzipzlib::TCompressionLevel::clLevel3, "MotDePasse");

    Zip->AddFile("C:\\img\\img0.png");
    Zip->AddFile("C:\\img\\img1.png");

    Zip->BuildZip("C:\\img\\images.zip");

    delete Zip;

Et voilà, maintenant lors de l’extraction des fichiers, un mot de passe sera demandé.

7-Zip Enter password window
Fenêtre de saisie de mot passe du logiciel 7-Zip

L’API PageSpeed v2

Dans cet article je vais revisiter l’article que j’avais écrit en mai 2012 sur l’utilisation de l’API PageSpeed de Google. Cette fois-ci c’est C++Builder XE7 qui sera utilisé avec la version 2 de l’API. Tout comme la première fois, les résultats seront présentés dans un diagramme circulaire à l’aide du contrôle TChart. En plus du code qui sera légèrement différent, un composant TImage sera utilisé pour y afficher une capture d’écran du site web.

La première étape est de créer un nouveau projet FireMonkey. Dans la Form il faut insérer un TRESTClient, un TRESTRequest, un TRESTResponse, un TChart, un TImage et un TLayout dans lequel il y aura un TEdit un TButton. On aligne le contrôle TLayout à Bottom et vous pouvez donner comme texte à votre bouton le mot « Analyser ».

Voici ce à quoi l’interface devrait ressembler:
Analyseur de site web

Dans votre fichier cpp voici la liste de fichier d’en-tête à utiliser ainsi que la macro qui va contenir votre clef d’API. N’essayez pas d’utiliser celle-ci, il s’agit de caractères écrits de façon aléatoire.

#include <System.JSON.hpp>
#include <FMXTee.Series.hpp>
#include <System.NetEncoding.hpp>
#define GOOGLEAPIKEY "dskk1j3sW4WBYdkjds8sSDSD" // Clef d'API

Voici le code à ajouter dans votre constructeur:

    Chart1->Title->Text->Text = "Statistique de la page";
    Chart1->Legend->Title->Text->Text = "Ressource en octets";
    Chart1->Legend->Alignment = TLegendAlignment::laBottom;
    Chart1->BevelOuter = TPanelBevel::bvNone;
    Series::TPieSeries *Series = new Series::TPieSeries(this);
    Series->Marks->Visible = false;
    Chart1->AddSeries(Series);

Étant donné que nous accéderons à un site web qui utilise SSL (https), les fichiers ssleay32.dll et libeay32.dll devront être distribués avec votre application Windows. Une version Win32 et Win64 des fichiers est disponible sur le site web suivant: http://indy.fulgan.com/SSL.

La prochaine étape est d’ajouter le code dans l’événement OnClick du bouton.

    Chart1->Series[0]->Clear(); // Efface le contenu du diagramme

    RESTClient1->BaseURL =
        "https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url=" +
        Edit1->Text + "&key=" GOOGLEAPIKEY + "&screenshot=true";
    RESTRequest1->Execute();

    TJSONObject* Obj = static_cast<TJSONObject*>(RESTResponse1->JSONValue);
    TJSONPair* Pair = Obj->Get("pageStats");
    if(Pair)
    {
        TJSONNumber* Answer;
        TJSONObject* PageStats = static_cast<TJSONObject*>(Pair->JsonValue);
        if((Pair = PageStats->Get("htmlResponseBytes")) != NULL)
        {
            Answer = static_cast<TJSONNumber*>(Pair->JsonValue);
            Chart1->Series[0]->Add(Answer->AsInt, "HTML", TAlphaColor(claGreen));
        }
        if((Pair = PageStats->Get("cssResponseBytes")) != NULL)
        {
            Answer = static_cast<TJSONNumber*>(Pair->JsonValue);
            Chart1->Series[0]->Add(Answer->AsInt, "CSS", TAlphaColor(claOrange));
        }
        if((Pair = PageStats->Get("imageResponseBytes")) != NULL)
        {
            Answer = static_cast<TJSONNumber*>(Pair->JsonValue);
            Chart1->Series[0]->Add(Answer->AsInt, "Image", TAlphaColor(claYellow));
        }
        if((Pair = PageStats->Get("javascriptResponseBytes")) != NULL)
        {
            Answer = static_cast<TJSONNumber*>(Pair->JsonValue);
            Chart1->Series[0]->Add(Answer->AsInt, "JavaScript", TAlphaColor(claRed));
        }
        if((Pair = PageStats->Get("otherResponseBytes")) != NULL)
        {
            Answer = static_cast<TJSONNumber*>(Pair->JsonValue);
            Chart1->Series[0]->Add(Answer->AsInt, "Autre", TAlphaColor(claBlue));
        }
    }
    Pair = Obj->Get("screenshot");
    if(Pair)
    {
        TJSONObject* Screenshot = static_cast<TJSONObject*>(Pair->JsonValue);
        if((Pair = Screenshot->Get("data")) != NULL)
        {
            TJSONString* Answer = static_cast<TJSONString*>(Pair->JsonValue);
            String Value = Answer->Value();
            // On change de base64url à base64
            Value = ReplaceStr(ReplaceStr(Value, "_", "/"), "-", "+");
            // On transforme de base64 vers stream
            TBytes LDAta = TNetEncoding::Base64->DecodeStringToBytes(Value);
            TBytesStream* LStream = new TBytesStream(LDAta);
            try
            {
                Image1->Bitmap->LoadFromStream(LStream);
            }
            __finally
            {
                delete LStream;
            }
        }
    }

Un petit mot sur la ligne 49. La documentation de l’API PageSpeed à propos des données de l’image dit:

Base64-encoded screenshot of the page that was analyzed.

Les données peuvent comporter des caractères moins (-) et souligné (_), donc la documentation est erronée si on se fie à RFC 4648 section 5:

This encoding may be referred to as « base64url ». This encoding should not be regarded as the same as the « base64 » encoding and should not be referred to as only « base64 ». Unless clarified otherwise, « base64 » refers to the base 64 in the previous section.

This encoding is technically identical to the previous one, except for the 62:nd and 63:rd alphabet character, as indicated in Table 2.

Google n’aurait donc pas dû dire qu’il s’agit de base64. Il aurait dû dire que c’est du base64url. C’est pour cette raison que je remplace les deux caractères.

Pourtant, dans d’autres API comme celui de Gmail, Google met la bonne information:

The entire email message in an RFC 2822 formatted and base64url encoded string.

En terminant, je vous rappelle qu’un nouvel outil dans RAD Studio XE7 vous permet de tester les API REST. Il se trouve Tools / Rest Debugger. Voici une capture d’écran du logiciel:
RESTDebugger

UsendMii 0.2.2

Une nouvelle version de UsendMii est maintenant disponible.

Dans cette version, Bootstrap est utilisé pour l’interface web. Voici la nouvelle apparence du menu principal:
UsendMii new menuUn problème introduit dans la version précédente a été corrigé. Le téléchargement de capture d’écran à partir de la Wii U fonctionne de nouveau. Je rappelle que cette fonctionnalité permet à n’importe quel moment d’ouvrir le Navigateur Internet et de se connecter à UsendMii pour y envoyer l’image qui apparait sur la Wii U GamePad ou sur la télévision. Ici j’ai utilisé l’Éditeur Mii pour faire celle-ci (modifiée par suite pour garder qu’une section de l’image):
All Mii

Comment déboguer sur la OUYA avec RAD Studio

La OUYA est une console de jeu vidéo qui fonctionne sous le système d’exploitation Android. Il est donc possible grâce à FireMonkey de créer des applications pour cette plateforme. Bien sûr, les déboguer à partir de l’IDE serait un grand avantage. C’est ce que le logiciel adb nous permet de faire. Tout d’abord il faut démarrer la OUYA et aller dans le menu MANAGE / SYSTEM / DEVELOPPEMENT. Il faut activer ADB et ADB OVER NETWORK comme démontré dans la capture d’écran suivante:
OUYA ADB ON
L’adresse IP et le numéro de port vous seront utiles bientôt. Il faut d’abord se connecter à la OUYA. Pour cela l’application Android Debug Bridge (adb) située dans le dossier /<sdk>/platform-tools sera utilisée. Dans le SDK Manager pour Android on trouve ce chemin dans la section Adb location. Vous devez aller dans ce dossier et taper la ligne de commande suivante:
adb connect 192.168.1.104:5555
Si tout fonctionne le résultat devrait être: connected to 192.168.1.104:5555

Ensuite, ouvrez ou créez un nouveau projet de type Multi-Device Application dans RAD Studio. Sélectionnez le Target Android. Dans la section Target de celui-ci OUYA Console devrait apparaitre. Si ce n’est pas le cas il faut faire un Refresh.

OUYA Console Target

Il est maintenant possible de compiler le fichier APK et de tester l’application directement sur la console. Une fois fermée, elle devrait être disponible dans la section MAKE / SOFTWARE.

Si vous voulez simplement installer l’application, il est possible d’aller dans le menu MAKE / UPLOAD afin d’activer le téléchargement vers la OUYA.
OUYA Upload APK
Il vous suffit simplement d’ouvrir le lien donné dans votre navigateur web préféré et de déposer le fichier APK dans la zone appropriée.
Upload APK

Il est possible de faire d’autres tâches intéressantes avec adb. Par exemple, j’ai enregistré une capture d’écran sur la OUYA avec la ligne de commande suivante:
adb shell screencap -p /sdcard/screencap.png
Ensuite j’ai transferé l’image vers mon disque local C: avec cette ligne de commande:
adb pull /sdcard/screencap.png c:\

RAD Studio XE6: Interface bilingue?

Habituellement j’installe toujours RAD Studio en anglais car je suis habitué au vocabulaire technique dans la langue de Shakespeare. À la suite de l’installation de la version d’essai je me suis retrouvé avec une version complètement bilingue. Cela m’a pris plusieurs minutes avant de m’en apercevoir. Dans l’interface on voit un Find côtoyer un Chercher, un Output à coté d’un Sortie et ainsi de suite. Je vous laisse regarder par vous-mêmes:

RAD Studio XE6 Bilingue

À la suite d’un redémarrage causé par un plantage de l’EDI tout est redevenu normal. J’ai maintenant mon interface en anglais comme spécifié lors de l’installation.

J’ai bien sûr rapporté le bug à Embarcadero, mais ils sont incapables de le reproduire. Tenez-moi au courant si vous observez ce problème.

Server-Sent Events avec C++Builder

Les Server-Sent Events (SSE) servent à pousser des informations à partir d’un serveur HTTP vers un navigateur web. Habituellement c’est le client qui doit demander l’information au serveur, mais avec cette méthode c’est le contraire.

Le principe est simple: le client fait une requête à une adresse web dans le but de recevoir des évènements DOM. Pour effectuer cela, c’est EventSource de l’API JavaScript qui sera utilisé. Il est important de dire que les Server-Sent Events ne sont pas encore supportés dans Internet Explorer.

La première étape est de créer un nouveau projet C++Builder. Dans la Form il faut insérer un TIdHTTPServer. C’est ce contrôle de Indy qui sera utilisé pour le serveur. On commence d’abord par démarrer le serveur avec ligne de code dans le constructeur:

    IdHTTPServer1->Active = true;

Dans l’évènement OnCommandGet du TIdHTTPServer on ajoute ce code:

    if(ARequestInfo->URI == "/demo")
    {
        TIdTCPConnection* Connection = AContext->Connection;
        while(Connection->Connected())
        {
            System::Word Hour, Min, Sec, MSec;
            Now().DecodeTime(&Hour, &Min, &Sec, &MSec);
            String Message = "Voici l'heure sur le serveur: " +
                Format("%.2d:%.2d:%.2d.%.3d", ARRAYOFCONST((Hour, Min, Sec, MSec)));

            Connection->IOHandler->WriteBufferOpen();
            Connection->IOHandler->WriteLn("HTTP/1.1 200 OK");
            Connection->IOHandler->WriteLn("Content-Type: text/event-stream; charset=UTF-8");
            Connection->IOHandler->WriteLn("Cache-Control: no-cache");
            Connection->IOHandler->Write("data: " + Message + "\n\n");
            Connection->IOHandler->WriteBufferClose();

            Sleep(100); // Une petite pause
        }
    }
    else
    {
        AResponseInfo->ContentText =
            "<!doctype html>"
            "<html>"
            "    <head>"
            "        <title>Server-Sent Events</title>"
            "        <script type = \"text/javascript\">"
            ""
            "        var source = new EventSource(\"demo\");"
            "        source.onmessage = function(event)"
            "        {"
            "            document.getElementById(\"result\").innerHTML = event.data;"
            "        };"
            ""
            "        </script>"
            "    </head>"
            "    <body>"
            "        <p id=\"result\"></p>"
            "    </body>"
            "</html>";
    }

Voilà, maintenant il suffit d’ouvrir votre navigateur web à l’adresse 127.0.0.1 vous devriez voir l’heure de votre PC qui s’actualise à tous les 100ms.

Utiliser l’API Graph de Facebook avec C++Builder

Facebook se passe sans doute de présentation, par contre son API est peut-être moins connu. Dans cet article nous irons chercher les informations publiques d’un utilisateur qui ne nécessitent aucune autorisation.

La première étape est de créer un nouveau projet FireMonkey HD. Dans la Form il faut insérer un TIdHTTP, un TIdSSLIOHandlerSocketOpenSSL, un TStringGrid, un TImage, un contrôle TEdit, un TLabel et finalement un TButton. Vous pouvez donner comme texte à votre bouton le mot « Rechercher » et pour le TLabel vous pouvez y inscrire « Nom d’utilisateur: ». Je vous propose de placer les composants dans la fenêtre de la manière suivante:
Pour ceux qui se le demande j’ai utilisé le style Air.Style. Ça change un peu des fenêtres Windows que l’on voit tout le temps.

Dans votre fichier cpp voici le fichier d’en-tête à ajouter:

#include <Data.DBXJSON.hpp>

Voici le code à ajouter dans votre constructeur:

    IdHTTP1->IOHandler = IdSSLIOHandlerSocketOpenSSL1;

    // Ceci est nécessaire pour les redirections
    IdHTTP1->HandleRedirects = true;

    // Propriété par défaut pour le contrôle grille
    StringGrid1->ShowSelectedCell = false;
    StringGrid1->ReadOnly = true;
    StringGrid1->RowCount = 0;

    // Ajout de la première colonne
    StringGrid1->AddObject(new TStringColumn(this));
    StringGrid1->Columns[0]->Header = L"Nom";
    StringGrid1->Columns[0]->Width = 150;

    // Ajout de la deuxième colonne
    StringGrid1->AddObject(new TStringColumn(this));
    StringGrid1->Columns[1]->Header = L"Valeur";
    StringGrid1->Columns[1]->Width = 150;

Étant donné que nous accéderons à un site web qui utilise SSL (https), la première ligne de code est critique. Sans elle, une exception dans la classe EIdIOHandlerPropInvalid produira le message « IOHandler value is not valid ». Parce que nous utilisons OpenSSL, les fichiers ssleay32.dll et libeay32.dll devront être distribués avec votre application.

La prochaine étape est d’ajouter le code dans l’événement OnClick du bouton.

    System::Classes::TMemoryStream* ResponseContent = new System::Classes::TMemoryStream;

    try
    {
        // On vide la liste avant d'ajouter les valeurs
        StringGrid1->RowCount = 0;
        Image1->Bitmap = NULL;

        String URL = "https://graph.facebook.com/" + Edit1->Text;

        String Response = IdHTTP1->Get(URL);

        TJSONObject* Obj = static_cast<TJSONObject*>(TJSONObject::ParseJSONValue(Response));
        TJSONPair* Pair;
        TJSONString* Answer;

        if((Pair = Obj->Get("id")) != NULL)
        {   // ID Facebook
            const int Pos = StringGrid1->RowCount;
            StringGrid1->RowCount++;
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            StringGrid1->Cells[0][Pos] = "ID";
            StringGrid1->Cells[1][Pos] = AnsiDequotedStr(Answer->ToString(), '\"');
        }
        if((Pair = Obj->Get("name")) != NULL)
        {   // Nom complet
            const int Pos = StringGrid1->RowCount;
            StringGrid1->RowCount++;
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            StringGrid1->Cells[0][Pos] = "Nom";
            StringGrid1->Cells[1][Pos] = AnsiDequotedStr(Answer->ToString(), '\"');
        }
        if((Pair = Obj->Get("first_name")) != NULL)
        {   // Prénom
            const int Pos = StringGrid1->RowCount;
            StringGrid1->RowCount++;
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            StringGrid1->Cells[0][Pos] = "Prénom";
            StringGrid1->Cells[1][Pos] = AnsiDequotedStr(Answer->ToString(), '\"');
        }
        if((Pair = Obj->Get("last_name")) != NULL)
        {   // Nom de famille
            const int Pos = StringGrid1->RowCount;
            StringGrid1->RowCount++;
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            StringGrid1->Cells[0][Pos] = "Nom de famille";
            StringGrid1->Cells[1][Pos] = AnsiDequotedStr(Answer->ToString(), '\"');
        }
        if((Pair = Obj->Get("gender")) != NULL)
        {   // Sexe (female ou male)
            const int Pos = StringGrid1->RowCount;
            StringGrid1->RowCount++;
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            StringGrid1->Cells[0][Pos] = "Sexe";
            StringGrid1->Cells[1][Pos] = AnsiDequotedStr(Answer->ToString(), '\"');
        }

        // Téléchargement de l'image
        IdHTTP1->Get(URL + "/picture", ResponseContent);

        Image1->Bitmap = new Fmx::Types::TBitmap(ResponseContent);
    }
    catch(...)
    {
    }

    delete ResponseContent;

Dans le code on insère dans la liste seulement quelques informations, mais il en existe plusieurs autres qui sont disponibles.

À présent, vous connaissez le minimum requis pour commencer à vous amuser avec cette interface API .

Utiliser l’API Google Street View Image

L’API Google Street View Image permet de télécharger une image statique par l’envoi d’une requête HTTP standard.

La première étape est de créer un nouveau projet FireMonkey HD. Dans la Form il faut insérer un TIdHTTP, un TImage, un TEdit, un TTrackBar et quatre contrôles TButton. Je vous propose de placer les composants dans la fenêtre de la manière suivante:

Google Street View Image API

Le TEdit servira à entrer les coordonnées de latitude et longitude. La barre graduée permet d’agrandir et de réduire l’affichage de l’image. Un nombre plus petit signifie un plus grand niveau de zoom. Les boutons servent à déplacer la caméra dans différentes directions. Le résultat sera évidemment affiché dans le composant TImage.

Voici les attributs et la méthode à ajouter à votre fichier .h:

int FHeading;
int FFieldOfView;
int FPitch;

void __fastcall UpdateImage();

Voici tout le code qui sera nécessaire pour l’application:

//---------------------------------------------------------------------------
#include <fmx.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
    // On fait croire à Google que l'on est Firefox 16.0
    IdHTTP1->Request->UserAgent =
        "Mozilla/5.0 (Windows NT 5.1; rv:16.0) Gecko/20100101 Firefox/16.0";

    // Valeur par défaut
    FHeading = 0;
    FFieldOfView = 90;
    FPitch = 0;

    TrackBar1->Tracking = false;
    TrackBar1->Value = FFieldOfView;
    TrackBar1->Min = 10;
    TrackBar1->Max = 120;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::UpdateImage()
{
    System::Classes::TMemoryStream* ResponseContent = new System::Classes::TMemoryStream;

    try
    {
        String URL = Format("http://maps.googleapis.com/maps/api/streetview?size=%dx%d&location=%s&sensor=false&heading=%d&fov=%d&pitch=%d",
            ARRAYOFCONST((
            (int)Image1->Width, // Image Width
            (int)Image1->Height, // Image Height
            EditLocation->Text, // Location
            FHeading, // Heading (0 to 360)
            FFieldOfView, // Field of view
            FPitch // Pitch
            )));

        IdHTTP1->Get(URL, ResponseContent); // Téléchargement de l'image

        Image1->Bitmap = new Fmx::Types::TBitmap(ResponseContent);
    }
    catch(...)
    {
    }

    delete ResponseContent;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::EditLocationKeyUp(TObject *Sender, WORD &Key,
          System::WideChar &KeyChar, TShiftState Shift)
{
    if(Key == vkReturn)
    {   // La touche Entrée a été appuyée, on met à jour l'image
        UpdateImage();
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::TrackBar1Change(TObject *Sender)
{
    FFieldOfView = TrackBar1->Value;
    UpdateImage();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonLeftClick(TObject *Sender)
{
    // Déplacement de la caméra d'un angle de 20° vers la gauche
    FHeading = (FHeading - 20) % 360;
    UpdateImage();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonRightClick(TObject *Sender)
{
    // Déplacement de la caméra d'un angle de 20° vers la droite
    FHeading = (FHeading + 20) % 360;
    UpdateImage();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonTopClick(TObject *Sender)
{
    if((FPitch += 10) > 90)
    {   // La caméra est complètement vers le haut
        FPitch = 90;
    }
    UpdateImage();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonBottomClick(TObject *Sender)
{
    if((FPitch -= 10) < -90)
    {   // La caméra est complètement vers le bas
        FPitch = -90;
    }
    UpdateImage();
}
//---------------------------------------------------------------------------

Le code qui est le plus important se trouve dans la méthode UpdateImage. Tous les évènements servent uniquement à modifier l’un des paramètres du URL.

J’aurais bien aimé utiliser le composant TLocationSensor disponible dans C++Builder XE3 pour aller chercher mes coordonnées de latitude et longitude, mais je n’ai pas le matériel nécessaire pour le tester. C’est dommage car ça me semble facile à utiliser.

Utiliser l’API de VirusTotal avec C++Builder

VirusTotal est un service gratuit qui permet la détection de virus, vers, chevaux de Troie ou tout autre type de programme malveillant. En visitant le site web de cette application, il est possible de choisir un fichier sur votre disque dur et de le soumettre à une analyse. Par la suite, plusieurs antivirus vérifieront si les données comportent des partie suspectes. Le résultat sera ensuite disponible en ligne.

Cet outil web est vraiment génial, mais ce qui l’est encore plus c’est qu’il existe une interface API publique que l’on peut utiliser avec des requêtes POST par HTTP. Les réponses seront en format JSON (JavaScript Object Notation).

Avant de commencer, il faudra vous inscrire pour obtenir une clef d’API. Elle sera affichée dans la section API de votre profil. Il est à noter qu’avec cette clef vous ne pourrez effectuer que quatre requêtes par minute.

Maintenant, on peut commencer à coder. La première étape est de créer un nouveau projet FireMonkey HD. Dans la Form, il faut insérer un TIdHTTP, un TIdSSLIOHandlerSocketOpenSSL, un TOpenDialog, un contrôle TEdit deux TButton et un TMemo.

Je vous propose de placer les composants dans la fenêtre de la manière suivante:
VirusTotal API

Vous pourrez aussi utiliser des textes similaires à ceux de l’image pour les différents contrôles.

Dans votre fichier cpp voici la liste de fichiers d’en-tête à utiliser ainsi que la macro qui va contenir votre clef d’API. N’essayez pas d’utiliser celle-ci, il s’agit de caractères écrits de façon aléatoire.

#define APIKEY "g9898984d0909534e1230958567f098090928712a8034cb0809786201854f678"
#include <Data.DBXJSON.hpp>
#include <Idmultipartformdata.hpp>

Voici le code à ajouter dans votre constructeur:

    IdHTTP1->IOHandler = IdSSLIOHandlerSocketOpenSSL1;

    OpenDialog1->Filter = "Application (*.exe)|*.EXE|Tous les fichiers|*.*";
    OpenDialog1->Options = System::Uitypes::TOpenOptions()
        << TOpenOption::ofHideReadOnly << TOpenOption::ofEnableSizing <<
        TOpenOption::ofFileMustExist;

Étant donné que nous accéderons à un site web qui utilise SSL (https), la première ligne de code est critique. Parce que nous utilisons OpenSSL, les fichiers ssleay32.dll et libeay32.dll devront être distribués avec votre application.

La prochaine étape est d’ajouter le code dans l’événement Button1Click.

    if(OpenDialog1->Execute() == false)
    {
        return;
    }

    Idmultipartformdata::TIdMultiPartFormDataStream* Params =
        new Idmultipartformdata::TIdMultiPartFormDataStream();
    Params->AddFormField("apikey", APIKEY);
    Params->AddFile("file", OpenDialog1->FileName, "application/octet-stream");

    System::UnicodeString Response = IdHTTP1->Post(
        "https://www.virustotal.com/vtapi/v2/file/scan", Params);

    TJSONObject* Obj = static_cast<TJSONObject*>
        (TJSONObject::ParseJSONValue(Response));
    if(Obj)
    {
        TJSONPair* Pair;
        TJSONString* Answer;
        if((Pair = Obj->Get("response_code")) != NULL)
        {
            Answer = static_cast<TJSONString*>(Pair->JsonValue);
            if(Answer->ToString() == "1")
            {
                if((Pair = Obj->Get("resource")) != NULL)
                {
                    Answer = static_cast<TJSONString*>(Pair->JsonValue);
                    Edit1->Text = AnsiDequotedStr(Answer->ToString(), '\"');
                }
            }
            else
            {
                Edit1->Text = "Erreur";
            }
        }
    }

    delete Params;

Ce code est utilisé pour la soumission du fichier choisi avec le TOpenDialog vers le serveur de VirusTotal. Si le code de la réponse est 1, ce qui signifie donc que la requête à réussi, la ressource sera affichée dans le TEdit.

Finalement, il faut ajouter ce code dans l’événement Button2Click.

    Idmultipartformdata::TIdMultiPartFormDataStream* Params =
        new Idmultipartformdata::TIdMultiPartFormDataStream();
    Params->AddFormField("apikey", APIKEY);
    Params->AddFormField("resource", Edit1->Text);

    System::UnicodeString Response = IdHTTP1->Post(
        "https://www.virustotal.com/vtapi/v2/file/report", Params);

    TJSONObject* Obj = static_cast<TJSONObject*>
        (TJSONObject::ParseJSONValue(Response));
    if(Obj)
    {
        TJSONPair* Pair = Obj->Get("scans");;
        if(Pair)
        {
            TJSONObject* Scans = static_cast<TJSONObject*>(Pair->JsonValue);
            TJSONPairEnumerator* Enumerator = Scans->GetEnumerator();
            while(Enumerator->MoveNext())
            {
                String AntivirusName =
                    AnsiDequotedStr(Enumerator->Current->JsonString->ToString(), '\"');
                Memo1->Lines->Add(AntivirusName);

                TJSONObject* AntivirusData =
                    static_cast<TJSONObject*>(Enumerator->Current->JsonValue);

                String Value;
                TJSONString* Answer;
                if((Pair = AntivirusData->Get("detected")) != NULL)
                {
                    Answer = static_cast<TJSONString*>(Pair->JsonValue);
                    Value = AnsiDequotedStr(Answer->ToString(), '\"');
                    Memo1->Lines->Add("   Detected: " + Value);
                }
                if((Pair = AntivirusData->Get("version")) != NULL)
                {
                    Answer = static_cast<TJSONString*>(Pair->JsonValue);
                    Value = AnsiDequotedStr(Answer->ToString(), '\"');
                    Memo1->Lines->Add("   Version: " + Value);
                }
                if((Pair = AntivirusData->Get("result")) != NULL)
                {
                    Answer = static_cast<TJSONString*>(Pair->JsonValue);
                    Value = AnsiDequotedStr(Answer->ToString(), '\"');
                    Memo1->Lines->Add("   Result: " + Value);
                }
                if((Pair = AntivirusData->Get("update")) != NULL)
                {
                    Answer = static_cast<TJSONString*>(Pair->JsonValue);
                    Value = AnsiDequotedStr(Answer->ToString(), '\"');
                    Memo1->Lines->Add("   Update: " + Value);
                }
            }
        }
    }
    delete Params;

Cette méthode servira à aller chercher le résultat d’une analyse pour la ressource entrée dans le TEdit. Le tout sera affiché dans le contrôle TMemo.

Il est important de savoir que les fichiers soumis par l’API ont la priorité d’analyse la plus basse. Dépendant de la charge de travail de VirusTotal, il pourrait être possible d’attendre plusieurs heures avant que le fichier soit analysé. Il est donc important d’appeler ce code à intervalle régulier pour s’assurer que tous les résultats sont disponibles.

La réponse de la requête POST ressemble à ceci:

{
   "scans":{
      "AVG":{
         "detected":false,
         "version":"10.0.0.1190",
         "result":null,
         "update":"20120826"
      },
      "Panda":{
         "detected":false,
         "version":"10.0.3.5",
         "result":null,
         "update":"20120826"
      }
   },
   "scan_id":"51a16f4a4cd8089b559a9fe694c6ac67eb0d4ad05ef564b797e5e8caf3c18813-1346036118",
   "sha1":"189f6287cd36c2f49bc828a556a70d960b7547b3",
   "resource":"51a16f4a4cd8089b559a9fe694c6ac67eb0d4ad05ef564b797e5e8caf3c18813",
   "response_code":1,
   "scan_date":"2012-08-27 02:55:18",
   "permalink":"https://www.virustotal.com/file/51a16f4a4cd8089b559a9fe694c6ac67eb0d4ad05ef564b797e5e8caf3c18813/analysis/1346036118/",
   "verbose_msg":"Scan finished, scan information embedded in this object",
   "total":2,
   "positives":0,
   "sha256":"51a16f4a4cd8089b559a9fe694c6ac67eb0d4ad05ef564b797e5e8caf3c18813",
   "md5":"463b0fe60365c7cc84325646aabeb907"
}

Pour plus de détail sur l’API vous pouvez consulter la page web suivante: https://www.virustotal.com/documentation/public-api