IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Delphi Discussion :

Equivalent Delphi 7 du MultipartFormDataContent


Sujet :

Langage Delphi

  1. #1
    Expert confirmé
    Avatar de popo
    Homme Profil pro
    Analyste programmeur Delphi / C#
    Inscrit en
    Mars 2005
    Messages
    2 674
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste programmeur Delphi / C#
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 2 674
    Points : 5 259
    Points
    5 259
    Par défaut Equivalent Delphi 7 du MultipartFormDataContent
    Bonjour,

    Je dois appeler une API pour déposer des documents mais le code d'exemple est en C# et je voulais éviter de faire une DLL à appeler en COM depuis Delphi.

    Je comptais remplacer
    FileStream par TFileStream
    HttpClient par IXMLHttpRequest
    PostAsync sur webclient par une gestion de thread classique

    Mais je n'ai aucune idée de l'objet Delphi 7 à utiliser pour le MultipartFormDataContent
    Edit : à priori, je ne trouve pas non plus d'équivalent pour StreamContent.

    Voici le code C#
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    using (FileStream fileStream = new FileStream(fichier, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, useAsync: false))
    {
        /* Create a stream content for the file */
        StreamContent content = new StreamContent(fileStream, BufferSize);
     
        // Create Multipart form data content, add our submitter data and our stream content
        MultipartFormDataContent formData = new MultipartFormDataContent();
        string d = DateTime.Now.ToString("yyyyMMddHHmmss");
        formData.Add(new StringContent(d), "t"); /* date de l'envoi */
        formData.Add(new StringContent(Version), "VERSION");
        formData.Add(new StringContent(Libelle), "LIBELLE");
        formData.Add(new StringContent(Description), "DESCRIPTION");
     
        /* Reference pour identifier le document. */
        string Reference = Guid.NewGuid().ToString();
        formData.Add(new StringContent(Reference), "REFERENCE");
        formData.Add(content, "filename", fichier); /* le fichier */
     
     
         // Post the MIME multipart form data upload with the file
        string url=string.Format(URLAPPLI + "api/" + "UploadFile");
        Uri address = new Uri(url);
        var webclient = new HttpClient();     
     
        /*calcul du timeout en fonction de la taille du document*/
        FileInfo file = new FileInfo(fichier);
        long Taille = file.Length;
        int timoutcalcule = (int)(Taille / 100000)+1;
        webclient.Timeout = new TimeSpan(0, 0, timoutcalcule);
     
        /* format du retour */
        //webclient.DefaultRequestHeaders.Add("Accept", "text/xml");
        webclient.DefaultRequestHeaders.Add("Accept", "text/json");
     
        // appel de L'API
        var task = webclient.PostAsync(address,formData);
        task.Wait();
        HttpResponseMessage response = task.Result;
     
        // retour en JSON
        var resp = response.Content.ReadAsAsync<RetResponse>().Result;
        if (resp.Success)
        {
            ret = resp.Message + "- Id : " + resp.Id + " - ID Interne : " + resp.InternalID.ToString();
        }
        else
        {
            ret = resp.Message + "- Id : " + resp.Id;
        }
    }

    Merci

  2. #2
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    IXMLHttpRequest me choque, du coup tu n'es plus en type MIME donc ce n'est plus du Multipart !

    Je note JSON, c'est une API REST ?
    Tu n'as pas du JSON en entrée et en sortie ?
    En D7, faut une lib externe mais cela se bricole avec une Template et un format ...

    genre

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    const
      GET_TOKEN_JSON_PARAM_FMT = '{"token":"%s","refresh_token":"%s","refresh_token_key":"%s"}'; // Version rapide sans passer par un TJSONObject
    ...
          Source.WriteString(Format(GET_TOKEN_JSON_PARAM_FMT, [FToken, FRefreshToken, FRefreshTokenKey]));
    Peut-être ton entrée c'est de POST classique genre ?p1=v1&p2=v2...Il n'y a rien de plus simple que de générer la chaine de paramètre à la main

    Pas besoin de composant externe, tu as les API Windows
    Tient un code pour envoyer n'importe quel paramètre pour n'importe quelle URL

    exemple avec pour chaque Input


    tu récupéras le fichier HTML généré par le forum

    C'est du code XE2 pour le convertir en D7
    LPWSTR -> LPSTR
    RawByteString -> string

    Tu as une version plus ancienne dans la FAQ : Comment télécharger un fichier en http sans utiliser Indy ?
    Tu en trouve d'autres sur le forum

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    const
      BufferSize = 1024;
      accept: packed array[0..1] of LPWSTR = (PChar('text/*'), nil); // PCTSTR rgpszAcceptTypes[] = {_T(“text/*”), NULL};
    var
      ServerName, URL, Params, LocalFileName: string;
      hSession, hHTTP, hReq : HINTERNET;
      Buffer: array[1..BufferSize] of Byte;
      BufferLen: DWORD;
      sAppName: string;
      FS: TFileStream;
      Headers: string;
      Data: RawByteString;
    begin
      sAppName := ExtractFileName(Application.ExeName);
     
      hSession := InternetOpen(PChar(sAppName), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
      try
        if not InputQuery('Download URL', 'Server Name', ServerName) then
          Exit;
     
        hHTTP := InternetConnect(hSession, PChar(ServerName), INTERNET_DEFAULT_HTTP_PORT, nil, nil, INTERNET_SERVICE_HTTP, 0, 1);
        try
          if not InputQuery('Download URL', 'URL', URL) then
            Exit;
     
          hReq := HttpOpenRequest(hHTTP, PChar('POST'), PChar(URL), nil, nil, @accept, 0, 1);
          try
            Headers := 'Content-Type: application/x-www-form-urlencoded; charset=utf-8';
     
            if not InputQuery('Download URL', 'Params', Params) then
              Exit;
     
            Data := UTF8Encode(Params);
            if HttpSendRequest(hReq, PChar(Headers), length(Headers), PAnsiChar(Data), length(Data)) then
            begin
              LocalFileName := StringReplace(URL, '/', '_', [rfReplaceAll]);
              if not PromptForFileName(LocalFileName, '', '.txt', 'Download URL into Local File', '', True) then
                Exit;
     
              FS := TFileStream.Create(LocalFileName, fmCreate);
              try
                BufferLen := 0;
                repeat
                  if InternetReadFile(hReq, @Buffer, BufferSize, BufferLen) then
                    FS.WriteBuffer(Buffer, BufferLen);
     
                until BufferLen = 0;
              finally
                FS.Free;
              end;
            end
            else
              raise Exception.Create('HttpOpenRequest failed. ' + SysErrorMessage(GetLastError));
          finally
            InternetCloseHandle(hReq);
          end;
        finally
          InternetCloseHandle(hHTTP);
        end;
      finally
        InternetCloseHandle(hSession);
      end;
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  3. #3
    Expert confirmé
    Avatar de popo
    Homme Profil pro
    Analyste programmeur Delphi / C#
    Inscrit en
    Mars 2005
    Messages
    2 674
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste programmeur Delphi / C#
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 2 674
    Points : 5 259
    Points
    5 259
    Par défaut
    Effectivement, c'est du JSON.
    En entrée comme en sortie ça m'a été confirmé par le développeur de l'api.
    Former du JSON ne devrait pas poser de problème je pense (j'ai déjà SuperObject dans le projet).

    Utiliser les API Windows, tu veux certainement parler de ce qui se trouve dans WinInet.

    Je ne suis pas très à l'aise avec le sujet et je n'avais pas conscience de la notion de multi part.
    ça me donne à présent une idée de ce que je dois chercher.

    Au fait cette URL ne donne rien.
    http://www.developpez.net/forums/sea...archid=9661936

  4. #4
    Expert confirmé
    Avatar de popo
    Homme Profil pro
    Analyste programmeur Delphi / C#
    Inscrit en
    Mars 2005
    Messages
    2 674
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste programmeur Delphi / C#
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 2 674
    Points : 5 259
    Points
    5 259
    Par défaut
    J'ai trouvé cette page qui à l'air pas mal.
    http://www.delphigroups.info/2/5/492268.html
    J'essaie de bricoler un truc et je vous tiens au courant

  5. #5
    Expert confirmé
    Avatar de popo
    Homme Profil pro
    Analyste programmeur Delphi / C#
    Inscrit en
    Mars 2005
    Messages
    2 674
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste programmeur Delphi / C#
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 2 674
    Points : 5 259
    Points
    5 259
    Par défaut
    Un coup dans l'eau.
    Je ne peux pas utiliser Indy.
    A priori, ça ne passerait pas les proxy.

    Tout les exemples utilisant directement wininet que j'ai pu trouver envoient simplement du texte.
    J'ai besoin d'envoyer n'importe quel fichier (surtout du PDF et de documents office).

    Je n'arrive pas non plus à comprendre sous quelle forme je dois passer les informations.
    Puisqu'il s'agit de JSON je pensais à passer un truc de ce genre via la méthode Send:
    Code JSON : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    {
    	"t": "20161216095800",
    	"VERSION": "Ma version",
    	"LIBELLE": "Un libellé",
    	"DESCRIPTION": "Une description",
    	"REFERENCE": "4DA9B350-1C9B-479E-8C62-D5AAF1296A16"
    }

    Mais comment intégrer mon flux là dedans ?

  6. #6
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    As-tu vu mon code dans ma réponse précédente utilisant HttpSendRequest qui utilise justement une RawByteString parce que la donnée doit être géré comme un Binaire
    En D7, utilise un Array of Byte et un TFileStream (ou même un TMemoryStream)

    voici les extraits qui change

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
      Headers: string;
      Data: RawByteString;
    begin
    devient
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
      Headers: string;
      Data: array of Byte;
      FS: TFileStream;
    begin
    ou
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
      Headers: string;
      FM: TMemoryStream;
      FS: TFileStream;
    begin
    .

    ainsi que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Data := UTF8Encode(Params);
    if HttpSendRequest(hReq, PChar(Headers), length(Headers), PAnsiChar(Data), length(Data)) then
    begin
    devient
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    FS.LoadFromFile(LePFD);
    SetLength(Data, FS.Size);
    FS.Read(Data[0], FS.Size);
    if HttpSendRequest(hReq, PChar(Headers), length(Headers), PAnsiChar(Data[0]), length(Data)) then
    begin
    ou
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    FS.LoadFromFile(LePFD);
    FS.Seek(0, soFromBeginning); // en D7, je sais plus si c'est soFromBeginning ou soBeginning
    FM.LoadFromStream(FS);
    if HttpSendRequest(hReq, PChar(Headers), length(Headers), PAnsiChar(FM.Memory), length(Data)) then
    begin

    Pour le JSON, je pense qu'un encodage UTF8 est nécessaire
    Pour un PDF, tout dépend comment tu dois l'envoyer,
    - Si c'est directement, le binaire, tu dois voir avec l'auteur ce que tu dois mettre comme ContentType, CharSet et ContentEncoding, ce qui donne un truc genre 'Content-Type: application/octet-stream'.
    - Si c'est via un JSON, cela nécessite probablement un encodage en Base64 qui a l'avantage de n'utiliser QUE des caractères basiques qui ne souffrent d'aucune manipulation de CharSet donc 'Content-Type: application/json; charset=utf-8'.

    Un JSON avec le PDF en Binaire

    Code json : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    {
    	"t": "20161216095800",
    	"VERSION": "Ma version",
    	"LIBELLE": "Un libellé",
    	"DESCRIPTION": "Une description",
    	"REFERENCE": "4DA9B350-1C9B-479E-8C62-D5AAF1296A16"
            "Content": "AAB3... un tas de lettre ...G5Z6=="
    }
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  7. #7
    Expert confirmé
    Avatar de popo
    Homme Profil pro
    Analyste programmeur Delphi / C#
    Inscrit en
    Mars 2005
    Messages
    2 674
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste programmeur Delphi / C#
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 2 674
    Points : 5 259
    Points
    5 259
    Par défaut
    Bonjour et encore merci Shai,

    Ces informations m'ont grandement servi !
    Mais à priori, les informations ne semblaient pas avoir le bon format.
    J'ai ensuite cherché à reproduire une partie de ce que fait Indy avant de réaliser que l'unique partie qui ne me vas pas c'est l'envoi de la requête.
    Au final je laisse Indy formater le message correctement et je copie simplement le flux
    J'ai trouvé une astuce pour transformer un TIdMultiPartFormDataStream en flux compréhensible.
    ICI : http://stackoverflow.com/questions/3...inet-on-delphi

    L'astuce en question.
    C'est sensiblement ce que tu m'as proposé sauf que c'est le flux Indy que je copie.
    Code Delphi : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    PostData := TMemoryStream.Create;
    try
      PostData.CopyFrom(StreamContent, 0);
      ....
    finally
      FreeAndNil(PostData);
    end;

    Mais mon erreur principal a été de mettre le 'http://' dans le nom du serveur.

    Le code final et épuré de toutes les spécificités métiers :
    Code Delphi : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    Url := '/api/UploadFileBOW/' + CryptedId;
        server := UrlAppli;
        if blnSSL then
        begin
           Port := INTERNET_DEFAULT_HTTPS_PORT
        end
        else
        begin
           Port := 8080;
        end;
        pConnection := InternetConnect(pSession, PChar(Server), port, nil, nil, INTERNET_SERVICE_HTTP, 0, 0);
        if not Assigned(pConnection) then
           raise Exception.Create('InternetConnect failed. ' + IntToStr(GetLastError));
        try
           if blnSSL then
              flags := INTERNET_FLAG_SECURE
           else
              flags := 0;
           pRequest := HTTPOpenRequest(pConnection, 'POST', PChar(Url), nil, nil, nil, flags, 0);
           if not Assigned(pRequest) then
              raise Exception.Create('HttpOpenRequest failed. ' + IntToStr(GetLastError));
           try
              // Set buffer size
              dwSize:=SizeOf(dwFlags);
              // Get the current flags
              if (InternetQueryOption(pRequest, INTERNET_OPTION_SECURITY_FLAGS, @dwFlags, dwSize)) then
              begin
                 // Add desired flags
                 dwFlags:=dwFlags or SECURITY_FLAG_IGNORE_UNKNOWN_CA or SECURITY_FLAG_IGNORE_CERT_CN_INVALID or SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
                 // Set new flags
                 if not(InternetSetOption(pRequest, INTERNET_OPTION_SECURITY_FLAGS, @dwFlags, dwSize)) then
                 begin
                    // Get error code
                    dwError:=GetLastError;
                    Return := IntToStr(dwError);
                    // Failure
                 end;
              end
              else
              begin
                 // Get error code
                 dwError:=GetLastError;
                 // Failure
                 Return := IntToStr(dwError);
              end;
     
              PostData := TMemoryStream.Create;
              try
                Parameters := TIdMultiPartFormDataStream.Create;
                try
                  Parameters.AddFormField('t', Time);
                  Parameters.AddFormField('CANAL', Canal);
                  Parameters.AddFormField('VERSION', Version);
                  Parameters.AddFormField('DESCRIPTION', Description);
                  Parameters.AddFile('filename', UpdloadFileName, '');
     
                  PostData.CopyFrom(Parameters, 0);
     
                  Header := 'Accept: text/json'+SLineBreak
                   + 'Content-Type: ' + Parameters.RequestContentType + #13#10;
                finally
                  FreeAndNil(Parameters);
                end;
     
                if not HttpAddRequestHeaders(pRequest, PChar(Header), Length(Header), HTTP_ADDREQ_FLAG_ADD) then
                  raise Exception.Create('HttpAddRequestHeaders failed. ' + IntToStr(GetLastError));
                if not HTTPSendRequest(pRequest, nil, 0, PostData.Memory, PostData.Size) then
                  raise Exception.Create('HTTPSendRequest failed. ' + IntToStr(GetLastError));
     
                BufferLength:=SizeOf(StatusCode);
                Reserved    :=0;
     
                HttpQueryInfo(pRequest, HTTP_QUERY_STATUS_CODE or HTTP_QUERY_FLAG_NUMBER, @StatusCode, BufferLength, Reserved);
     
                BufStream := TMemoryStream.Create;
                try
                  while InternetReadFile(pRequest, @aBuffer, SizeOf(aBuffer), BytesRead) do
                    begin
                      if (BytesRead = 0) then Break;
                      BufStream.Write(aBuffer, BytesRead);
                    end;
                  aBuffer[0] := #0;
                  BufStream.Write(aBuffer, 1);
                  Return := PChar(BufStream.Memory);
                finally
                  FreeAndNil(BufStream);
                end;
              finally
                  FreeAndNil(PostData);
              end;
     
           finally
              InternetCloseHandle(pRequest);
           end;
        finally
           InternetCloseHandle(pConnection);
        end;
      finally
        InternetCloseHandle(pSession);
      end;

    Il ne me reste plus qu'a refactorer et placer ça dans un thread pour obtenir le même fonctionnel.

    Encore un grand merci.
    Je passe en résolu.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. equivalent delphi en c#
    Par b.grellee dans le forum Windows Forms
    Réponses: 7
    Dernier message: 10/04/2009, 12h12
  2. Equivalence Delphi / C++
    Par ambessa dans le forum Langage
    Réponses: 1
    Dernier message: 09/07/2008, 15h50
  3. Environnement equivalent à Delphi et C++ Builder
    Par b_reda31 dans le forum Langages de programmation
    Réponses: 2
    Dernier message: 04/02/2008, 23h37
  4. Equivalents Delphi de quelques variables VB
    Par AL1986 dans le forum Langage
    Réponses: 6
    Dernier message: 19/08/2007, 12h26
  5. Equivalent Delphi de la classe App de VB
    Par AL1986 dans le forum Langage
    Réponses: 7
    Dernier message: 07/08/2007, 17h07

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo