1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-pop3-store.c : class for a pop3 store
3 *
4 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5 *
6 * This library is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13 * for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Authors: Dan Winship <danw@ximian.com>
19 * Michael Zucchi <notzed@ximian.com>
20 */
21
22 #include "evolution-data-server-config.h"
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <ctype.h>
30
31 #include <glib/gi18n-lib.h>
32
33 #include "camel-pop3-folder.h"
34 #include "camel-pop3-settings.h"
35 #include "camel-pop3-store.h"
36
37 #ifdef G_OS_WIN32
38 #include <winsock2.h>
39 #include <ws2tcpip.h>
40 #endif
41
42 /* Specified in RFC 1939 */
43 #define POP3_PORT 110
44 #define POP3S_PORT 995
45
46 /* defines the length of the server error message we can display in the error dialog */
47 #define POP3_ERROR_SIZE_LIMIT 60
48
49 struct _CamelPOP3StorePrivate {
50 GMutex property_lock;
51 CamelDataCache *cache;
52 CamelPOP3Engine *engine;
53 };
54
55 enum {
56 PROP_0,
57 PROP_CONNECTABLE,
58 PROP_HOST_REACHABLE
59 };
60
61 extern CamelServiceAuthType camel_pop3_password_authtype;
62 extern CamelServiceAuthType camel_pop3_apop_authtype;
63
64 /* Forward Declarations */
65 static void camel_network_service_init (CamelNetworkServiceInterface *iface);
66
G_DEFINE_TYPE_WITH_CODE(CamelPOP3Store,camel_pop3_store,CAMEL_TYPE_STORE,G_ADD_PRIVATE (CamelPOP3Store)G_IMPLEMENT_INTERFACE (CAMEL_TYPE_NETWORK_SERVICE,camel_network_service_init))67 G_DEFINE_TYPE_WITH_CODE (
68 CamelPOP3Store,
69 camel_pop3_store,
70 CAMEL_TYPE_STORE,
71 G_ADD_PRIVATE (CamelPOP3Store)
72 G_IMPLEMENT_INTERFACE (
73 CAMEL_TYPE_NETWORK_SERVICE,
74 camel_network_service_init))
75
76 /* returns error message with ': ' as prefix */
77 static gchar *
78 get_valid_utf8_error (const gchar *text)
79 {
80 gchar *tmp = camel_utf8_make_valid (text);
81 gchar *ret = NULL;
82
83 /*TODO If the error message > size limit log it somewhere */
84 if (!tmp || g_utf8_strlen (tmp, -1) > POP3_ERROR_SIZE_LIMIT) {
85 g_free (tmp);
86 return NULL;
87 }
88
89 /* Translators: This is the separator between an error and an explanation */
90 ret = g_strconcat (_(": "), tmp, NULL);
91
92 g_free (tmp);
93 return ret;
94 }
95
96 static gboolean
connect_to_server(CamelService * service,GCancellable * cancellable,GError ** error)97 connect_to_server (CamelService *service,
98 GCancellable *cancellable,
99 GError **error)
100 {
101 CamelPOP3Store *store = CAMEL_POP3_STORE (service);
102 CamelNetworkSettings *network_settings;
103 CamelNetworkSecurityMethod method;
104 CamelSettings *settings;
105 CamelStream *stream = NULL;
106 CamelPOP3Engine *pop3_engine = NULL;
107 CamelPOP3Command *pc;
108 GIOStream *base_stream;
109 GIOStream *tls_stream;
110 gboolean disable_extensions;
111 gboolean success = TRUE;
112 gchar *host;
113 guint32 flags = 0;
114 gint ret;
115 GError *local_error = NULL;
116
117 settings = camel_service_ref_settings (service);
118
119 network_settings = CAMEL_NETWORK_SETTINGS (settings);
120 host = camel_network_settings_dup_host (network_settings);
121 method = camel_network_settings_get_security_method (network_settings);
122
123 disable_extensions = camel_pop3_settings_get_disable_extensions (
124 CAMEL_POP3_SETTINGS (settings));
125
126 g_object_unref (settings);
127
128 base_stream = camel_network_service_connect_sync (
129 CAMEL_NETWORK_SERVICE (service), cancellable, error);
130
131 if (base_stream != NULL) {
132 stream = camel_stream_new (base_stream);
133 g_object_unref (base_stream);
134 } else {
135 success = FALSE;
136 goto exit;
137 }
138
139 /* parent class connect initialization */
140 if (CAMEL_SERVICE_CLASS (camel_pop3_store_parent_class)->
141 connect_sync (service, cancellable, error) == FALSE) {
142 g_object_unref (stream);
143 success = FALSE;
144 goto exit;
145 }
146
147 if (disable_extensions)
148 flags |= CAMEL_POP3_ENGINE_DISABLE_EXTENSIONS;
149
150 if (!(pop3_engine = camel_pop3_engine_new (stream, flags, cancellable, &local_error)) ||
151 local_error != NULL) {
152 if (local_error)
153 g_propagate_error (error, local_error);
154 else
155 g_set_error (
156 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
157 _("Failed to read a valid greeting from POP server %s"),
158 host);
159 g_object_unref (stream);
160 success = FALSE;
161 goto exit;
162 }
163
164 if (method != CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT) {
165 g_object_unref (stream);
166 goto exit;
167 }
168
169 if (!(pop3_engine->capa & CAMEL_POP3_CAP_STLS)) {
170 g_set_error (
171 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
172 _("Failed to connect to POP server %s in secure mode: %s"),
173 host, _("STLS not supported by server"));
174 goto stls_exception;
175 }
176
177 pc = camel_pop3_engine_command_new (
178 pop3_engine, 0, NULL, NULL,
179 cancellable, error, "STLS\r\n");
180 while (camel_pop3_engine_iterate (pop3_engine, NULL, cancellable, NULL) > 0)
181 ;
182
183 ret = pc->state == CAMEL_POP3_COMMAND_OK;
184 camel_pop3_engine_command_free (pop3_engine, pc);
185
186 if (ret == FALSE) {
187 gchar *tmp;
188
189 tmp = get_valid_utf8_error ((gchar *) pop3_engine->line);
190 g_set_error (
191 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
192 /* Translators: Last %s is an optional
193 * explanation beginning with ": " separator. */
194 _("Failed to connect to POP server %s in secure mode%s"),
195 host, (tmp != NULL) ? tmp : "");
196 g_free (tmp);
197 goto stls_exception;
198 }
199
200 /* Okay, now toggle SSL/TLS mode */
201 base_stream = camel_stream_ref_base_stream (stream);
202 tls_stream = camel_network_service_starttls (
203 CAMEL_NETWORK_SERVICE (service), base_stream, error);
204 g_object_unref (base_stream);
205
206 if (tls_stream != NULL) {
207 camel_stream_set_base_stream (stream, tls_stream);
208 /* Truncate any left cached input from the insecure part of the session */
209 camel_pop3_stream_discard_cache (pop3_engine->stream);
210 g_object_unref (tls_stream);
211 } else {
212 g_prefix_error (
213 error,
214 _("Failed to connect to POP server %s in secure mode: "),
215 host);
216 goto stls_exception;
217 }
218
219 g_clear_object (&stream);
220
221 /* rfc2595, section 4 states that after a successful STLS
222 * command, the client MUST discard prior CAPA responses */
223 if (!camel_pop3_engine_reget_capabilities (pop3_engine, cancellable, error))
224 goto exception;
225
226 goto exit;
227
228 stls_exception:
229 /* As soon as we send a STLS command, all hope
230 * is lost of a clean QUIT if problems arise. */
231 /* if (clean_quit) {
232 / * try to disconnect cleanly * /
233 pc = camel_pop3_engine_command_new (
234 pop3_engine, 0, NULL, NULL,
235 cancellable, NULL, "QUIT\r\n");
236 while (camel_pop3_engine_iterate (pop3_engine, NULL, cancellable, NULL) > 0)
237 ;
238 camel_pop3_engine_command_free (pop3_engine, pc);
239 }*/
240
241 exception:
242 g_clear_object (&stream);
243 g_clear_object (&pop3_engine);
244
245 success = FALSE;
246
247 exit:
248 g_free (host);
249
250 g_mutex_lock (&store->priv->property_lock);
251 if (pop3_engine != NULL)
252 store->priv->engine = g_object_ref (pop3_engine);
253 g_mutex_unlock (&store->priv->property_lock);
254
255 g_clear_object (&pop3_engine);
256
257 return success;
258 }
259
260 static CamelAuthenticationResult
try_sasl(CamelPOP3Store * store,const gchar * mechanism,GCancellable * cancellable,GError ** error)261 try_sasl (CamelPOP3Store *store,
262 const gchar *mechanism,
263 GCancellable *cancellable,
264 GError **error)
265 {
266 CamelPOP3Engine *pop3_engine;
267 CamelPOP3Stream *pop3_stream;
268 CamelNetworkSettings *network_settings;
269 CamelAuthenticationResult result;
270 CamelSettings *settings;
271 CamelService *service;
272 guchar *line, *resp;
273 CamelSasl *sasl = NULL;
274 gchar *string;
275 gchar *host;
276 guint len;
277 gint ret;
278
279 service = CAMEL_SERVICE (store);
280
281 settings = camel_service_ref_settings (service);
282
283 network_settings = CAMEL_NETWORK_SETTINGS (settings);
284 host = camel_network_settings_dup_host (network_settings);
285
286 g_object_unref (settings);
287
288 pop3_engine = camel_pop3_store_ref_engine (store);
289 if (!pop3_engine) {
290 g_set_error_literal (
291 error, CAMEL_SERVICE_ERROR,
292 CAMEL_SERVICE_ERROR_UNAVAILABLE,
293 _("You must be working online to complete this operation"));
294 result = CAMEL_AUTHENTICATION_ERROR;
295 goto exit;
296 }
297
298 pop3_stream = pop3_engine->stream;
299
300 sasl = camel_sasl_new ("pop", mechanism, service);
301 if (sasl == NULL) {
302 g_set_error (
303 error, CAMEL_SERVICE_ERROR,
304 CAMEL_SERVICE_ERROR_URL_INVALID,
305 _("No support for %s authentication"), mechanism);
306 result = CAMEL_AUTHENTICATION_ERROR;
307 goto exit;
308 }
309
310 string = g_strdup_printf ("AUTH %s\r\n", camel_sasl_is_xoauth2_alias (mechanism) ? "XOAUTH2" : mechanism);
311 ret = camel_stream_write_string (
312 CAMEL_STREAM (pop3_stream), string, cancellable, error);
313 g_free (string);
314
315 if (ret == -1)
316 goto ioerror;
317
318 while (1) {
319 GError *local_error = NULL;
320
321 if (camel_pop3_stream_line (pop3_stream, &line, &len, cancellable, error) == -1)
322 goto ioerror;
323
324 if (strncmp ((gchar *) line, "+OK", 3) == 0) {
325 result = CAMEL_AUTHENTICATION_ACCEPTED;
326 break;
327 }
328
329 if (strncmp ((gchar *) line, "-ERR", 4) == 0) {
330 result = CAMEL_AUTHENTICATION_REJECTED;
331 break;
332 }
333
334 /* If we dont get continuation, or the sasl object's run out
335 * of work, or we dont get a challenge, its a protocol error,
336 * so fail, and try reset the server. */
337 if (strncmp ((gchar *) line, "+ ", 2) != 0
338 || camel_sasl_get_authenticated (sasl)
339 || (resp = (guchar *) camel_sasl_challenge_base64_sync (sasl, (const gchar *) line + 2, cancellable, &local_error)) == NULL) {
340 if (camel_stream_write_string (CAMEL_STREAM (pop3_stream), "*\r\n", cancellable, NULL)) {
341 /* coverity[unchecked_value] */
342 if (!camel_pop3_stream_line (pop3_stream, &line, &len, cancellable, NULL)) {
343 ;
344 }
345 }
346
347 if (local_error) {
348 g_propagate_error (error, local_error);
349 local_error = NULL;
350 goto ioerror;
351 }
352
353 g_set_error (
354 error, CAMEL_SERVICE_ERROR,
355 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
356 _("Cannot login to POP server %s: "
357 "SASL Protocol error"), host);
358 result = CAMEL_AUTHENTICATION_ERROR;
359 goto exit;
360 }
361
362 string = g_strdup_printf ("%s\r\n", resp);
363 ret = camel_stream_write_string (
364 CAMEL_STREAM (pop3_stream), string, cancellable, error);
365 g_free (string);
366
367 g_free (resp);
368
369 if (ret == -1)
370 goto ioerror;
371
372 }
373
374 goto exit;
375
376 ioerror:
377 g_prefix_error (
378 error, _("Failed to authenticate on POP server %s: "), host);
379 result = CAMEL_AUTHENTICATION_ERROR;
380
381 exit:
382 if (sasl != NULL)
383 g_object_unref (sasl);
384
385 g_free (host);
386
387 g_clear_object (&pop3_engine);
388
389 return result;
390 }
391
392 static void
pop3_store_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)393 pop3_store_set_property (GObject *object,
394 guint property_id,
395 const GValue *value,
396 GParamSpec *pspec)
397 {
398 switch (property_id) {
399 case PROP_CONNECTABLE:
400 camel_network_service_set_connectable (
401 CAMEL_NETWORK_SERVICE (object),
402 g_value_get_object (value));
403 return;
404 }
405
406 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
407 }
408
409 static void
pop3_store_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)410 pop3_store_get_property (GObject *object,
411 guint property_id,
412 GValue *value,
413 GParamSpec *pspec)
414 {
415 switch (property_id) {
416 case PROP_CONNECTABLE:
417 g_value_take_object (
418 value,
419 camel_network_service_ref_connectable (
420 CAMEL_NETWORK_SERVICE (object)));
421 return;
422
423 case PROP_HOST_REACHABLE:
424 g_value_set_boolean (
425 value,
426 camel_network_service_get_host_reachable (
427 CAMEL_NETWORK_SERVICE (object)));
428 return;
429 }
430
431 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
432 }
433
434 static void
pop3_store_dispose(GObject * object)435 pop3_store_dispose (GObject *object)
436 {
437 CamelPOP3StorePrivate *priv;
438
439 priv = CAMEL_POP3_STORE (object)->priv;
440
441 /* Force disconnect so we dont have it run
442 * later, after we've cleaned up some stuff. */
443 camel_service_disconnect_sync (
444 CAMEL_SERVICE (object), TRUE, NULL, NULL);
445
446 g_clear_object (&priv->cache);
447 g_clear_object (&priv->engine);
448
449 /* Chain up to parent's dispose() method. */
450 G_OBJECT_CLASS (camel_pop3_store_parent_class)->dispose (object);
451 }
452
453 static void
pop3_store_finalize(GObject * object)454 pop3_store_finalize (GObject *object)
455 {
456 CamelPOP3StorePrivate *priv;
457
458 priv = CAMEL_POP3_STORE (object)->priv;
459
460 g_mutex_clear (&priv->property_lock);
461
462 /* Chain up to parent's finalize() method. */
463 G_OBJECT_CLASS (camel_pop3_store_parent_class)->finalize (object);
464 }
465
466 static gchar *
pop3_store_get_name(CamelService * service,gboolean brief)467 pop3_store_get_name (CamelService *service,
468 gboolean brief)
469 {
470 CamelNetworkSettings *network_settings;
471 CamelSettings *settings;
472 gchar *host;
473 gchar *user;
474 gchar *name;
475
476 settings = camel_service_ref_settings (service);
477
478 network_settings = CAMEL_NETWORK_SETTINGS (settings);
479 host = camel_network_settings_dup_host (network_settings);
480 user = camel_network_settings_dup_user (network_settings);
481
482 g_object_unref (settings);
483
484 if (brief)
485 name = g_strdup_printf (
486 _("POP3 server %s"), host);
487 else
488 name = g_strdup_printf (
489 _("POP3 server for %s on %s"), user, host);
490
491 g_free (host);
492 g_free (user);
493
494 return name;
495 }
496
497 static gboolean
pop3_store_connect_sync(CamelService * service,GCancellable * cancellable,GError ** error)498 pop3_store_connect_sync (CamelService *service,
499 GCancellable *cancellable,
500 GError **error)
501 {
502 CamelPOP3Store *store = (CamelPOP3Store *) service;
503 CamelPOP3Engine *pop3_engine;
504 CamelSettings *settings;
505 CamelSession *session;
506 const gchar *user_data_dir;
507 gboolean success = TRUE;
508 gchar *mechanism;
509
510 /* Chain up to parent's method. */
511 if (!CAMEL_SERVICE_CLASS (camel_pop3_store_parent_class)->connect_sync (service, cancellable, error))
512 return FALSE;
513
514 session = camel_service_ref_session (service);
515 user_data_dir = camel_service_get_user_data_dir (service);
516
517 settings = camel_service_ref_settings (service);
518
519 mechanism = camel_network_settings_dup_auth_mechanism (
520 CAMEL_NETWORK_SETTINGS (settings));
521
522 g_object_unref (settings);
523
524 if (!session || !camel_session_get_online (session)) {
525 g_set_error (
526 error, CAMEL_SERVICE_ERROR,
527 CAMEL_SERVICE_ERROR_UNAVAILABLE,
528 _("You must be working online to complete this operation"));
529 success = FALSE;
530 goto exit;
531 }
532
533 g_mutex_lock (&store->priv->property_lock);
534
535 if (store->priv->cache == NULL) {
536 CamelDataCache *cache;
537
538 cache = camel_data_cache_new (user_data_dir, error);
539 if (cache != NULL) {
540 /* Ensure cache will never expire, otherwise
541 * it causes redownload of messages. */
542 camel_data_cache_set_expire_age (cache, -1);
543 camel_data_cache_set_expire_access (cache, -1);
544
545 store->priv->cache = g_object_ref (cache);
546
547 g_object_unref (cache);
548 }
549 }
550
551 g_mutex_unlock (&store->priv->property_lock);
552
553 success = connect_to_server (service, cancellable, error);
554
555 if (!success)
556 goto exit;
557
558 success = camel_session_authenticate_sync (
559 session, service, mechanism, cancellable, error);
560
561 if (!success)
562 goto exit;
563
564 /* Now that we are in the TRANSACTION state,
565 * try regetting the capabilities */
566 pop3_engine = camel_pop3_store_ref_engine (store);
567 if (pop3_engine) {
568 pop3_engine->state = CAMEL_POP3_ENGINE_TRANSACTION;
569 if (!camel_pop3_engine_reget_capabilities (pop3_engine, cancellable, error))
570 success = FALSE;
571 g_clear_object (&pop3_engine);
572 } else {
573 g_set_error_literal (
574 error, CAMEL_SERVICE_ERROR,
575 CAMEL_SERVICE_ERROR_UNAVAILABLE,
576 _("You must be working online to complete this operation"));
577 success = FALSE;
578 }
579
580 exit:
581 g_free (mechanism);
582
583 g_object_unref (session);
584
585 if (!success) {
586 /* to not leak possible connection to the server */
587 g_mutex_lock (&store->priv->property_lock);
588 g_clear_object (&store->priv->engine);
589 g_mutex_unlock (&store->priv->property_lock);
590 }
591
592 return success;
593 }
594
595 static gboolean
pop3_store_disconnect_sync(CamelService * service,gboolean clean,GCancellable * cancellable,GError ** error)596 pop3_store_disconnect_sync (CamelService *service,
597 gboolean clean,
598 GCancellable *cancellable,
599 GError **error)
600 {
601 CamelServiceClass *service_class;
602 CamelPOP3Store *store = CAMEL_POP3_STORE (service);
603 gboolean success;
604
605 if (clean) {
606 CamelPOP3Engine *pop3_engine;
607 CamelPOP3Command *pc;
608
609 pop3_engine = camel_pop3_store_ref_engine (store);
610
611 if (pop3_engine) {
612 if (camel_pop3_engine_busy_lock (pop3_engine, cancellable, NULL)) {
613 pc = camel_pop3_engine_command_new (
614 pop3_engine, 0, NULL, NULL,
615 cancellable, error, "QUIT\r\n");
616 while (camel_pop3_engine_iterate (pop3_engine, NULL, cancellable, NULL) > 0)
617 ;
618 camel_pop3_engine_command_free (pop3_engine, pc);
619 camel_pop3_engine_busy_unlock (pop3_engine);
620 }
621
622 g_clear_object (&pop3_engine);
623 }
624 }
625
626 /* Chain up to parent's disconnect() method. */
627 service_class = CAMEL_SERVICE_CLASS (camel_pop3_store_parent_class);
628
629 success = service_class->disconnect_sync (service, clean, cancellable, error);
630
631 g_mutex_lock (&store->priv->property_lock);
632 g_clear_object (&store->priv->engine);
633 g_mutex_unlock (&store->priv->property_lock);
634
635 return success;
636 }
637
638 static CamelAuthenticationResult
pop3_store_authenticate_sync(CamelService * service,const gchar * mechanism,GCancellable * cancellable,GError ** error)639 pop3_store_authenticate_sync (CamelService *service,
640 const gchar *mechanism,
641 GCancellable *cancellable,
642 GError **error)
643 {
644 CamelPOP3Store *store = CAMEL_POP3_STORE (service);
645 CamelNetworkSettings *network_settings;
646 CamelAuthenticationResult result;
647 CamelSettings *settings;
648 CamelPOP3Command *pcu = NULL;
649 CamelPOP3Command *pcp = NULL;
650 CamelPOP3Engine *pop3_engine;
651 const gchar *password;
652 gboolean enable_utf8;
653 gchar *host;
654 gchar *user;
655 gint status;
656
657 password = camel_service_get_password (service);
658
659 settings = camel_service_ref_settings (service);
660 enable_utf8 = camel_pop3_settings_get_enable_utf8 (CAMEL_POP3_SETTINGS (settings));
661
662 network_settings = CAMEL_NETWORK_SETTINGS (settings);
663 host = camel_network_settings_dup_host (network_settings);
664 user = camel_network_settings_dup_user (network_settings);
665
666 g_object_unref (settings);
667
668 pop3_engine = camel_pop3_store_ref_engine (store);
669 if (!pop3_engine) {
670 g_set_error_literal (
671 error, CAMEL_SERVICE_ERROR,
672 CAMEL_SERVICE_ERROR_UNAVAILABLE,
673 _("You must be working online to complete this operation"));
674 result = CAMEL_AUTHENTICATION_ERROR;
675 goto exit;
676 }
677
678 if (!camel_pop3_engine_busy_lock (pop3_engine, cancellable, error)) {
679 g_free (host);
680 g_free (user);
681 g_clear_object (&pop3_engine);
682
683 return CAMEL_AUTHENTICATION_ERROR;
684 }
685
686 if ((pop3_engine->capa & CAMEL_POP3_CAP_UTF8) != 0 && enable_utf8) {
687 pcu = camel_pop3_engine_command_new (
688 pop3_engine, 0, NULL, NULL, cancellable, error,
689 "UTF8");
690 if (error && *error) {
691 g_prefix_error (
692 error,
693 _("Unable to connect to POP server %s.\n"
694 "Error enabling UTF-8 mode: "), host);
695 result = CAMEL_AUTHENTICATION_ERROR;
696 goto exit;
697 }
698 while (camel_pop3_engine_iterate (pop3_engine, NULL, cancellable, NULL) > 0)
699 ;
700 camel_pop3_engine_command_free (pop3_engine, pcu);
701 pcu = NULL;
702 }
703
704 if (mechanism == NULL) {
705 if (password == NULL) {
706 g_set_error_literal (
707 error, CAMEL_SERVICE_ERROR,
708 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
709 _("Authentication password not available"));
710 result = CAMEL_AUTHENTICATION_ERROR;
711 goto exit;
712 }
713
714 /* pop engine will take care of pipelining ability */
715 pcu = camel_pop3_engine_command_new (
716 pop3_engine, 0, NULL, NULL, cancellable, error,
717 "USER %s\r\n", user);
718 if (error && *error) {
719 g_prefix_error (
720 error,
721 _("Unable to connect to POP server %s.\n"
722 "Error sending password: "), host);
723 result = CAMEL_AUTHENTICATION_ERROR;
724 goto exit;
725 }
726
727 pcp = camel_pop3_engine_command_new (
728 pop3_engine, 0, NULL, NULL, cancellable, error,
729 "PASS %s\r\n", password);
730
731 if (error && *error) {
732 g_prefix_error (
733 error,
734 _("Unable to connect to POP server %s.\n"
735 "Error sending password: "), host);
736 result = CAMEL_AUTHENTICATION_ERROR;
737 goto exit;
738 }
739 } else if (strcmp (mechanism, "+APOP") == 0 && pop3_engine->apop) {
740 gchar *secret, *md5asc, *d;
741 gsize secret_len;
742
743 if (password == NULL) {
744 g_set_error_literal (
745 error, CAMEL_SERVICE_ERROR,
746 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
747 _("Authentication password not available"));
748 result = CAMEL_AUTHENTICATION_ERROR;
749 goto exit;
750 }
751
752 d = pop3_engine->apop;
753
754 while (*d != '\0') {
755 if (!isascii ((gint) * d)) {
756
757 g_set_error (
758 error, CAMEL_SERVICE_ERROR,
759 CAMEL_SERVICE_ERROR_URL_INVALID,
760 /* Translators: Do not translate APOP. */
761 _("Unable to connect to POP server %s: "
762 "Invalid APOP ID received. Impersonation "
763 "attack suspected. Please contact your admin."),
764 host);
765
766 result = CAMEL_AUTHENTICATION_ERROR;
767 goto exit;
768 }
769 d++;
770 }
771
772 secret_len =
773 strlen (pop3_engine->apop) +
774 strlen (password) + 1;
775 secret = g_alloca (secret_len);
776 g_snprintf (
777 secret, secret_len, "%s%s",
778 pop3_engine->apop, password);
779 md5asc = g_compute_checksum_for_string (
780 G_CHECKSUM_MD5, secret, -1);
781 pcp = camel_pop3_engine_command_new (
782 pop3_engine, 0, NULL, NULL, cancellable, error,
783 "APOP %s %s\r\n", user, md5asc);
784 g_free (md5asc);
785
786 } else {
787 GList *link;
788 const gchar *test_mechanism = mechanism;
789
790 if (camel_sasl_is_xoauth2_alias (test_mechanism))
791 test_mechanism = "XOAUTH2";
792
793 link = pop3_engine->auth;
794 while (link != NULL) {
795 CamelServiceAuthType *auth = link->data;
796
797 if (g_strcmp0 (auth->authproto, test_mechanism) == 0) {
798 result = try_sasl (
799 store, mechanism,
800 cancellable, error);
801 goto exit;
802 }
803 link = g_list_next (link);
804 }
805
806 g_set_error (
807 error, CAMEL_SERVICE_ERROR,
808 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
809 _("No support for %s authentication"), mechanism);
810 result = CAMEL_AUTHENTICATION_ERROR;
811 goto exit;
812 }
813
814 while ((status = camel_pop3_engine_iterate (pop3_engine, pcp, cancellable, error)) > 0)
815 ;
816
817 if (status == -1) {
818 g_prefix_error (
819 error,
820 _("Unable to connect to POP server %s.\n"
821 "Error sending password: "), host);
822 result = CAMEL_AUTHENTICATION_ERROR;
823
824 } else if (pcu && pcu->state != CAMEL_POP3_COMMAND_OK) {
825 gchar *tmp;
826
827 /* Abort authentication if the server rejects the user
828 * name. Reprompting for a password won't do any good. */
829 tmp = get_valid_utf8_error ((gchar *) pop3_engine->line);
830 g_set_error (
831 error, CAMEL_SERVICE_ERROR,
832 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
833 /* Translators: Last %s is an optional explanation
834 * beginning with ": " separator. */
835 _("Unable to connect to POP server %s.\n"
836 "Error sending username%s"),
837 host, (tmp != NULL) ? tmp : "");
838 g_free (tmp);
839 result = CAMEL_AUTHENTICATION_ERROR;
840
841 } else if (pcp->state != CAMEL_POP3_COMMAND_OK) {
842 result = CAMEL_AUTHENTICATION_REJECTED;
843 } else {
844 result = CAMEL_AUTHENTICATION_ACCEPTED;
845 }
846
847 exit:
848 if (pcp != NULL)
849 camel_pop3_engine_command_free (pop3_engine, pcp);
850
851 if (pcu != NULL)
852 camel_pop3_engine_command_free (pop3_engine, pcu);
853
854 g_free (host);
855 g_free (user);
856
857 camel_pop3_engine_busy_unlock (pop3_engine);
858 g_clear_object (&pop3_engine);
859
860 return result;
861 }
862
863 static GList *
pop3_store_query_auth_types_sync(CamelService * service,GCancellable * cancellable,GError ** error)864 pop3_store_query_auth_types_sync (CamelService *service,
865 GCancellable *cancellable,
866 GError **error)
867 {
868 CamelServiceClass *service_class;
869 CamelPOP3Store *store = CAMEL_POP3_STORE (service);
870 GList *types = NULL;
871 GError *local_error = NULL;
872
873 /* Chain up to parent's query_auth_types() method. */
874 service_class = CAMEL_SERVICE_CLASS (camel_pop3_store_parent_class);
875 types = service_class->query_auth_types_sync (
876 service, cancellable, &local_error);
877
878 if (local_error != NULL) {
879 g_propagate_error (error, local_error);
880 return NULL;
881 }
882
883 if (connect_to_server (service, cancellable, error)) {
884 CamelPOP3Engine *pop3_engine;
885
886 pop3_engine = camel_pop3_store_ref_engine (store);
887
888 if (pop3_engine) {
889 types = g_list_concat (types, g_list_copy (pop3_engine->auth));
890 pop3_store_disconnect_sync (service, TRUE, cancellable, NULL);
891
892 g_clear_object (&pop3_engine);
893 }
894 }
895
896 return types;
897 }
898
899 static gboolean
pop3_store_can_refresh_folder(CamelStore * store,CamelFolderInfo * info,GError ** error)900 pop3_store_can_refresh_folder (CamelStore *store,
901 CamelFolderInfo *info,
902 GError **error)
903 {
904 /* any pop3 folder can be refreshed */
905 return TRUE;
906 }
907
908 static CamelFolder *
pop3_store_get_folder_sync(CamelStore * store,const gchar * folder_name,CamelStoreGetFolderFlags flags,GCancellable * cancellable,GError ** error)909 pop3_store_get_folder_sync (CamelStore *store,
910 const gchar *folder_name,
911 CamelStoreGetFolderFlags flags,
912 GCancellable *cancellable,
913 GError **error)
914 {
915 if (g_ascii_strcasecmp (folder_name, "inbox") != 0) {
916 g_set_error (
917 error, CAMEL_FOLDER_ERROR,
918 CAMEL_FOLDER_ERROR_INVALID,
919 _("No such folder “%s”."), folder_name);
920 return NULL;
921 }
922
923 return camel_pop3_folder_new (store, cancellable, error);
924 }
925
926 static CamelFolderInfo *
pop3_store_get_folder_info_sync(CamelStore * store,const gchar * top,CamelStoreGetFolderInfoFlags flags,GCancellable * cancellable,GError ** error)927 pop3_store_get_folder_info_sync (CamelStore *store,
928 const gchar *top,
929 CamelStoreGetFolderInfoFlags flags,
930 GCancellable *cancellable,
931 GError **error)
932 {
933 g_set_error (
934 error, CAMEL_STORE_ERROR,
935 CAMEL_STORE_ERROR_NO_FOLDER,
936 _("POP3 stores have no folder hierarchy"));
937
938 return NULL;
939 }
940
941 static CamelFolder *
pop3_store_get_trash_folder_sync(CamelStore * store,GCancellable * cancellable,GError ** error)942 pop3_store_get_trash_folder_sync (CamelStore *store,
943 GCancellable *cancellable,
944 GError **error)
945 {
946 /* no-op */
947 return NULL;
948 }
949
950 static const gchar *
pop3_store_get_service_name(CamelNetworkService * service,CamelNetworkSecurityMethod method)951 pop3_store_get_service_name (CamelNetworkService *service,
952 CamelNetworkSecurityMethod method)
953 {
954 const gchar *service_name;
955
956 switch (method) {
957 case CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT:
958 service_name = "pop3s";
959 break;
960
961 default:
962 service_name = "pop3";
963 break;
964 }
965
966 return service_name;
967 }
968
969 static guint16
pop3_store_get_default_port(CamelNetworkService * service,CamelNetworkSecurityMethod method)970 pop3_store_get_default_port (CamelNetworkService *service,
971 CamelNetworkSecurityMethod method)
972 {
973 guint16 default_port;
974
975 switch (method) {
976 case CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT:
977 default_port = POP3S_PORT;
978 break;
979
980 default:
981 default_port = POP3_PORT;
982 break;
983 }
984
985 return default_port;
986 }
987
988 static void
camel_pop3_store_class_init(CamelPOP3StoreClass * class)989 camel_pop3_store_class_init (CamelPOP3StoreClass *class)
990 {
991 GObjectClass *object_class;
992 CamelServiceClass *service_class;
993 CamelStoreClass *store_class;
994
995 object_class = G_OBJECT_CLASS (class);
996 object_class->set_property = pop3_store_set_property;
997 object_class->get_property = pop3_store_get_property;
998 object_class->dispose = pop3_store_dispose;
999 object_class->finalize = pop3_store_finalize;
1000
1001 service_class = CAMEL_SERVICE_CLASS (class);
1002 service_class->settings_type = CAMEL_TYPE_POP3_SETTINGS;
1003 service_class->get_name = pop3_store_get_name;
1004 service_class->connect_sync = pop3_store_connect_sync;
1005 service_class->disconnect_sync = pop3_store_disconnect_sync;
1006 service_class->authenticate_sync = pop3_store_authenticate_sync;
1007 service_class->query_auth_types_sync = pop3_store_query_auth_types_sync;
1008
1009 store_class = CAMEL_STORE_CLASS (class);
1010 store_class->can_refresh_folder = pop3_store_can_refresh_folder;
1011 store_class->get_folder_sync = pop3_store_get_folder_sync;
1012 store_class->get_folder_info_sync = pop3_store_get_folder_info_sync;
1013 store_class->get_trash_folder_sync = pop3_store_get_trash_folder_sync;
1014
1015 /* Inherited from CamelNetworkService. */
1016 g_object_class_override_property (
1017 object_class,
1018 PROP_CONNECTABLE,
1019 "connectable");
1020
1021 /* Inherited from CamelNetworkService. */
1022 g_object_class_override_property (
1023 object_class,
1024 PROP_HOST_REACHABLE,
1025 "host-reachable");
1026 }
1027
1028 static void
camel_network_service_init(CamelNetworkServiceInterface * iface)1029 camel_network_service_init (CamelNetworkServiceInterface *iface)
1030 {
1031 iface->get_service_name = pop3_store_get_service_name;
1032 iface->get_default_port = pop3_store_get_default_port;
1033 }
1034
1035 static void
camel_pop3_store_init(CamelPOP3Store * pop3_store)1036 camel_pop3_store_init (CamelPOP3Store *pop3_store)
1037 {
1038 pop3_store->priv = camel_pop3_store_get_instance_private (pop3_store);
1039
1040 g_mutex_init (&pop3_store->priv->property_lock);
1041 }
1042
1043 /**
1044 * camel_pop3_store_ref_cache:
1045 * @store: a #CamelPOP3Store
1046 *
1047 * Returns the #CamelDataCache for @store.
1048 *
1049 * The returned #CamelDataCache is referenced for thread-safety and must be
1050 * unreferenced with g_object_unref() when finished with it.
1051 *
1052 * Returns: a #CamelDataCache
1053 **/
1054 CamelDataCache *
camel_pop3_store_ref_cache(CamelPOP3Store * store)1055 camel_pop3_store_ref_cache (CamelPOP3Store *store)
1056 {
1057 CamelDataCache *cache = NULL;
1058
1059 g_return_val_if_fail (CAMEL_IS_POP3_STORE (store), NULL);
1060
1061 g_mutex_lock (&store->priv->property_lock);
1062
1063 if (store->priv->cache != NULL)
1064 cache = g_object_ref (store->priv->cache);
1065
1066 g_mutex_unlock (&store->priv->property_lock);
1067
1068 return cache;
1069 }
1070
1071 /**
1072 * camel_pop3_store_ref_engine:
1073 * @store: a #CamelPOP3Store
1074 *
1075 * Returns the #CamelPOP3Engine for @store.
1076 *
1077 * The returned #CamelPOP3Engine is referenced for thread-safety and must be
1078 * unreferenced with g_object_unref() when finished with it.
1079 *
1080 * Returns: a #CamelPOP3Store
1081 **/
1082 CamelPOP3Engine *
camel_pop3_store_ref_engine(CamelPOP3Store * store)1083 camel_pop3_store_ref_engine (CamelPOP3Store *store)
1084 {
1085 CamelPOP3Engine *engine = NULL;
1086
1087 g_return_val_if_fail (CAMEL_IS_POP3_STORE (store), NULL);
1088
1089 g_mutex_lock (&store->priv->property_lock);
1090
1091 if (store->priv->engine != NULL)
1092 engine = g_object_ref (store->priv->engine);
1093
1094 g_mutex_unlock (&store->priv->property_lock);
1095
1096 return engine;
1097 }
1098
1099 /**
1100 * camel_pop3_store_expunge:
1101 * @store: a #CamelPOP3Store
1102 * @error: return location for a #GError, or %NULL
1103 * @cancellable: optional #GCancellable object, or %NULL
1104 *
1105 * Expunge messages from the store. This will result in the connection
1106 * being closed, which may cause later commands to fail if they can't
1107 * reconnect.
1108 **/
1109 gboolean
camel_pop3_store_expunge(CamelPOP3Store * store,GCancellable * cancellable,GError ** error)1110 camel_pop3_store_expunge (CamelPOP3Store *store,
1111 GCancellable *cancellable,
1112 GError **error)
1113 {
1114 CamelPOP3Command *pc;
1115 CamelPOP3Engine *pop3_engine;
1116 CamelServiceConnectionStatus status;
1117
1118 status = camel_service_get_connection_status (CAMEL_SERVICE (store));
1119
1120 if (status != CAMEL_SERVICE_CONNECTED) {
1121 g_set_error (
1122 error, CAMEL_SERVICE_ERROR,
1123 CAMEL_SERVICE_ERROR_UNAVAILABLE,
1124 _("You must be working online to complete this operation"));
1125 return FALSE;
1126 }
1127
1128 pop3_engine = camel_pop3_store_ref_engine (store);
1129
1130 if (!camel_pop3_engine_busy_lock (pop3_engine, cancellable, error)) {
1131 g_clear_object (&pop3_engine);
1132 return FALSE;
1133 }
1134
1135 pc = camel_pop3_engine_command_new (
1136 pop3_engine, 0, NULL, NULL, cancellable, error, "QUIT\r\n");
1137
1138 while (camel_pop3_engine_iterate (pop3_engine, NULL, cancellable, NULL) > 0)
1139 ;
1140
1141 camel_pop3_engine_command_free (pop3_engine, pc);
1142
1143 camel_pop3_engine_busy_unlock (pop3_engine);
1144 g_clear_object (&pop3_engine);
1145
1146 return TRUE;
1147 }
1148
1149 /**
1150 * camel_pop3_store_cache_add:
1151 * @store: a #CamelPOP3Store
1152 * @uid: a message UID
1153 * @error: return location for a #GError, or %NULL
1154 *
1155 * Creates a cache file for @uid in @store and returns a #CamelStream for it.
1156 * If an error occurs in opening the cache file, the function sets @error and
1157 * returns %NULL.
1158 *
1159 * The returned #CamelStream is referenced for thread-safety and must be
1160 * unreferenced when finished with it.
1161 *
1162 * Returns: a #CamelStream, or %NULL
1163 **/
1164 CamelStream *
camel_pop3_store_cache_add(CamelPOP3Store * store,const gchar * uid,GError ** error)1165 camel_pop3_store_cache_add (CamelPOP3Store *store,
1166 const gchar *uid,
1167 GError **error)
1168 {
1169 CamelDataCache *cache;
1170 GIOStream *base_stream;
1171 CamelStream *stream = NULL;
1172
1173 g_return_val_if_fail (CAMEL_IS_POP3_STORE (store), NULL);
1174 g_return_val_if_fail (uid != NULL, NULL);
1175
1176 cache = camel_pop3_store_ref_cache (store);
1177 g_return_val_if_fail (cache != NULL, NULL);
1178
1179 base_stream = camel_data_cache_add (cache, "cache", uid, error);
1180 if (base_stream != NULL) {
1181 stream = camel_stream_new (base_stream);
1182 g_object_unref (base_stream);
1183 }
1184
1185 g_object_unref (cache);
1186
1187 return stream;
1188 }
1189
1190 /**
1191 * camel_pop3_store_cache_get:
1192 * @store: a #CamelPOP3Store
1193 * @uid: a message UID
1194 * @error: return location for a #GError, or %NULL
1195 *
1196 * Opens the cache file for @uid in @store and returns a #CamelStream for it.
1197 * If no matching cache file exists, the function returns %NULL. If an error
1198 * occurs in opening the cache file, the function sets @error and returns
1199 * %NULL.
1200 *
1201 * The returned #CamelStream is referenced for thread-safety and must be
1202 * unreferenced when finished with it.
1203 *
1204 * Returns: a #CamelStream, or %NULL
1205 **/
1206 CamelStream *
camel_pop3_store_cache_get(CamelPOP3Store * store,const gchar * uid,GError ** error)1207 camel_pop3_store_cache_get (CamelPOP3Store *store,
1208 const gchar *uid,
1209 GError **error)
1210 {
1211 CamelDataCache *cache;
1212 GIOStream *base_stream;
1213 CamelStream *stream = NULL;
1214
1215 g_return_val_if_fail (CAMEL_IS_POP3_STORE (store), NULL);
1216 g_return_val_if_fail (uid != NULL, NULL);
1217
1218 cache = camel_pop3_store_ref_cache (store);
1219 g_return_val_if_fail (cache != NULL, NULL);
1220
1221 base_stream = camel_data_cache_get (cache, "cache", uid, error);
1222 if (base_stream != NULL) {
1223 GInputStream *input_stream;
1224 gchar buffer[1];
1225 gssize n_bytes;
1226
1227 input_stream = g_io_stream_get_input_stream (base_stream);
1228
1229 n_bytes = g_input_stream_read (
1230 input_stream, buffer, 1, NULL, error);
1231
1232 if (n_bytes == 1 && buffer[0] == '#')
1233 stream = camel_stream_new (base_stream);
1234
1235 g_object_unref (base_stream);
1236 }
1237
1238 g_object_unref (cache);
1239
1240 return stream;
1241 }
1242
1243 /**
1244 * camel_pop3_store_cache_has:
1245 * @store: a #CamelPOP3Store
1246 * @uid: a message UID
1247 *
1248 * Returns whether @store has a cached message for @uid.
1249 *
1250 * Returns: whether @uid is cached
1251 **/
1252 gboolean
camel_pop3_store_cache_has(CamelPOP3Store * store,const gchar * uid)1253 camel_pop3_store_cache_has (CamelPOP3Store *store,
1254 const gchar *uid)
1255 {
1256 CamelStream *stream;
1257 gboolean uid_is_cached;
1258
1259 g_return_val_if_fail (CAMEL_IS_POP3_STORE (store), FALSE);
1260 g_return_val_if_fail (uid != NULL, FALSE);
1261
1262 stream = camel_pop3_store_cache_get (store, uid, NULL);
1263 uid_is_cached = (stream != NULL);
1264 g_clear_object (&stream);
1265
1266 return uid_is_cached;
1267 }
1268
1269