1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4 * Copyright (C) 2001-2003 Ximian, Inc. <www.ximain.com>
6 * Authors: Christopher Toshok <toshok@ximian.com>
7 * Michael Zucchi <notzed@ximian.com>
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of version 2 of the GNU General Public
11 * License as published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
32 #include <sys/types.h>
37 #include <camel/camel-url.h>
38 #include <camel/camel-string-utils.h>
39 #include <camel/camel-session.h>
40 #include <camel/camel-tcp-stream-raw.h>
41 #include <camel/camel-tcp-stream-ssl.h>
43 #include <camel/camel-disco-store.h>
44 #include <camel/camel-disco-diary.h>
46 #include "camel-nntp-summary.h"
47 #include "camel-nntp-store.h"
48 #include "camel-nntp-store-summary.h"
49 #include "camel-nntp-folder.h"
50 #include "camel-nntp-private.h"
51 #include "camel-nntp-resp-codes.h"
54 extern int camel_verbose_debug;
55 #define dd(x) (camel_verbose_debug?(x):0)
58 #define NNTPS_PORT 563
60 #define DUMP_EXTENSIONS
62 static CamelDiscoStoreClass *parent_class = NULL;
63 static CamelServiceClass *service_class = NULL;
65 /* Returns the class for a CamelNNTPStore */
66 #define CNNTPS_CLASS(so) CAMEL_NNTP_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so))
67 #define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
68 #define CNNTPF_CLASS(so) CAMEL_NNTP_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
70 static void nntp_construct (CamelService *service, CamelSession *session,
71 CamelProvider *provider, CamelURL *url,
76 nntp_can_work_offline(CamelDiscoStore *store)
88 connect_to_server (CamelService *service, int ssl_mode, CamelException *ex)
90 CamelNNTPStore *store = (CamelNNTPStore *) service;
91 CamelDiscoStore *disco_store = (CamelDiscoStore*) service;
92 CamelStream *tcp_stream;
93 gboolean retval = FALSE;
100 CAMEL_NNTP_STORE_LOCK(store, command_lock);
102 /* setup store-wide cache */
103 if (store->cache == NULL) {
104 if (store->storage_path == NULL)
107 store->cache = camel_data_cache_new (store->storage_path, 0, ex);
108 if (store->cache == NULL)
111 /* Default cache expiry - 2 weeks old, or not visited in 5 days */
112 camel_data_cache_set_expire_age (store->cache, 60*60*24*14);
113 camel_data_cache_set_expire_access (store->cache, 60*60*24*5);
116 if (!(h = camel_service_gethost (service, ex)))
119 port = service->url->port ? service->url->port : NNTP_PORT;
122 if (ssl_mode != USE_SSL_NEVER) {
123 port = service->url->port ? service->url->port : NNTPS_PORT;
124 tcp_stream = camel_tcp_stream_ssl_new (service->session, service->url->host, CAMEL_TCP_STREAM_SSL_ENABLE_SSL2 | CAMEL_TCP_STREAM_SSL_ENABLE_SSL3);
126 tcp_stream = camel_tcp_stream_raw_new ();
129 tcp_stream = camel_tcp_stream_raw_new ();
130 #endif /* HAVE_SSL */
132 ret = camel_tcp_stream_connect (CAMEL_TCP_STREAM (tcp_stream), h, port);
136 camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
137 _("Connection cancelled"));
139 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
140 _("Could not connect to %s (port %d): %s"),
141 service->url->host, port, g_strerror (errno));
143 camel_object_unref (tcp_stream);
148 store->stream = (CamelNNTPStream *) camel_nntp_stream_new (tcp_stream);
149 camel_object_unref (tcp_stream);
151 /* Read the greeting, if any. */
152 if (camel_nntp_stream_line (store->stream, &buf, &len) == -1) {
154 camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
155 _("Connection cancelled"));
157 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
158 _("Could not read greeting from %s: %s"),
159 service->url->host, g_strerror (errno));
161 camel_object_unref (store->stream);
162 store->stream = NULL;
167 len = strtoul (buf, (char **) &buf, 10);
168 if (len != 200 && len != 201) {
169 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
170 _("NNTP server %s returned error code %d: %s"),
171 service->url->host, len, buf);
173 camel_object_unref (store->stream);
174 store->stream = NULL;
179 /* set 'reader' mode & ignore return code */
180 if (camel_nntp_command (store, (char **) &buf, "mode reader") < 0 ||
181 /* hack: inn seems to close connections if nothing is done within
182 the first ten seconds. a non-existent command is enough though */
183 camel_nntp_command (store, (char **) &buf, "date") < 0)
186 path = g_build_filename (store->storage_path, ".ev-journal", NULL);
187 disco_store->diary = camel_disco_diary_new (disco_store, path, ex);
193 CAMEL_NNTP_STORE_UNLOCK(store, command_lock);
201 { "", USE_SSL_ALWAYS },
202 { "always", USE_SSL_ALWAYS },
203 { "when-possible", USE_SSL_WHEN_POSSIBLE },
204 { "never", USE_SSL_NEVER },
205 { NULL, USE_SSL_NEVER },
209 nntp_connect_online (CamelService *service, CamelException *ex)
215 use_ssl = camel_url_get_param (service->url, "use_ssl");
217 for (i = 0; ssl_options[i].value; i++)
218 if (!strcmp (ssl_options[i].value, use_ssl))
220 ssl_mode = ssl_options[i].mode;
222 ssl_mode = USE_SSL_NEVER;
224 if (ssl_mode == USE_SSL_ALWAYS) {
225 /* Connect via SSL */
226 return connect_to_server (service, ssl_mode, ex);
227 } else if (ssl_mode == USE_SSL_WHEN_POSSIBLE) {
228 /* If the server supports SSL, use it */
229 if (!connect_to_server (service, ssl_mode, ex)) {
230 if (camel_exception_get_id (ex) == CAMEL_EXCEPTION_SERVICE_UNAVAILABLE) {
231 /* The ssl port seems to be unavailable, fall back to plain NNTP */
232 camel_exception_clear (ex);
233 return connect_to_server (service, USE_SSL_NEVER, ex);
241 /* User doesn't care about SSL */
242 return connect_to_server (service, ssl_mode, ex);
245 return connect_to_server (service, USE_SSL_NEVER, ex);
250 nntp_connect_offline (CamelService *service, CamelException *ex)
252 CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(service);
253 CamelDiscoStore *disco_store = (CamelDiscoStore *) nntp_store;
256 if (nntp_store->storage_path == NULL)
259 /* setup store-wide cache */
260 if (nntp_store->cache == NULL) {
261 nntp_store->cache = camel_data_cache_new (nntp_store->storage_path, 0, ex);
262 if (nntp_store->cache == NULL)
265 /* Default cache expiry - 2 weeks old, or not visited in 5 days */
266 camel_data_cache_set_expire_age (nntp_store->cache, 60*60*24*14);
267 camel_data_cache_set_expire_access (nntp_store->cache, 60*60*24*5);
270 path = g_build_filename (nntp_store->storage_path, ".ev-journal", NULL);
271 disco_store->diary = camel_disco_diary_new (disco_store, path, ex);
274 if (!disco_store->diary)
281 nntp_disconnect_online (CamelService *service, gboolean clean, CamelException *ex)
283 CamelNNTPStore *store = CAMEL_NNTP_STORE (service);
286 CAMEL_NNTP_STORE_LOCK(store, command_lock);
289 camel_nntp_command (store, &line, "quit");
291 if (!service_class->disconnect (service, clean, ex)) {
292 CAMEL_NNTP_STORE_UNLOCK(store, command_lock);
296 camel_object_unref (store->stream);
297 store->stream = NULL;
299 CAMEL_NNTP_STORE_UNLOCK(store, command_lock);
305 nntp_disconnect_offline (CamelService *service, gboolean clean, CamelException *ex)
307 CamelDiscoStore *disco = CAMEL_DISCO_STORE(service);
309 if (!service_class->disconnect (service, clean, ex))
313 camel_object_unref (disco->diary);
321 nntp_store_get_name (CamelService *service, gboolean brief)
324 return g_strdup_printf ("%s", service->url->host);
326 return g_strdup_printf (_("USENET News via %s"), service->url->host);
330 extern CamelServiceAuthType camel_nntp_password_authtype;
333 nntp_store_query_auth_types (CamelService *service, CamelException *ex)
335 return g_list_append (NULL, &camel_nntp_password_authtype);
339 nntp_get_folder(CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex)
341 CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store);
344 CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock);
346 folder = camel_nntp_folder_new(store, folder_name, ex);
348 CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
354 * Converts a fully-fledged newsgroup name to a name in short dotted notation,
355 * e.g. nl.comp.os.linux.programmeren becomes n.c.o.l.programmeren
359 nntp_newsgroup_name_short (const char *name)
364 resptr = tmp = g_malloc0 (strlen (name) + 1);
366 while ((ptr2 = strchr (name, '.'))) {
377 strcpy (resptr, name);
382 * This function converts a NNTPStoreSummary item to a FolderInfo item that
383 * can be returned by the get_folders() call to the store. Both structs have
384 * essentially the same fields.
387 static CamelFolderInfo *
388 nntp_folder_info_from_store_info (CamelNNTPStore *store, gboolean short_notation, CamelStoreInfo *si)
390 CamelURL *base_url = ((CamelService *) store)->url;
391 CamelFolderInfo *fi = g_malloc0(sizeof(*fi));
394 fi->full_name = g_strdup (si->path);
397 fi->name = nntp_newsgroup_name_short (si->path);
399 fi->name = g_strdup (si->path);
401 fi->unread_message_count = -1;
402 /* fi->path is the 'canonicalised' path used by the UI (folder-tree). Not
403 * as important these days, but folders used to get added to the tree based
404 * on its path rather than the structure of the CamelFolderInfo's.
406 * must be delimited by '/' which also means that if the user doesn't want
407 * a flat list of newsgroups, you'll have to replace '.' with '/' for
409 /*camel_folder_info_build_path(fi, '/');*/
410 fi->path = g_strdup_printf ("/%s", si->path);
411 url = camel_url_new_with_base (base_url, fi->path);
412 fi->url = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
413 camel_url_free (url);
418 static CamelFolderInfo *
419 nntp_folder_info_from_name (CamelNNTPStore *store, gboolean short_notation, const char *name)
421 CamelFolderInfo *fi = g_malloc0(sizeof(*fi));
422 CamelURL *base_url = ((CamelService *)store)->url;
425 fi->full_name = g_strdup (name);
428 fi->name = nntp_newsgroup_name_short (name);
430 fi->name = g_strdup (name);
432 fi->unread_message_count = -1;
434 fi->path = g_strdup_printf ("/%s", name);
436 url = camel_url_new_with_base (base_url, fi->path);
437 fi->url = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
438 camel_url_free (url);
443 static CamelStoreInfo *
444 nntp_store_info_from_line (CamelNNTPStore *store, char *line)
446 CamelStoreSummary *summ = (CamelStoreSummary *)store->summary;
447 CamelURL *base_url = ((CamelService *)store)->url;
448 CamelNNTPStoreInfo *nsi = (CamelNNTPStoreInfo*)camel_store_summary_info_new(summ);
449 CamelStoreInfo *si = (CamelStoreInfo*)nsi;
453 relpath = g_strdup_printf ("/%s", line);
454 url = camel_url_new_with_base (base_url, relpath);
456 si->uri = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
457 camel_url_free (url);
459 si->path = g_strdup (line);
460 nsi->full_name = g_strdup (line);
461 return (CamelStoreInfo*) si;
464 static CamelFolderInfo *
465 nntp_store_get_subscribed_folder_info (CamelNNTPStore *store, const char *top, guint flags, CamelException *ex)
469 CamelFolderInfo *first = NULL, *last = NULL, *fi = NULL;
471 /* since we do not do a tree, any request that is not for root is sure to give no results */
472 if (top != NULL && top[0] != 0)
475 for (i=0;(si = camel_store_summary_index ((CamelStoreSummary *) store->summary, i));i++) {
476 if (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) {
477 fi = nntp_folder_info_from_store_info (store, store->do_short_folder_notation, si);
480 fi->flags |= CAMEL_FOLDER_NOINFERIORS | CAMEL_FOLDER_NOCHILDREN;
487 camel_store_summary_info_free ((CamelStoreSummary *) store->summary, si);
494 * get folder info, using the information in our StoreSummary
496 static CamelFolderInfo *
497 nntp_store_get_cached_folder_info (CamelNNTPStore *store, const char *orig_top, guint flags, CamelException *ex)
500 int subscribed_or_flag = (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) ? 0 : 1,
501 root_or_flag = (orig_top == NULL || orig_top[0] == '\0') ? 1 : 0,
502 recursive_flag = flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE;
504 CamelFolderInfo *first = NULL, *last = NULL, *fi = NULL;
506 char *top = g_strconcat(orig_top?orig_top:"", ".", NULL);
507 int toplen = strlen(top);
509 for (i = 0; (si = camel_store_summary_index ((CamelStoreSummary *) store->summary, i)); i++) {
510 if ((subscribed_or_flag || (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)) &&
511 (root_or_flag || g_ascii_strncasecmp (si->path, top, toplen) == 0)) {
512 if (recursive_flag || strchr (si->path + toplen, '.') == NULL) {
514 fi = nntp_folder_info_from_store_info(store, FALSE, si);
517 if (store->folder_hierarchy_relative) {
519 fi->name = g_strdup (si->path + ((toplen == 1) ? 0 : toplen));
522 /* apparently, this is an indirect subitem. if it's not a subitem of
523 the item we added last, we need to add a portion of this item to
524 the list as a placeholder */
525 len = strlen (last->full_name);
527 g_ascii_strncasecmp(si->path, last->full_name, len) != 0 ||
528 si->path[len] != '.') {
529 tmpname = g_strdup(si->path);
530 *(strchr(tmpname + toplen, '.')) = '\0';
531 fi = nntp_folder_info_from_name(store, FALSE, tmpname);
532 fi->flags |= CAMEL_FOLDER_NOSELECT;
533 if (store->folder_hierarchy_relative) {
535 fi->name = g_strdup(tmpname + ((toplen==1) ? 0 : toplen));
547 } else if (subscribed_or_flag && first) {
548 /* we have already added subitems, but this item is no longer a subitem */
549 camel_store_summary_info_free((CamelStoreSummary *)store->summary, si);
552 camel_store_summary_info_free((CamelStoreSummary *)store->summary, si);
559 /* retrieves the date from the NNTP server */
561 nntp_get_date(CamelNNTPStore *nntp_store)
564 int ret = camel_nntp_command(nntp_store, (char **)&line, "date");
567 nntp_store->summary->last_newslist[0] = 0;
571 while (*ptr == ' ' || *ptr == '\t')
574 if (strlen (ptr) == NNTP_DATE_SIZE) {
575 memcpy (nntp_store->summary->last_newslist, ptr, NNTP_DATE_SIZE);
583 store_info_sort (gconstpointer a, gconstpointer b)
585 return strcmp ((*(CamelNNTPStoreInfo**) a)->full_name, (*(CamelNNTPStoreInfo**) b)->full_name);
588 static CamelFolderInfo *
589 nntp_store_get_folder_info_all(CamelNNTPStore *nntp_store, const char *top, guint32 flags, gboolean online, CamelException *ex)
591 CamelNNTPStoreSummary *summary = nntp_store->summary;
594 unsigned char *line, *space;
600 if (online && (top == NULL || top[0] == 0)) {
601 /* we may need to update */
602 if (summary->last_newslist[0] != 0) {
604 memcpy(date, summary->last_newslist + 2, 6); /* YYMMDDD */
606 memcpy(date + 7, summary->last_newslist + 8, 6); /* HHMMSS */
609 nntp_get_date (nntp_store);
611 ret = camel_nntp_command (nntp_store, (char **) &line, "newgroups %s", date);
613 /* newgroups not supported :S so reload the complete list */
615 camel_store_summary_clear ((CamelStoreSummary*) summary);
616 summary->last_newslist[0] = 0;
617 goto do_complete_list;
620 while ((ret = camel_nntp_stream_line (nntp_store->stream, &line, &len)) > 0) {
621 if ((space = strchr(line, ' ')))
624 si = camel_store_summary_path ((CamelStoreSummary *) nntp_store->summary, line);
626 camel_store_summary_info_free ((CamelStoreSummary *) nntp_store->summary, si);
628 si = nntp_store_info_from_line (nntp_store, line);
629 camel_store_summary_add ((CamelStoreSummary*) nntp_store->summary, si);
634 /* seems we do need a complete list */
635 /* at first, we do a DATE to find out the last load occasion */
636 nntp_get_date (nntp_store);
638 ret = camel_nntp_command (nntp_store, (char **)&line, "list");
641 line = _("Stream error");
643 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_INVALID,
644 _("Error retrieving newsgroups:\n\n%s"), line);
648 while ((ret = camel_nntp_stream_line(nntp_store->stream, &line, &len)) > 0) {
649 if ((space = strchr(line, ' ')))
652 si = nntp_store_info_from_line (nntp_store, line);
653 camel_store_summary_add ((CamelStoreSummary*) nntp_store->summary, si);
654 /* check to see if it answers our current query */
659 g_ptr_array_sort (CAMEL_STORE_SUMMARY (nntp_store->summary)->folders, store_info_sort);
663 camel_store_summary_save ((CamelStoreSummary *) nntp_store->summary);
666 return nntp_store_get_cached_folder_info (nntp_store, top, flags, ex);
671 static CamelFolderInfo *
672 nntp_get_folder_info (CamelStore *store, const char *top, guint32 flags, gboolean online, CamelException *ex)
674 CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(store);
675 CamelFolderInfo *first = NULL;
677 dd(printf("g_f_i: fast %d subscr %d recursive %d online %d top \"%s\"\n",
678 flags & CAMEL_STORE_FOLDER_INFO_FAST,
679 flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
680 flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE,
684 CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock);
686 if (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED)
687 first = nntp_store_get_subscribed_folder_info (nntp_store, top, flags, ex);
689 first = nntp_store_get_folder_info_all (nntp_store, top, flags, online, ex);
691 CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
695 static CamelFolderInfo *
696 nntp_get_folder_info_online (CamelStore *store, const char *top, guint32 flags, CamelException *ex)
698 return nntp_get_folder_info (store, top, flags, TRUE, ex);
701 static CamelFolderInfo *
702 nntp_get_folder_info_offline(CamelStore *store, const char *top, guint32 flags, CamelException *ex)
704 return nntp_get_folder_info (store, top, flags, FALSE, ex);
708 nntp_store_folder_subscribed (CamelStore *store, const char *folder_name)
710 CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store);
714 si = camel_store_summary_path ((CamelStoreSummary *) nntp_store->summary, folder_name);
716 truth = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
717 camel_store_summary_info_free ((CamelStoreSummary *) nntp_store->summary, si);
724 nntp_store_subscribe_folder (CamelStore *store, const char *folder_name,
727 CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(store);
731 CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock);
733 si = camel_store_summary_path(CAMEL_STORE_SUMMARY(nntp_store->summary), folder_name);
735 camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
736 _("You cannot subscribe to this newsgroup:\n\n"
737 "No such newsgroup. The selected item is a probably a parent folder."));
739 if (!(si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)) {
740 si->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
741 fi = nntp_folder_info_from_store_info(nntp_store, nntp_store->do_short_folder_notation, si);
742 fi->flags |= CAMEL_FOLDER_NOINFERIORS | CAMEL_FOLDER_NOCHILDREN;
743 camel_store_summary_touch ((CamelStoreSummary *) nntp_store->summary);
744 camel_store_summary_save ((CamelStoreSummary *) nntp_store->summary);
745 CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
746 camel_object_trigger_event ((CamelObject *) nntp_store, "folder_subscribed", fi);
747 camel_folder_info_free (fi);
752 CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
756 nntp_store_unsubscribe_folder (CamelStore *store, const char *folder_name,
759 CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(store);
761 CamelStoreInfo *fitem;
762 CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock);
764 fitem = camel_store_summary_path(CAMEL_STORE_SUMMARY(nntp_store->summary), folder_name);
767 camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
768 _("You cannot unsubscribe to this newsgroup:\n\n"
769 "newsgroup does not exist!"));
771 if (fitem->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) {
772 fitem->flags &= ~CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
773 fi = nntp_folder_info_from_store_info (nntp_store, nntp_store->do_short_folder_notation, fitem);
774 camel_store_summary_touch ((CamelStoreSummary *) nntp_store->summary);
775 camel_store_summary_save ((CamelStoreSummary *) nntp_store->summary);
776 CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
777 camel_object_trigger_event ((CamelObject *) nntp_store, "folder_unsubscribed", fi);
778 camel_folder_info_free (fi);
783 CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
786 /* stubs for various folder operations we're not implementing */
788 static CamelFolderInfo *
789 nntp_create_folder (CamelStore *store, const char *parent_name,
790 const char *folder_name, CamelException *ex)
792 camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
793 _("You cannot create a folder in a News store: subscribe instead."));
798 nntp_rename_folder (CamelStore *store, const char *old_name, const char *new_name_in, CamelException *ex)
800 camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
801 _("You cannot rename a folder in a News store."));
805 nntp_delete_folder (CamelStore *store, const char *folder_name, CamelException *ex)
807 nntp_store_subscribe_folder (store, folder_name, ex);
808 camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
809 _("You cannot remove a folder in a News store: unsubscribe instead."));
814 nntp_store_finalize (CamelObject *object)
816 /* call base finalize */
817 CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (object);
818 struct _CamelNNTPStorePrivate *p = nntp_store->priv;
820 camel_service_disconnect ((CamelService *)object, TRUE, NULL);
822 if (nntp_store->summary) {
823 camel_store_summary_save ((CamelStoreSummary *) nntp_store->summary);
824 camel_object_unref (nntp_store->summary);
827 camel_object_unref (nntp_store->mem);
828 nntp_store->mem = NULL;
829 if (nntp_store->stream)
830 camel_object_unref (nntp_store->stream);
832 if (nntp_store->base_url)
833 g_free (nntp_store->base_url);
834 if (nntp_store->storage_path)
835 g_free (nntp_store->storage_path);
837 e_mutex_destroy(p->command_lock);
843 nntp_store_class_init (CamelNNTPStoreClass *camel_nntp_store_class)
845 CamelDiscoStoreClass *camel_disco_store_class = CAMEL_DISCO_STORE_CLASS (camel_nntp_store_class);
846 CamelStoreClass *camel_store_class = CAMEL_STORE_CLASS (camel_nntp_store_class);
847 CamelServiceClass *camel_service_class = CAMEL_SERVICE_CLASS (camel_nntp_store_class);
849 parent_class = CAMEL_DISCO_STORE_CLASS (camel_type_get_global_classfuncs (camel_disco_store_get_type ()));
850 service_class = CAMEL_SERVICE_CLASS (camel_type_get_global_classfuncs (camel_service_get_type ()));
852 /* virtual method overload */
853 camel_service_class->construct = nntp_construct;
854 camel_service_class->query_auth_types = nntp_store_query_auth_types;
855 camel_service_class->get_name = nntp_store_get_name;
857 camel_disco_store_class->can_work_offline = nntp_can_work_offline;
858 camel_disco_store_class->connect_online = nntp_connect_online;
859 camel_disco_store_class->connect_offline = nntp_connect_offline;
860 camel_disco_store_class->disconnect_online = nntp_disconnect_online;
861 camel_disco_store_class->disconnect_offline = nntp_disconnect_offline;
862 camel_disco_store_class->get_folder_online = nntp_get_folder;
863 camel_disco_store_class->get_folder_resyncing = nntp_get_folder;
864 camel_disco_store_class->get_folder_offline = nntp_get_folder;
866 camel_disco_store_class->get_folder_info_online = nntp_get_folder_info_online;
867 camel_disco_store_class->get_folder_info_resyncing = nntp_get_folder_info_online;
868 camel_disco_store_class->get_folder_info_offline = nntp_get_folder_info_offline;
870 camel_store_class->free_folder_info = camel_store_free_folder_info_full;
872 camel_store_class->folder_subscribed = nntp_store_folder_subscribed;
873 camel_store_class->subscribe_folder = nntp_store_subscribe_folder;
874 camel_store_class->unsubscribe_folder = nntp_store_unsubscribe_folder;
876 camel_store_class->create_folder = nntp_create_folder;
877 camel_store_class->delete_folder = nntp_delete_folder;
878 camel_store_class->rename_folder = nntp_rename_folder;
881 /* construction function in which we set some basic store properties */
883 nntp_construct (CamelService *service, CamelSession *session,
884 CamelProvider *provider, CamelURL *url,
887 CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(service);
888 CamelURL *summary_url;
891 /* construct the parent first */
892 CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex);
893 if (camel_exception_is_set (ex))
896 /* find out the storage path, base url */
897 nntp_store->storage_path = camel_session_get_storage_path (session, service, ex);
898 if (!nntp_store->storage_path)
902 nntp_store->base_url = camel_url_to_string (service->url, (CAMEL_URL_HIDE_PASSWORD |
903 CAMEL_URL_HIDE_PARAMS |
904 CAMEL_URL_HIDE_AUTH));
906 tmp = g_build_filename (nntp_store->storage_path, ".ev-store-summary", NULL);
907 nntp_store->summary = camel_nntp_store_summary_new ();
908 camel_store_summary_set_filename ((CamelStoreSummary *) nntp_store->summary, tmp);
909 summary_url = camel_url_new (nntp_store->base_url, NULL);
910 camel_store_summary_set_uri_base ((CamelStoreSummary *) nntp_store->summary, summary_url);
913 camel_url_free (summary_url);
914 if (camel_store_summary_load ((CamelStoreSummary *)nntp_store->summary) == 0)
918 if (camel_url_get_param (url, "show_short_notation"))
919 nntp_store->do_short_folder_notation = TRUE;
921 nntp_store->do_short_folder_notation = FALSE;
922 if (camel_url_get_param (url, "folder_hierarchy_relative"))
923 nntp_store->folder_hierarchy_relative = TRUE;
925 nntp_store->folder_hierarchy_relative = FALSE;
930 nntp_store_init (gpointer object, gpointer klass)
932 CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(object);
933 CamelStore *store = CAMEL_STORE (object);
934 struct _CamelNNTPStorePrivate *p;
936 store->flags = CAMEL_STORE_SUBSCRIPTIONS;
938 nntp_store->mem = (CamelStreamMem *)camel_stream_mem_new();
940 p = nntp_store->priv = g_malloc0(sizeof(*p));
941 p->command_lock = e_mutex_new(E_MUTEX_REC);
945 camel_nntp_store_get_type (void)
947 static CamelType camel_nntp_store_type = CAMEL_INVALID_TYPE;
949 if (camel_nntp_store_type == CAMEL_INVALID_TYPE) {
950 camel_nntp_store_type =
951 camel_type_register (CAMEL_DISCO_STORE_TYPE,
953 sizeof (CamelNNTPStore),
954 sizeof (CamelNNTPStoreClass),
955 (CamelObjectClassInitFunc) nntp_store_class_init,
957 (CamelObjectInitFunc) nntp_store_init,
958 (CamelObjectFinalizeFunc) nntp_store_finalize);
961 return camel_nntp_store_type;
964 /* enter owning lock */
966 camel_nntp_store_set_folder (CamelNNTPStore *store, CamelFolder *folder, CamelFolderChangeInfo *changes, CamelException *ex)
970 if (store->current_folder && strcmp (folder->full_name, store->current_folder) == 0)
973 /* FIXME: Do something with changeinfo */
974 ret = camel_nntp_summary_check ((CamelNNTPSummary *) folder->summary, changes, ex);
976 g_free (store->current_folder);
977 store->current_folder = g_strdup (folder->full_name);
983 camel_nntp_try_authenticate (CamelNNTPStore *store)
985 CamelService *service = (CamelService *) store;
986 CamelSession *session = camel_service_get_session (service);
990 if (!service->url->user)
993 /* if nessecary, prompt for the password */
994 if (!service->url->passwd) {
998 prompt = g_strdup_printf (_("Please enter the NNTP password for %s@%s"),
1000 service->url->host);
1002 camel_exception_init (&ex);
1004 service->url->passwd =
1005 camel_session_get_password (session, prompt, CAMEL_SESSION_PASSWORD_SECRET,
1006 service, "password", &ex);
1007 camel_exception_clear (&ex);
1010 if (!service->url->passwd)
1014 /* now, send auth info (currently, only authinfo user/pass is supported) */
1015 ret = camel_nntp_command(store, &line, "authinfo user %s", service->url->user);
1016 if (ret == NNTP_AUTH_ACCEPTED) {
1018 } else if (ret == NNTP_AUTH_CONTINUE) {
1019 ret = camel_nntp_command (store, &line, "authinfo pass %s", service->url->passwd);
1020 if (ret == NNTP_AUTH_ACCEPTED)
1029 nntp_connected (CamelNNTPStore *store, CamelException *ex)
1031 if (store->stream == NULL)
1032 return camel_service_connect (CAMEL_SERVICE (store), ex);
1037 /* Enter owning lock */
1039 camel_nntp_command (CamelNNTPStore *store, char **line, const char *fmt, ...)
1041 const unsigned char *p, *ps;
1048 e_mutex_assert_locked(store->priv->command_lock);
1050 if (!nntp_connected (store, NULL))
1053 /* Check for unprocessed data, ! */
1054 if (store->stream->mode == CAMEL_NNTP_STREAM_DATA) {
1055 g_warning("Unprocessed data left in stream, flushing");
1056 while (camel_nntp_stream_getd(store->stream, (unsigned char **)&p, &u) > 0)
1059 camel_nntp_stream_set_mode(store->stream, CAMEL_NNTP_STREAM_LINE);
1064 while ((c = *p++)) {
1068 camel_stream_write ((CamelStream *) store->mem, ps, p - ps - (c == '%' ? 1 : 2));
1072 s = va_arg(ap, char *);
1073 camel_stream_write((CamelStream *)store->mem, s, strlen(s));
1076 d = va_arg(ap, int);
1077 camel_stream_printf((CamelStream *)store->mem, "%d", d);
1080 u = va_arg(ap, unsigned int);
1081 camel_stream_printf((CamelStream *)store->mem, "%u", u);
1084 s = va_arg(ap, char *);
1085 camel_stream_printf((CamelStream *)store->mem, "<%s>", s);
1088 u = va_arg(ap, unsigned int);
1089 u2 = va_arg(ap, unsigned int);
1091 camel_stream_printf((CamelStream *)store->mem, "%u", u);
1093 camel_stream_printf((CamelStream *)store->mem, "%u-%u", u, u2);
1096 g_warning("Passing unknown format to nntp_command: %c\n", c);
1102 camel_stream_write ((CamelStream *) store->mem, ps, p-ps-1);
1103 dd(printf("NNTP_COMMAND: '%.*s'\n", (int)store->mem->buffer->len, store->mem->buffer->data));
1104 camel_stream_write ((CamelStream *) store->mem, "\r\n", 2);
1106 if (camel_stream_write ((CamelStream *) store->stream, store->mem->buffer->data, store->mem->buffer->len) == -1 && errno != EINTR) {
1107 camel_stream_reset ((CamelStream *) store->mem);
1109 g_byte_array_set_size (store->mem->buffer, 0);
1112 /* some error, re-connect */
1113 camel_service_disconnect (CAMEL_SERVICE (store), FALSE, NULL);
1115 if (!nntp_connected (store, NULL))
1118 goto command_begin_send;
1121 camel_stream_reset ((CamelStream *) store->mem);
1123 g_byte_array_set_size (store->mem->buffer, 0);
1125 if (camel_nntp_stream_line (store->stream, (unsigned char **) line, &u) == -1)
1128 u = strtoul (*line, NULL, 10);
1130 /* Check for 'authentication required' codes */
1131 if (u == NNTP_AUTH_REQUIRED &&
1132 camel_nntp_try_authenticate(store))
1133 goto command_begin_send;
1135 /* the server doesn't like us anymore, but we still like her! */
1136 if (u == 401 || u == 503)
1139 /* Handle all switching to data mode here, to make callers job easier */
1140 if (u == 215 || (u >= 220 && u <=224) || (u >= 230 && u <= 231))
1141 camel_nntp_stream_set_mode(store->stream, CAMEL_NNTP_STREAM_DATA);