I'm creating a ticketsystem in PHP which fetch the emails and put them into a database. Strange thing is that when there are multiple emails in the inbox, they get put into the DB multiple times. Mail 1 is being inserted 1 time in the DB. Mail 2 is being inserted 2 times in the DB. Mail 3 is being inserted 3 times in the DB. And probably so on... (only tested with 3 mails at the same time)
Code I'm using is:
<?php
error_reporting( E_ALL );
ini_set('display_errors', 1);
$path = $_SERVER['SCRIPT_FILENAME'];
$path = str_replace('/includes/cronjob.php', '/', $path);
require_once($path . 'config/config.php');
require_once($path . 'config/config_extra.php');
require_once($path . 'config/db_connect.php');
require_once($path . 'classes/tickets.class.php');
require_once($path . 'classes/crm.class.php');
require_once($path . 'classes/mails.class.php');
require_once($path . 'includes/functions.php');
$tickets = new tickets();
$crm = new crm();
$mails = new mails();
$now = time(); // current time
if($lic_info['trs'] == 0){ die(); }
$ticketAccounts = $tickets->get_accounts();
if($ticketAccounts > 0){
foreach($ticketAccounts AS $key=>$ticketAccounts){
$mailboxPort = get_mailboxPort($ticketAccounts['type']);
$mailbox = '{'.$ticketAccounts['server'].':'.$mailboxPort.'/imap/ssl/novalidate-cert}';
$mailFolder = $ticketAccounts['folder'];
$mailDoneFolder = $ticketAccounts['moveFolder'];
$mailErrorFolder = $ticketAccounts['errorFolder'];
$mbox = imap_open($mailbox.$mailFolder, $ticketAccounts['username'], $ticketAccounts['password']); // log in to mail server
if(imap_num_msg($mbox) < 1){ die(); }
if (!$mbox)
echo ($lang['error_msg']['mailconnect'] . print_r(imap_errors(), true)); // remove the print_r for production use
else
{
$box = imap_check($mbox); // get the inbox
for ($imap_idx = 1; $imap_idx <= $box->Nmsgs; $imap_idx++) // loop through the messages
{
$headers = imap_headerinfo($mbox, $imap_idx); // http://www.php.net/manual/en/function.imap-headerinfo.php
$raw_headers = imap_fetchheader($mbox, $imap_idx); // http://www.php.net/manual/en/function.imap-fetchheader.php
$selected_headers = '';
$text_part = '';
$html_part = '';
$original_message = imap_body($mbox, $imap_idx); // save the copy of the entire thing, attachments and all
// build selected headers string
for ($ii = 0; $ii < count($headers->from); $ii++)
$selected_headers .= 'From: ' . $headers->from[$ii]->mailbox . '@' . $headers->from[$ii]->host . "
";
for ($ii = 0; $ii < count($headers->to); $ii++)
$selected_headers .= 'To: ' . $headers->to[$ii]->mailbox . '@' . $headers->to[$ii]->host . "
";
for ($ii = 0; $ii < count($headers->cc); $ii++)
$selected_headers .= 'Cc: ' . $headers->cc[$ii]->mailbox . '@' . $headers->cc[$ii]->host . "
";
for ($ii = 0; $ii < count($headers->bcc); $ii++)
$selected_headers .= 'Bcc: ' . $headers->bcc[$ii]->mailbox . '@' . $headers->bcc[$ii]->host . "
";
if (!empty($headers->date))
$selected_headers .= 'Date: ' . $headers->date . "
";
if (!empty($headers->subject))
$selected_headers .= 'Subject: ' . $headers->subject . "
";
// see below; getMsg uses global variables
getMsg($mbox, $imap_idx);
$text_part = $plainmsg; // text portion of the email
$html_part = $htmlmsg; // html portion of the email
// check for text portion first
$msg_text = trim(strip_tags($plainmsg, '<a>'));
if ($msg_text == '')
{
// text portion is empty, check html portion
$msg_text = trim($htmlmsg);
if ($msg_text == '')
{
// no text or html portion auto-detected, check manually
$msg_text = imap_body($mbox, $imap_idx); // get the entire raw message
// check for quoted-printable encoding with possible boundary markers and headers at the top
$chunks = explode("
", trim($msg_text));
if (count($chunks) > 1) // if there are multiple lines
{
$quoted_printable = false;
if (strpos($chunks[0], '--') === 0) // if the first line is a boundary marker (starts with '--')
{
array_shift($chunks); // remove the first line
if (strpos($chunks[count($chunks) - 1], '--') === 0) // check the last line
{
array_pop($chunks); // remove that too
}
}
if (strpos(strtolower($chunks[0]), 'content-type') === 0)
array_shift($chunks); // remove the first line if it's a content-type header
if (strpos(strtolower($chunks[0]), 'content-transfer-encoding') === 0)
{
if (strpos(strtolower($chunks[0]), 'quoted-printable'))
$quoted_printable = true; // this email was sent using quoted-printable encoding
array_shift($chunks); // remove the content-transfer-encoding header
}
$msg_text = implode("
", $chunks); // put the remaining lines back together
if ($quoted_printable) $msg_text = quoted_printable_decode($msg_text);
$msg_text = utf8_decode($msg_text);
}
}
}
$from = trim($headers->from[0]->mailbox . '@' . $headers->from[0]->host);
$msgId = isset($headers->message_id) ? trim($headers->message_id) : '';
$time = strtotime($headers->date);
if ($time == 0)
$time = $now;
/******************************************************
At this point:
$headers: the object returned from imap_headerinfo
$selected_headers: text of some headers to display to a user checking mail (subject, from, etc)
$text_part: the text portion, if found
$html_part: the html portion, if found
$msg_text: either the text part, html part, or manually-decoded part (this is the variable to use as email body)
$original_message: the entire unprocessed email body, including all parts and any attachments
$from: From address
$msgId: message ID from the headers
$time: email delivery time, as a Unix timestamp
$attachments: array of attachments (see below)
******************************************************/
$to = $headers->to[$ii]->mailbox . '@' . $headers->to[$ii]->host;
if(strpos($headers->subject,'[GR_Ticket:') !== false){ $ticketnr = extract_unit($headers->subject, '[GR_Ticket:', ']'); }
$msg_text = editChars($html_part);
if(strpos($msg_text,'<html') !== false){
$msg_text = editString($msg_text);
}
$headers->subject = editChars($headers->subject);
$headers->subject = str_replace('_', ' ', $headers->subject);
//echo strip_tags($html_part);die();
$queue = $tickets->search_queue($to);
if(isset($ticketnr)){
$checkTicket = $tickets->show_ticket($ticketnr);
if($checkTicket == 0){ $newTicket = 1; } else { $newTicket = 0; }
} else {
$newTicket = 1;
}
$findSender = $crm->find_company_sender($from);
if($findSender == 0){ $fromComp = 0; } else { $fromComp = $findSender['id']; }
$findSender = $crm->find_contact_sender($from);
if($findSender == 0){ $fromContact = 0; } else { $fromContact = $findSender['id']; }
if($newTicket == 0){
$ticketNumber = $tickets->create_reply($headers->from[0]->personal,$from,$fromComp,$fromContact,$ticketnr,$headers->subject,$msg_text,$time,$headers->toaddress,$to,1);
}
if($newTicket == 1){
$ticketNumber = $tickets->create_ticket($headers->from[0]->personal,$from,$fromComp,$fromContact,$headers->subject,$msg_text,$time,$headers->toaddress,$to,0,0,1,0,0,$queue['id']);
if($ticketNumber > 0){
//$mails->auto_reply($ticketNumber,$from,$headers->subject,$msg_text,$queue['id']);
}
}
// BELANGRIJK: Ticketnummer: echo '<br /><br />' . $ticketNumber . '<br /><br />';
$mailuid = trim($headers->Msgno, ' ');
if($ticketNumber == 0){
if(!imap_getmailboxes($mbox, $mailbox, $mailErrorFolder)){
imap_createmailbox($mbox,$mailbox.$mailErrorFolder);
}
$imapresult = imap_mail_move($mbox, '1:'.$mailuid, 'Errors');
if($imapresult==false){die(imap_last_error());}
imap_expunge($mbox);
$tickets->create_error($from,$to,$queue['id'],$lang['error_msg']['import_error']);die();
}
// process attachments
foreach ($attachments as $filename => $data)
{
if($newTicket == 0){
if(!is_dir($path . 'attachments/tickets/' . $ticketnr)){
mkdir($path . 'attachments/tickets/' . $ticketnr);
}
if(!is_dir($path . 'attachments/tickets/'.$ticketnr.'/'.$ticketNumber.'')){
mkdir($path . 'attachments/tickets/'.$ticketnr.'/'.$ticketNumber.'');
}
file_put_contents($path . 'attachments/tickets/'.$ticketnr.'/'.$ticketNumber.'/' . $filename, $data);
$tickets->add_attachment($ticketnr,$ticketNumber,$filename);
} else {
if(!is_dir($path . 'attachments/tickets/' . $ticketNumber)){
mkdir($path . 'attachments/tickets/' . $ticketNumber);
}
file_put_contents($path . 'attachments/tickets/'.$ticketNumber.'/' . $filename, $data);
$tickets->add_attachment($ticketNumber,0,$filename);
}
}
unset($ticketnr);
unset($newTicket);
unset($ticketNumber);
}
// Move emails and close the mailbox
if(!imap_getmailboxes($mbox, $mailbox, $mailDoneFolder)){
imap_createmailbox($mbox,$mailbox.$mailDoneFolder);
}
$imapresult = imap_mail_move($mbox, '1:'.$mailuid, $mailDoneFolder);
if($imapresult==false){die(imap_last_error());}
imap_expunge($mbox);
imap_close($mbox);
}
}
}
function getMsg($mbox,$mid) {
// input $mbox = IMAP stream, $mid = message id
// output all the following:
global $htmlmsg,$plainmsg,$charset,$attachments;
// the message may in $htmlmsg, $plainmsg, or both
$htmlmsg = $plainmsg = $charset = '';
$attachments = array();
// HEADER
$h = imap_header($mbox,$mid);
// add code here to get date, from, to, cc, subject...
// BODY
$s = imap_fetchstructure($mbox,$mid);
if (empty($s->parts)) // not multipart
getMsgPart($mbox,$mid,$s,0); // no part-number, so pass 0
else { // multipart: iterate through each part
foreach ($s->parts as $partno0=>$p)
getMsgPart($mbox,$mid,$p,$partno0+1);
}
}
function getMsgPart($mbox,$mid,$p,$partno) {
// $partno = '1', '2', '2.1', '2.1.3', etc if multipart, 0 if not multipart
global $htmlmsg,$plainmsg,$charset,$attachments;
// DECODE DATA
$data = ($partno)?
imap_fetchbody($mbox,$mid,$partno): // multipart
imap_body($mbox,$mid); // not multipart
// Any part may be encoded, even plain text messages, so check everything.
if ($p->encoding==4)
$data = quoted_printable_decode($data);
elseif ($p->encoding==3)
$data = base64_decode($data);
// no need to decode 7-bit, 8-bit, or binary
// PARAMETERS
// get all parameters, like charset, filenames of attachments, etc.
$params = array();
if ($p->ifparameters)
foreach ($p->parameters as $x)
$params[ strtolower( $x->attribute ) ] = $x->value;
if ($p->ifdparameters)
foreach ($p->dparameters as $x)
$params[ strtolower( $x->attribute ) ] = $x->value;
// ATTACHMENT
// Any part with a filename is an attachment,
// so an attached text file (type 0) is not mistaken as the message.
if (!empty($params['filename']) || !empty($params['name'])) {
// filename may be given as 'Filename' or 'Name' or both
$filename = (!empty($params['filename']))? $params['filename'] : $params['name'];
// filename may be encoded, so see imap_mime_header_decode()
$attachments[$filename] = $data; // this is a problem if two files have same name
}
// TEXT
elseif ($p->type==0 && $data) {
// Messages may be split in different parts because of inline attachments,
// so append parts together with blank row.
if ($p->ifsubtype && strtolower($p->subtype)=='plain')
$plainmsg .= trim($data) ."
";
else
$htmlmsg .= $data ."<br><br>";
$charset = $params['charset']; // assume all parts are same charset
}
// EMBEDDED MESSAGE
// Many bounce notifications embed the original message as type 2,
// but AOL uses type 1 (multipart), which is not handled here.
// There are no PHP functions to parse embedded messages,
// so this just appends the raw source to the main message.
elseif ($p->type==2 && $data) {
$plainmsg .= trim($data) ."
";
}
// SUBPART RECURSION
if (!empty($p->parts)) {
foreach ($p->parts as $partno0=>$p2)
getMsgPart($mbox,$mid,$p2,$partno.'.'.($partno0+1)); // 1.2, 1.2.1, etc.
}
}
?>
But when I move the DB insert outside the fetchmail loop, all the emails get moved to the correct "Done" folder, but only one email gets inserted into the DB. Code I'm using when this happens:
<?php
error_reporting( E_ALL );
ini_set('display_errors', 1);
$path = $_SERVER['SCRIPT_FILENAME'];
$path = str_replace('/includes/cronjob.php', '/', $path);
require_once($path . 'config/config.php');
require_once($path . 'config/config_extra.php');
require_once($path . 'config/db_connect.php');
require_once($path . 'classes/tickets.class.php');
require_once($path . 'classes/crm.class.php');
require_once($path . 'classes/mails.class.php');
require_once($path . 'includes/functions.php');
$tickets = new tickets();
$crm = new crm();
$mails = new mails();
$now = time(); // current time
if($lic_info['trs'] == 0){ die(); }
$ticketAccounts = $tickets->get_accounts();
if($ticketAccounts > 0){
foreach($ticketAccounts AS $key=>$ticketAccounts){
$mailboxPort = get_mailboxPort($ticketAccounts['type']);
$mailbox = '{'.$ticketAccounts['server'].':'.$mailboxPort.'/imap/ssl/novalidate-cert}';
$mailFolder = $ticketAccounts['folder'];
$mailDoneFolder = $ticketAccounts['moveFolder'];
$mailErrorFolder = $ticketAccounts['errorFolder'];
$mbox = imap_open($mailbox.$mailFolder, $ticketAccounts['username'], $ticketAccounts['password']); // log in to mail server
if(imap_num_msg($mbox) < 1){ die(); }
if (!$mbox)
echo ($lang['error_msg']['mailconnect'] . print_r(imap_errors(), true)); // remove the print_r for production use
else
{
$box = imap_check($mbox); // get the inbox
for ($imap_idx = 1; $imap_idx <= $box->Nmsgs; $imap_idx++) // loop through the messages
{
$headers = imap_headerinfo($mbox, $imap_idx); // http://www.php.net/manual/en/function.imap-headerinfo.php
$raw_headers = imap_fetchheader($mbox, $imap_idx); // http://www.php.net/manual/en/function.imap-fetchheader.php
$selected_headers = '';
$text_part = '';
$html_part = '';
$original_message = imap_body($mbox, $imap_idx); // save the copy of the entire thing, attachments and all
// build selected headers string
for ($ii = 0; $ii < count($headers->from); $ii++)
$selected_headers .= 'From: ' . $headers->from[$ii]->mailbox . '@' . $headers->from[$ii]->host . "
";
for ($ii = 0; $ii < count($headers->to); $ii++)
$selected_headers .= 'To: ' . $headers->to[$ii]->mailbox . '@' . $headers->to[$ii]->host . "
";
for ($ii = 0; $ii < count($headers->cc); $ii++)
$selected_headers .= 'Cc: ' . $headers->cc[$ii]->mailbox . '@' . $headers->cc[$ii]->host . "
";
for ($ii = 0; $ii < count($headers->bcc); $ii++)
$selected_headers .= 'Bcc: ' . $headers->bcc[$ii]->mailbox . '@' . $headers->bcc[$ii]->host . "
";
if (!empty($headers->date))
$selected_headers .= 'Date: ' . $headers->date . "
";
if (!empty($headers->subject))
$selected_headers .= 'Subject: ' . $headers->subject . "
";
// see below; getMsg uses global variables
getMsg($mbox, $imap_idx);
$text_part = $plainmsg; // text portion of the email
$html_part = $htmlmsg; // html portion of the email
// check for text portion first
$msg_text = trim(strip_tags($plainmsg, '<a>'));
if ($msg_text == '')
{
// text portion is empty, check html portion
$msg_text = trim($htmlmsg);
if ($msg_text == '')
{
// no text or html portion auto-detected, check manually
$msg_text = imap_body($mbox, $imap_idx); // get the entire raw message
// check for quoted-printable encoding with possible boundary markers and headers at the top
$chunks = explode("
", trim($msg_text));
if (count($chunks) > 1) // if there are multiple lines
{
$quoted_printable = false;
if (strpos($chunks[0], '--') === 0) // if the first line is a boundary marker (starts with '--')
{
array_shift($chunks); // remove the first line
if (strpos($chunks[count($chunks) - 1], '--') === 0) // check the last line
{
array_pop($chunks); // remove that too
}
}
if (strpos(strtolower($chunks[0]), 'content-type') === 0)
array_shift($chunks); // remove the first line if it's a content-type header
if (strpos(strtolower($chunks[0]), 'content-transfer-encoding') === 0)
{
if (strpos(strtolower($chunks[0]), 'quoted-printable'))
$quoted_printable = true; // this email was sent using quoted-printable encoding
array_shift($chunks); // remove the content-transfer-encoding header
}
$msg_text = implode("
", $chunks); // put the remaining lines back together
if ($quoted_printable) $msg_text = quoted_printable_decode($msg_text);
$msg_text = utf8_decode($msg_text);
}
}
}
$from = trim($headers->from[0]->mailbox . '@' . $headers->from[0]->host);
$msgId = isset($headers->message_id) ? trim($headers->message_id) : '';
$time = strtotime($headers->date);
if ($time == 0)
$time = $now;
/******************************************************
At this point:
$headers: the object returned from imap_headerinfo
$selected_headers: text of some headers to display to a user checking mail (subject, from, etc)
$text_part: the text portion, if found
$html_part: the html portion, if found
$msg_text: either the text part, html part, or manually-decoded part (this is the variable to use as email body)
$original_message: the entire unprocessed email body, including all parts and any attachments
$from: From address
$msgId: message ID from the headers
$time: email delivery time, as a Unix timestamp
$attachments: array of attachments (see below)
******************************************************/
$to = $headers->to[$ii]->mailbox . '@' . $headers->to[$ii]->host;
if(strpos($headers->subject,'[GR_Ticket:') !== false){ $ticketnr = extract_unit($headers->subject, '[GR_Ticket:', ']'); }
$msg_text = editChars($html_part);
if(strpos($msg_text,'<html') !== false){
$msg_text = editString($msg_text);
}
$headers->subject = editChars($headers->subject);
$headers->subject = str_replace('_', ' ', $headers->subject);
//echo strip_tags($html_part);die();
$queue = $tickets->search_queue($to);
if(isset($ticketnr)){
$checkTicket = $tickets->show_ticket($ticketnr);
if($checkTicket == 0){ $newTicket = 1; } else { $newTicket = 0; }
} else {
$newTicket = 1;
}
$findSender = $crm->find_company_sender($from);
if($findSender == 0){ $fromComp = 0; } else { $fromComp = $findSender['id']; }
$findSender = $crm->find_contact_sender($from);
if($findSender == 0){ $fromContact = 0; } else { $fromContact = $findSender['id']; }
}
if($newTicket == 0){
$ticketNumber = $tickets->create_reply($headers->from[0]->personal,$from,$fromComp,$fromContact,$ticketnr,$headers->subject,$msg_text,$time,$headers->toaddress,$to,1);
}
if($newTicket == 1){
$ticketNumber = $tickets->create_ticket($headers->from[0]->personal,$from,$fromComp,$fromContact,$headers->subject,$msg_text,$time,$headers->toaddress,$to,0,0,1,0,0,$queue['id']);
if($ticketNumber > 0){
//$mails->auto_reply($ticketNumber,$from,$headers->subject,$msg_text,$queue['id']);
}
}
$mailuid = trim($headers->Msgno, ' ');
if($ticketNumber == 0){
if(!imap_getmailboxes($mbox, $mailbox, $mailErrorFolder)){
imap_createmailbox($mbox,$mailbox.$mailErrorFolder);
}
$imapresult = imap_mail_move($mbox, '1:'.$mailuid, 'Errors');
if($imapresult==false){die(imap_last_error());}
imap_expunge($mbox);
$tickets->create_error($from,$to,$queue['id'],$lang['error_msg']['import_error']);die();
}
// process attachments
foreach ($attachments as $filename => $data)
{
if($newTicket == 0){
if(!is_dir($path . 'attachments/tickets/' . $ticketnr)){
mkdir($path . 'attachments/tickets/' . $ticketnr);
}
if(!is_dir($path . 'attachments/tickets/'.$ticketnr.'/'.$ticketNumber.'')){
mkdir($path . 'attachments/tickets/'.$ticketnr.'/'.$ticketNumber.'');
}
file_put_contents($path . 'attachments/tickets/'.$ticketnr.'/'.$ticketNumber.'/' . $filename, $data);
$tickets->add_attachment($ticketnr,$ticketNumber,$filename);
} else {
if(!is_dir($path . 'attachments/tickets/' . $ticketNumber)){
mkdir($path . 'attachments/tickets/' . $ticketNumber);
}
file_put_contents($path . 'attachments/tickets/'.$ticketNumber.'/' . $filename, $data);
$tickets->add_attachment($ticketNumber,0,$filename);
}
}
unset($ticketnr);
unset($newTicket);
unset($ticketNumber);
// Move emails and close the mailbox
if(!imap_getmailboxes($mbox, $mailbox, $mailDoneFolder)){
imap_createmailbox($mbox,$mailbox.$mailDoneFolder);
}
$imapresult = imap_mail_move($mbox, '1:'.$mailuid, $mailDoneFolder);
if($imapresult==false){die(imap_last_error());}
imap_expunge($mbox);
imap_close($mbox);
}
}
}
function getMsg($mbox,$mid) {
// input $mbox = IMAP stream, $mid = message id
// output all the following:
global $htmlmsg,$plainmsg,$charset,$attachments;
// the message may in $htmlmsg, $plainmsg, or both
$htmlmsg = $plainmsg = $charset = '';
$attachments = array();
// HEADER
$h = imap_header($mbox,$mid);
// add code here to get date, from, to, cc, subject...
// BODY
$s = imap_fetchstructure($mbox,$mid);
if (empty($s->parts)) // not multipart
getMsgPart($mbox,$mid,$s,0); // no part-number, so pass 0
else { // multipart: iterate through each part
foreach ($s->parts as $partno0=>$p)
getMsgPart($mbox,$mid,$p,$partno0+1);
}
}
function getMsgPart($mbox,$mid,$p,$partno) {
// $partno = '1', '2', '2.1', '2.1.3', etc if multipart, 0 if not multipart
global $htmlmsg,$plainmsg,$charset,$attachments;
// DECODE DATA
$data = ($partno)?
imap_fetchbody($mbox,$mid,$partno): // multipart
imap_body($mbox,$mid); // not multipart
// Any part may be encoded, even plain text messages, so check everything.
if ($p->encoding==4)
$data = quoted_printable_decode($data);
elseif ($p->encoding==3)
$data = base64_decode($data);
// no need to decode 7-bit, 8-bit, or binary
// PARAMETERS
// get all parameters, like charset, filenames of attachments, etc.
$params = array();
if ($p->ifparameters)
foreach ($p->parameters as $x)
$params[ strtolower( $x->attribute ) ] = $x->value;
if ($p->ifdparameters)
foreach ($p->dparameters as $x)
$params[ strtolower( $x->attribute ) ] = $x->value;
// ATTACHMENT
// Any part with a filename is an attachment,
// so an attached text file (type 0) is not mistaken as the message.
if (!empty($params['filename']) || !empty($params['name'])) {
// filename may be given as 'Filename' or 'Name' or both
$filename = (!empty($params['filename']))? $params['filename'] : $params['name'];
// filename may be encoded, so see imap_mime_header_decode()
$attachments[$filename] = $data; // this is a problem if two files have same name
}
// TEXT
elseif ($p->type==0 && $data) {
// Messages may be split in different parts because of inline attachments,
// so append parts together with blank row.
if ($p->ifsubtype && strtolower($p->subtype)=='plain')
$plainmsg .= trim($data) ."
";
else
$htmlmsg .= $data ."<br><br>";
$charset = $params['charset']; // assume all parts are same charset
}
// EMBEDDED MESSAGE
// Many bounce notifications embed the original message as type 2,
// but AOL uses type 1 (multipart), which is not handled here.
// There are no PHP functions to parse embedded messages,
// so this just appends the raw source to the main message.
elseif ($p->type==2 && $data) {
$plainmsg .= trim($data) ."
";
}
// SUBPART RECURSION
if (!empty($p->parts)) {
foreach ($p->parts as $partno0=>$p2)
getMsgPart($mbox,$mid,$p2,$partno.'.'.($partno0+1)); // 1.2, 1.2.1, etc.
}
}
?>
Anyone who can point me to what is going wrong here?