Allow set_driver to fail and handle this case.
[cascardo/rnetproxy.git] / pop.c
1 /*
2 ** Copyright (C) 2006 Thadeu Lima de Souza Cascardo <cascardo@minaslivre.org>
3 ** Copyright (C) 2009 Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
4 **  
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **  
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 ** GNU General Public License for more details.
14 **  
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 **  
19 */
20
21 #include <glib.h>
22 #include <string.h>
23 #include "hcconn_internal.h"
24 #include "pop.h"
25 #include "usermap.h"
26
27 typedef struct
28 {
29   GString *buffer;
30   GString *line;
31   GString *push;
32   gchar *user;
33   HCConn *lowconn;
34 } pop_t;
35
36 void
37 pop_destroy (pop_t *pop)
38 {
39   g_string_free (pop->buffer, TRUE);
40   g_string_free (pop->line, TRUE);
41   g_string_free (pop->push, TRUE);
42   if (pop->user)
43     g_free (pop->user);
44   g_slice_free (pop_t, (gpointer) pop);
45 }
46
47 static int
48 pop_check_user (pop_t *pop)
49 {
50   gchar *end;
51   gchar *s;
52   end = pop->line->str + pop->line->len;
53   s = pop->line->str;
54   while (s < end && *s == ' ') s++;
55   if (end - s < 5)
56     return -1;
57   if (g_ascii_strncasecmp (s, "USER ", 5) != 0)
58     return -1;
59   s += 5;
60   while (s < end && *s == ' ') s++;
61   if (s == end)
62     return -1;
63   end--;
64   while (end >= s && (*end == '\n' || *end == '\r')) end--;
65   if (end < s)
66     return -1;
67   if (pop->user)
68     g_free (pop->user);
69   pop->user = g_strndup (s, end - s + 2);
70   pop->user[end - s + 1] = 0;
71   return 0;
72 }
73
74 static int
75 pop_getline (pop_t *pop)
76 {
77   char * end;
78   size_t len;
79   if (pop->buffer->len == 0)
80     return -1;
81   end = memchr (pop->buffer->str, '\n', pop->buffer->len);
82   if (end == NULL)
83     return -1;
84   len = end - pop->buffer->str + 1;
85   g_string_truncate (pop->line, 0);
86   g_string_append_len (pop->line, pop->buffer->str, len);
87   g_string_erase (pop->buffer, 0, len);
88   return 0;
89 }
90
91 static void
92 pop_watch (HCConn *conn, HCEvent event, gpointer data)
93 {
94   char buffer[4096];
95   int r;
96   HCConn *pop_conn = data;
97   pop_t *pop = pop_conn->layer;
98   switch (event)
99     {
100     case HC_EVENT_READ:
101       while ((r = hc_conn_read (conn, buffer, sizeof (buffer))) > 0)
102         {
103           g_string_append_len (pop->buffer, buffer, r);
104           while (pop_getline (pop) == 0)
105             {
106               if (pop_check_user (pop) == 0)
107                 {
108                   g_message ("User is trying to authenticate as %s.",
109                              pop->user);
110                   if (usermap_perm (pop->user) == ACCESS_DENY)
111                     {
112                       g_message ("Denying access to user %s.", pop->user);
113                       if (pop_conn->func)
114                         pop_conn->func (pop_conn, HC_EVENT_CLOSE,
115                                         pop_conn->data);
116                       return;
117                     }
118                 }
119               g_string_append_len (pop->push, pop->line->str, pop->line->len);
120               if (pop_conn->func)
121                 pop_conn->func (pop_conn, HC_EVENT_READ, pop_conn->data);
122             }
123         }
124       break;
125     case HC_EVENT_CLOSE:
126       if (pop_conn->func)
127         pop_conn->func (pop_conn, event, pop_conn->data);
128       break;
129     }
130 }
131
132 static ssize_t
133 pop_read (gpointer data, gchar *buffer, size_t len)
134 {
135   pop_t *pop = data;
136   int r;
137   if (len > pop->push->len)
138     {
139       r = pop->push->len;
140       memcpy (buffer, pop->push->str, r);
141       g_string_truncate (pop->push, 0);
142     }
143   else
144     {
145       r = len;
146       memcpy (buffer, pop->push->str, r);
147       g_string_erase (pop->push, 0, r);
148     }
149   return r;
150 }
151
152 static ssize_t
153 pop_write (gpointer data, gchar *buffer, size_t len)
154 {
155   pop_t *pop = data;
156   hc_conn_write (pop->lowconn, buffer, len);
157   return len;
158 }
159
160 static void
161 pop_close (gpointer data)
162 {
163   pop_t *pop = data;
164   hc_conn_close (pop->lowconn);
165   pop_destroy (pop);
166 }
167
168 int
169 hc_conn_set_driver_pop (HCConn *conn, HCConn *lowconn)
170 {
171   pop_t *pop;
172   pop = g_slice_new (pop_t);
173   pop->buffer = g_string_sized_new (4096);
174   pop->line = g_string_sized_new (4096);
175   pop->push = g_string_sized_new (4096);
176   pop->user = NULL;
177   pop->lowconn = lowconn;
178   conn->read = pop_read;
179   conn->write = pop_write;
180   conn->close = pop_close;
181   conn->layer = pop;
182   hc_conn_set_callback (lowconn, pop_watch, conn);
183   return 0;
184 }