Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: lostcoder/pan2
base: master
...
head fork: lostcoder/pan2
compare: nntp
Checking mergeability… Don't worry, you can still create the pull request.
  • 7 commits
  • 8 files changed
  • 0 commit comments
  • 1 contributor
View
3  configure.in
@@ -68,7 +68,8 @@ AC_PROG_RANLIB
dnl for libuu
AC_CHECK_HEADERS([errno.h fcntl.h])
-IT_PROG_INTLTOOL([0.35.5],[no-xml])
+IT_PROG_INTLTOOL([0.35.5])
+dnl ,[no-xml])
GETTEXT_PACKAGE=pan
AC_SUBST(GETTEXT_PACKAGE)
AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE],["$GETTEXT_PACKAGE"],[Gettext package])
View
2  pan/general/string-view.h
@@ -113,7 +113,7 @@ namespace pan
int strcmp (const StringView& p) const {
return strcmp (str, len, p.str, p.len); }
- int strncasecmp (const char * p, unsigned int l) {
+ int strncasecmp (const char * p, unsigned int l) const {
if (len >= l)
return g_ascii_strncasecmp (str, p, l);
else {
View
3  pan/tasks/nntp-pool.cc
@@ -191,7 +191,6 @@ NNTP_Pool :: on_nntp_done (NNTP* nntp, Health health, const StringView& response
if ( (s.find ("502") != s.npos)
|| (s.find ("400") != s.npos)
|| (s.find ("451") != s.npos)
- || (s.find ("480") != s.npos) // http://bugzilla.gnome.org/show_bug.cgi?id=409085
|| (s.find ("too many") != s.npos)
|| (s.find ("limit reached") != s.npos)
|| (s.find ("maximum number of connections") != s.npos))
@@ -264,7 +263,7 @@ NNTP_Pool :: request_nntp (WorkerPool& threadpool)
<< "max: " << max << ' ' << std::endl;
#endif
- if (!idle && ((pending+active)<max) && new_connections_are_allowed())
+ if (!idle && !pending && ((pending+active)<max) && new_connections_are_allowed())
{
debug ("trying to create a socket");
View
327 pan/tasks/nntp.cc
@@ -58,10 +58,17 @@ namespace
{
enum
{
- AUTH_REQUIRED = 480,
- AUTH_NEED_MORE = 381,
AUTH_ACCEPTED = 281,
- AUTH_REJECTED = 482,
+ AUTH_NEED_MORE = 381, // password required
+ AUTH_REQUIRED = 480,
+ AUTH_REJECTED = 481,
+ AUTH_OUT_OF_SEQ = 482,
+ AUTH_NEED_TLS = 483,
+ AUTH_SASL_ACCPT_DATA = 283,
+ AUTH_SASL_CONT = 383,
+
+ TLS_MORE = 382,
+ TLS_CANT_START = 580,
SERVER_READY = 200,
SERVER_READY_NO_POSTING = 201,
@@ -73,6 +80,7 @@ namespace
GROUP_NONEXISTENT = 411,
INFORMATION_FOLLOWS = 215,
+ CAPABILITY_LIST = 101,
XOVER_FOLLOWS = 224,
XOVER_NO_ARTICLES = 420,
@@ -95,10 +103,60 @@ namespace
ERROR_CMD_NOT_UNDERSTOOD = 500,
ERROR_CMD_NOT_SUPPORTED = 501,
NO_PERMISSION = 502,
- FEATURE_NOT_SUPPORTED = 503
+ FEATURE_NOT_SUPPORTED = 503,
+ ERROR_BASE64_DECODE = 504
};
}
+namespace pan {
+ class Handshake: public NNTP::Listener
+ {
+ enum State {
+ INIT,
+ START,
+ CAPABILITY,
+ STARTTLS,
+ TLS,
+ SASL,
+ AUTH_U,
+ AUTH_P,
+ MODE,
+ DONE
+ };
+ NNTP &nntp;
+ NNTP::Listener *pool;
+ State state;
+
+ public:
+ Handshake (NNTP &con, NNTP::Listener *l): nntp(con), pool(l), state(INIT),
+ has_capability(true), has_mode(false), has_over(false), has_post(false),
+ tls(false), auth(false)
+ {}
+ void start () { step (); }
+ void on_nntp_line (NNTP * nntp, const StringView & line);
+ void on_nntp_done (NNTP * nntp, Health health, const StringView & response);
+
+ bool has_capability;
+ bool has_mode;
+ bool has_over;
+ bool has_post;
+ bool has_auth;
+ bool has_sasl;
+ bool has_tls;
+ bool tls;
+ bool auth;
+ std::string sasl_mech;
+
+ private:
+ void step ();
+ };
+};
+
+NNTP::~NNTP ()
+{
+ if (hs) delete hs;
+}
+
void
NNTP :: fire_done_func (Health health, const StringView& response)
{
@@ -160,33 +218,15 @@ NNTP :: on_socket_response (Socket * sock UNUSED, const StringView& line_in)
state = CMD_DONE;
break;
- case AUTH_REQUIRED: { // must send username
- if (!_username.empty()) {
- _commands.push_front (_previous_command);
- _socket->write_command_va (this, "AUTHINFO USER %s\r\n", _username.c_str());
- state = CMD_NEXT;
- } else {
- std::string host;
- _socket->get_host (host);
- Log::add_err_va (_("%s requires a username, but none is set."), host.c_str());
- state = CMD_FAIL;
- }
- break;
- }
-
- case AUTH_NEED_MORE: { // must send password
- if (!_password.empty()) {
- _socket->write_command_va (this, "AUTHINFO PASS %s\r\n", _password.c_str());
- state = CMD_NEXT;
- } else {
- std::string host;
- _socket->get_host (host);
- Log::add_err_va (_("%s requires a password, but none is set."), host.c_str());
- state = CMD_FAIL;
+ case AUTH_REQUIRED: {
+ std::string host;
+ _socket->get_host (host);
+ Log::add_err_va (_("%s requires a username, but none is set."), host.c_str());
}
+ state = CMD_FAIL;
break;
- }
+ case AUTH_NEED_MORE:
case AUTH_ACCEPTED:
state = CMD_DONE;
break;
@@ -222,6 +262,7 @@ NNTP :: on_socket_response (Socket * sock UNUSED, const StringView& line_in)
state = CMD_FAIL;
break;
+ case CAPABILITY_LIST:
case XOVER_FOLLOWS:
case ARTICLE_FOLLOWS:
case NEWGROUPS_FOLLOWS:
@@ -231,6 +272,9 @@ NNTP :: on_socket_response (Socket * sock UNUSED, const StringView& line_in)
break;
case AUTH_REJECTED:
+ case AUTH_NEED_TLS:
+ case AUTH_OUT_OF_SEQ:
+ case TLS_CANT_START:
case NO_GROUP_SELECTED:
case ERROR_CMD_NOT_UNDERSTOOD:
case ERROR_CMD_NOT_SUPPORTED:
@@ -338,14 +382,17 @@ NNTP :: xover (const Quark & group,
uint64_t high,
Listener * l)
{
- _listener = l;
+ _listener = l;
- if (group != _group)
- _commands.push_back (build_command ("GROUP %s\r\n", group.c_str()));
+ if (group != _group)
+ _commands.push_back (build_command ("GROUP %s\r\n", group.c_str()));
- _commands.push_back (build_command ("XOVER %"G_GUINT64_FORMAT"-%"G_GUINT64_FORMAT"\r\n", low, high));
+ if (hs->has_over)
+ _commands.push_back (build_command ("OVER %"G_GUINT64_FORMAT"-%"G_GUINT64_FORMAT"\r\n", low, high));
+ else
+ _commands.push_back (build_command ("XOVER %"G_GUINT64_FORMAT"-%"G_GUINT64_FORMAT"\r\n", low, high));
- write_next_command ();
+ write_next_command ();
}
void
@@ -405,7 +452,6 @@ NNTP :: group (const Quark & group,
write_next_command ();
}
-
void
NNTP :: goodbye (Listener * l)
{
@@ -417,7 +463,10 @@ NNTP :: goodbye (Listener * l)
void
NNTP :: handshake (Listener * l)
{
- _listener = l;
+ hs = new Handshake(*this, l);
+ _listener = static_cast<Listener*>(hs);
+ hs->start();
+/* _listener = l;
// queue up two or three commands:
// (1) handshake, which is an empty string
@@ -432,6 +481,7 @@ NNTP :: handshake (Listener * l)
_commands.push_back ("MODE READER\r\n");
write_next_command ();
+*/
}
void
@@ -484,3 +534,210 @@ NNTP :: post (const StringView & msg,
write_next_command ();
}
}
+
+void Handshake::step()
+{
+ switch(state)
+ {
+ case INIT:
+ state = START;
+ case START:
+ nntp._commands.push_back ("");
+ nntp.write_next_command ();
+ break;
+ case CAPABILITY:
+ if (has_capability)
+ {
+ has_auth = false;
+ has_tls = false;
+ has_sasl = false;
+ has_post = false;
+ nntp._listener = this;
+ nntp._commands.push_back("CAPABILITIES\r\n");
+ nntp.write_next_command ();
+ break;
+ }
+ state = AUTH_U;
+ case AUTH_U:
+ if (has_auth && !nntp._username.empty()) {
+ char buf[512];
+ nntp._listener = this;
+ snprintf (buf, sizeof(buf), "AUTHINFO USER %s\r\n", nntp._username.c_str());
+ nntp._commands.push_back (buf);
+ nntp.write_next_command ();
+ break;
+ }
+ case AUTH_P:
+ if (state == AUTH_P && !nntp._password.empty()) {
+ char buf[512];
+ nntp._listener = this;
+ snprintf (buf, sizeof(buf), "AUTHINFO PASS %s\r\n", nntp._username.c_str());
+ nntp._commands.push_back (buf);
+ nntp.write_next_command ();
+ break;
+ }
+ state = MODE;
+ case MODE:
+ if (has_mode) {
+ nntp._listener = this;
+ nntp._commands.push_back ("MODE READER\r\n");
+ nntp.write_next_command ();
+ break;
+ }
+ state = DONE;
+ case DONE:
+ break;
+ case STARTTLS:
+ nntp._listener = this;
+ nntp_commands.push_back ("STARTTLS\r\n");
+ nntp.write_next_command ();
+ break;
+ case TLS:
+ nntp._listener = this;
+ _socket->handshake ();
+ break;
+
+ case SASL:
+ break;
+ }
+}
+
+void Handshake::on_nntp_line (NNTP * nntp, const StringView & line)
+{
+ if (state == CAPABILITY)
+ {
+ if (line.strncasecmp ("STARTTLS", 8) == 0)
+ has_tls = true;
+ else if (line.strncasecmp ("MODE READER", 11) == 0)
+ has_mode = true;
+ else if (line.strncasecmp ("OVER", 4) == 0)
+ has_over = true;
+ else if (line.strncasecmp ("POST", 4) == 0)
+ has_post = true;
+ else if (line.strncasecmp ("AUTHINFO", 8) == 0)
+ {
+ if (line.strstr ("USER"))
+ has_auth = true;
+ if (line.strstr ("SASL"))
+ has_sasl = true;
+ }
+ else if (line.strncasecmp ("SASL", 4) == 0)
+ {
+ StringView temp (line);
+ temp.rtruncate (5);
+ temp.trim ();
+ sasl_mech = temp.to_string ();
+ }
+ }
+}
+
+void Handshake::on_nntp_done (NNTP * nntp, Health health, const StringView & response)
+{
+ std::string host;
+ nntp->_socket->get_host(host);
+
+ if (health == ERR_NETWORK || health == ERR_LOCAL)
+ {
+ state = DONE;
+ pool->on_nntp_done(nntp, health, response);
+ return;
+ }
+ switch(state)
+ {
+ case START:
+ if (health != OK)
+ {
+ state = DONE;
+ pool->on_nntp_done(nntp, health, response);
+ }
+ else
+ state = CAPABILITY;
+ break;
+ case CAPABILITY:
+ if (health == ERR_COMMAND)
+ {
+ has_capability = false;
+ has_mode = true;
+ has_auth = true;
+ has_tls = false;
+ has_sasl = false;
+ has_over = false;
+ has_post = true;
+ tls = false;
+ }
+ if (has_tls && ! tls)
+ state = STARTTLS;
+ else if (has_auth && !auth)
+ state = AUTH_U;
+ else if (has_mode)
+ state = MODE;
+ else
+ state = DONE;
+ break;
+ case STARTTLS:
+ if (health == OK)
+ state = TLS;
+ else if (has_auth)
+ state = AUTH_U;
+ else if (has_mode)
+ state = MODE;
+ else
+ state = DONE;
+ break;
+ case TLS:
+ if (health != OK)
+ Log::add_err_va (_("TLS negotiation failed."), host.c_str());
+ else
+ tls = true;
+ if (tls)
+ // capabilities can change after encryption
+ state = CAPABILITY;
+ else if (has_auth)
+ state = AUTH_U;
+ else if (has_mode)
+ state = MODE;
+ else
+ state = DONE;
+ break;
+ case SASL:
+ Log::add_err_va (_("%s requires a username, but none is set."), host.c_str());
+ break;
+ case AUTH_U:
+ state = AUTH_P;
+ if (health == ERR_COMMAND)
+ {
+ if (has_mode)
+ state = MODE;
+ else
+ state = DONE;
+ Log::add_err_va (_("AUTH username for %s not accepted."), host.c_str());
+ }
+ break;
+ case AUTH_P:
+ if (health == OK)
+ {
+ auth = true;
+ // capabilities may change after authorization
+ state = CAPABILITY;
+ }
+ else if (has_mode)
+ state = MODE;
+ else
+ state = DONE;
+ if (health == ERR_COMMAND)
+ Log::add_err_va (_("AUTH password for %s not accepted."), host.c_str());
+ break;
+ case MODE:
+ state = DONE;
+ break;
+ case INIT:
+ case DONE:
+ break;
+ }
+
+ step();
+
+ if (state == DONE)
+ pool->on_nntp_done(nntp, OK, "");
+
+}
View
17 pan/tasks/nntp.h
@@ -30,6 +30,7 @@
namespace pan
{
+ class Handshake;
/**
* Asynchronously processes NNTP protocol commands.
*
@@ -102,13 +103,12 @@ namespace pan
_listener(0),
_username(username),
_password(password),
- _nntp_response_text(false)
+ _nntp_response_text(false),
+ hs(0)
{
}
- virtual ~NNTP ()
- {
- }
+ virtual ~NNTP ();
public:
@@ -221,6 +221,12 @@ namespace pan
*/
void noop (Listener * l);
+ /**
+ * Executes a POST command: "POST"
+ *
+ * Listener::on_nntp_done() will be called whether the
+ * command is successful or not.
+ */
void post (const StringView & message,
Listener * l);
@@ -260,6 +266,9 @@ namespace pan
virtual void on_socket_error (Socket*);
virtual void on_socket_abort (Socket*);
+ Handshake *hs;
+ friend class Handshake;
+
public:
/**
View
77 pan/tasks/socket-gnutls.cc
@@ -0,0 +1,77 @@
+// :tabSize=2:indentSize=2:noTabs=true:
+
+#include <errno.h>
+#include <pan/tasks/socket-gnutls.h>
+
+using namespace pan;
+
+SocketTLS::SocketTLS() : handshake(false), tls(false)
+{
+}
+
+ssize_t SocketTLS::read_p(SocketTLS *self, char *data, int len)
+{
+ gsize in;
+ GIOChannelError *err;
+ GIOStatus stat = g_io_channel_read_chars(self->_channel, data, len, &in, &err);
+
+ if (stat == G_IO_STATUS_AGAIN)
+ gnutls_transport_set_errno(self->session, EAGAIN);
+ else if (stat != G_IO_STATUS_NORMAL)
+ gnutls_transport_set_errno(self->session, (int) *err);
+ g_clear_error(&err);
+
+ return (stat==G_IO_NORMAL)? in : -1;
+}
+ssize_t SocketTLS::write_p(SocketTLS *self, char *data, int len)
+{
+ gsize out;
+ GIOChannelError *err;
+ GIOStatus stat = g_io_channel_write_chars(self->_channel, data, len, &out, &err);
+
+ if (stat == G_IO_STATUS_AGAIN)
+ gnutls_transport_set_errno(self->session, EAGAIN);
+ else if (stat != G_IO_STATUS_NORMAL)
+ gnutls_transport_set_errno(self->session, (int) *err);
+ else // OK
+ {
+ g_io_channel_flush(self->_channel, NULL);
+ return out;
+ }
+ g_clear_error(&err);
+ return -1;
+}
+
+void SocketTLS::starttls()
+{
+ int ret;
+ handshake = true;
+
+ gnutls_anon_allocate_client_credentials (&anoncred);
+ gnutls_init (&session, GNUTLS_CLIENT);
+ gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
+ gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) _channel);
+ gnutls_transport_set_pull_function (session, (gnutls_pull_func) read_p);
+ gnutls_transport_set_pull_function (session, (gnutls_push_func) write_p);
+ gnutls_transport_set_lowat (session, 0);
+ ret = gnutls_handshake (session);
+ if (ret == GNUTLS_E_AGAIN)
+ if ( gnutls_record_get_direction(session) )
+ set_watch_mode (WRITE_NOW);
+ else
+ set_watch_mode (READ_NOW);
+ else if (ret == 0)
+ {
+ // handshake already finished
+ // should never happen
+ handshake = false;
+ tls = true;
+ _listener->on_socket_response (this, StringView("200 tls handshake done\r\n"));
+ }
+ else
+ {
+ // fatal handshake
+ handshake = false;
+ _listener->on_socket_response (this, StringView("500 tls handshake failed\r\n"));
+ }
+}
View
50 pan/tasks/socket-gnutls.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// :tabSize=2:indentSize=2:noTabs=true:
+/*
+ * Pan - A Newsreader for Gtk+
+ * Copyright (C) 2011 K. Haley <haleykd@users.sf.net>
+ *
+ * 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; version 2 of the License.
+ *
+ * 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
+ */
+
+#ifndef __SocketGNUTLS_h__
+#define __SocketGNUTLS_h__
+
+#include <pan/tasks/socket-impl-gio.h>
+
+namespace pan
+{
+ /**
+ * GnuTLS implementation of Socket
+ *
+ * @ingroup tasks
+ */
+ class SocketTLS: public GIOChannelSocket
+ {
+ public:
+ SocketTLS();
+ void starttls();
+ private:
+ bool handshake;
+ bool tls;
+ gnutls_session_t session;
+ gnutls_anon_client_credentials_t anoncred;
+
+ virtual DoResult do_read ();
+ virtual DoResult do_write ();
+ static ssize_t read_p(SocketTLS *, char *, int);
+ static ssize_t write_p(SocketTLS *, char *, int);
+ };
+}
+#endif
View
20 pan/tasks/socket-impl-gio.h
@@ -41,26 +41,28 @@ namespace pan
virtual void write_command (const StringView& chars, Listener *);
virtual void get_host (std::string& setme) const;
- private:
+ protected:
GIOChannel * _channel;
- unsigned int _tag_watch;
- unsigned int _tag_timeout;
Listener * _listener;
GString * _out_buf;
GString * _in_buf;
std::string _partial_read;
- std::string _host;
bool _io_performed;
-
- private:
+ enum DoResult { IO_ERR, IO_READ, IO_WRITE, IO_DONE };
+ virtual DoResult do_read ();
+ virtual DoResult do_write ();
enum WatchMode { READ_NOW, WRITE_NOW, IGNORE_NOW };
void set_watch_mode (WatchMode mode);
+
+ private:
+ unsigned int _tag_watch;
+ unsigned int _tag_timeout;
+ std::string _host;
+
+ private:
static gboolean gio_func (GIOChannel*, GIOCondition, gpointer);
gboolean gio_func (GIOChannel*, GIOCondition);
static gboolean timeout_func (gpointer);
- enum DoResult { IO_ERR, IO_READ, IO_WRITE, IO_DONE };
- DoResult do_read ();
- DoResult do_write ();
public:

No commit comments for this range

Something went wrong with that request. Please try again.