aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/MCCore.h1
-rw-r--r--src/core/basetypes/MCLibetpanTypes.h1
-rw-r--r--src/core/nntp/MCNNTP.h9
-rw-r--r--src/core/nntp/MCNNTPGroupInfo.cc64
-rw-r--r--src/core/nntp/MCNNTPGroupInfo.h38
-rw-r--r--src/core/nntp/MCNNTPProgressCallback.h20
-rw-r--r--src/core/nntp/MCNNTPSession.cc547
-rw-r--r--src/core/nntp/MCNNTPSession.h88
8 files changed, 768 insertions, 0 deletions
diff --git a/src/core/MCCore.h b/src/core/MCCore.h
index 74c16147..323bb606 100644
--- a/src/core/MCCore.h
+++ b/src/core/MCCore.h
@@ -14,6 +14,7 @@
#include <MailCore/MCBaseTypes.h>
#include <MailCore/MCIMAP.h>
#include <MailCore/MCPOP.h>
+#include <MailCore/MCNNTP.h>
#include <MailCore/MCRFC822.h>
#include <MailCore/MCSMTP.h>
#include <MailCore/MCRenderer.h>
diff --git a/src/core/basetypes/MCLibetpanTypes.h b/src/core/basetypes/MCLibetpanTypes.h
index e2027804..fcb8e70a 100644
--- a/src/core/basetypes/MCLibetpanTypes.h
+++ b/src/core/basetypes/MCLibetpanTypes.h
@@ -37,6 +37,7 @@ extern "C" {
struct mailimap_envelope;
typedef struct mailpop3 mailpop3;
typedef struct mailsmtp mailsmtp;
+ typedef struct newsnntp newsnntp;
struct mailsem;
#ifdef __cplusplus
}
diff --git a/src/core/nntp/MCNNTP.h b/src/core/nntp/MCNNTP.h
new file mode 100644
index 00000000..c2da3a66
--- /dev/null
+++ b/src/core/nntp/MCNNTP.h
@@ -0,0 +1,9 @@
+#ifndef MAILCORE_MCNNTP_H
+
+#define MAILCORE_MCNNTP_H
+
+#include <MailCore/MCNNTPGroupInfo.h>
+#include <MailCore/MCNNTPProgressCallback.h>
+#include <MailCore/MCNNTPSession.h>
+
+#endif
diff --git a/src/core/nntp/MCNNTPGroupInfo.cc b/src/core/nntp/MCNNTPGroupInfo.cc
new file mode 100644
index 00000000..787967f3
--- /dev/null
+++ b/src/core/nntp/MCNNTPGroupInfo.cc
@@ -0,0 +1,64 @@
+//
+// MCNNTPGroupInfo.cpp
+// mailcore2
+//
+// Created by Robert Widmann on 3/6/14.
+// Copyright (c) 2014 MailCore. All rights reserved.
+//
+
+#include "MCNNTPGroupInfo.h"
+
+using namespace mailcore;
+
+void NNTPGroupInfo::init()
+{
+ mMessageCount = 0;
+}
+
+NNTPGroupInfo::NNTPGroupInfo()
+{
+ init();
+}
+
+
+NNTPGroupInfo::NNTPGroupInfo(NNTPGroupInfo * other)
+{
+ init();
+ setMessageCount(other->messageCount());
+ setName(other->name());
+}
+
+NNTPGroupInfo::~NNTPGroupInfo()
+{
+}
+
+String * NNTPGroupInfo::description()
+{
+ return String::stringWithUTF8Format("<%s:%p> Group name: %s; Message count: %u",
+ MCUTF8(className()), this, MCUTF8(mName), mMessageCount);
+}
+
+Object * NNTPGroupInfo::copy()
+{
+ return new NNTPGroupInfo(this);
+}
+
+void NNTPGroupInfo::setName(String * name)
+{
+ MC_SAFE_REPLACE_COPY(String, mName, name);
+}
+
+String * NNTPGroupInfo::name()
+{
+ return mName;
+}
+
+void NNTPGroupInfo::setMessageCount(uint32_t messageCount)
+{
+ mMessageCount = messageCount;
+}
+
+uint32_t NNTPGroupInfo::messageCount()
+{
+ return mMessageCount;
+} \ No newline at end of file
diff --git a/src/core/nntp/MCNNTPGroupInfo.h b/src/core/nntp/MCNNTPGroupInfo.h
new file mode 100644
index 00000000..aeab58ca
--- /dev/null
+++ b/src/core/nntp/MCNNTPGroupInfo.h
@@ -0,0 +1,38 @@
+#ifndef MAILCORE_MCNNTPGROUPINFO_H
+
+#define MAILCORE_MCNNTPGROUPINFO_H
+
+#include <MailCore/MCBaseTypes.h>
+
+#ifdef __cplusplus
+
+namespace mailcore {
+
+ class NNTPGroupInfo : public Object {
+ public:
+ NNTPGroupInfo();
+ virtual ~NNTPGroupInfo();
+
+ virtual void setName(String * uid);
+ virtual String * name();
+
+ virtual void setMessageCount(uint32_t messages);
+ virtual uint32_t messageCount();
+
+ public: // subclass behavior
+ NNTPGroupInfo(NNTPGroupInfo * other);
+ virtual String * description();
+ virtual Object * copy();
+
+ private:
+ String * mName;
+ uint32_t mMessageCount;
+
+ void init();
+ };
+
+}
+
+#endif
+
+#endif
diff --git a/src/core/nntp/MCNNTPProgressCallback.h b/src/core/nntp/MCNNTPProgressCallback.h
new file mode 100644
index 00000000..f863cc0c
--- /dev/null
+++ b/src/core/nntp/MCNNTPProgressCallback.h
@@ -0,0 +1,20 @@
+#ifndef MAILCORE_MCNNTPPROGRESSCALLBACK_H
+
+#define MAILCORE_MCNNTPPROGRESSCALLBACK_H
+
+#ifdef __cplusplus
+
+namespace mailcore {
+
+ class NNTPSession;
+
+ class NNTPProgressCallback {
+ public:
+ virtual void bodyProgress(NNTPSession * session, unsigned int current, unsigned int maximum) {};
+ };
+
+}
+
+#endif
+
+#endif
diff --git a/src/core/nntp/MCNNTPSession.cc b/src/core/nntp/MCNNTPSession.cc
new file mode 100644
index 00000000..02db197e
--- /dev/null
+++ b/src/core/nntp/MCNNTPSession.cc
@@ -0,0 +1,547 @@
+//
+// MCNNTPSession.cpp
+// mailcore2
+//
+// Created by Robert Widmann on 3/6/14.
+// Copyright (c) 2014 MailCore. All rights reserved.
+//
+
+#include "MCNNTPSession.h"
+
+#include <string.h>
+#include <libetpan/libetpan.h>
+
+#include "MCNNTPGroupInfo.h"
+#include "MCMessageHeader.h"
+#include "MCConnectionLoggerUtils.h"
+#include "MCCertificateUtils.h"
+
+#define NNTP_DEFAULT_PORT 119
+#define NNTPS_DEFAULT_PORT 563
+
+using namespace mailcore;
+
+enum {
+ STATE_DISCONNECTED,
+ STATE_CONNECTED,
+ STATE_LOGGEDIN,
+ STATE_LISTED,
+ STATE_SELECTED,
+};
+
+void NNTPSession::init()
+{
+ mHostname = NULL;
+ mPort = NNTP_DEFAULT_PORT;
+ mUsername = NULL;
+ mPassword = NULL;
+ mConnectionType = ConnectionTypeClear;
+ mCheckCertificateEnabled = true;
+ mTimeout = 30;
+
+ mNNTP = NULL;
+ mState = STATE_DISCONNECTED;
+ mConnectionLogger = NULL;
+}
+
+NNTPSession::NNTPSession()
+{
+ init();
+}
+
+NNTPSession::~NNTPSession()
+{
+ MC_SAFE_RELEASE(mHostname);
+ MC_SAFE_RELEASE(mUsername);
+ MC_SAFE_RELEASE(mPassword);
+}
+
+void NNTPSession::setHostname(String * hostname)
+{
+ MC_SAFE_REPLACE_COPY(String, mHostname, hostname);
+}
+
+String * NNTPSession::hostname()
+{
+ return mHostname;
+}
+
+void NNTPSession::setPort(unsigned int port)
+{
+ mPort = port;
+}
+
+unsigned int NNTPSession::port()
+{
+ return mPort;
+}
+
+void NNTPSession::setUsername(String * username)
+{
+ MC_SAFE_REPLACE_COPY(String, mUsername, username);
+}
+
+String * NNTPSession::username()
+{
+ return mUsername;
+}
+
+void NNTPSession::setPassword(String * password)
+{
+ MC_SAFE_REPLACE_COPY(String, mPassword, password);
+}
+
+String * NNTPSession::password()
+{
+ return mPassword;
+}
+
+void NNTPSession::setConnectionType(ConnectionType connectionType)
+{
+ mConnectionType = connectionType;
+}
+
+ConnectionType NNTPSession::connectionType()
+{
+ return mConnectionType;
+}
+
+void NNTPSession::setTimeout(time_t timeout)
+{
+ mTimeout = timeout;
+}
+
+time_t NNTPSession::timeout()
+{
+ return mTimeout;
+}
+
+void NNTPSession::setCheckCertificateEnabled(bool enabled)
+{
+ mCheckCertificateEnabled = enabled;
+}
+
+bool NNTPSession::isCheckCertificateEnabled()
+{
+ return mCheckCertificateEnabled;
+}
+
+bool NNTPSession::checkCertificate()
+{
+ if (!isCheckCertificateEnabled())
+ return true;
+ return mailcore::checkCertificate(mNNTP->nntp_stream, hostname());
+}
+
+static void logger(newsnntp * nntp, int log_type, const char * buffer, size_t size, void * context)
+{
+ NNTPSession * session = (NNTPSession *) context;
+
+ if (session->connectionLogger() == NULL)
+ return;
+
+ ConnectionLogType type = getConnectionType(log_type);
+ bool isBuffer = isBufferFromLogType(log_type);
+
+ if (isBuffer) {
+ Data * data = Data::dataWithBytes(buffer, (unsigned int) size);
+ session->connectionLogger()->log(session, type, data);
+ }
+ else {
+ session->connectionLogger()->log(session, type, NULL);
+ }
+}
+
+
+void NNTPSession::setup()
+{
+ mNNTP = newsnntp_new(0, NULL);
+ newsnntp_set_logger(mNNTP, logger, this);
+}
+
+void NNTPSession::unsetup()
+{
+ if (mNNTP != NULL) {
+ if (mNNTP->nntp_stream != NULL) {
+ mailstream_close(mNNTP->nntp_stream);
+ mNNTP->nntp_stream = NULL;
+ }
+ newsnntp_free(mNNTP);
+ mNNTP = NULL;
+ }
+}
+
+void NNTPSession::loginIfNeeded(ErrorCode * pError)
+{
+ connectIfNeeded(pError);
+ if (* pError != ErrorNone) {
+ return;
+ }
+
+ if (mState == STATE_CONNECTED) {
+ login(pError);
+ }
+ else {
+ * pError = ErrorNone;
+ }
+}
+
+void NNTPSession::readerIfNeeded(ErrorCode * pError)
+{
+ connectIfNeeded(pError);
+ if (* pError != ErrorNone)
+ return;
+
+ if (mState == STATE_CONNECTED) {
+ newsnntp_mode_reader(mNNTP);
+ }
+ else {
+ * pError = ErrorNone;
+ }
+}
+
+void NNTPSession::login(ErrorCode * pError)
+{
+ int r;
+
+ if (mUsername != NULL) {
+ r = newsnntp_authinfo_username(mNNTP, mUsername->UTF8Characters());
+ if (r == NEWSNNTP_ERROR_STREAM) {
+ * pError = ErrorConnection;
+ return;
+ }
+ else if (r != NEWSNNTP_NO_ERROR) {
+ * pError = ErrorAuthentication;
+ return;
+ }
+ }
+ if (mPassword != NULL) {
+ r = newsnntp_authinfo_password(mNNTP, mPassword->UTF8Characters());
+ if (r == NEWSNNTP_ERROR_STREAM) {
+ * pError = ErrorConnection;
+ return;
+ }
+ else if (r != NEWSNNTP_NO_ERROR) {
+ * pError = ErrorAuthentication;
+ return;
+ }
+ }
+
+ mState = STATE_LOGGEDIN;
+ * pError = ErrorNone;
+}
+
+void NNTPSession::connect(ErrorCode * pError)
+{
+ int r;
+
+ setup();
+
+ switch (mConnectionType) {
+ case ConnectionTypeStartTLS:
+ MCLog("connect %s %u", MCUTF8(hostname()), (unsigned int) port());
+ r = newsnntp_socket_connect(mNNTP, MCUTF8(hostname()), port());
+ if (r != NEWSNNTP_NO_ERROR) {
+ * pError = ErrorConnection;
+ return;
+ }
+ MCLog("done");
+ if (!checkCertificate()) {
+ * pError = ErrorCertificate;
+ return;
+ }
+ break;
+
+ case ConnectionTypeTLS:
+ MCLog("connect %s %u", MCUTF8(hostname()), (unsigned int) port());
+ r = newsnntp_ssl_connect(mNNTP, MCUTF8(hostname()), port());
+ if (r != NEWSNNTP_NO_ERROR) {
+ * pError = ErrorConnection;
+ return;
+ }
+ if (!checkCertificate()) {
+ * pError = ErrorCertificate;
+ return;
+ }
+ break;
+
+ default:
+ r = newsnntp_socket_connect(mNNTP, MCUTF8(hostname()), port());
+ if (r != NEWSNNTP_NO_ERROR) {
+ * pError = ErrorConnection;
+ return;
+ }
+ break;
+ }
+
+ mailstream_low * low;
+ String * identifierString;
+ char * identifier;
+
+ low = mailstream_get_low(mNNTP->nntp_stream);
+ if (mUsername != NULL) {
+ identifierString = String::stringWithUTF8Format("%s@%s:%u", MCUTF8(mUsername), MCUTF8(mHostname), mPort);
+ }
+ else {
+ identifierString = String::stringWithUTF8Format("%s:%u", MCUTF8(mUsername), mPort);
+ }
+ identifier = strdup(identifierString->UTF8Characters());
+ mailstream_low_set_identifier(low, identifier);
+ mState = STATE_CONNECTED;
+ * pError = ErrorNone;
+}
+
+void NNTPSession::connectIfNeeded(ErrorCode * pError)
+{
+ if (mState == STATE_DISCONNECTED) {
+ connect(pError);
+ }
+ else {
+ * pError = ErrorNone;
+ }
+}
+
+void NNTPSession::disconnect()
+{
+ if (mNNTP == NULL)
+ return;
+
+ newsnntp_quit(mNNTP);
+ mState = STATE_DISCONNECTED;
+ unsetup();
+}
+
+void NNTPSession::checkAccount(ErrorCode * pError)
+{
+ loginIfNeeded(pError);
+}
+
+Array * NNTPSession::listAllNewsgroups(ErrorCode * pError)
+{
+ int r;
+ clist * grp_list;
+
+ loginIfNeeded(pError);
+ if (* pError != ErrorNone) {
+ return NULL;
+ }
+
+ r = newsnntp_list(mNNTP, &grp_list);
+ if (r == NEWSNNTP_ERROR_STREAM) {
+ * pError = ErrorConnection;
+ return NULL;
+ }
+ else if (r != NEWSNNTP_NO_ERROR) {
+ * pError = ErrorFetchMessageList;
+ return NULL;
+ }
+
+ Array * result = Array::array();
+ clistiter * iter;
+ for(iter = clist_begin(grp_list) ;iter != NULL ; iter = clist_next(iter)) {
+ struct newsnntp_group_info * grp_info;
+ String * name;
+
+ grp_info = (struct newsnntp_group_info *) clist_content(iter);
+
+ name = String::stringWithUTF8Characters(grp_info->grp_name);
+
+ NNTPGroupInfo * info = new NNTPGroupInfo();
+ info->setName(name);
+ result->addObject(info);
+ info->release();
+ }
+
+ newsnntp_list_free(grp_list);
+ * pError = ErrorNone;
+ mState = STATE_LISTED;
+
+ return result;
+}
+
+Array * NNTPSession::listSubscribedNewsgroups(ErrorCode * pError)
+{
+ int r;
+ clist * subd_groups;
+
+ MCLog("fetch subscribed");
+ loginIfNeeded(pError);
+ if (* pError != ErrorNone)
+ return NULL;
+
+ r = newsnntp_list_subscriptions(mNNTP, &subd_groups);
+ MCLog("fetch subscribed %u", r);
+
+ Array * result = Array::array();
+ clistiter * iter;
+ for(iter = clist_begin(subd_groups) ;iter != NULL ; iter = clist_next(iter)) {
+ struct newsnntp_group_info * grp_info;
+ String * name;
+
+ grp_info = (struct newsnntp_group_info *) clist_content(iter);
+
+ name = String::stringWithUTF8Characters(strdup(grp_info->grp_name));
+ name->retain();
+
+ NNTPGroupInfo * info = new NNTPGroupInfo();
+ info->setName(name);
+ result->addObject(info);
+ info->release();
+ }
+ newsnntp_list_subscriptions_free(subd_groups);
+ * pError = ErrorNone;
+
+ return result;
+}
+
+MessageHeader * NNTPSession::fetchHeader(String *groupName, unsigned int index, ErrorCode * pError)
+{
+ int r;
+ char * content;
+ size_t content_len;
+
+ MCLog("fetch header at index %u", index);
+ loginIfNeeded(pError);
+ if (* pError != ErrorNone) {
+ return NULL;
+ }
+
+ selectGroup(groupName, pError);
+ if (* pError != ErrorNone) {
+ return NULL;
+ }
+
+ r = newsnntp_head(mNNTP, index, &content, &content_len);
+ if (r != NEWSNNTP_NO_ERROR) {
+ * pError = ErrorFetchMessageList;
+ return NULL;
+ }
+
+ Data * data;
+ data = new Data(content, (unsigned int) content_len);
+ MessageHeader * result = new MessageHeader();
+ result->importHeadersData(data);
+ result->autorelease();
+ data->release();
+
+ newsnntp_head_free(content);
+ * pError = ErrorNone;
+
+ return result;
+}
+
+Data * NNTPSession::fetchArticle(String *groupName, unsigned int index, NNTPProgressCallback * callback, ErrorCode * pError)
+{
+ int r;
+ char * content;
+ size_t content_len;
+
+ MCLog("fetch article at index %u", index);
+
+ loginIfNeeded(pError);
+ if (* pError != ErrorNone) {
+ return NULL;
+ }
+
+ selectGroup(groupName, pError);
+ if (* pError != ErrorNone) {
+ return NULL;
+ }
+
+ r = newsnntp_article(mNNTP, index, &content, &content_len);
+ if (r == NEWSNNTP_ERROR_STREAM) {
+ * pError = ErrorConnection;
+ return NULL;
+ }
+ else if (r != NEWSNNTP_NO_ERROR) {
+ * pError = ErrorFetchMessageList;
+ return NULL;
+ }
+
+ Data * result;
+ result = Data::dataWithBytes(content, (unsigned int) content_len);
+ newsnntp_article_free(content);
+ * pError = ErrorNone;
+
+ return result;
+}
+
+IndexSet * NNTPSession::fetchArticles(String * groupName, ErrorCode * pError)
+{
+ int r;
+ clist * msg_list;
+
+ selectGroup(groupName, pError);
+ if (* pError != ErrorNone) {
+ return NULL;
+ }
+
+ r = newsnntp_listgroup(mNNTP, groupName->UTF8Characters(), &msg_list);
+ if (r == NEWSNNTP_ERROR_STREAM) {
+ * pError = ErrorConnection;
+ return NULL;
+ }
+ else if (r != NEWSNNTP_NO_ERROR) {
+ * pError = ErrorFetchMessageList;
+ return NULL;
+ }
+
+ IndexSet * result = new IndexSet();
+ clistiter * iter;
+ for(iter = clist_begin(msg_list) ;iter != NULL ; iter = clist_next(iter)) {
+ uint32_t *msg_info;
+
+ msg_info = (uint32_t *) clist_content(iter);
+ if (!msg_info) {
+ continue;
+ }
+
+ result->addIndex(*msg_info);
+ }
+
+ newsnntp_listgroup_free(msg_list);
+ * pError = ErrorNone;
+ mState = STATE_LISTED;
+
+ return result;
+}
+
+void NNTPSession::selectGroup(String * folder, ErrorCode * pError)
+{
+ int r;
+ struct newsnntp_group_info * info;
+
+ readerIfNeeded(pError);
+ if (* pError != ErrorNone) {
+ return;
+ }
+
+ r = newsnntp_group(mNNTP, folder->UTF8Characters(), &info);
+ if (r == NEWSNNTP_ERROR_STREAM) {
+ * pError = ErrorConnection;
+ MCLog("select error : %s %i", MCUTF8DESC(this), * pError);
+ return;
+ }
+ else if (r == NEWSNNTP_ERROR_NO_SUCH_NEWS_GROUP) {
+ * pError = ErrorNonExistantFolder;
+ return;
+ }
+ else if (r == MAILIMAP_ERROR_PARSE) {
+ * pError = ErrorParse;
+ return;
+ }
+
+ mState = STATE_SELECTED;
+ * pError = ErrorNone;
+ MCLog("select ok");
+}
+
+void NNTPSession::setConnectionLogger(ConnectionLogger * logger)
+{
+ mConnectionLogger = logger;
+}
+
+ConnectionLogger * NNTPSession::connectionLogger()
+{
+ return mConnectionLogger;
+}
diff --git a/src/core/nntp/MCNNTPSession.h b/src/core/nntp/MCNNTPSession.h
new file mode 100644
index 00000000..a6fb511c
--- /dev/null
+++ b/src/core/nntp/MCNNTPSession.h
@@ -0,0 +1,88 @@
+#ifndef MAILCORE_MCNNTPSESSION_H
+#define MAILCORE_MCNNTPSESSION_H
+
+#include <MailCore/MCBaseTypes.h>
+#include <MailCore/MCMessageConstants.h>
+
+#ifdef __cplusplus
+
+namespace mailcore {
+
+ class NNTPProgressCallback;
+ class MessageHeader;
+
+ class NNTPSession : public Object {
+ public:
+ NNTPSession();
+ virtual ~NNTPSession();
+
+ virtual void setHostname(String * hostname);
+ virtual String * hostname();
+
+ virtual void setPort(unsigned int port);
+ virtual unsigned int port();
+
+ virtual void setUsername(String * username);
+ virtual String * username();
+
+ virtual void setPassword(String * password);
+ virtual String * password();
+
+ virtual void setConnectionType(ConnectionType connectionType);
+ virtual ConnectionType connectionType();
+
+ virtual void setTimeout(time_t timeout);
+ virtual time_t timeout();
+
+ virtual void setCheckCertificateEnabled(bool enabled);
+ virtual bool isCheckCertificateEnabled();
+
+ virtual void connect(ErrorCode * pError);
+ virtual void disconnect();
+
+ virtual void login(ErrorCode * pError);
+
+ virtual void checkAccount(ErrorCode * pError);
+
+ virtual Array * /* NNTPGroupInfo */ listAllNewsgroups(ErrorCode * pError);
+ virtual Array * listSubscribedNewsgroups(ErrorCode * pError);
+
+ virtual MessageHeader * fetchHeader(String *groupName, unsigned int index, ErrorCode * pError);
+
+ virtual IndexSet * fetchArticles(String * groupname, ErrorCode * pError);
+
+ Data * fetchArticle(String *groupName, unsigned int index, NNTPProgressCallback * callback, ErrorCode * pError);
+
+ virtual void setConnectionLogger(ConnectionLogger * logger);
+ virtual ConnectionLogger * connectionLogger();
+
+ private:
+ String * mHostname;
+ unsigned int mPort;
+ String * mUsername;
+ String * mPassword;
+ ConnectionType mConnectionType;
+ bool mCheckCertificateEnabled;
+ time_t mTimeout;
+
+ newsnntp * mNNTP;
+ int mState;
+
+ ConnectionLogger * mConnectionLogger;
+
+ void init();
+ bool checkCertificate();
+ void setup();
+ void unsetup();
+ void connectIfNeeded(ErrorCode * pError);
+ void loginIfNeeded(ErrorCode * pError);
+ void readerIfNeeded(ErrorCode * pError);
+ void listIfNeeded(ErrorCode * pError);
+ void selectGroup(String * folder, ErrorCode * pError);
+ };
+
+}
+
+#endif
+
+#endif