df4e3260dbccaa4be0f1fd0d8d79f6ebf9c12b9d
[platform/upstream/evolution-data-server.git] / camel / camel-network-service.c
1 /*
2  * camel-network-service.c
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) version 3.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
16  *
17  */
18
19 #include "camel-network-service.h"
20
21 #include <config.h>
22 #include <glib/gi18n-lib.h>
23
24 #include <camel/camel-enumtypes.h>
25 #include <camel/camel-network-settings.h>
26 #include <camel/camel-service.h>
27 #include <camel/camel-session.h>
28 #include <camel/camel-tcp-stream-raw.h>
29
30 #include <camel/camel-tcp-stream-ssl.h>
31
32 #define PRIVATE_KEY "CamelNetworkService:private"
33
34 #define CAMEL_NETWORK_SERVICE_GET_PRIVATE(obj) \
35         (g_object_get_data (G_OBJECT (obj), PRIVATE_KEY))
36
37 typedef struct _CamelNetworkServicePrivate CamelNetworkServicePrivate;
38
39 struct _CamelNetworkServicePrivate {
40         gint placeholder;
41 };
42
43 /* Forward Declarations */
44 void            camel_network_service_init      (CamelNetworkService *service);
45
46 G_DEFINE_INTERFACE (
47         CamelNetworkService,
48         camel_network_service,
49         CAMEL_TYPE_SERVICE)
50
51 static CamelNetworkServicePrivate *
52 network_service_private_new (CamelNetworkService *service)
53 {
54         return g_slice_new0 (CamelNetworkServicePrivate);
55 }
56
57 static void
58 network_service_private_free (CamelNetworkServicePrivate *priv)
59 {
60         g_slice_free (CamelNetworkServicePrivate, priv);
61 }
62
63 static CamelStream *
64 network_service_connect_sync (CamelNetworkService *service,
65                               GCancellable *cancellable,
66                               GError **error)
67 {
68         CamelNetworkSecurityMethod method;
69         CamelNetworkSettings *network_settings;
70         CamelSettings *settings;
71         CamelSession *session;
72         CamelStream *stream;
73         const gchar *service_name;
74         guint16 default_port;
75         guint16 port;
76         gchar *socks_host;
77         gint socks_port;
78         gchar *host;
79         gint status;
80
81         session = camel_service_ref_session (CAMEL_SERVICE (service));
82         settings = camel_service_ref_settings (CAMEL_SERVICE (service));
83         g_return_val_if_fail (CAMEL_IS_NETWORK_SETTINGS (settings), NULL);
84
85         network_settings = CAMEL_NETWORK_SETTINGS (settings);
86         method = camel_network_settings_get_security_method (network_settings);
87         host = camel_network_settings_dup_host (network_settings);
88         port = camel_network_settings_get_port (network_settings);
89
90         g_object_unref (settings);
91
92         service_name = camel_network_service_get_service_name (service, method);
93         default_port = camel_network_service_get_default_port (service, method);
94
95         /* If the URL explicitly gives a port number, make
96          * it override the service name and default port. */
97         if (port > 0) {
98                 service_name = g_alloca (16);
99                 sprintf ((gchar *) service_name, "%u", port);
100                 default_port = 0;
101         }
102
103         switch (method) {
104                 case CAMEL_NETWORK_SECURITY_METHOD_NONE:
105                         stream = camel_tcp_stream_raw_new ();
106                         break;
107
108                 case CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT:
109                         stream = camel_tcp_stream_ssl_new_raw (
110                                 session, host,
111                                 CAMEL_TCP_STREAM_SSL_ENABLE_TLS);
112                         break;
113
114                 case CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT:
115                         stream = camel_tcp_stream_ssl_new (
116                                 session, host,
117                                 CAMEL_TCP_STREAM_SSL_ENABLE_SSL2 |
118                                 CAMEL_TCP_STREAM_SSL_ENABLE_SSL3);
119                         break;
120
121                 default:
122                         g_return_val_if_reached (NULL);
123         }
124
125         camel_session_get_socks_proxy (session, host, &socks_host, &socks_port);
126
127         if (socks_host != NULL) {
128                 camel_tcp_stream_set_socks_proxy (
129                         CAMEL_TCP_STREAM (stream),
130                         socks_host, socks_port);
131                 g_free (socks_host);
132         }
133
134         status = camel_tcp_stream_connect (
135                 CAMEL_TCP_STREAM (stream), host,
136                 service_name, default_port, cancellable, error);
137
138         if (status == -1) {
139                 /* Translators: The first '%s' is replaced with a host name, the second '%s' with service name or port number */
140                 g_prefix_error (
141                         error, _("Could not connect to '%s:%s': "), host, service_name ? service_name : "???");
142                 g_object_unref (stream);
143                 stream = NULL;
144         }
145
146         g_free (host);
147
148         g_object_unref (session);
149
150         return stream;
151 }
152
153 static void
154 camel_network_service_default_init (CamelNetworkServiceInterface *interface)
155 {
156         interface->connect_sync = network_service_connect_sync;
157 }
158
159 void
160 camel_network_service_init (CamelNetworkService *service)
161 {
162         /* This is called from CamelService during instance
163          * construction.  It is not part of the public API. */
164
165         g_return_if_fail (CAMEL_IS_NETWORK_SERVICE (service));
166
167         g_object_set_data_full (
168                 G_OBJECT (service), PRIVATE_KEY,
169                 network_service_private_new (service),
170                 (GDestroyNotify) network_service_private_free);
171 }
172
173 /**
174  * camel_network_service_get_service_name:
175  * @service: a #CamelNetworkService
176  * @method: a #CamelNetworkSecurityMethod
177  *
178  * Returns the standard network service name for @service and the security
179  * method @method, as defined in /etc/services.  For example, the service
180  * name for unencrypted IMAP or encrypted IMAP using STARTTLS is "imap",
181  * but the service name for IMAP over SSL is "imaps".
182  *
183  * Returns: the network service name for @service and @method, or %NULL
184  *
185  * Since: 3.2
186  **/
187 const gchar *
188 camel_network_service_get_service_name (CamelNetworkService *service,
189                                         CamelNetworkSecurityMethod method)
190 {
191         CamelNetworkServiceInterface *interface;
192         const gchar *service_name = NULL;
193
194         g_return_val_if_fail (CAMEL_IS_NETWORK_SERVICE (service), NULL);
195
196         interface = CAMEL_NETWORK_SERVICE_GET_INTERFACE (service);
197
198         if (interface->get_service_name != NULL)
199                 service_name = interface->get_service_name (service, method);
200
201         return service_name;
202 }
203
204 /**
205  * camel_network_service_get_default_port:
206  * @service: a #CamelNetworkService
207  * @method: a #CamelNetworkSecurityMethod
208  *
209  * Returns the default network port number for @service and the security
210  * method @method, as defined in /etc/services.  For example, the default
211  * port for unencrypted IMAP or encrypted IMAP using STARTTLS is 143, but
212  * the default port for IMAP over SSL is 993.
213  *
214  * Returns: the default port number for @service and @method
215  *
216  * Since: 3.2
217  **/
218 guint16
219 camel_network_service_get_default_port (CamelNetworkService *service,
220                                         CamelNetworkSecurityMethod method)
221 {
222         CamelNetworkServiceInterface *interface;
223         guint16 default_port = 0;
224
225         g_return_val_if_fail (CAMEL_IS_NETWORK_SERVICE (service), 0);
226
227         interface = CAMEL_NETWORK_SERVICE_GET_INTERFACE (service);
228
229         if (interface->get_default_port != NULL)
230                 default_port = interface->get_default_port (service, method);
231
232         return default_port;
233 }
234
235 /**
236  * camel_network_service_connect_sync:
237  * @service: a #CamelNetworkService
238  * @cancellable: optional #GCancellable object, or %NULL
239  * @error: return location for a #GError, or %NULL
240  *
241  * Attempts to establish a network connection to the server described by
242  * @service, using the preferred #CamelNetworkSettings:security-method to
243  * secure the connection.  If a connection cannot be established, or the
244  * connection attempt is cancelled, the function sets @error and returns
245  * %NULL.
246  *
247  * Returns: a #CamelStream, or %NULL
248  *
249  * Since: 3.2
250  **/
251 CamelStream *
252 camel_network_service_connect_sync (CamelNetworkService *service,
253                                     GCancellable *cancellable,
254                                     GError **error)
255 {
256         CamelNetworkServiceInterface *interface;
257
258         g_return_val_if_fail (CAMEL_IS_NETWORK_SERVICE (service), NULL);
259
260         interface = CAMEL_NETWORK_SERVICE_GET_INTERFACE (service);
261         g_return_val_if_fail (interface->connect_sync != NULL, NULL);
262
263         return interface->connect_sync (service, cancellable, error);
264 }