Toon posts:

[PHP/ZEND] E-mail piping

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ik probeer een supportsysteem met Zend Framework te maken waarbij e-mails die verzonden worden naar een bepaald e-mailadres in een database gezet moeten worden.

In DirectAdmin heb ik een forwarder aangemaakt. Alle e-mails die worden verzonden naar pipe@domein.nl worden geforward naar "| /usr/local/bin/php -q /home/[gebruikersnaam]/domains/[domein.nl]/public_html/klanten/[klantnaam]/pipe/catch"

Ik heb dus een PipeController met een catchAction die er zo uitziet:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
class PipeController extends Zend_Controller_Action {
    public function init() {
        
    }
    public function catchAction() {
        // disable view
        $this->_helper->viewRenderer->setNoRender(true);
        // get database resource
        $db = Zend_Registry::get('db');
        ## START MAIL SECTION ##
        $mail = new Zend_Mail_Storage_Maildir(array('dirname' => '/home/[gebruikersnaam]/Maildir/'));
        $value = $mail;
        ## END MAIL SECTION ##
        // insert data into test table
        $data['value'] = $value;
        $data['date'] = date('Y-m-d H:i:s');
        $db->insert('tests', $data);
    }
}
?>


Maar ik krijg de beknopte error (die op Google vrijwel niets oplevert): "invalid maildir given"
De slash achter Maildir weghalen heb ik al geprobeerd. Verder heb ik ook geen flauw idee of ik Zend_Mail_Storage_Mbox of Zend_Mail_Storage_Maildir moet gebruiken. Is in de documentatie dit alles wat hierover te vinden is?

Acties:
  • 0 Henk 'm!

Verwijderd

Ik mis even de reden waarom je die config zou doen in dir//ectadmin ? Ik denk dat je er vanuit gaat dat nu email geforward wordt naar de ControllerAction ? Of alleen dat dit de trigger is om het script te starten ?


Mbt de error ... weet je zeker dat er van een maildir gebruik gemaakt wordt en deze staat op : /home/[gebruikersnaam]/Maildir/ ?


Een andere oplossing zou zijn om dmv imap een email box uit te lezen en de emails te verwerken hiervoor moet je natuurlijk wel een cronjob instellen maar dat zou niet zo moeilijk moeten zijn.

----- EDIT -----

Na wat gegoogled te hebben denk ik iets meer inzicht te hebben in wat je wil doen en wat er misgaat.

Door een emailpipe te doen wordt de email als een input naar een script gestuurd, deze email zou je als raw post data binnen moeten krijgen en zelf parsen.

De Zend_Mail_Storage_Maildir methode is dus niet te gebruiken voor deze opzet. Alles als deze wel echt aankomt in een mailbox/maildir.

In principe is de oplossing bij simpel, je leest de input uit met

PHP:
1
2
3
4
5
6
$fd = fopen("php://stdin", "r");
$email = "";
while (!feof($fd)) {
    $email .= fread($fd, 1024);
}
fclose($fd);


daarna parse je de input zelf naar je gewenste format.

[ Voor 39% gewijzigd door Verwijderd op 18-02-2011 16:44 ]


Acties:
  • 0 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
Controleer via shell of uberhaupt je lokale pad "/home/[gebruikersnaam]/domains/[domein.nl]/public_html/klanten/[klantnaam]/pipe/catch" het wel doet. Volgens mij vergeet je dat bij PHP CLI je geen webserver hebt en dus ook geen mod_rewrite :)

Je kan misschien eerder gewoon de mail laten aankomen in de inbox en via een cronjob die uitlezen op nieuwe mails en dan verwerken.

Acties:
  • 0 Henk 'm!

  • Sypher
  • Registratie: Oktober 2002
  • Laatst online: 01-10 20:31
Dat kan je eigenlijk vrij eenvoudig doen met ZF:

PHP:
1
2
3
4
5
6
7
8
try {
    $message = new Zend_Mail_Message(array(
        'file' => 'php://stdin',
    ));
} catch (Zend_Mail_Exception $e)
{
    die('Unable to read email message: '.$e->getMessage());
}


Vervolgens heb je je Zend_Mail_Message object en kan je er je gang op gaan (->isMultiPart(), ->getContent() etc).

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Verwijderd schreef op vrijdag 18 februari 2011 @ 16:25:
Ik mis even de reden waarom je die config zou doen in dir//ectadmin ? Ik denk dat je er vanuit gaat dat nu email geforward wordt naar de ControllerAction ? Of alleen dat dit de trigger is om het script te starten ?
De e-mail moet inderdaad geforward worden en dan door het PHP script worden opgevangen zodat het direct kan worden weggeschreven in de database.
Met een cronjob de mailbox leeghalen kan ook, maar ik weet dat het ook op de bovenstaande manier kan. Dat doen ze ook bij osTicket.
Mbt de error ... weet je zeker dat er van een maildir gebruik gemaakt wordt en deze staat op : /home/[gebruikersnaam]/Maildir/ ?
Nee. Hoe kom ik daar achter?
In principe is de oplossing bij simpel, je leest de input uit met

PHP:
1
2
3
4
5
6
$fd = fopen("php://stdin", "r");
$email = "";
while (!feof($fd)) {
    $email .= fread($fd, 1024);
}
fclose($fd);

daarna parse je de input zelf naar je gewenste format.
Elke e-mail ziet er qua opbouw anders uit. Dit betekent dat ik een shitload een reguliere expressies zou moeten gaan schrijven. Het moet toch makkelijker kunnen?
Cartman! schreef op vrijdag 18 februari 2011 @ 18:42:
Controleer via shell of uberhaupt je lokale pad "/home/[gebruikersnaam]/domains/[domein.nl]/public_html/klanten/[klantnaam]/pipe/catch" het wel doet. Volgens mij vergeet je dat bij PHP CLI je geen webserver hebt en dus ook geen mod_rewrite :)

Je kan misschien eerder gewoon de mail laten aankomen in de inbox en via een cronjob die uitlezen op nieuwe mails en dan verwerken.
Jij bedoelt dat ik gewoon een absoluut pad moet gebruiken zoals /home/[gebruikersnaam]/domains/[domein.nl]/public_html/klanten/[klantnaam]/pipe.php
Daar zit inderdaad wel wat in. Maar hoe krijg ik dan de database resource vanuit ZF? Die moet ik toch eerst bootstrappen?
Sypher schreef op vrijdag 18 februari 2011 @ 18:45:
Dat kan je eigenlijk vrij eenvoudig doen met ZF:

PHP:
1
2
3
4
5
6
7
8
try {
    $message = new Zend_Mail_Message(array(
        'file' => 'php://stdin',
    ));
} catch (Zend_Mail_Exception $e)
{
    die('Unable to read email message: '.$e->getMessage());
}


Vervolgens heb je je Zend_Mail_Message object en kan je er je gang op gaan (->isMultiPart(), ->getContent() etc).
Dat zou mooi zijn! Ik krijg alleen delivery failure.

Acties:
  • 0 Henk 'm!

  • Sypher
  • Registratie: Oktober 2002
  • Laatst online: 01-10 20:31
Verwijderd schreef op zaterdag 19 februari 2011 @ 12:59:
Jij bedoelt dat ik gewoon een absoluut pad moet gebruiken zoals /home/[gebruikersnaam]/domains/[domein.nl]/public_html/klanten/[klantnaam]/pipe.php
Daar zit inderdaad wel wat in. Maar hoe krijg ik dan de database resource vanuit ZF? Die moet ik toch eerst bootstrappen?
Gewoon de bootstrap includen?
Doe ik ook, al gebruik ik dan geen Zend_Application. Werkt prima verder.
Dat zou mooi zijn! Ik krijg alleen delivery failure.
In die failure staat vast wat er mis is ;)

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Het werkt al iets beter nu. Als ik een e-mail verzend, krijg ik een failure maar er wordt ook data aan de database toegevoegd. In de failure krijg ik de output van pipe.php, waarin o.a. de broncode staat van de 'Welcome to the Zend Framework! This is your project's main page' pagina. Uiteraard moet er geen failure worden verzonden.

In pipe.php staat het volgende (het meeste uit index.php gekopieerd)
code:
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
<?php

// Define path to application directory
defined('APPLICATION_PATH')
    || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));

// Define application environment
defined('APPLICATION_ENV')
    || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));

// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
    realpath(APPLICATION_PATH . '/../library'),
    get_include_path(),
)));

ini_set('include_path', '/home/[gebruikersnaam]/domains/[domeinnaam]/library');

/** Zend_Application */
require_once 'Zend/Application.php';

// Create application, bootstrap, and run
$application = new Zend_Application(
    APPLICATION_ENV,
    APPLICATION_PATH . '/configs/application.ini'
);
$application->bootstrap()
            ->run();
// get database
$db = Zend_Registry::get('db');
## START MAIL SECTION ##
try {
    $message = new Zend_Mail_Message(array('file' => 'php://stdin'));
    $value = $message->getContent();
} catch (Zend_Mail_Exception $e) {
    $value = $e->getMessage();
}
## END MAIL SECTION ##
// insert data into test table
$data['value'] = $value;
$data['date'] = date('Y-m-d H:i:s');
$db->insert('tests', $data);


Wat krijg ik nu in de database:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Dit is een meerdelig bericht in de MIME-indeling.

------=_NextPart_000_0036_01CBD039.C735DBA0
Content-Type: text/plain;
    charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

Dit is de content.
------=_NextPart_000_0036_01CBD039.C735DBA0
Content-Type: text/html;
    charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

<HTML><HEAD></HEAD>
<BODY dir=3Dltr>
<DIV dir=3Dltr>
<DIV style=3D"FONT-FAMILY: 'Calibri'; COLOR: #000000; FONT-SIZE: 12pt">
<DIV>Dit is de content.</DIV></DIV></DIV></BODY></HTML>

------=_NextPart_000_0036_01CBD039.C735DBA0--


Ik wil dus dat alleen 'Dit is de content' in de database komt te staan. Die rommelige HTML code zal wel van Hotmail af komen of niet? Is er geen standaard functie binnen ZF die deze filtert?

Acties:
  • 0 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
Wat hij zegt dus ;) Ik maak meestal een losse bootstrap voor CLI-toepassingen.

Acties:
  • 0 Henk 'm!

Verwijderd

Ik heb het zelf ook even getest op eigen domein, zo snel je niets echo'd in je pipe script zou die failure moeten verdwijnen.

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Cartman! schreef op zaterdag 19 februari 2011 @ 13:48:
[...]

Wat hij zegt dus ;) Ik maak meestal een losse bootstrap voor CLI-toepassingen.
Verwijderd schreef op zaterdag 19 februari 2011 @ 14:04:
Ik heb het zelf ook even getest op eigen domein, zo snel je niets echo'd in je pipe script zou die failure moeten verdwijnen.
Hoe doe je dat? Ik werk nog maar erg kort met ZF.
Met mijn pipe.php kan ik volgensmij ook niet de view benaderen zodat ik deze uit kan zetten.

Acties:
  • 0 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
Gewoon een php-file requiren met daarin de init van je db en evt. andere componenten die je nodig hebt.. In feite is dat dus het eerste stuk t/m regel 30 wat je nu al in pipe.php hebt zitten.

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ok, maar hoe zet ik dan de view uit? Ik snap hoe je dit binnen een action maar dat is nu niet het geval.
Ik heb nu onder $application->bootstrap()->run(); het volgende gezet:
code:
1
2
$bootstrap = $application->getBootstrap();
$bootstrap->bootstrap('view');

Dat levert dit op: Fatal error: Uncaught exception 'Zend_Application_Bootstrap_Exception' with message 'Resource matching "View" not found' etc.
Vreemd, want binnen de bootstrap klasse kan ik wel gewoon $this->bootstrap('view'); doen.

Acties:
  • 0 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
Ik gebruik geen Zend_Application dus dat zou ik niet weten. Maar waarom zou je die $bootstrap->bootstrap('view'); uitvoeren als je geen view hebt en die niet wilt?

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ik dacht dat ik dan $view = $bootstrap->getResource('view'); kon doen en dan de view op de een of andere manier uitzetten.

Hoe zet jij de views uit dan?

Acties:
  • 0 Henk 'm!

  • mithras
  • Registratie: Maart 2003
  • Niet online
Wat ik vaak doe is een script in mijn bin map zetten. Naast "application" "public" en "library" heb ik dus in "bin" al mijn cli scripts staan. Die worden bijvoorbeeld aangeroepen door cron e.d.

Wat ik daarin doe is wel gebruik maken van Zend_Application, omdat je zo makkelijk resources kan bootstrappen. Runnen doe ik daarentegen níét, omdat je (standaard) zit met een http request en http response.

Wat ik doe:
PHP:
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
<?php
// Define path to (public) webroot
defined('BIN_PATH')
    || define('BIN_PATH', dirname(__FILE__));

// Define path to application directory
defined('APPLICATION_PATH')
    || define('APPLICATION_PATH', realpath(BIN_PATH . '/../application'));

// Define application environment
defined('APPLICATION_ENV')
    || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));

// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
    realpath(APPLICATION_PATH . '/../library'),
    get_include_path(),
)));

/** Zend_Application */
require_once 'Zend/Application.php';

// Create application, bootstrap, and run
$application = new Zend_Application(
    APPLICATION_ENV,
    APPLICATION_PATH . '/configs/application.ini'
);

$application->bootstrap('cli');

// Hier verder code van het script


Ik maak dan een Cli resource die afhankelijk is van een aantal andere resources (cache, log, session etc). Alle andere (http) resources zoals frontController, view, layout, navigation, jQuery etc laat ik dan niet bootstrappen.
Verwijderd schreef op zondag 20 februari 2011 @ 13:05:
Ok, maar hoe zet ik dan de view uit? Ik snap hoe je dit binnen een action maar dat is nu niet het geval.
Ik heb nu onder $application->bootstrap()->run(); het volgende gezet:
code:
1
2
$bootstrap = $application->getBootstrap();
$bootstrap->bootstrap('view');

Dat levert dit op: Fatal error: Uncaught exception 'Zend_Application_Bootstrap_Exception' with message 'Resource matching "View" not found' etc.
Vreemd, want binnen de bootstrap klasse kan ik wel gewoon $this->bootstrap('view'); doen.
Zit je in een module? Dan heb je waarschijnlijk Zend_Application_Module_Bootstrap te pakken en niet Zend_Application_Bootstrap_Bootstrap. Dit kan het oplossen:

PHP:
1
$bootstrap->getApplication()->getBootstrap()->bootstrap();


Met bootstrap('view') zet je trouwens je view aan, je kan zoiets niet meer uitzetten. De truuk is om de resources niet te bootstrappen (zie hierboven) in plaats van ze allemaal aan te zetten en daarna proberen uit te schakelen (wat dus niet kan).

[ Voor 4% gewijzigd door mithras op 20-02-2011 13:26 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ik snap niet wat je daar doet.

Het is nu wel gelukt overigens. Inderdaad best logisch, de application bootstrappen maar niet runnen. Bedankt :)

Maar dan nu het volgende probleem, hoe filter ik die headers (ik denk dat het headers zijn) van mijn content? Ik krijg nu bijvoorbeeld dit binnen:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Dit is een meerdelig bericht in de MIME-indeling.

------=_NextPart_000_0011_01CBD107.A245F0E0
Content-Type: text/plain;
    charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

Testbericht
------=_NextPart_000_0011_01CBD107.A245F0E0
Content-Type: text/html;
    charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

<HTML><HEAD></HEAD>
<BODY dir=3Dltr>
<DIV dir=3Dltr>
<DIV style=3D"FONT-FAMILY: 'Calibri'; COLOR: #000000; FONT-SIZE: 12pt">
<DIV>tesyuu</DIV></DIV></DIV></BODY></HTML>

------=_NextPart_000_0011_01CBD107.A245F0E0--

Of is dat niet mogelijk?

Acties:
  • 0 Henk 'm!

  • bakakaizoku
  • Registratie: Januari 2002
  • Laatst online: 01-10 21:16
De mail zelf parsen en alles wat begint met en na ------=_NextPart komt weg laten?

PSN: bakakaizoku - WoW: Thiccblonde (GM of Phoenix Ascension) @ Twisting-Nether


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Je zou denken dat het zou kunnen met Zend_Mime. Ik kan er echter bijna niets over vinden. In de documentatie vind ik alleen encode functies. Ik zie wel het bestand library/Zend/Mime/Decode.php staan, alleen geen flauw idee hoe ik het moet gebruiken.

Lastig onderwerp dat e-mail piping, er is zo weinig over te vinden.

Acties:
  • 0 Henk 'm!

  • mithras
  • Registratie: Maart 2003
  • Niet online
Verwijderd schreef op zondag 20 februari 2011 @ 14:44:
Je zou denken dat het zou kunnen met Zend_Mime. Ik kan er echter bijna niets over vinden. In de documentatie vind ik alleen encode functies. Ik zie wel het bestand library/Zend/Mime/Decode.php staan, alleen geen flauw idee hoe ik het moet gebruiken.

Lastig onderwerp dat e-mail piping, er is zo weinig over te vinden.
Heb je ook de volledige source van je mail? Ik zie hier namelijk alleen maar een gedeelte. Wat je dan kan doen is waarschijnlijk gebruik maken van Zend_Mail_Message:

PHP:
1
$mail = new Zend_Mail_Message(array('raw' => $source));

En op die manier de parts eruit halen en die door Zend_Mime_Decode heen halen. Dan kan je misschien nog met de php functie quoted_printable_decode() de =0C e.d. tekens decoderen.

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
mithras schreef op zondag 20 februari 2011 @ 15:07:
[...]
Heb je ook de volledige source van je mail? Ik zie hier namelijk alleen maar een gedeelte. Wat je dan kan doen is waarschijnlijk gebruik maken van Zend_Mail_Message:

PHP:
1
$mail = new Zend_Mail_Message(array('raw' => $source));

En op die manier de parts eruit halen en die door Zend_Mime_Decode heen halen. Dan kan je misschien nog met de php functie quoted_printable_decode() de =0C e.d. tekens decoderen.
Waar haal je $source vanaf?

Acties:
  • 0 Henk 'm!

  • mithras
  • Registratie: Maart 2003
  • Niet online
Uhm, dat is je input van je email die je naar je script piped :?

Ik kwam op fora achter 'raw', maar Sypher suggereert iets soortgelijks met 'file': Sypher in "\[PHP/ZEND] E-mail piping"

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
De mail wordt verzonden naar het script. In het script is dus neem ik aan geen variabel beschikbaar die de e-mail bevat.

Acties:
  • 0 Henk 'm!

  • Wizz15
  • Registratie: Januari 2004
  • Laatst online: 26-10-2022
Verwijderd schreef op zondag 20 februari 2011 @ 15:59:
De mail wordt verzonden naar het script. In het script is dus neem ik aan geen variabel beschikbaar die de e-mail bevat.
Als je mail niet in een variabele staat, hoe wil je hem dan opslaan in de database? ;) Ik zag in je script een paar posts hierboven (weet niet of die nog up-to-date is) een variabele '$value' in een try-catch blok. Volgens mij sla je daar de inhoud van de mail op, dus $source zou je moeten vervangen met $value volgens mij.

Let wel, ik heb het hier even vluchtig doorgelezen, dus het kan zijn dat ik wat over het hoofd heb gezien...

Edit: dit is het stukje waar ik het over had:

PHP:
1
2
3
4
5
6
7
8
## START MAIL SECTION ##
try {
    $message = new Zend_Mail_Message(array('file' => 'php://stdin'));
    $value = $message->getContent();
} catch (Zend_Mail_Exception $e) {
    $value = $e->getMessage();
}
## END MAIL SECTION ##


Edit 2:

Ik zie net dat je een multipart bericht binnenkrijgt. Normaal gesproken krijg je met getContent() inderdaad alleen de inhoud zonder headers, net als nu. De tekst die je echter boven je bericht ziet heeft te maken met het feit dat het een multi-part email is.

In de officiele documentatie van Zend Framework staan wat code voorbeelden om te checken of het bericht wat je binnenkrijgt een multi-part bericht is of niet, en hoe je dat bericht vervolgens uitleest.

De desbetreffende code is op deze pagina te vinden: http://framework.zend.com/manual/en/zend.mail.read.html onder het kopje: 'Working with messages'. :)

Even uit de losse pols:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
## START MAIL SECTION ##
try {
    $message = new Zend_Mail_Message(array('file' => 'php://stdin'));
    
    if(! $message->isMultiPart()) {
        // bericht is niet multipart, dus gewoon ophalen met getContent()
        $value = $message->getContent();
    }
    else {
         // Hier moet je checken hoeveel 'parts' je bericht heeft, en die allemaal aflopen
         // Ik pak hier alleen het eerste 'part' met getPart(1);
         $value = $message->getPart(1)->getContent();
    }
} catch (Zend_Mail_Exception $e) {
    $value = $e->getMessage();
}
## END MAIL SECTION ##

[ Voor 70% gewijzigd door Wizz15 op 20-02-2011 17:36 ]

PSN: RikBruil | BFBC2 stats


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Sorry voor de late reactie. Maar het werkt inderdaad als ik eerst getPart(1) doe. Het principe is duidelijk.
Het werkt nu, dus ik kan voorlopig weer vooruit. Super bedankt iedereen!

[ Voor 4% gewijzigd door Verwijderd op 23-02-2011 19:42 ]

Pagina: 1