/*
 * Caudium - An extensible World Wide Web server
 * Copyright © 2000-2004 The Caudium Group
 * Based on IMHO © Stefan Wallström and Bosse Lincoln.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */
/*
 * $Id: smtp.h,v 1.26.2.1 2004/02/11 11:24:45 vida Exp $
 */

#ifndef __SMTP_H
#define __SMTP_H

// function to format a mail for sending
// Make a string of the mail in the sessobj, and send it if "sendmail"
// is to be used.
// If createonly==1, the mail will be converted to a string, but no
// other actions are taken. The mail string is returned 
// args is the arguments:
//   int includeipnumber (QUERY (includeipnumber))
//   string siteorg (QUERY(siteorg))   
//   int  addmaildomain (QUERY (addmaildomain))
//   array morecharsets (QUERY(morecharsets)
//   int shareddepth (QUERY(shareddepth)
//   string pref_extraheader (QUERY(pref_extraheader))
//   string mdntype (QUERY(mdntype))
//   string uploaddir (QUERY(uploaddir)) 
//   string camas_version
// returns: 
//  An array, the first element is the maildata to be used for RFC822 DATA command
//  the second is an array containing the recipients
//  the third is the maildata, without attachments if saveattachments = "0"
array(string) sendmail (object sessobj, string to, string cc, string bcc, 
 string messageid, string subject, string message, int createonly, mapping args)
{
  int parts=1;
  if (sessobj->attachments)
    parts=sizeof(sessobj->attachments)+1;
  string charset="";
  string header_charset="";
  string _message="";
  string from_name = sessobj->name;
  string from_addr = sessobj->address;
  if(args["from"])
  {
    array(string) from = CAMAS.Tools.fixaddresses(args["from"]);
    if(sizeof(from[0]))
      sscanf(from[0], "%s<%s>", from_name, from_addr);
  }
  mixed err=0;
  array charsets=({ });
  if(sessobj->replytoidx !=-1 && sessobj->replytocharset)
    charsets += ({ sessobj->replytocharset });
  charsets +=({ "iso-8859-1",args["lang_charsets"][sessobj->language] });
  charsets+= ( Array.map(args["morecharsets"]/",",String.trim_whites) - ({ "" })  );
  charsets += ({ "utf-8" });
  foreach( charsets,charset) {
    err=0;
    //KLUDGE: encoder for this character sets seems to allow 0x80-0xff
    if( String.width(message) == 8 && (<"us-ascii","us_ascii","ascii" >)[charset])
       if(search(message & ("\x80" * sizeof(message)), "\x80") != -1)
         continue;
    err=catch { _message=Locale.Charset.encoder(charset)->feed(message)->drain();};
    if(!err)
      break;
  }

  object mail = MIME.Message("",
                           ([ "MIME-Version" : "1.0",
                              "Content-Type" : ((parts==1)?"text/plain; charset="+charset:"multipart/mixed"),
                              "User-Agent" : "CAMAS/"+args["camas_version"]
			      +" (CAudium Mail Access System)" ]) );

  if (messageid)
    mail->headers["in-reply-to"] = messageid;

  if((string)sessobj["in_reply_to"]!="0")
    mail->headers["in-reply-to"] = sessobj["in_reply_to"];
  
  string headers = to + cc + bcc + from_name +" <"+ from_addr +">"+subject;

  foreach( charsets,header_charset) {
    err=0;
    //KLUDGE: encoder for this character sets seems to allow 0x80-0xff
    if( String.width(headers) == 8 && (<"us-ascii","us_ascii","ascii" >)[charset])
       if(search(headers & ("\x80" * sizeof(headers)), "\x80") != -1)
         continue;
    err=catch { Locale.Charset.encoder(header_charset)->feed(headers)->drain();};
    if(!err)
      break;
  }
  array addresses;
  array sendrecipients = ({ });
  addresses = CAMAS.Tools.address_split(to);
  mail->headers["to"]  = CAMAS.Tools.encode_addresses(addresses,header_charset);
  sendrecipients += addresses;
  if (strlen(cc) > 0) {
    addresses = CAMAS.Tools.address_split(cc);
    mail->headers["cc"]  = CAMAS.Tools.encode_addresses(addresses,header_charset);
    sendrecipients += addresses;
  }
  if (strlen(bcc) > 0) {
    addresses = CAMAS.Tools.address_split(bcc);
    sendrecipients += addresses;
  }

  foreach(indices(sendrecipients), int i) {
    sscanf(sendrecipients[i], "%*s<%s>", sendrecipients[i]);
    sscanf(sendrecipients[i], "%*[ ]%s", sendrecipients[i]);
    if(sscanf(reverse(sendrecipients[i]), "%*[ ]%s", sendrecipients[i])==2)
      sendrecipients[i]=reverse(sendrecipients[i]);
    if(sizeof(sendrecipients[i]) && args["addmaildomain"] && !has_value (sendrecipients[i], "@")) {
      sendrecipients[i]=sendrecipients[i]+"@"+sessobj->maildomain;
    }
  }

  // Do not encode the mail address. Right or wrong? Problem with "_".
  mail->headers["from"] = CAMAS.Tools.encode_header(from_name, header_charset)+" <"+from_addr+">";
  mail->headers["subject"] = CAMAS.Tools.encode_header(subject,header_charset);
  mail->headers["date"] = Calendar.now()->format_smtp();

  if (args["includeipnumber"])
    mail->headers["X-Originating-IP"] = "[" + sessobj->ip + "]";

  array extra_from = ({ "$USER", "$VERSION"});
  array extra_to = ({ });

  extra_to += ({ sessobj->login });
  extra_to += ({ args["camas_version"] });

  if (args["pref_extraheader"] != "")
    sessobj->extraheader = replace(args["pref_extraheader"], extra_from, extra_to);

  if (sizeof(sessobj->extraheader)) {
    string key   = "";
    string value = "";
    foreach (sessobj->extraheader/"\n", string l) {
      string k = "";
      string v = "";
      if (sscanf(l," %s",v) && sizeof(v))
        value += v;
      else if (sscanf(l,"%s:%*[ \t]%s",k,v) > 0 &&
               sizeof(k) && sizeof(v)) {
        if (sizeof(key) && !(mail->headers[key]))
          mail->headers[key] = value;
        key = k;
        value = v;
      }
    }
    if (sizeof(key) && !(mail->headers[key]))
      mail->headers[key] = value;
  }

  if (sessobj->mdn && args["mdntype"] != "None") {
    if (args["mdntype"] == "Both")
      mail->headers["disposition-notification-to"] = mail->headers["return-receipt-to"] = mail->headers->from;
    else {
      if (args["mdntype"] == "Netscape")
        mail->headers["disposition-notification-to"] = mail->headers->from;
      else // Microsoft
        mail->headers["return-receipt-to"] = mail->headers->from;
    }
  }

  if (sizeof (args["siteorg"]))
    mail->headers["organization"] = args["siteorg"];
  else
    if (sizeof (sessobj->organization))
      mail->headers["organization"] = sessobj->organization;

  mail->setencoding("8bit");

  if(parts==1)
    mail->setdata(createonly?_message:CAMAS.Tools.wrap_lines(_message,sessobj->wrapcolumn));
  else {
    mail["body_parts"]=({ });
    object mailpart = MIME.Message("",
                               ([ "MIME-Version" : "1.0",
                                  "Content-Type" : "text/plain; charset="+charset ]) );
    mailpart->setencoding("8bit");
    mailpart->setdata(createonly?_message:CAMAS.Tools.wrap_lines(_message,sessobj->wrapcolumn));
    mail->body_parts+=({ mailpart });

    foreach(sessobj->attachments, mapping file) {
      object mailpart = MIME.Message("",
                               ([ "MIME-Version" : "1.0",
                                  "Content-Type" : file->type ]) );
      string fname=file->fname;
      if( !CAMAS.Tools.is_7bitsafe( fname ) ) {
        string enc=(args["lang_charsets"][sessobj->language]
                    || "iso-8859-1" );
        fname=Locale.Charset.encoder(enc)->feed(fname)->drain();
        fname=MIME.encode_word(({ fname, enc }),"b");
      }
      mailpart->setdisp_param("filename", fname);
      mailpart->setparam("name", fname);
      mailpart->setencoding((file->type=="text/plain")?"8bit":"base64");
      if (!(file->data))
        mailpart->setdata(Stdio.read_bytes(args["uploaddir"]+"/"+sessobj->login+"_"+CAMAS.Tools.encode_fname(file->fname)));
      else
        mailpart->setdata(file->data);
      mail->body_parts+=({ mailpart });
    }
  }

  // keep out the full maildata otherwise we cannot send attachments
  string maildata = (string)mail;
  
  if (sessobj->saveattachments == "0") {
    if(parts>1) {
      mail->body_parts=({ mail->body_parts[0] });
    }
  }
  return ({ CAMAS.Tools.smtp_format(maildata, 0), sendrecipients, (string)mail });
}

// call the sendmail binary to send mail
void low_sendmail(string binarypath, string from, string maildata)
{
  object in = Stdio.File("stdout");
  object out=in->pipe();
  Process.spawn(binarypath+" -t -f "+from, out, 0, out);
  in->write(CAMAS.Tools.smtp_format(maildata,0));
  in->close();
}

/*
 * CAMAS SMTP client
 */

int|string smtp_send (object sessobj, array recipients, string maildata, string smtpserver, int smtpport, string smtpmaildomain)
{
  object ESMTP_client;
  mixed err = catch {
                ESMTP_client = Protocols.ESMTP.client (smtpserver, smtpport, smtpmaildomain);
              };
  if (err)
    return "Failed to connect to SMTP server.";
  else
  {
    int dsn = 0;
    if (sessobj->dsnsuccess) dsn |= Protocols.ESMTP.client.DSN_SUCCESS;
    if (sessobj->dsndelay) dsn |= Protocols.ESMTP.client.DSN_DELAY;
    if (dsn) dsn |= Protocols.ESMTP.client.DSN_FAILURE;
    err = catch {
            ESMTP_client->send_message (sessobj->address, recipients, maildata, dsn);
          };
  if (err)
      return "Failed to send mail.";
    else {
      ESMTP_client->close ();
      destruct (ESMTP_client);
    }
  }
}

#endif // __SMTP_H
/*                                                                             
 * Local Variables:                                                            
 * c-basic-offset: 2                                                           
 * End:                                                                        
 *                                                                             
 * vim: softtabstop=2 tabstop=2 expandtab autoindent formatoptions=croqlt smartindent cindent shiftwidth=2
 */

