summaryrefslogtreecommitdiffstats
path: root/libmail/rfcaddr.C
diff options
context:
space:
mode:
Diffstat (limited to 'libmail/rfcaddr.C')
-rw-r--r--libmail/rfcaddr.C442
1 files changed, 442 insertions, 0 deletions
diff --git a/libmail/rfcaddr.C b/libmail/rfcaddr.C
new file mode 100644
index 0000000..e91cbef
--- /dev/null
+++ b/libmail/rfcaddr.C
@@ -0,0 +1,442 @@
+/*
+** Copyright 2002-2011, Double Precision Inc.
+**
+** See COPYING for distribution information.
+*/
+#include "libmail_config.h"
+#include <string.h>
+#include "rfcaddr.H"
+#include "mail.H"
+#include "misc.H"
+#include "rfc2047encode.H"
+#include "rfc2047decode.H"
+#include "rfc822/rfc822.h"
+#include "unicode/unicode.h"
+
+#include <iostream>
+#include <algorithm>
+
+#if LIBIDN
+#include <idna.h>
+#include <stringprep.h>
+
+namespace mail {
+ class libidn_init {
+
+ public:
+ libidn_init();
+ ~libidn_init();
+ };
+
+ libidn_init libidn_init_instance;
+}
+
+mail::libidn_init::libidn_init()
+{
+ if (!stringprep_check_version(STRINGPREP_VERSION))
+ {
+ std::cerr << "Required stringprep version "
+ << STRINGPREP_VERSION << " is not installed"
+ << std::endl;
+ abort();
+ }
+}
+
+mail::libidn_init::~libidn_init()
+{
+}
+#endif
+
+using namespace std;
+
+mail::address::address(string n, string a): name(n), addr(a)
+{
+}
+
+mail::address::~address()
+{
+}
+
+string mail::address::toString() const
+{
+ if (addr.length() == 0)
+ return name;
+
+ string q=addr;
+
+ // Only add < > if there's a name
+
+ if (name.length() > 0)
+ {
+ q=" <" + q + ">";
+
+ // If name is all alphabetics, don't bother with quotes.
+
+ string::const_iterator b=name.begin(), e=name.end();
+
+ while (b != e)
+ {
+ if (!strchr(" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789?*+-/=_",
+ *b))
+ {
+ string s="\"";
+
+ b=name.begin(), e=name.end();
+
+ while (b != e)
+ {
+ if (strchr("\\\"()<>", *b))
+ s += "\\";
+ // These chars must be escaped.
+
+ s.append(&*b, 1);
+ b++;
+ }
+ return s + "\"" + q;
+ }
+ b++;
+ }
+ q=name + q;
+ }
+ return q;
+}
+
+template<class T>
+string mail::address::toString(string hdr, const vector<T> &h,
+ size_t w)
+{
+ int len=hdr.length();
+ string comma="";
+ bool first=true;
+
+ string spc="";
+
+ spc.insert(spc.end(), len < 20 ? len:20, ' ');
+ // Leading whitespace for folded lines.
+
+ typename std::vector<T>::const_iterator b=h.begin(), e=h.end();
+
+ while (b != e)
+ {
+ string s= b->toString();
+
+ if (!first && len + comma.length() + s.length() > w)
+ {
+ // Line too long, wrap it.
+
+ if (comma.size() > 0)
+ comma=",";
+
+ hdr=hdr + comma + "\n" + spc;
+ len=spc.size();
+ comma="";
+ }
+ first=false;
+ hdr = hdr + comma + s;
+
+ len += comma.length() + s.length();
+ comma=", ";
+
+ if (b->addr.length() == 0)
+ comma="";
+ b++;
+ }
+ return hdr;
+}
+
+template
+string mail::address::toString(string hdr, const vector<mail::address> &h,
+ size_t w);
+
+template
+string mail::address::toString(string hdr,
+ const vector<mail::emailAddress> &h,
+ size_t w);
+
+string mail::address::getCanonAddress() const
+{
+ string a=addr;
+
+ size_t n=a.find_last_of('@');
+
+ if (n < a.size())
+ {
+ a=a.substr(0, n) +
+ mail::iconvert::convert_tocase(a.substr(n),
+ "iso-8859-1",
+ unicode_lc);
+ }
+
+ return a;
+}
+
+bool mail::address::operator==(const mail::address &o) const
+{
+ return getCanonAddress() == o.getCanonAddress();
+}
+
+static void parseCallback(const char *str, int pos, void *voidarg)
+{
+ *(size_t *)voidarg=pos;
+}
+
+template<class T> bool mail::address::fromString(string addresses,
+ vector<T> &h,
+ size_t &errIndex)
+{
+ errIndex=std::string::npos;
+
+ struct rfc822t *t=rfc822t_alloc_new(addresses.c_str(),
+ &parseCallback, &errIndex);
+ if (!t)
+ return false;
+
+ struct rfc822a *a=rfc822a_alloc(t);
+
+ if (!a)
+ {
+ rfc822t_free(t);
+ return false;
+ }
+
+ try {
+ int i;
+
+ for (i=0; i<a->naddrs; i++)
+ {
+ string name;
+ string addr;
+
+ char *n=rfc822_display_name_tobuf(a, i, NULL);
+
+ if (!n)
+ {
+ rfc822a_free(a);
+ rfc822t_free(t);
+ return false;
+ }
+
+ try {
+ name=n;
+ free(n);
+ } catch (...) {
+ free(n);
+ LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
+ }
+
+
+ n=rfc822_getaddr(a, i);
+
+ if (!n)
+ {
+ rfc822a_free(a);
+ rfc822t_free(t);
+ return false;
+ }
+
+ try {
+ addr=n;
+ free(n);
+ } catch (...) {
+ free(n);
+ LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
+ }
+
+ if (a->addrs[i].name == 0)
+ name="";
+
+ h.push_back( mail::address(name, addr));
+ }
+ rfc822a_free(a);
+ rfc822t_free(t);
+ } catch (...) {
+ rfc822a_free(a);
+ rfc822t_free(t);
+ LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
+ }
+ return true;
+}
+
+template
+bool mail::address::fromString(string addresses,
+ vector<mail::address> &h,
+ size_t &errIndex);
+
+template
+bool mail::address::fromString(string addresses,
+ vector<mail::emailAddress> &h,
+ size_t &errIndex);
+
+
+void mail::address::setName(std::string s)
+{
+ name=s;
+}
+
+void mail::address::setAddr(std::string s)
+{
+ addr=s;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+mail::emailAddress::emailAddress() : mail::address("", "")
+{
+}
+
+mail::emailAddress::~emailAddress()
+{
+}
+
+mail::emailAddress::emailAddress(const mail::address &a)
+ : mail::address(a.getName(), a.getAddr())
+{
+ decode();
+}
+
+std::string mail::emailAddress::setDisplayName(const std::string &s,
+ const std::string &charset)
+{
+ std::vector<unicode_char> ucbuf;
+
+ if (!mail::iconvert::convert(s, charset, ucbuf))
+ return "Encoding error";
+
+ decodedName=ucbuf;
+
+ name=mail::rfc2047::encodeAddrName(s, charset);
+
+ return "";
+}
+
+std::string mail::emailAddress::setDisplayAddr(const std::string &s,
+ const std::string &charset)
+{
+ std::vector<unicode_char> ucbuf;
+
+ if (!mail::iconvert::convert(s, charset, ucbuf))
+ return "Encoding error";
+
+#if LIBIDN
+ std::vector<unicode_char>::iterator b, e;
+
+ for (b=ucbuf.begin(), e=ucbuf.end(); b != e; ++b)
+ {
+ if (*b == '@')
+ {
+ ++b;
+ break;
+ }
+ }
+
+ size_t addr_start= b-ucbuf.begin();
+
+ // Assume non-latin usernames are simply UTF-8 */
+
+ std::string name_portion=
+ mail::iconvert::convert(std::vector<unicode_char>(ucbuf.begin(),
+ b),
+ "utf-8");
+
+ ucbuf.push_back(0);
+
+ char *ascii_ptr;
+
+ int rc=idna_to_ascii_4z(&ucbuf[addr_start], &ascii_ptr, 0);
+
+ if (rc != IDNA_SUCCESS)
+ return std::string(std::string("Address encoding error: ") +
+ idna_strerror((Idna_rc)rc));
+
+ try {
+ name_portion += ascii_ptr;
+ free(ascii_ptr);
+ } catch (...) {
+ free(ascii_ptr);
+ throw;
+ }
+
+ ucbuf.pop_back();
+ addr=name_portion;
+#else
+ std::vector<unicode_char>::iterator b, e;
+
+ for (b=ucbuf.begin(), e=ucbuf.end(); b != e; ++b)
+ {
+ if (*b < ' ' || *b > 0x7F)
+ return "Internationalized addresses not supported";
+ }
+
+ addr.clear();
+ addr.reserve(ucbuf.size());
+ addr.insert(addr.end(), ucbuf.begin(), ucbuf.end());
+
+#endif
+ decodedAddr=ucbuf;
+ return "";
+}
+
+void mail::emailAddress::setName(string s)
+{
+ mail::address::setName(s);
+ decode();
+}
+
+void mail::emailAddress::setAddr(string s)
+{
+ mail::address::setAddr(s);
+ decode();
+}
+
+void mail::emailAddress::decode()
+{
+ std::vector<unicode_char> ucname, ucaddr;
+
+ mail::iconvert::convert(mail::rfc2047::decoder().decode(name,
+ "utf-8"),
+ "utf-8", ucname);
+
+ ucaddr.reserve(addr.size());
+
+ std::string::iterator b, e;
+
+ for (std::string::iterator b=addr.begin(), e=addr.end(); b != e; ++b)
+ ucaddr.push_back((unsigned char)*b);
+
+#if LIBIDN
+ size_t at=std::find(ucaddr.begin(), ucaddr.end(), '@')
+ - ucaddr.begin();
+
+ if (at != std::string::npos)
+ {
+ ++at;
+ ucaddr.push_back(0);
+
+ unicode_char *ucbuf;
+
+ int rc=idna_to_unicode_4z4z(&ucaddr[at], &ucbuf, 0);
+
+ if (rc == IDNA_SUCCESS)
+ {
+ size_t i;
+
+ for (i=0; ucbuf[i]; ++i)
+ ;
+
+ try {
+ ucaddr.erase(ucaddr.begin()+at,
+ ucaddr.end());
+ ucaddr.insert(ucaddr.end(),
+ ucbuf, ucbuf+i);
+ free(ucbuf);
+ } catch (...) {
+ free(ucbuf);
+ throw;
+ }
+ }
+ }
+#endif
+ decodedName=ucname;
+ decodedAddr=ucaddr;
+}
+