1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3  * Pan - A Newsreader for Gtk+
4  * Copyright (C) 2002-2006  Charles Kerr <charles@rebelbase.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
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, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #include <config.h>
21 #include <cerrno>
22 #include <cstdio>
23 #include <map>
24 #include <iostream>
25 #include <fstream>
26 extern "C" {
27   #include <sys/types.h> // for chmod
28   #include <sys/stat.h> // for chmod
29   #include <unistd.h>
30   #include <glib.h>
31   #include <glib/gi18n.h>
32 }
33 #include <pan/general/debug.h>
34 #include <pan/general/file-util.h>
35 #include <pan/general/line-reader.h>
36 #include <pan/general/log.h>
37 #include "data-io.h"
38 
39 using namespace pan;
40 
41 namespace
42 {
get_pan_home_file(const char * fname)43   std::string get_pan_home_file (const char * fname)
44   {
45     const std::string home (file::get_pan_home());
46     char * filename (g_build_filename (home.c_str(), fname, NULL));
47     std::string retval (filename);
48     g_free (filename);
49     return retval;
50   }
51 
get_tasks_filename()52   std::string get_tasks_filename ()
53   {
54     return get_pan_home_file ("tasks.nzb");
55   }
56 
get_group_descriptions_filename()57   std::string get_group_descriptions_filename ()
58   {
59     return get_pan_home_file ("newsgroups.dsc");
60   }
61 
get_group_permissions_filename()62   std::string get_group_permissions_filename ()
63   {
64     return get_pan_home_file ("newsgroups.ynm");
65   }
66 
get_group_xovers_filename()67   std::string get_group_xovers_filename ()
68   {
69     return get_pan_home_file ("newsgroups.xov");
70   }
71 
get_group_headers_filename(const Quark & group)72   std::string get_group_headers_filename (const Quark& group)
73   {
74     const std::string home (file::get_pan_home());
75     char * filename (g_build_filename (home.c_str(), "groups", group.c_str(), NULL));
76     char * dirname (g_path_get_dirname (filename));
77     file :: ensure_dir_exists (dirname);
78     std::string retval (filename);
79     g_free (dirname);
80     g_free (filename);
81     return retval;
82   }
83 
get_download_stats_filename()84   std::string get_download_stats_filename ()
85   {
86     return get_pan_home_file ("downloads.stats");
87   }
88 
89 }
90 
91 std::string
get_scorefile_name() const92 DataIO :: get_scorefile_name () const
93 {
94   std::string s;
95 
96   const char * env_str (g_getenv ("SCOREFILE"));
97   if (env_str && *env_str)
98     s = env_str;
99 
100   if (s.empty()) {
101     char * path (g_build_filename (g_get_home_dir(), "News", "Score", NULL));
102     if (file :: file_exists (path))
103       s = path;
104     g_free (path);
105   }
106 
107   if (s.empty())
108     s = get_pan_home_file ("Score");
109 
110   return s;
111 }
112 
113 std::string
get_posting_name() const114 DataIO :: get_posting_name () const
115 {
116   return get_pan_home_file ("posting.xml");
117 }
118 
119 std::string
get_server_filename() const120 DataIO :: get_server_filename () const
121 {
122   return get_pan_home_file ("servers.xml");
123 }
124 
125 /****
126 *****
127 ****/
128 
129 void
clear_group_headers(const Quark & group)130 DataIO :: clear_group_headers (const Quark& group)
131 {
132   const std::string filename (get_group_headers_filename (group));
133   std::remove (filename.c_str());
134 }
135 
136 /****
137 *****
138 ****/
139 
140 LineReader*
read_tasks() const141 DataIO :: read_tasks () const
142 {
143   const std::string filename (get_tasks_filename ());
144   return file::file_exists(filename.c_str()) ? read_file(filename) : 0;
145 }
146 
147 LineReader*
read_group_descriptions() const148 DataIO :: read_group_descriptions () const
149 {
150    return read_file (get_group_descriptions_filename ());
151 }
152 
153 LineReader*
read_group_permissions() const154 DataIO :: read_group_permissions () const
155 {
156    return read_file (get_group_permissions_filename ());
157 }
158 
159 LineReader*
read_download_stats() const160 DataIO :: read_download_stats () const
161 {
162    return read_file (get_download_stats_filename ());
163 }
164 
165 LineReader*
read_group_xovers() const166 DataIO :: read_group_xovers () const
167 {
168   return read_file (get_group_xovers_filename ());
169 }
170 
171 LineReader*
read_group_headers(const Quark & group) const172 DataIO :: read_group_headers (const Quark& group) const
173 {
174   const std::string filename (get_group_headers_filename (group));
175   return file::file_exists(filename.c_str()) ? read_file(filename) : 0;
176 }
177 
178 LineReader*
read_file(const StringView & filename) const179 DataIO :: read_file (const StringView& filename) const
180 {
181   return new FileLineReader (filename);
182 }
183 
184 /****
185 *****
186 ****/
187 
188 namespace
189 {
190   std::map<std::ofstream*, std::string> ostream_to_filename;
191 
get_ostream(const std::string filename)192   std::ostream* get_ostream (const std::string filename)
193   {
194     const std::string tmp (filename + ".tmp");
195     std::ofstream * o (new std::ofstream (
196       tmp.c_str(), std::ios_base::out|std::ios_base::binary));
197 
198     if (!o->good())
199       Log::add_err_va (_("Unable to save “%s” %s"), filename.c_str(), "");
200     ostream_to_filename[o] = filename;
201     return o;
202   }
203 
finalize_ostream(std::ofstream * o)204   void finalize_ostream (std::ofstream * o)
205   {
206     g_assert (ostream_to_filename.count(o));
207     const std::string filename (ostream_to_filename[o]);
208     ostream_to_filename.erase (o);
209 
210     o->flush ();
211     const bool ok = !o->fail();
212     const int my_errno = errno;
213     delete o;
214 
215     const std::string tmpfile (filename + ".tmp");
216     if (ok) {
217 #if defined(G_OS_WIN32)
218       ::remove (filename.c_str());
219 #endif
220       int ret = 0;
221       if ((ret = rename (tmpfile.c_str(), filename.c_str())))
222       {
223         std::cerr << LINE_ID << " ERROR renaming from [" << tmpfile << "] to [" << filename << "]: " << g_strerror(errno) <<" : "<<ret<< '\n';
224       } else
225       {
226         if ((ret = chmod (filename.c_str(), 0600)))
227           std::cerr << LINE_ID << " ERROR chmodding [" << filename << "]: " << g_strerror(errno) << " : "<<ret<<'\n';
228       }
229 //      std::cerr<<"dbg "<<ret<<"\n";
230     } else {
231       Log::add_err_va (_("Unable to save “%s” %s"), filename.c_str(), file::pan_strerror(my_errno));
232     }
233   }
234 }
235 
236 std::ostream*
write_tasks()237 DataIO :: write_tasks ()
238 {
239   return write_file (get_tasks_filename ());
240 }
241 
242 std::ostream*
write_server_properties()243 DataIO :: write_server_properties ()
244 {
245   return write_file (get_server_filename());
246 }
247 
248 std::ostream*
write_group_xovers()249 DataIO :: write_group_xovers ()
250 {
251   return write_file (get_group_xovers_filename ());
252 }
253 
254 std::ostream*
write_group_descriptions()255 DataIO :: write_group_descriptions ()
256 {
257   return write_file (get_group_descriptions_filename ());
258 }
259 
260 std::ostream*
write_group_permissions()261 DataIO :: write_group_permissions ()
262 {
263   return write_file (get_group_permissions_filename ());
264 }
265 
266 std::ostream*
write_group_headers(const Quark & group)267 DataIO :: write_group_headers (const Quark& group)
268 {
269   return write_file (get_group_headers_filename (group));
270 }
271 
272 std::ostream*
write_download_stats()273 DataIO :: write_download_stats ()
274 {
275    return write_file (get_download_stats_filename ());
276 }
277 
278 std::ostream*
write_file(const StringView & filename)279 DataIO :: write_file (const StringView& filename)
280 {
281   return get_ostream (filename);
282 }
283 
284 void
write_done(std::ostream * out)285 DataIO :: write_done (std::ostream* out)
286 {
287   finalize_ostream (dynamic_cast<std::ofstream*>(out));
288 }
289