Logo Search packages:      
Sourcecode: bayonne version File versions  Download package

vpim.cpp

// Copyright (C) 2000 Open Source Telecom Corporation.
//
// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#include <server.h>
#include <cc++/strchar.h>
#include <cc++/audio.h>

#ifdef      HAVE_SSTREAM
#include <sstream>
#else
#include <strstream>
#endif

#ifdef      CCXX_NAMESPACES
namespace ost {
using namespace std;
#endif

class SMTPSession : public TCPSession
{
private:
      char **msgs;
      char **caps;
      char *header;
      Protocol *smtp;
      InetHostAddress peer;
      char buffer[256];

      void initial(void);
      void run(void);

      void final(void)
            {delete this;};

      int getCode(void);
      char *getFrom(void);
      void putAddr(void);
      void putAudio(char *name, char *cap);
      void binhex(char *msg);
      InetHostAddress &getPeer(Protocol *pro);

public:
      SMTPSession(Protocol *smtp, const char *hdr, char **msgs, char **caps);
      ~SMTPSession();
};

class VPIM : public Protocol, private Module
{
private:
      modtype_t getType(void)
            {return MODULE_DELIVERY;};

      char *getName(void)
            {return "sendmail";};

      char *dispatch(Trunk *trunk);
public:
      VPIM();
} vpim;

SMTPSession::SMTPSession(Protocol *pro, const char *_hdr, char **_msgs, char **_caps) :
#ifdef      COMMON_OST_NAMESPACE
TCPSession(getPeer(pro), pro->getPort(), 1024, keythreads.priManager())
#else
TCPSession(pro->getSessions(), getPeer(pro), pro->getPort(), 1024, keythreads.priManager())
#endif
{
      header = new char[strlen(_hdr) + 1];
      strcpy(header, _hdr);
      msgs = _msgs;
      caps = _caps;
      smtp = pro;
}

SMTPSession::~SMTPSession()
{
      terminate();
      (smtp->getSessions())->post();
      delete[] header;
}

InetHostAddress &SMTPSession::getPeer(Protocol *pro)
{
      peer = pro->getAddress();
      return peer;
}

void SMTPSession::initial(void)
{
      timeout_t timeout = atol(smtp->getLast("timeout")) * 1000l;

      if(waitConnection(timeout))
      {
            slog(Slog::levelError) << "smtp: " << peer.getHostname() << ": timeout connecting" << endl;
            exit();
      }
}

void SMTPSession::run(void)
{
      char *from;
      char *to;
      int index = 0;

#ifdef      HAVE_SSTREAM
      istringstream hdr;
      hdr.str() = header;
#else
      istrstream hdr(header);
#endif

      if(getCode() != 220)
      {
            slog(Slog::levelError) << "smtp: " << peer.getHostname() << ": failed" << endl;
            exit();
      }
      *this << "HELO " << smtp->getLast("deliver") << '\r' << endl;
      if(getCode() != 250)
      {
            slog(Slog::levelError) << "smtp: " << peer.getHostname() << ": refused" << endl;
            exit();
      }
      from = getFrom();
      if(!from)
      {
            slog(Slog::levelError) << "smtp: " << peer.getHostname() << ": no sender" << endl;
            exit();
      }
      *this << "MAIL FROM:<" << from << ">\r" << endl;
      if(getCode() != 250)
      {
            slog(Slog::levelError) << "smtp: " << peer.getHostname() << ": failed" << endl;
            exit();
      }

      while(!hdr.eof())
      {
            hdr.getline(buffer, sizeof(buffer));
            if(!strnicmp(buffer, "To: ", 4))
                  putAddr();
            if(!strnicmp(buffer, "Cc: ", 4))
                  putAddr();
            if(!strnicmp(buffer, "Bc: ", 4))
                  putAddr();
      }

      *this << "DATA" << '\r' << endl;
      if(getCode() != 354)
      {
            slog(Slog::levelError) << "smtp: " << peer.getHostname() << ": refused input" << endl;
            exit();
      }
      *this << header;
      flush();

      while(msgs[index])
      {
            if(!stricmp(caps[index], "form"))
                  ;
            else if(!stricmp(caps[index], "fax"))
                  ;
            else
                  putAudio(msgs[index], caps[index]);
            ++index;
      }

      *this << ".\r" << endl;
      if(getCode() != 250)
            slog(Slog::levelError) << "smtp: " << peer.getHostname() << ": incomplete" << endl;
      *this << "QUIT" << '\r' << endl;
      getCode();
}

void SMTPSession::putAudio(char *msg, char *cap)
{
      long duration;
      Audio::Encoding fmt;
      AudioFile af;
      struct stat ino;
      char *ext = strrchr(msg, '.');

      if(!ext)
            ext = "";

      if(stat(msg, &ino))
            return;

      af.open(msg);
      fmt = af.getEncoding();
      af.setPosition();
      duration = af.getPosition() / af.getSampleRate();
      af.close();

      *this << "\r\n--MessageBoundry\r" << endl;
      switch(fmt)
      {
      case Audio::okiADPCM:
      case Audio::g721ADPCM:
      case Audio::voxADPCM:
            *this << "Content-Type: Audio/32KADPCM" << '\r' << endl;
            break;
      default:
            if(!stricmp(ext, ".wav") || !stricmp(ext, ".wave"))
                  *this << "Content-Type: audio/x-wav" << '\r' << endl;
            else
                  *this << "Content-Type: audio/basic" << '\r' << endl;
      }
      *this << "Content-Transfer-Encoding: Base64" << '\r' << endl;
      if(!stricmp(cap, "Name"))
      {
            *this << "Content-Disposition: inline; voice=Originator-Spoken-Name" << '\r' << endl;
            *this << "Content-ID: " << msg << '@' << smtp->getLast("deliver") << '\r' << endl;        
      }
      else if(!stricmp(cap, "Forward"))
      {
            *this << "Content-Description: Forwarded Message Annotation" << '\r' << endl;
            *this << "Content-Disposition: inline; voice=Voice-Message" << '\r' << endl;
      }
      else
      {
            *this << "Content-Description: Bayonne Voice Message" << '\r' << endl;
            *this << "Content-Disposition: inline; voice=Voice-Message; filename=" << msg << '\r' << endl;
            *this << "Content-Duration: " << duration << '\r' << end;
      }
      *this << '\r' << endl;
      binhex(msg);
}     

void SMTPSession::binhex(char *msg)
{
      const int linelen = 72;
      ifstream src;
      int i, c, lp = 0;
      bool eofmark = false;
      unsigned char input[3], output[5];
      static unsigned char table[] = 
            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

      src.open(msg);
      if(!src.is_open())
            return;     

      while(!eofmark)
      {
            memset(input, 0, sizeof(input));
            for(i = 0; i < 3; ++i)
            {
                  c = src.get();
                  if(c == EOF)
                  {
                        eofmark = true;
                        break;
                  }
                  input[i] = (unsigned char)c;
            }
            if(i > 0)
            {
                  output[0] = table[input[0] >> 2];
                  output[1] = table[((input[0] & 3) << 4) | (input[1] >> 4)];
                  output[2] = table[((input[1] & 0x0f) << 2) | (input[2] >> 6)];
                  output[3] = table[input[2] & 0x3f];
            }
            switch(i)
            {
            case 2:
                  output[3] = '=';
                  break;
            case 1:
                  output[2] = '=';
            }
            *this << output;
            lp += strlen((char *)output);
            if(lp >= linelen)
            {
                  *this << '\r' << endl;
                  lp = 0;
            }
      }
      if(lp)
            *this << '\r' << endl;

      src.close();
}
            
void SMTPSession::putAddr(void)
{
      char *t;
      char *f = strchr(buffer, '<');

      if(!f)
            return;     
      t = strchr(f, '>');
      *t = 0;
      *this << "RCPT TO:<" << ++f << ">\r" << endl;
      switch(getCode())
      {
      case 250:
            return;
      default:
            slog(Slog::levelWarning) << "smtp: cannot send " << f << end;
            return;
      }
}

char *SMTPSession::getFrom(void)
{
#ifdef      HAVE_SSTREAM
      istringstream hdr;
      hdr.str() = header;
#else
      istrstream hdr(header);
#endif
      char *cp;
      char *tail;

      for(;;)
      {
            hdr.getline(buffer, sizeof(buffer));
            if(!strncmp(buffer, "From:", 5))
                  break;
      }
      cp = strchr(buffer, '<');
      if(!cp)
            return NULL;
      tail = strchr(cp, '>');
      *tail = 0;
      return ++cp;
}

int SMTPSession::getCode(void)
{
      char buf[256];
      char *cp;
      int code;

      for(;;)
      {
            getline(buf, 256);
            if(eof())
                  return -1;

            cp = buf;
            while(isspace(*cp))
                  ++cp;

            if(*cp >= '0' && *cp <= '9')
            {
                  code = atoi(cp);
                  while(*(++cp))
                  {
                        if(*cp == ' ')
                              return code;
                        if(*cp == '-')
                              break;
                  }
            }
      }
}     

static char *cap(const char *str)
{
      static char buf[256];
      char *bp = buf + 1;

      buf[0] = toupper(*str);
      ++str;
      while(*str)
      {
            *(bp++) = tolower(*str);
            ++str;
      }
      *bp = 0;
      return buf;
}

VPIM::VPIM() :
Module(), Protocol("smtp", 25)
{
      static Keydata::Define keys[] = {
      {"deliver", "localhost"},     // default delivery target
      {"subject", "VPIM Message"},  // default subject
      {"sender", "VPIM User"},      // part of default from
      {"from", "\"Telephone Answering\" <non-mail-user@localhost>"},
      {"to", "General Delivery <0@localhost>"},
      {"timeout", "60"},
      {NULL, NULL}};

      load(keys);

      driver->addModule(this);
}

#define     MSGS  32

char *VPIM::dispatch(Trunk *trunk)
{
      char *msgs[MSGS + 1];
      char *caps[MSGS + 1];
      const char *sens = "Private";
      const char *urg = "High";
      const char *from = getLast("from");
      const char *subj = getLast("subject");
      char *addr;
      char *sp, *ext, *cp;
#ifdef      HAVE_SSTREAM
      ostringstream hdr;
#else
      char buf[2048];
      ostrstream hdr(buf, sizeof(buf));
#endif
      SMTPSession *smtp;
      Semaphore *start = getSessions();
      time_t now;
      int msgcnt = 0;
      int tocnt = 1;
      bool to = false;

      time(&now);
#ifdef      HAVE_SSTREAM
      hdr.str() = "";
#else
      buf[0] = 0;
#endif

      while(NULL != (cp = trunk->getValue(NULL)) && msgcnt < MSGS)
      {
            if(!stricmp(cp, "&once"))
            {
                  if(!trunk->getOnce())
                        return NULL;
            }

            if(!stricmp(cp, "&subject") || !stricmp(cp, "&subj"))
            {
                  subj = trunk->getValue(getLast("subject"));
                  continue;
            }
            if(!stricmp(cp, "&from"))
            {
                  from = trunk->getValue(getLast("from"));
                  continue;
            }
            if(!stricmp(cp, "&to"))
            {
                  to = true;
                  cp = trunk->getValue(getLast("to"));
                  if(!cp)
                              continue;
                  addr = strchr(cp, '@');
                  hdr << "To: " << addr;
                  if(addr)
                        hdr << '\r' << endl;
                  else
                        hdr << '@' << getLast("deliver") << '\r' << endl;
                  continue;
            }
            if(!stricmp(cp, "&cc"))
            {
                  to = true;
                  cp = trunk->getValue(getLast("to"));
                  if(!cp)
                              continue; 
                  addr = strchr(cp, '@');
                  hdr << "Cc: " << addr;
                  if(addr)
                        hdr << '\r' << endl;
                  else
                        hdr << '@' << getLast("deliver") << '\r' << endl;
                  continue;
            }
            if(!stricmp(cp, "&urgency") || !stricmp(cp, "&important"))
            {
                  urg = trunk->getValue("High");
                  continue;
            }
            if(!stricmp(cp, "&classify") || !stricmp(cp, "&sensitivity"))
            {
                  sens = trunk->getValue("Private");
                  continue;
            }
            if(!stricmp(cp, "&bc"))
            {
                  to = true;
                  cp = trunk->getValue(getLast("to"));
                  if(!cp)
                              continue; 
                  addr = strchr(cp, '@');
                  hdr << "Bc: " << addr;
                  if(addr)
                        hdr << '\r' << endl;
                  else
                        hdr << '@' << getLast("deliver") << '\r' << endl;
                  continue;
            }
            if(!stricmp(cp, "&name"))
            {
                  caps[msgcnt] = "Name";
                  msgs[msgcnt++] = trunk->getValue(NULL);
                  continue;
            }
            if(!stricmp(cp, "&fax"))
            {
                  caps[msgcnt] = "Fax";
                  msgs[msgcnt++] = trunk->getValue(NULL);
            }
            if(!stricmp(cp, "&message"))
            {
                  caps[msgcnt] = "Message";
                  msgs[msgcnt++] = trunk->getValue(NULL);
                  continue;
            }
            if(!stricmp(cp, "&forward") || !stricmp(cp, "&annotation"))
            {
                  caps[msgcnt] = "Forward";
                  msgs[msgcnt++] = trunk->getValue(NULL);
                  continue;
            }
            if(!stricmp(cp, "&form"))
            {
                  caps[msgcnt] = "Form";
                  msgs[msgcnt++] = trunk->getValue(NULL);
                  continue;
            }
            if(!stricmp(cp, "&vcard") || !stricmp(cp, "&card"))
            {
                  caps[msgcnt] = "Card";
                  msgs[msgcnt++] = trunk->getValue(NULL);
            }
            if(strchr(cp, '@'))
            {
                  hdr << "To: " << cp << '\r' << endl;
                  continue;
            }     

            // if unknown rule, skip...

            if(*cp == '&')
            {
                  trunk->getOption(NULL);
                  continue;
            }

            // else we start guessing...

            ext = strrchr(ext, '.');
            caps[msgcnt] = NULL;
            msgs[msgcnt] = cp;
            if(!stricmp(ext, ".au"))
                  caps[msgcnt] = "Message";
            else if(!stricmp(ext, ".wav"))
                  caps[msgcnt] = "Message";
            else if(!stricmp(ext, ".adpcm"))
                  caps[msgcnt] = "Message";
            else if(!stricmp(ext, ".ul") || !stricmp(ext, ".al"))
                  caps[msgcnt] = "Message";
            else if(!stricmp(ext, ".htm") || !stricmp(ext, ".html"))
                  caps[msgcnt] = "Form";
            if(caps[msgcnt])
            {
                  ++msgcnt;
                  continue;
            }
            hdr << "To: " << cp << '@' << getLast("deliver") << '\r' << endl;
      }
      msgs[msgcnt] = NULL;

      if(!to)
            hdr << "To: " << getLast("to") << '\r' << endl;

      hdr << "From: ";
      if(!strchr(from, '<'))
            hdr << '\"' << getLast("sender") << "\" <";

      hdr << from;
      if(!strchr(from, '@'))
            hdr << '@' << getLast("deliver");
      
      if(!strchr(from, '>'))
            hdr << '>';
      hdr << '\r' << endl;
            
      hdr << "Subject: " << subj << '\r' << endl;
      
      hdr << "MIME-Version: 1.0 (Voice 2.0) \r\n";
      hdr << "Content-type: Multipart/Voice-Message; Version=2.0;\r\n";
      hdr << "  Boundry=\"MessageBoundry\"\r\n";
      hdr << "Content-Transfer-Encoding: 7bit\r\n";
      hdr << "Message-ID: " << keyserver.getNode() << trunk->getSymbol(SYM_ID) << now << '@' << getLast("deliver") << '\r' << endl;

      hdr << "Sensitivity: " << cap(sens) << '\r' << endl;
      hdr << "Importance: " << cap(urg) << '\r' << endl;

      hdr << '\r' << endl;
      
#ifdef      HAVE_SSTREAM
      smtp = new SMTPSession(this, hdr.str().c_str(), msgs, caps);
#else
      hdr << ends;
      buf[sizeof(buf) - 1] = 0;
      smtp = new SMTPSession(this, buf, msgs, caps);
#endif
      return NULL;
}

#ifdef      CCXX_NAMESPACES
};
#endif

Generated by  Doxygen 1.6.0   Back to index