Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Lecture de mails, collecte de besoins

31 réponses
Avatar
Gloops
Bonjour tout le monde,

J'=E9tais habitu=E9 avec d'autres langages =E0 trouver =E0 t=E9l=E9charge=
r des=20
routines assez simples =E0 utiliser pour lire des mails. D'ailleurs c'est=
=20
simple, =E0 ce que je me rappelle sur salemioche.com (mais d=E9m=E9nag=E9=
=20
depuis) on trouvait une page qui pr=E9sentait le protocole POP, et par=20
ailleurs l'impl=E9mentation en VB6.

Sous C#, j'ai l'impression qu'il y a eu moins de monde sur la question.

J'ai quand m=EAme trouv=E9 quelque chose d'int=E9ressant l=E0 :
http://www.csharpfr.com/codes/CLIENT-POP3_11467.aspx

En client lourd on trouve l=E0 un travail sur les pi=E8ces jointes, qui p=
eut=20
n=E9cessiter quelques petites mises au point mais reste int=E9ressant com=
me=20
point de d=E9part sur ce sujet, mais est plus lourd =E0 adapter en client=
=20
l=E9ger :
http://www.csharpfr.com/codes/INTERFACE-POPMAIL-PIECES-JOINTES_11559.aspx=


Pour rappel le client l=E9ger est l'application qui s'attache =E0 affiche=
r=20
le plus rapidement possible la liste des mails re=E7us, et pour favoriser=
=20
la rapidit=E9 r=E9alise peu ou pas de traitement sur le corps de chaque=20
mail. J'en ai par exemple r=E9alis=E9 un avec un reportViewer, ce qui per=
met=20
d'imprimer la liste des mails, qui peut =EAtre int=E9ressante avant de=20
purger le serveur.

Il me semble bien avoir travaill=E9 aussi avec quelque chose=20
d'interm=E9diaire entre le client l=E9ger et le traitement des pi=E8ces=20
jointes, mais ce que je retrouve =E0 cet instant est en VB6.

A pr=E9sent, comme j'ai plusieurs applications =E0 =E9crire faisant inter=
venir=20
des mails, je m'attaque =E0 une DLL =E0 appeler depuis une application=20
WinForm ou un site web. =E7a commence =E0 fonctionner, je suppose que ce =
qui=20
va arriver est que je vais mettre en =9Cuvre ce qui manque sur mon site, =

et revenir l=E0-dessus ensuite.

Ce type de d=E9veloppement =E9tant destin=E9 =E0 =EAtre utilis=E9 dans de=
nombreuses=20
applications, il est pr=E9f=E9rable qu'il corresponde aux souhaits d'un=20
grand nombre. Aussi, j'=E9num=E8re les grandes lignes, de fa=E7on qu'on p=
uisse=20
m'indiquer quelque chose qui para=EEt manquer.

Par ailleurs, un travail (non publi=E9 me semble-t-il) a =E9t=E9 r=E9alis=
=E9 sur=20
le premier projet mentionn=E9 ci-dessus pour d=E9coder les sujets (et=20
accessoirement les noms des correspondants puisqu'ils peuvent aussi =EAtr=
e=20
=E9crits avec des caract=E8res accentu=E9s), toutefois ce travail ne g=E8=
re pas=20
encore les UTF8. En cherchant quelque chose l=E0-dessus je vais bien=20
trouver, est-ce que je peux cette fois esp=E9rer arriver =E0 quelque chos=
e=20
d'exhaustif sur le d=E9codage des sujets ? J'ai commenc=E9 par chercher=20
quelque chose de tout pr=EAt, mais en vain. A ce stade le d=E9codage=20
fonctionne en jeu de caract=E8res constant.

A ce stade, voici ce qui fonctionne sur ce dont je parle :
- connexion avec les informations re=E7ues en param=E8tres du constructeu=
r,=20
site, nom d'utilisateur, mot de passe ; le tout sur le port 110 en=20
protocole POP
- information de l'utilisateur =E0 l'aide de deux d=E9l=E9gu=E9s, fournis=
sant=20
deux niveaux d'information (ce que sur le projet ci-dessus on trouve=20
respectivement dans la barre d'=E9tat et le richtextbox).
- sur chaque message il est possible d'obtenir la date, le sujet,=20
l'=E9metteur (from), le destinataire (to), du dernier message lu, et=20
l'int=E9gralit=E9 du message, ceci n=E9cessitant bien entendu une connexi=
on=20
plus longue (par d=E9faut on ne lit que les 30 premi=E8res lignes du mess=
age=20
pour avoir les ent=EAtes, et lorsque l'application appelante demande=20
l'int=E9gralit=E9 du message on relance une lecture sur le serveur)
- =E0 r=E9aliser dans quelques semaines : internationalisation de ce=20
traitement, pour le moment les messages sont conserv=E9s dans la langue=20
d'origine

Au point o=F9 j'en suis le stockage des informations de connexion puis de=
s=20
mails re=E7us est du ressort de l'application appelante. Il ne me para=EE=
t=20
d'ailleurs pas exclu d'en rester l=E0 sur ce point, d'autant que le=20
stockage de param=E8tres d'application crypt=E9s se fait de mani=E8re=20
diff=E9rente en Winform et en Webform.

10 réponses

1 2 3 4
Avatar
Gloops
Histoire de conserver mon serveur j'ai modifié quelques bricoles, et en
appuyant sur F10 au lieu de F11 j'ai juste eu le temps d'apercevoir un
truc : +OK Message 1 deleted.
(et de m'apercevoir que mon application imap ne se synchronise pas
exactement au moment où je le croyais)

Bon alors il se confirme qu'il n'y avait pas une astuce à chercher et
que j'étais sur une fausse piste.

Il s'agit que je m'assois posément pour bien comprendre la structure du
programme, si ça se trouve à un endroit j'avais un dépassement de p ile
qui a été mal géré -ou quelque chose du même style, car dépas sement de
pile il me semble que Visual Studio sait gérer, du moins dans un certai n
nombre de cas.

Merci pour ces lumières.

TcpClient client;
//SslStream stream;
private System.Net.Sockets.TcpClient sockServer;
public System.Net.Sockets.NetworkStream ns;
private System.IO.StreamReader stream;

private string Read()
{
char[] buffer = new char[1024];
int length = stream.Read(buffer, 0, buffer.Length);
byte[] bbuf =
System.Text.Encoding.ASCII.GetBytes(buffer, 0, length);
return System.Text.Encoding.ASCII.GetString(bbuf);
TcpClient client;
//SslStream stream;
private System.Net.Sockets.TcpClient sockServer;
public System.Net.Sockets.NetworkStream ns;
private System.IO.StreamReader stream;

private string Read()
{
char[] buffer = new char[1024];
int length = stream.Read(buffer, 0, buffer.Length);
byte[] bbuf =
System.Text.Encoding.ASCII.GetBytes(buffer, 0, length);
return System.Text.Encoding.ASCII.GetString(bbuf);

// ah tiens voilà qui va m'éviter d'ouvrir un fil
// pour demander comment on convertit
// un tableau de bytes en chaîne de caractères :)

}

private void Write(string Data)
{
byte[] buffer = System.Text.Encoding.ASCII.GetBytes(Da ta);
ns.Write(buffer, 0, buffer.Length);
}

}

private void Write(string Data)
{
byte[] buffer = System.Text.Encoding.ASCII.GetBytes(Da ta);
ns.Write(buffer, 0, buffer.Length);
}


Patrice a écrit, le 05/09/2009 14:19 :
Donc je pensais à qq chose comme le code ci-dessous (éventuellement
modifier si ton POP n'utilise pas SSL). En réponse à mon DELE 1 j' ai
une réponse +OK marked for deletion.

Si tu ne vois pas ce qui cloche il faudrait que tu fasses qq chose du
même genre (donc réduit à a plus simple expression) et que tu mon tres le
code. Cela permettrait sans doute d'avancer plus efficacement sur ton
problème...

using System;
using System.Diagnostics;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.IO;

namespace ConsoleApplication1
{
class Program
{
class Pop3
{
TcpClient client;
SslStream stream;

private string Read()
{
byte[] buffer=new byte[1024];
int length=stream.Read(buffer,0,buffer.Length);
return
System.Text.Encoding.ASCII.GetString(buffer,0,length);
}

private void Write(string Data)
{
byte[] buffer=System.Text.Encoding.ASCII.GetBytes(Data );
stream.Write(buffer);
}

public Pop3()
{
const string User="?";
const string Password="?";
const string Hostname="pop.gmail.com";
client = new TcpClient(Hostname, 995);
// GMail utilise SSL
stream=new SslStream(client.GetStream(),true);
stream.AuthenticateAsClient(Hostname);
Console.WriteLine(Read());
Write(string.Format("USER {0}rn",User));
Console.WriteLine(Read());
Write(string.Format("PASS {0}rn",Password));
Console.WriteLine(Read());
Write("DELE 1rn");
Console.WriteLine(Read());
}
}

static void Main(string[] args)
{
Pop3 client = new Pop3();
Console.ReadKey();
}
}
}




Avatar
Gloops
Bon, je crois que j'ai avancé dans la compréhension du problème.

Donc, je suis parti d'un client léger qui fonctionne, à part qu'il
n'implémente pas la commande DELE mais vraisemblablement on peut
l'ajouter sans problème.

J'ai fait migrer tous les objets utiles dans une classe Mail, comportant
les streams, et aussi les propriétés d'un message mail.

L'ouverture des flux se fait dans le constructeur de cette classe Mail.

Cette classe présente en propriétés publiques ce qu'il faut pour li re
les informations depuis le programme appelant.

Une méthode transfère les propriétés d'un mail vers les proprié tés de la
classe :

public void fillMessObj(int i)
{
//pB.Increment(1);
Message("Retrieve Message " + i.ToString() + " ...");
int[] index = { i };
int intSizeMsg = getNewMessInfo(2, 2, index);
//send("retr " + i.ToString() + "rn");
send("TOP " + i.ToString() + " 30rn");
iNumMessage = i;
parseMail(intSizeMsg);
}
et parseMail comme son nom l'indique fait tout le boulot, on y trouve
par exemple :
szTemp = sr.ReadLine();
if ((temp = szTemp.Trim().IndexOf("To:")) == 0)
szTo = szTemp.Trim().Substring(3,
szTemp.Trim().Length - 3);


Et tout ça fonctionne très bien.

Comme je le disais, les choses se sont gâtées lorsque j'ai voulu effa cer
un mail :

public void delMail(int i)
{
((StreamReader)sr).BaseStream.Flush();
((StreamReader)sr).DiscardBufferedData();
string strComm = "DELE " + i.ToString();
send(strComm);
aLog();
}

et on perd le contrôle, même en mode pas à pas, dès qu'on cherche à lire
la réponse du serveur.

A présent, j'ai créé deux getters pour rendre publics les streams n s et
sr, ce qui m'a permis d'envoyer la commande depuis le programme principal .

En réponse à DELE je commence par avoir une chaîne vide en retour, mais
ensuite j'ai bien "+OK 1 message deleted."

Si je mets un autre ReadLine après je ne reprends pas le contrôle, ma is
ça c'est de bonne guère.

Pour l'émission j'ai dans le programme appelant :

private void send(String bToSend)
{
Mail.Reader.BaseStream.Flush();
Byte[] bOutStream;
Mail.stream.Write(bOutStream = bCast(bToSend), 0, bOutStream.Length);
}

Ah oui, stream, c'est ça, dans la classe Mail :
public System.Net.Sockets.NetworkStream stream
{
get { return ns; }
//set { myVar = value; }
}

et de même reader retourne sr.

et pour la réception :
strRetour = Mail.Reader.ReadLine();

et comme ça, ça marche, le souci étant alors que ça réduit trè s
sensiblement l'intérêt d'avoir écrit une dll pour externaliser le
traitement des mails.



Le souci apparaît lorsque la commande est passée dans la classe Mail,
connue en référence sous forme de dll, et je n'ai observé ce problè me
que pour la commande DELE.

Bon là je vais laisser mûrir un peu, parce que j'avoue que ça me la isse
un peu perplexe, quand même.
Avatar
Fred
in news:, Gloops wrote :

Pour résumer un peu :

send("TOP " + i.ToString() + " 30rn");



fonctionne
et

string strComm = "DELE " + i.ToString();
send(strComm);



ne fonctionne pas ?
Curieux en effet, c'est presque la même commande.
Tiens nous au courant de tes recherches !


--
Fred

Avatar
Gloops
Fred a écrit, le 05/09/2009 17:32 :
in news:, Gloops wrote :

Pour résumer un peu :

send("TOP " + i.ToString() + " 30rn");



fonctionne
et

string strComm = "DELE " + i.ToString();
send(strComm);



ne fonctionne pas ?
Curieux en effet, c'est presque la même commande.
Tiens nous au courant de tes recherches !





C'est exactement ça, enfin via la dll. C'est bien parce que c'est
bizarre qu'on a mis du temps à comprendre ça.

J'ai bien regardé ma dll, ce n'est pas juste un message qu'elle
implémente, c'est un objet mail, représentant une connexion qui reste
ouverte, et qui comporte, en propriétés, des copies des propriété s du
message mail (dont on a passé le numéro par la méthode fillMessObj (que
j'ai modifiée pour ne traiter qu'un message à la fois, afin que ce so it
l'application appelante qui réalise la boucle). Donc même une fois le
message en question supprimé, même si on a gardé quelques chaînes de
caractères qui représentent ce qu'il y avait dedans, on n'appelle pas
d'objet supprimé, d'ailleurs sinon ce ne serait pas plantage, mais
NullReferenceException. Enfin du moins il me semble.

Alors je crois que nous sommes d'accord sur la conclusion à ce stade :
c'est curieux.


Je ne sais pas si on peut faire quelque chose avec la ligne vide en
réponse à DELE, avant la vraie réponse. Dans Telnet, la réponse a rrive
juste après DELE, si il y a une ligne vide on ne la voit pas.

En revanche j'imagine que ça peut éclairer que les flux, rendus publi cs,
et appelés dans le programme principal, fonctionnent toujours.

Peut-être que je finirai par essayer d'envoyer le DELE via la dll, et d e
lire la réponse depuis le programme principal, d'ailleurs si ça march e
comme ça ce serait moindre mal puisque c'est surtout en émission que la
syntaxe est un peu lourde.

[HS] Pour la commande TOP 1 30 : j'ai relu la syntaxe de POP, cette
commande retourne les entêtes plus 30 lignes du corps du message. Si on
ne veut que les entêtes (officiels) la commande c'est TOP 1 0 pour le
premier message (sauf que comme j'ai un mot de passe qui ne fait pas
partie des entêtes officiels, et qu'il est sur la deuxième ligne du
corps, pour l'avoir il faudra au moins TOP 1 2 pour le premier message).
Bon enfin là l'enjeu est de quelques secondes sur de nombreux messages ...
Avatar
Patrice
De mon côté, si je reprends le code complet de tout à l'heure et que
j'ajoute :
...
Write(String.Format("NOOPn"));
//Console.Write(Read());
Write(String.Format("NOOPn"));
Console.Write(Read());
Write(String.Format("NOOPn"));
Console.Write(Read());
Write(String.Format("NOOPn"));
Console.Write(Read());
Write(String.Format("NOOPn"));
Console.Write(Read());
Write("DELE 1n");
Console.Write(Read());
...

Le fait de ne pas lire le résultat du premier NOOP fait que le DELE fait
pourtant bien après ne retournera toujours rien. Si j'enlève la ligne en
commentaire, cela marche à nouveau. Cela me fait dire que le mieux est sans
doute d'avoir une méthode unique qui envoie le message et lit la réponse
correspondante.

As tu essayer par exemple de faire un DELE juste après t'être connecté sans
rien faire d'autre auparavant. Cela permettra de voir si tu as réellement un
problème sur la commande DELE, ou si, comme je le soupçonne, tu as un
problème avant, par exemple dans la lecture des infos sur les messages,
problème qui pertube la suite de la communication...

Difficile d'en dire plus tant que tu ne nous fourniras pas du code qui nous
permetrait de voir de visu le problème...

--
Patrice
Avatar
Gloops
Gloops a écrit, le 04/09/2009 11:31 :
[Perte de contrôle en réponse à la commande POP DELE]
[seulement dans le cas où cette commande est passée via une dll]

Je crois que je tiens une piste, même si je n'ai pas vraiment fini de
l'exploiter.

L'objet NetworkStream, retourné par la méthode TcpClient.GetStream(), a
une propriété publique int ReadTimeout, dont voici la description :

Substitué. Obtient ou définit la durée pendant laquelle une opéra tion de
lecture reste bloquée en attendant des données.

Par défaut cette propriété, dans le flux défini par la dll, est à -1, ce
qui signifie qu'on attend indéfiniment. Il est possible de définir un e
valeur plus élevée, ce qui signifie qu'au-delà du temps indiqué p ar
l'entier en question, le programme reprend le contrôle. Il ne faut alor s
pas oublier de traiter l'exception par un try ... catch, et y indiquer
le retour d'une chaîne vide.

Je n'ai pas d'explication certaine quant au fait que l'exécution se fai t
depuis le programme principal mais pas depuis la dll, mais il n'est pas
interdit de penser que le temps de passage des informations entre les
deux joue un rôle. En tout cas, maintenant que j'ai trouvé comment
éviter que mon programme reste bloqué, je vais pouvoir tester divers
enchaînements et il devrait finir par y avoir moyen d'obtenir le bon
résultat.

ça me plaisait bien timeout = -1, comme ça le serveur avait le temp s de
répondre, mais si il se trouve que sa première réponse est revenue avant
que le programme commence vraiment à la guetter ...

Peut-être bientôt d'autres nouvelles, dans ces conditions.
Avatar
Gloops
Ah, là, une fois que c'est dit ça paraît tout bête, mais je crois que tu
as dit quelque chose de très puissant, là, en regroupant écriture e t
lecture. D'ailleurs, dans le Telnet, c'est ce qui se fait.
Et puis le coup du DELE qui réagit à quelque chose de commis bien ava nt,
je dois dire que ça explique bien des choses.

Et puis pour progresser dans le même sens de façon plus ... besogneus e,
disons, les délégués auraient pu me permettre de créer une
journalisation assez simplement pour y voir plus clair. Normalement j'ai
d'office l'écriture dans la fenêtre Output, mais finalement là-deda ns il
y a du monde, donc je n'ai même pas beaucoup cherché à y lire quelq ue chose.

Finalement, en insistant un peu, peut-être que je ne serai pas obligé de
tricher côté Timeout.
______________________________________
Patrice a écrit, le 05/09/2009 18:54 :
De mon côté, si je reprends le code complet de tout à l'heure et que
j'ajoute :
....
Write(String.Format("NOOPn"));
//Console.Write(Read());
Write(String.Format("NOOPn"));
Console.Write(Read());
Write(String.Format("NOOPn"));
Console.Write(Read());
Write(String.Format("NOOPn"));
Console.Write(Read());
Write(String.Format("NOOPn"));
Console.Write(Read());
Write("DELE 1n");
Console.Write(Read());
....

Le fait de ne pas lire le résultat du premier NOOP fait que le DELE f ait
pourtant bien après ne retournera toujours rien. Si j'enlève la lig ne en
commentaire, cela marche à nouveau. Cela me fait dire que le mieux es t
sans doute d'avoir une méthode unique qui envoie le message et lit la
réponse correspondante.

As tu essayer par exemple de faire un DELE juste après t'être conne cté
sans rien faire d'autre auparavant. Cela permettra de voir si tu as
réellement un problème sur la commande DELE, ou si, comme je le
soupçonne, tu as un problème avant, par exemple dans la lecture des
infos sur les messages, problème qui pertube la suite de la
communication...

Difficile d'en dire plus tant que tu ne nous fourniras pas du code qui
nous permetrait de voir de visu le problème...

--
Patrice


Avatar
Gloops
Gloops a écrit, le 05/09/2009 19:06 :
Gloops a écrit, le 04/09/2009 11:31 :
[Perte de contrôle en réponse à la commande POP DELE]
[seulement dans le cas où cette commande est passée via une dll]




Bon, eh bien voilà, c'est fait, on dirait bien.
Je n'ai pas passé l'après-midi que j'avais prévue, mais au moins
maintenant je peux effacer un message avec ce module.

Merci beaucoup à ceux qui m'ont encouragé et apporté leur précieu x
concours, particulièrement Patrice.

Il y avait déjà des chaînes de réponse non lues pendant la phase de
connexion, donc ça démarrait déjà très fort.

Ensuite j'ai refait le parcours pour les TOP et RETR, où je ne prenais
pas le bon point de repère pour la fin du message. En fait c'est tout
simple il suffisait de se rappeler que le signal de fin est donné par
une ligne qui ne comporte qu'un point.

Après je me suis encore rendu compte que j'avais oublié le retour de
ligne à la fin du DELE (à moins que je l'aie enlevé en route), et p uis
maintenant c'est bon. A peu près en même temps que je me suis décid é à
envoyer les messages qu'il faut à l'écran, avec le délégué dé taillé.
Maintenant je vais tester sans rien lui affecter, pour vérifier ce
qu'affiche le délégué synthétique.

Je me demande si ça vaut le coup d'implémenter un appel à ANSI.SYS pour
afficher les envois dans une couleur et les réponses dans une autre, en
mettant les préfixes voulus.
Avatar
Gloops
Gloops a écrit, le 06/09/2009 02:11 :
Gloops a écrit, le 05/09/2009 19:06 :
Gloops a écrit, le 04/09/2009 11:31 :
[Perte de contrôle en réponse à la commande POP DELE]
[seulement dans le cas où cette commande est passée via une dll]




Bon, eh bien voilà, c'est fait, on dirait bien.




Complément d'information : maintenant, c'est sur QUIT, que je ne reço is
pas la réponse.

Bon, on ne va pas se laisser bloquer par ça, j'ai mis un timeout, et on
se déconnecte effectivement, ce qui confirme les suppressions de messag es.
Avatar
Patrice
Ca marche chez moi (+OK Farewell).

Je fais finalement qq chose comme :

private string Read()
{
byte[] buffer=new byte[client.ReceiveBufferSize];
int length=stream.Read(buffer,0,buffer.Length);
return
System.Text.Encoding.ASCII.GetString(buffer,0,length);
}

private string Request(string Data)
{
const string CrLf="rn";
byte[]
buffer=System.Text.Encoding.ASCII.GetBytes(Data+CrLf);
stream.Write(buffer);
return Read();
}

ce qui permet :
- pour le Request, de mettre automatiquement le CrLf qui va bien plutôt que
de le mettre explictitement
- toujours pour le request de lire la réponse dans la foulée.

Content de voir en tout cas que cela commence à prendre forme de ton côté...

--
Patrice


"Gloops" a écrit dans le message de
news:
Gloops a écrit, le 06/09/2009 02:11 :
Gloops a écrit, le 05/09/2009 19:06 :
Gloops a écrit, le 04/09/2009 11:31 :
[Perte de contrôle en réponse à la commande POP DELE]
[seulement dans le cas où cette commande est passée via une dll]




Bon, eh bien voilà, c'est fait, on dirait bien.




Complément d'information : maintenant, c'est sur QUIT, que je ne reçois
pas la réponse.

Bon, on ne va pas se laisser bloquer par ça, j'ai mis un timeout, et on
se déconnecte effectivement, ce qui confirme les suppressions de messages.
1 2 3 4