Coder Social home page Coder Social logo

karastojko / mailio Goto Github PK

View Code? Open in Web Editor NEW
343.0 343.0 93.0 1.06 MB

mailio is a cross platform C++ library for MIME format and SMTP, POP3 and IMAP protocols. It is based on standard C++ 17 and Boost library.

License: Other

C++ 98.53% CMake 1.47%
c-plus-plus-11 imap library pop3 smtp

mailio's People

Contributors

audaki avatar david-antiteum avatar developerpaul123 avatar karastojko avatar lhkipp avatar lifof avatar mrsoymilk avatar sabelka avatar sledgehammer999 avatar terminator356 avatar tiluha avatar trevormellon avatar tsilia avatar tsurumi-yizhou avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mailio's Issues

Trying to get unread messages or move mail

Hi, I want to parse mail and then either move it, or be able to search through a mailbox and find unread mail, parse it, and mark it as read so it wont be parsed in the future.
I looked through the
imaps::search_condition_t::
And did not see any conditions that I could use to find out if an email is read or unread.

I looked through the mailbox class and saw mail name and address (which I'm assuming is TO on an email) and did not see a way to move emails from one box to another.

How do I either move mail from one box to another or find mail that is currently unread?

message::parse_many_ids() fails to parse ID lists without spaces

Hello.
I have run into an issue: message::parse_many_ids() can't parse ID strings which don't contain a space separator, like this one:
<[email protected]><[email protected]><[email protected]>

It's a great library which implements almost all features I need, but right now it's not possible for me to fetch some e-mails due to an exception thrown: Parsing failure of the message ID. Thunderbird fetches such messages successfully.

Please, is there anything you could do to support such space-lacking ID lists as this seems to be a common misbehaviour of mail servers?

I've read another issue (#7) and saw you mentioned 'strict' mode which can be turned off to lift some restrictions. I did so and it didn't resolve the issue. I guess, this doesn't apply to parsing ID lists. Could you please extend the non-strict mode to ID lists?
Thank you.

CMake version

On Ubuntu 20.04 the latest cmake version is 3.16.3.
Could you consider to change?

 cmake_minimum_required (VERSION 3.16.4)

to

 cmake_minimum_required (VERSION 3.16.3)

Empty values of 'References' and 'In-Reply-To' headers cause exceptions

Hello.
It seems that References and In-Reply-To headers are treated the same way as Message-id is: if the header has empty value or a value that can't be parsed as a correct one, an exception is thrown which prevents emails with such headers from getting fetched by the library. But using an empty string in these headers seems to be a common behaviour among mail clients/servers. I have few emails sent with MS Outlook which have empty References, In-Reply-To and even Message-id headers, e.g. (emphasis is mine):

...
References:
In-Reply-To:
Subject: =?koi8-r?B?UkU6IOvBzcXS2Q==?=
Date: Mon, 9 Dec 2019 14:14:31 +0300
Message-ID: [email protected]
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="----=_NextPart_000_0042_01D5AE9A.F9895020"
X-Mailer: Microsoft Outlook 15.0
...

Is this somehow possible to make parsing less restrictive and allow empty IDs in corresponding headers?
I saw that the code relies on throwing an exception and then tries to access first element from a vector of IDs returned by the mime::parse_many_ids() method.

It probably would be better not to throw exception in case when ID is empty, and then check if the returned vector of IDs parsed by mime::parse_many_ids() is not empty. If it is, just consider using an empty ID later on. I could implement it in a PR if you're OK with this. Otherwise, messages sent by MS Outlook and some other clients can't be accessed by the library.

INSTALL cannot find build/docs/html

cmake ..

gives me no warning or error.

make install

stops with the following error:

[100%] Built target pop3s_fetch_one
Install the project...
-- Install configuration: ""
CMake Error at cmake_install.cmake:41 (file):
  file INSTALL cannot find
  "/home/<username>/work/3rdParty/mailio/build/docs/html".

make: *** [Makefile:118: install] Error 1

Custom headers

I'd like to add custom header, for example X-Mailer.
I cannot find proper method to do it.

How to add custom headers?

UB in static variables initialization

codec::CRLF is initialized in codec.cpp
mime::END_OF_LINE is initialized in mime.cpp and depends on codec::CRLF
I exactly ran into a situation where CRLF was \r\n, but END_OF_LINE was empty

Passing an empty string as password causes a segfault

This can be easily replicated with the smtps_simple_msg example.

This is the backtrace I get:

#0  0x00007ffff777aaf8 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string (this=0x7fffffffdb60, __str=<error reading variable: Cannot access memory at address 0x8>)
    at /build/gcc/src/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/ext/new_allocator.h:82
#1  0x00007ffff7ecbc6c in std::operator+<char, std::char_traits<char>, std::allocator<char> > (__lhs=<error reading variable: Cannot access memory at address 0x8>, __rhs=0x7ffff7f629f7 "\r\n")
    at /usr/include/c++/10.1.0/bits/basic_string.h:6062
#2  0x00007ffff7ecfdc2 in mailio::dialog::send_sync<boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::executor>&> > (this=0x55555558ea60, socket=..., 
    line=<error reading variable: Cannot access memory at address 0x8>) at /tmp/mailio/src/dialog.cpp:110
#3  0x00007ffff7ebc091 in mailio::dialog_ssl::send (this=0x55555558ea60, line=<error reading variable: Cannot access memory at address 0x8>) at /tmp/mailio/src/dialog.cpp:277
#4  0x00007ffff7f5fd08 in mailio::smtp::auth_login (this=0x7fffffffde10, username="[email protected]", password="") at /tmp/mailio/src/smtp.cpp:197
#5  0x00007ffff7f60772 in mailio::smtps::authenticate (this=0x7fffffffde10, username="[email protected]", password="", method=mailio::smtps::auth_method_t::START_TLS) at /tmp/mailio/src/smtp.cpp:312
#6  0x000055555556890f in main () at /tmp/mailio/examples/smtps_simple_msg.cpp:45

The same code works fine when you provide a password. If the lack of password is an issue, I would expect an exception to be thrown or an error code to be returned.

Happens on:
Arch
gcc-10.1.0
mailio compiled from the master branch

Possible bug: header name doesn't get regex-matched in non-strict mode if the corresponding value is empty

Hello.
In the mime.cpp there could be a logic error introduced along with adding support for non-strict mode. As you can see in the source file:

mailio/src/mime.cpp

Lines 703 to 711 in 3fccce6

if (header_value.empty())
if (_strict_mode)
throw mime_error("Parsing failure, header name or value empty: " + header_line);
else
return;
smatch m;
if (!regex_match(header_name, m, HEADER_NAME_REGEX))
throw mime_error("Format failure of the header name `" + header_name + "`.");

in case when the method accepts an empty header_value, header_name wouldn't get checked with regex_match because the code would have returned before this check. But it seems reasonable to check the header_name regardless of whether the mode is strict or not.

I've checked this using the following code and the behaviour was different in two cases: empty value and non-empty value. In the second case an exception is thrown:

msg_wrapper msg;
msg.strict_mode(false);
msg.strict_codec_mode(false);
std::string header_name, header_value;
msg.ParseHeader("Invalid header: ", header_name, header_value);
msg.ParseHeader("Invalid header: non-empty value", header_name, header_value); // exception

(msg_wrapper is a thin wrapper class as mime::parse_header_name_value() method is protected).

Question: SMTP vs SMTPS

Hello there,

just a couple of questions, I am a bit confused about your naming convention for smtp and smtps.
Isn't smtps deprecated like 15 years ago ?

You have the following:
smtp:::auth_method_t::NONE
smtp:::auth_method_t::LOGIN
smtps:::auth_method_t::NONE
smtp:s::auth_method_t::LOGIN
smtp:s::auth_method_t::START_TLS

what is the difference between the two NONEs and the two LOGINs.

As I understand STARTTLS asks the server to upgrade the insecure connection to a secure one. So somehow it makes sense to me to have only one set of NONE, LOGIN and START_LS under a same nemespace..

explicitly defaulted copy constructor is implicitly deleted

/usr/local/mailio/include/mailio/dialog.hpp:213:5: warning: explicitly defaulted copy constructor is implicitly deleted [-Wdefaulted-function-deleted]
dialog_ssl(const dialog_ssl&) = default;
^
/usr/local/mailio/include/mailio/dialog.hpp:196:20: note: copy constructor of 'dialog_ssl' is implicitly deleted because base class 'mailio::dialog' has a deleted copy constructor
class dialog_ssl : public dialog
^
/usr/local/mailio/include/mailio/dialog.hpp:61:5: note: 'dialog' has been explicitly marked deleted here
dialog(const dialog& other) = delete;
^
1 warning generated.

I'm getting this warning which might cause memory leak. Could you please take a look at it? Thank you.

[Question] Is there a way to fetch a summary of email over IMAP?

Use case: In an email clieant, want to display the emails over the past 5 days. I want to show, say, the subject, from, date fields for those emails. Like one search query with a set of header fields (without changing the seen status, like BODY.PEEK). Is this currently possble?

Define or delete MAILIO_EXPORT if it's not needed

MAILIO_EXPORT seems to lack any definition.
At the same time some classes don't use it, which doesn't seem to be consistent.

For example:
class dialog_ssl : public dialog
class q_codec : public codec

Default Date header is wrong when sending emails

When the server used for sending the emails has set up a default server-wide time zone which is not UTC (can be tested by entering "date" into the shell) the default date header when sending emails is wrong.

Fixed by #8

Documentation failing to build/install

I am trying to build mailio on Ubuntu 20.10 however, there seems to be an issue with the documentation build.

I have ensured that the oxygen and dot commands are present on my system but the build fails, below is the last few lines before the failure.

[100%] Linking CXX executable smtps_simple_msg
make[2]: Leaving directory '/home/chris/Downloads/mailio/build'
[100%] Built target smtps_simple_msg
make[1]: Leaving directory '/home/chris/Downloads/mailio/build'
Install the project...
-- Install configuration: ""
CMake Error at cmake_install.cmake:41 (file):
file INSTALL cannot find "/home/chris/Downloads/mailio/build/docs/html": No
such file or directory.

make: *** [Makefile:86: install] Error 1

If I run cmake with -DMAILIO_BUILD_DOCUMENTATION=OFF everything builds and install OK.

build error on mac and the message is: ld: library not found for -licudata

message:ld: library not found for -licudata

output:
[ 33%] Building CXX object CMakeFiles/mailio.dir/src/pop3.cpp.o
[ 36%] Building CXX object CMakeFiles/mailio.dir/src/quoted_printable.cpp.o
[ 39%] Building CXX object CMakeFiles/mailio.dir/src/q_codec.cpp.o
[ 42%] Building CXX object CMakeFiles/mailio.dir/src/smtp.cpp.o
[ 45%] Linking CXX shared library libmailio.dylib
ld: library not found for -licudata
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [libmailio.dylib] Error 1
make[1]: *** [CMakeFiles/mailio.dir/all] Error 2
make: *** [all] Error 2

platform: macos 11.1
cc: clang 11.0.0
Target: x86_64-apple-darwin20.2.0
cmake: 3.19.2
make: 4.3

Question - SMTP send exception

Dear @karastojko

Hope you are doing well, and thanks for your useful library.

i am trying to use the SMTP send email the same as smtps_simple_msg.cpp and just change the smtps::auth_method_t to LOGIN.

and set the smtp address and credentials after the compiling i got this error : Mail message rejection.

I am trying to figure-out the problem and find these data and it maybe useful:

in below part of smtp.cpp the error returns : ( I Just print the error to get server message )

   _dlg->send("DATA");
    line = _dlg->receive();
    tokens = parse_line(line);
    std::cout << "Receive :" << line << std::endl;
    if (!positive_intermediate(std::get<0>(tokens)))
        throw smtp_error("Mail message rejection.");

return value is: 250 HELP
and because of HELP it throw the exception.

just for more information I apply the same setting (ex: email address, smtp_server_address, authentication_method, ...) in my gitlab test server and it works properly.

Wrong mail header and contents on Ubuntu 20.04

When I try the simple example:
example
the mail is sent in a wrong way:
Screenshot from 2020-08-17 23-05-16
The screenshot is taken from Gmail. As you can see TO, SUBJECT, CONTENTS are all inserted in the FROM field.
TO is also inserted into BCC (so the mail arrives).

Ubuntu 20.04
gcc 9.3.0
smtp is "smtp.gmail.com", 587

Empty Email Contents

msg.line_policy(mailio::codec::line_len_policy_t::VERYLARGE, mailio::codec::line_len_policy_t::VERYLARGE);
conn.fetch(2, msg);

std::cout << msg.subject() << std::endl;
std::cout << msg.content() << std::endl;

The email that i fetch gives me the subject line and its the correct subject line, however the contents are empty. The email has plenty of contents in it, images and text. I previously got the contents of another email to output, the email with the contents output had no attachment. The email without the contents output has an attachment.
How can i get the correct contents of the message?

Message parser chocking on charset-encoded addresses

Hello there!

The message parser is giving me the following error:

mailio::message_error: Parsing failure of address or group at `<`.

Thrown from message.cpp:716, because of the following sender address:

 =?windows-1252?Q?Action_fran=E7aise_?=<[email protected]>

My initial intuition is that MIME header encodings are not supported, but I might very well be wrong. I'll do a bit more digging, but any guidance would be appreciated :)

Thanks,

Windows version crashes when timeout is not zero

Hi, I'm not sure what happened to the issue I logged last year, but it's gone now.

You confirmed a bug in the Windows version where if a timeout is specified for connect, a crash results. Have you gotten anywhere with this problem yet? No rush, just wondering...

Won't Compile

Trying to install on Ubuntu Server, using C++17 with boost installed.
I get compile errors upon running make command:

In file included from /home/pecacheu/mail/mailio/src/dialog.cpp:17:0:
/home/pecacheu/mail/mailio/include/mailio/dialog.hpp:159:34: error: ‘io_context’ is not a member of ‘boost::asio’                                                                                                    std::unique_ptr<boost::asio::io_context> _ios;                                                                                                                                                                                               ^~~~~~~~~~
/home/pecacheu/mail/mailio/include/mailio/dialog.hpp:159:34: note: suggested alternative: ‘connect’
     std::unique_ptr<boost::asio::io_context> _ios;
                                  ^~~~~~~~~~
                                  connect
/home/pecacheu/mail/mailio/include/mailio/dialog.hpp:159:44: error: template argument 1 is invalid                                                                                                                   std::unique_ptr<boost::asio::io_context> _ios;
                                            ^
/home/pecacheu/mail/mailio/include/mailio/dialog.hpp:159:44: error: template argument 2 is invalid
/home/pecacheu/mail/mailio/src/dialog.cpp:28:20: error: ‘boost::asio::io_context’ has not been declared                                                                                                          using boost::asio::io_context;
                    ^~~~~~~~~~
/home/pecacheu/mail/mailio/src/dialog.cpp: In constructor ‘mailio::dialog::dialog(const string&, unsigned int, std::chrono::milliseconds)’:
/home/pecacheu/mail/mailio/src/dialog.cpp:40:48: error: expected type-specifier before ‘io_context’
     _hostname(hostname), _port(port), _ios(new io_context()), _socket(*_ios), _timer(*_ios), _strmbuf(new streambuf()), _istrm(new istream(_strmbuf.get())),
                                                ^~~~~~~~~~
/home/pecacheu/mail/mailio/src/dialog.cpp:40:72: error: invalid type argument of unary ‘*’ (have ‘int’)
   _hostname(hostname), _port(port), _ios(new io_context()), _socket(*_ios), _timer(*_ios), _strmbuf(new streambuf()), _istrm(new istream(_strmbuf.get())),
                                                                      ^~~~                                                                                                                                      /home/pecacheu/mail/mailio/src/dialog.cpp:40:87: error: invalid type argument of unary ‘*’ (have ‘int’)
 tname), _port(port), _ios(new io_context()), _socket(*_ios), _timer(*_ios), _strmbuf(new streambuf()), _istrm(new istream(_strmbuf.get())),
                                                                      ^~~~
/home/pecacheu/mail/mailio/src/dialog.cpp:47:32: error: invalid type argument of unary ‘*’ (have ‘int’)
             tcp::resolver res(*_ios);
                                ^~~~
/home/pecacheu/mail/mailio/src/dialog.cpp:48:82: error: no matching function for call to ‘boost::asio::ip::basic_resolver<boost::asio::ip::tcp>::resolve(const string&, std::__cxx11::string)’
         boost::asio::connect(_socket, res.resolve(_hostname, to_string(_port)));
                                                                              ^
In file included from /usr/include/boost/asio.hpp:63:0,
                 from /home/pecacheu/mail/mailio/include/mailio/dialog.hpp:19,
                 from /home/pecacheu/mail/mailio/src/dialog.cpp:17:
/usr/include/boost/asio/ip/basic_resolver.hpp:99:12: note: candidate: boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::iterator boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::resolve(const query&) [with InternetProtocol = boost::asio::ip::tcp; ResolverService = boost::asio::ip::resolver_service<boost::asio::ip::tcp>; boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::iterator = boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp>; boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::query = boost::asio::ip::basic_resolver_query<boost::asio::ip::tcp>]
   iterator resolve(const query& q)                                                                                                                                                                                         ^~~~~~~                                                                                                                                                                                             /usr/include/boost/asio/ip/basic_resolver.hpp:99:12: note:   candidate expects 1 argument, 2 provided
/usr/include/boost/asio/ip/basic_resolver.hpp:124:12: note: candidate: boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::iterator boost::asio::ip::basic_resolver<InternetProtocol, ResolverSe
rvice>::resolve(const query&, boost::system::error_code&) [with InternetProtocol = boost::asio::ip::tcp; ResolverService = boost::asio::ip::resolver_service<boost::asio::ip::tcp>; boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::iterator = boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp>; boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::query = boost::asio::ip::basic_resolver_query<boost::asio::ip::tcp>]
   iterator resolve(const query& q, boost::system::error_code& ec)
            ^~~~~~~
/usr/include/boost/asio/ip/basic_resolver.hpp:124:12: note:   no known conversion for argument 2 from ‘std::__cxx11::string {aka std::__cxx11::basic_string<char> ’ to ‘boost::system::error_code&’
/usr/include/boost/asio/ip/basic_resolver.hpp:188:12: note: candidate: boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::iterator boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::resolve(const endpoint_type&) [with InternetProtocol = boost::asio::ip::tcp; ResolverService = boost::asio::ip::resolver_service<boost::asio::ip::tcp>; boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::iterator = boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp>; boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::endpoint_type = boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>]
   iterator resolve(const endpoint_type& e)
            ^~~~~~~
/usr/include/boost/asio/ip/basic_resolver.hpp:188:12: note:   candidate expects 1 argument, 2 provided
/usr/include/boost/asio/ip/basic_resolver.hpp:215:12: note: candidate: boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::iterator boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::resolve(const endpoint_type&, boost::system::error_code&) [with InternetProtocol = boost::asio::ip::tcp; ResolverService = boost::asio::ip::resolver_service<boost::asio::ip::tcp>; boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::iterator = boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp>; boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::endpoint_type = boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>]
   iterator resolve(const endpoint_type& e, boost::system::error_code& ec)
            ^~~~~~~
/usr/include/boost/asio/ip/basic_resolver.hpp:215:12: note:   no known conversion for argument 1 from ‘const string {aka const std::__cxx11::basic_string<char>}’ to ‘const endpoint_type& {aka const boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>&}’
/home/pecacheu/mail/mailio/src/dialog.cpp: In constructor ‘mailio::dialog::dialog(mailio::dialog&&)’:
/home/pecacheu/mail/mailio/src/dialog.cpp:62:40: error: use of deleted function  boost::asio::basic_deadline_timer<boost::posix_time::ptime>::basic_deadline_timer(boost::asio::basic_deadline_timer<boost::posix_time::ptime>&&)’
     _timer_expired(other._timer_expired)
                                        ^
In file included from /usr/include/boost/asio.hpp:22:0,
                 from /home/pecacheu/mail/mailio/include/mailio/dialog.hpp:19,
                 from /home/pecacheu/mail/mailio/src/dialog.cpp:17:
/usr/include/boost/asio/basic_deadline_timer.hpp:126:7: note: ‘boost::asio::basic_deadline_timer<boost::posix_time::ptime>::basic_deadline_timer(boost::asio::basic_deadline_timer<boost::posix_time::ptime>&&)’ is implicitly deleted because the default definition would be ill-formed:
 class basic_deadline_timer
       ^~~~~~~~~~~~~~~~~~~~
/usr/include/boost/asio/basic_deadline_timer.hpp:126:7: error: ‘boost::asio::basic_io_object<IoObjectService, Movable>::basic_io_object(const boost::asio::basic_io_object<IoObjectService, Movable>&) [with IoObjectService = boost::asio::deadline_timer_service<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime> >; bool Movable = false]’ is private within this context
In file included from /usr/include/boost/asio/basic_socket.hpp:20:0,
                 from /usr/include/boost/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/boost/asio.hpp:21,
                 from /home/pecacheu/mail/mailio/include/mailio/dialog.hpp:19,
                 from /home/pecacheu/mail/mailio/src/dialog.cpp:17:
/usr/include/boost/asio/basic_io_object.hpp:169:3: note: declared private here
   basic_io_object(const basic_io_object&);
   ^~~~~~~~~~~~~~~
/home/pecacheu/mail/mailio/src/dialog.cpp:64:10: error: request for member ‘rese ’ in ‘((mailio::dialog*)this)->mailio::dialog::_ios’, which is of non-class type ‘int’
     _ios.reset(other._ios.release());
          ^~~~~
/home/pecacheu/mail/mailio/src/dialog.cpp:64:27: error: request for member ‘release’ in ‘other.mailio::dialog::_ios’, which is of non-class type ‘int’
     _ios.reset(other._ios.release());
                           ^~~~~~~
/home/pecacheu/mail/mailio/src/dialog.cpp: In member function ‘void mailio::dialog::connect_async()’:
/home/pecacheu/mail/mailio/src/dialog.cpp:137:24: error: invalid type argument of unary ‘*’ (have ‘int’)
     tcp::resolver res(*_ios);
                        ^~~~
/home/pecacheu/mail/mailio/src/dialog.cpp:145:80: error: no matching function for call to ‘boost::asio::ip::basic_resolver<boost::asio::ip::tcp>::resolve(const string&, std::__cxx11::string)’
    boost::asio::async_connect(_socket, res.resolve(_hostname, to_string(_port)),
                                                                               ^
In file included from /usr/include/boost/asio.hpp:63:0,
                 from /home/pecacheu/mail/mailio/include/mailio/dialog.hpp:19,
                 from /home/pecacheu/mail/mailio/src/dialog.cpp:17:
/usr/include/boost/asio/ip/basic_resolver.hpp:99:12: note: candidate: boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::iterator boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::resolve(const query&) [with InternetProtocol = boost::asio::ip::tcp; ResolverService = boost::asio::ip::resolver_service<boost::asio::ip::tcp>; boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::iterator = boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp>; boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::query = boost::asio::ip::basic_resolver_query<boost::asio::ip::tcp>]
   iterator resolve(const query& q)
            ^~~~~~~
/usr/include/boost/asio/ip/basic_resolver.hpp:99:12: note:   candidate expects 1 argument, 2 provided
/usr/include/boost/asio/ip/basic_resolver.hpp:124:12: note: candidate: boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::iterator boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::resolve(const query&, boost::system::error_code&) [with InternetProtocol = boost::asio::ip::tcp; ResolverService = boost::asio::ip::resolver_service<boost::asio::ip::tcp>; boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::iterator = boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp>; boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::query = boost::asio::ip::basic_resolver_query<boost::asio::ip::tcp>]
   iterator resolve(const query& q, boost::system::error_code& ec)
            ^~~~~~~
/usr/include/boost/asio/ip/basic_resolver.hpp:124:12: note:   no known conversion for argument 2 from ‘std::__cxx11::string {aka std::__cxx11::basic_string<char> ’ to ‘boost::system::error_code&’
/usr/include/boost/asio/ip/basic_resolver.hpp:188:12: note: candidate: boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::iterator boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::resolve(const endpoint_type&) [with InternetProtocol = boost::asio::ip::tcp; ResolverService = boost::asio::ip::resolver_service<boost::asio::ip::tcp>; boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::iterator = boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp>; boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::endpoint_type = boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>]
   iterator resolve(const endpoint_type& e)
            ^~~~~~~
/usr/include/boost/asio/ip/basic_resolver.hpp:188:12: note:   candidate expects 1 argument, 2 provided
/usr/include/boost/asio/ip/basic_resolver.hpp:215:12: note: candidate: boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::iterator boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::resolve(const endpoint_type&, boost::system::error_code&) [with InternetProtocol = boost::asio::ip::tcp; ResolverService = boost::asio::ip::resolver_service<boost::asio::ip::tcp>; boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::iterator = boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp>; boost::asio::ip::basic_resolver<InternetProtocol, ResolverService>::endpoint_type = boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>]
   iterator resolve(const endpoint_type& e, boost::system::error_code& ec)
            ^~~~~~~
/usr/include/boost/asio/ip/basic_resolver.hpp:215:12: note:   no known conversion for argument 1 from ‘const string {aka const std::__cxx11::basic_string<char>}’ to ‘const endpoint_type& {aka const boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>&}’
/home/pecacheu/mail/mailio/src/dialog.cpp:157:13: error: base operand of ‘->’ is not a pointer
         _ios->run_one();
             ^~
/home/pecacheu/mail/mailio/src/dialog.cpp: In member function ‘void mailio::dialog::send_async(Socket&, std::__cxx11::string)’:
/home/pecacheu/mail/mailio/src/dialog.cpp:180:13: error: base operand of ‘->’ is not a pointer
         _ios->run_one();
             ^~
/home/pecacheu/mail/mailio/src/dialog.cpp: In member function ‘std::__cxx11::string mailio::dialog::receive_async(Socket&, bool)’:
/home/pecacheu/mail/mailio/src/dialog.cpp:208:13: error: base operand of ‘->’ is not a pointer
         _ios->run_one();
             ^~
CMakeFiles/mailio.dir/build.make:182: recipe for target 'CMakeFiles/mailio.dir/src/dialog.cpp.o' failed
make[2]: *** [CMakeFiles/mailio.dir/src/dialog.cpp.o] Error 1
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/mailio.dir/all' failed
make[1]: *** [CMakeFiles/mailio.dir/all] Error 2
Makefile:129: recipe for target 'all' failed
make: *** [all] Error 2

Parsing failure.

Hi, Im working with the following code:

mailio::imap::mailbox_stat_t label_stats = conn.select(std::list<std::string>({"Labels","Label A"}));    

mailio::message msg;
msg.line_policy(mailio::codec::line_len_policy_t::VERYLARGE, mailio::codec::line_len_policy_t::VERYLARGE);
std::cout << label_stats.messages_first_unseen << std::endl;
if(label_stats.messages_first_unseen != 0) {
    conn.fetch(label_stats.messages_first_unseen, msg);
}
std::cout << label_stats.messages_first_unseen << std::endl;

I get the results:

5
Parsing failure.

Whenever I try to get the message i get a parse failure on the fetch line, however when i check the mailbox the mail is marked as read so it is touching the correct email. I don't understand the possible causes of a parse failure given the source code.

Mulitpart body message

Hello,
How to send multi content body?
this

        // html
        msg.content_type(message::media_type_t::TEXT, "html", "utf-8");
        msg.content("<html><body style=\"color: red;\">Hello HTML World!</body></html>");
        //plain
        msg.content_type(message::media_type_t::TEXT, "plain", "utf-8");
        msg.content("Hello PLAIN World!");

overwrites html part.

Exceptions seem to lack information

Hello.
Exceptions in mailio often don't seem to be very informative, e.g.:
throw message_error("Parsing failure of the message ID.");
or
throw pop3_error("Parser failure.");
which unfortunately don't say much about the value that caused this exception or the reason.

Perhaps you could somehow extend exception interfaces to allow saving and retrieving additional info which could be the exact value that failed to parse or at least some context that could help to track down the issue if the exact value isn't available at the time when exception is thrown?
It probably would be better if there was another parameter for message_error (and the likes) constructor, like const std::string& token or const std::string& context which could also be retrieved in exception handler using a corresponding method, like get_error_token() or get_context().
This way it doesn't break anything for anyone because all who wants to have nice and clean human-readable messages will still get those with no edits required on their side. It would require editing of the library code though.

An example implementation:

class message_error : public std::runtime_error
{
    ...
    explicit message_error(const std::string& msg, const std::string& context)
        : std::runtime_error(msg)
        , context_(context)
    {
    }
    ...
    std::string get_context() const
    {
        return context_;
    }
}

...
// later in the library code
throw message_error("Parsing failure of the message ID.", header_value);

// later in the user code
try
{
    ...
}
catch (const mailio::message_error& ex)
{
    std::cerr << ex.what() << " " << ex.get_context() << std::endl;
}

Also, it would probably be better to store both the invalid token AND the context in which it appeared because there are cases when few case blocks of switch produce exceptions with the same message but for different states of the parser, or the cases like this one when you throw pop3_error and we lose the original exception reason:

mailio/src/pop3.cpp

Lines 127 to 134 in f82f7e8

catch (out_of_range&)
{
throw pop3_error("Parser failure.");
}
catch (invalid_argument&)
{
throw pop3_error("Parser failure.");
}

In such cases it probably could be better to save ex.what() as the exception context similarly to what was described above.

Setting Sender: header

Dear @karastojko,

let me begin with saying that your email library is exactly what I was looking for, it's a joy to use, thank you for your work.

For our application we will need to set a Sender header (different than From: header), see https://stackoverflow.com/a/14253556 for RFC explanation.

Since we'll need this feature soon I will probably implement this myself in the next days and I would like to create a pull request, would you be willing to accept this pull request? Do you have any requirements for contributors or for this specific feature?

rgds, Kira

Exception with CJK characters

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injectorboost::system::system_error >'
what(): context: asio.ssl error

Line handling

I try to send both plain e-mails with quoted content (received from another system) and HTML messages. The library cuts the lines in all cases. Questions/issues:

  • If the quoted message has a \n in it, the system raises a mailio::codec_error. In all other mail systems I can send it, even if it is not the standard \r\n. Can this check somehow be switched off (it is hard to control what is in the quoted message, I want to forward)?
  • I figured out the existence of the codec_error exception so it does not crash now (though does not send it either), but could not find a complete list of possible raised exception. Could it be listed somewhere in a "help" file? It would be important to prevent my whole program crashing from a yet unknown exception.
  • In case of HTML the lines should not be cut at all. Where can I switch it off? Sometimes the line is simply longer and that should be OK.

Thanks,

Segmentation fault when sending more than one mail

Hello there,

First of all this is a very nice piece of code. Thanks for sharing.

I am having a segmentation fault when sending more than one mail. For example if you modify the smtps_simple_msg.cpp to send two emails, you'll have the problem on the second submit().

The problem seems to be the constant:

const local_time_facet* message::TIME_FACET = new local_time_facet("%a, %d %b %Y %H:%M:%S %q");

when following line runs:

ss.imbue(locale(ss.getloc(), TIME_FACET));

the std::locale (according to the specs) deletes the allocated facet. So then when running the second time the TIME_FACET constant points to an already-deleted memory. address.

The obvious solution would be to create the facet every time:

ss.imbue(locale(ss.getloc(), new local_time_facet("%a, %d %b %Y %H:%M:%S %q")));

Let me know your thoughts.

Regards...

Upload your library to vcpkg?

Hi, long time user Geoffrey here.

How much work would it be to upload your library to vcpkg? That package manager is great at reducing the complexity of deploying things like mailio

Keep up the good work.
Geoff.

issue on message.content()

Hi,
i can't read content sent from gmail account to gmail imap server, i get empty string,
on this line
std::cout << data << std::endl;

`mailio::imaps *conn = new mailio::imaps(this->imap_host, this->imap_port);

      conn->authenticate(this->account_mail, this->account_password, mailio::imaps::auth_method_t::LOGIN);
      mailio::message msg;
msg.line_policy(mailio::codec::line_len_policy_t::NONE);
conn->fetch("inbox", new_index, msg);
      std::cout << msg.subject() << std::endl;
      std::string data = msg.content();
      std::cout << "size of =" << sizeof(data) << std::endl;
      msg.parse(data);
      std::cout << data << std::endl;
      delete conn;`

Async Implementation

I am wondering if an async api will be provided.
Initially I thought it was supported because I saw a connect_async() but then I realized that is just a synchronous method with timeout support.
We would like to use this library because we liked the decoupled design but we need to use an async api for email sending

Selecting nested folders and fetching messages in nested folders

Hi, I'm trying to select each message in a folder in order, read the messages, download the attachments and then delete the message.

I'm using a mail service that has a nested folder structure like Parent Folder/NestedFolder. They call them labels but they seem to act as folders.
With the code
mailio::imaps::mailbox_stat_t ret = conn.select(std::list<std::string>({"Parent Folder","NestedFolder"}));
I was able to select FolderB and view some stats about it, however two things are happening when i do this.
It shows the correct number of messages in the folder however it wont give me the UID of the first message with uid_next.
How do i get the uid of each message in the folder in order? I want the uid of all the messages and to handle all the messages in the folder.

I also cant seem to figure out how to use fetch. When i use the following code:
conn.fetch("NestedFolder", (uid), msg);
It gives me the error
Select or examine mailbox failure.
How do select a nested mailbox folder?

'Line policy overflow in a header.' exception thrown for every message in the mailbox.

Hello.
Great library! However I've run into an issue I can't seem to resolve on my own.

I have a mailbox which has lots of messages inside, nothing unusual. Every time I try to fetch a message from that mailbox via pop3::fetch() method, an exception is thrown, as the title says: Line policy overflow in a header.
I was able to trace the issue to some internal methods and came to a conclusion that the default policy for header length, RECOMMENDED, is the root cause of the issue. As far as I could understand, it sets maximum header line size to 78 bytes. Each message in my mailbox seems to contain the following header:

Received: from [XXX.XX.XXX.XXX] (port=42456 helo=xxxx.xxxxxxxxx.xx)
	by pop5.intr with esmtp (Exim 4.90_1)
	(envelope-from <[email protected]>)
	id 1krkeu-0007Ua-UQ
	for [email protected]; Tue, 22 Dec 2020 16:37:13 +0000

which gets concatenated by the library into single lengthy header:

Received: from [XXX.XX.XXX.XXX] (port=42456 helo=xxxx.xxxxxxxxx.xx)by pop5.intr with esmtp (Exim 4.90_1)(envelope-from <[email protected]>)id 1krkeu-0007Ua-UQfor [email protected]; Tue, 22 Dec 2020 16:37:13 +0000

and causes an exception (I redacted email and server addresses but kept their length intact).

If I'm correct I should just set another policy, but I can't figure out how to do it. As far as I can see, the policy is set somewhere in the guts of the library.

Is there any interface method on pop3 or another class that I could use to change the restriction to smth else, like MANDATORY or NONE? I couldn't find one.
Or, alternatively, is there any way to fetch raw message and parse it using a mime class instance tuned to use other header policy than RECOMMENDED?

Thank you in advance.

Wrong content_type_t when adding an attachment

If an email has content in HTML and then an attachment, the content losses the type.

  • In message::attach it overwrites the attribute _content_type
  • In message::format it sets the content as text/plain

UTF-8 encoded Subject value causes exception

Hello.
Looks like a raw UTF-8 value of the Subject header causes an exception to be thrown, e.g.: Format failure of the header value 'Тестовое письмо' which happens when mime::parse_header_name_value() processes string Subject: Тестовое письмо

The Issues section of the main project page states that:
Non-ASCII subject is assumed to be UTF-8
which doesn't seem to be correct anymore as the mime::parse_header_name_value() method only checks if the value supplied matches HEADER_VALUE_REGEX.

make examples

Hello,
Thank you for your work !
In the makefile, to compile the examples, I needed to change the makefile to :

$(EXAMPLES_BINS):
test -d $(EXAMPLES_BUILD_DIR) || mkdir -p $(EXAMPLES_BUILD_DIR) $(CXX) $(CXX_FLAGS) $(INCS_DIR) $(LIBS_DIR) $(EXAMPLES_DIR)/$@.cpp -o $(EXAMPLES_BUILD_DIR)/$@ -L$(LIB_DIR) $(LD_FLAGS) -l$(PROJECT)

the librairies should go after the example.cpp

HTML Formatted Mail Problem

Can this library supports html formatted mails? I've assigned a HTML formatted string to message content, however it has sent the exact HTML string not the interpreted version of HTML string.

CMake Setup Suggestions

First just want to say this is a great library, but I have some questions/suggestions regarding the project setup.

  1. Is there any particular reason why you have a static and dynamic build targets for the same library? Typically, you would allow the user/consumer of your library to decide if they want a shared library or a static lib. They could do this by setting the built in CMake variable BUILD_SHARED_LIBS.
  2. I had to move the cmake_policy call to the top level cmake lists to avoid the known bug with cmake 3.12 regard FindDoxygen
  3. Any reason why the examples you have in the repo can't be part of the project? They would be trivial to add via cmake.
  4. I would also suggest adding options to give the user explicit control over building test, examples and documentation. So adding options like MAILIO_BUILD_TESTS, MAILIO_BUILD_DOCUMENTATION, MAILIO_BUILD_EXAMPLES.
  5. The check for Boost_FOUND is probably not needed since it's required via CMake.
  6. I would suggest exporting your library so that it's easy to use in other CMake based projects.

Let me know what you think about the above items. If you're open to it I can file a PR to address all of these items.

Setting Return-Path

We are using mailio mostly as an email sender at FirstBill, and we send emails in the name of our customers. We wanted our customers to get the bounce messages when an email is not deliverable, there is a difference between Sender and Return-Path header.

The first SMTP relay usually takes the value in SMTP: MAIL FROM as Return-Path, and consecutive mail servers often look into the mail header for a "Return-Path" header (e.g. delay bounce). It is sometimes necessary to set a different sender to return-path (we need it for DKIM + DMARC and good deliverability).

So a Return-Path should be able to set SMTP: MAIL FROM and add an email header Return-Path

Why codec bit7 bit8 append \r\n?

Like the code bellow, even if the source content does not contains?

string bit7::decode(const vector<string>& text) const
{
    string dec_text;
    for (const auto& line : text)
    {
        if (line.length() > string::size_type(_decoder_line_policy))
            throw codec_error("Line policy overflow.");
        
        for (auto ch : line)
        {
            if (!is_allowed(ch))
                throw codec_error("Bad character `" + string(1, ch) + "`.");

            dec_text += ch;
        }
        dec_text += "\r\n";
    }
    trim_right(dec_text);
    
    return dec_text;    
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.