From: kupcevic@sourceware.org <kupcevic@sourceware.org>
To: cluster-devel.redhat.com
Subject: [Cluster-devel] conga/ricci common/Makefile common/XML.cpp com ...
Date: 23 Oct 2006 21:13:29 -0000 [thread overview]
Message-ID: <20061023211329.14323.qmail@sourceware.org> (raw)
CVSROOT: /cvs/cluster
Module name: conga
Branch: RHEL5
Changes by: kupcevic at sourceware.org 2006-10-23 21:13:22
Modified files:
ricci/common : Makefile XML.cpp utils.cpp
ricci/modules/cluster/clumon/src/daemon: Monitor.cpp
ricci/modules/log: LogParser.cpp
ricci/modules/rpm: PackageHandler.cpp
ricci/modules/service: ServiceManager.cpp
ricci/modules/storage: MountHandler.cpp
ricci/ricci : Ricci.cpp SSLInstance.cpp
Added files:
ricci/common : File.cpp
ricci/include : File.h
Log message:
ricci: file handling - agregate file IO into single routine (bz211564)
Patches:
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/common/File.cpp.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=NONE&r2=1.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/common/Makefile.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.6&r2=1.6.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/common/XML.cpp.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.6&r2=1.6.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/common/utils.cpp.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.6&r2=1.6.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/include/File.h.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=NONE&r2=1.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/cluster/clumon/src/daemon/Monitor.cpp.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.10&r2=1.10.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/log/LogParser.cpp.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.6&r2=1.6.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/rpm/PackageHandler.cpp.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.9&r2=1.9.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/service/ServiceManager.cpp.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.5&r2=1.5.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/modules/storage/MountHandler.cpp.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.5&r2=1.5.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/ricci/Ricci.cpp.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.18&r2=1.18.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/ricci/SSLInstance.cpp.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.5&r2=1.5.2.1
/cvs/cluster/conga/ricci/common/File.cpp,v --> standard output
revision 1.1.2.1
--- conga/ricci/common/File.cpp
+++ - 2006-10-23 21:13:25.290828000 +0000
@@ -0,0 +1,214 @@
+/*
+ Copyright Red Hat, Inc. 2006
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+/*
+ * Author: Stanko Kupcevic <kupcevic@redhat.com>
+ */
+
+
+#include "File.h"
+#include <fstream>
+#include <fcntl.h>
+#include <errno.h>
+
+using namespace std;
+
+
+File_pimpl::File_pimpl(void* fs,
+ bool& owner) :
+ fs(fs)
+{
+ if (fs == 0)
+ throw String("fs_ptr is null!!!");
+ owner = true;
+}
+
+File_pimpl::~File_pimpl()
+{
+ fstream* ptr = (fstream*) fs;
+ delete ptr;
+}
+
+
+
+File
+File::open(const String& filepath,
+ bool rw)
+{
+ if (access(filepath.c_str(), R_OK))
+ throw String("missing file ") + filepath;
+ ios_base::openmode mode = ios_base::in;
+ if (rw)
+ mode |= ios_base::out;
+
+ counting_auto_ptr<File_pimpl> pimpl;
+ bool ownership_taken = false;
+ fstream* fs = new fstream(filepath.c_str(), mode);
+ try {
+ pimpl = counting_auto_ptr<File_pimpl>(new File_pimpl(fs, ownership_taken));
+ } catch ( ... ) {
+ if (!ownership_taken)
+ delete fs;
+ throw;
+ }
+ return File(pimpl, filepath, rw);
+}
+
+File
+File::create(const String& filepath,
+ bool truncate)
+{
+ int t = ::open(filepath.c_str(),
+ O_CREAT|O_RDWR,
+ S_IRUSR|S_IWUSR|S_IRGRP);
+ if (t != -1)
+ while (close(t) && errno == EINTR)
+ ;
+
+ ios_base::openmode mode = ios_base::in;
+ mode |= ios_base::out;
+ if (truncate)
+ mode |= ios_base::trunc;
+
+ counting_auto_ptr<File_pimpl> pimpl;
+ bool ownership_taken = false;
+ fstream* fs = new fstream(filepath.c_str(), mode);
+ try {
+ pimpl = counting_auto_ptr<File_pimpl>(new File_pimpl(fs, ownership_taken));
+ } catch ( ... ) {
+ if (!ownership_taken)
+ delete fs;
+ throw;
+ }
+ return File(pimpl, filepath, true);
+}
+
+File::File(counting_auto_ptr<File_pimpl> pimpl,
+ const String& path,
+ bool writable) :
+ _mutex(counting_auto_ptr<Mutex>(new Mutex())),
+ _pimpl(pimpl),
+ _path(path),
+ _writable(writable)
+{
+ if (!((fstream*) _pimpl->fs)->is_open())
+ throw String("unable to open ") + _path;
+ check_failed();
+}
+
+File::~File()
+{
+ if (_writable)
+ ((fstream*) _pimpl->fs)->flush();
+}
+
+String
+File::path() const
+{
+ return _path;
+}
+
+long
+File::size() const
+{
+ MutexLocker l(*_mutex);
+ ((fstream*) _pimpl->fs)->seekg(0, ios::end);
+ check_failed();
+ long s = ((fstream*) _pimpl->fs)->tellg();
+ check_failed();
+ if (s < 0)
+ throw String("size of file ") + _path + " is negative!!!";
+ return s;
+}
+
+String
+File::read() const
+{
+ MutexLocker l(*_mutex);
+
+ long len = size();
+ auto_ptr<char> buff(new char[len]);
+ ((fstream*) _pimpl->fs)->seekg(0, ios::beg);
+ check_failed();
+ ((fstream*) _pimpl->fs)->read(buff.get(), len);
+ ::shred(buff.get(), len);
+ check_failed();
+ return String(buff.get(), len);
+}
+
+File&
+File::append(const String& data)
+{
+ MutexLocker l(*_mutex);
+ if (!_writable)
+ throw String("not writable");
+ ((fstream*) _pimpl->fs)->seekp(0, ios::end);
+ check_failed();
+ ((fstream*) _pimpl->fs)->write(data.c_str(), data.size());
+ check_failed();
+ ((fstream*) _pimpl->fs)->flush();
+ check_failed();
+ return *this;
+}
+
+String
+File::replace(const String& data)
+{
+ MutexLocker l(*_mutex);
+ if (!_writable)
+ throw String("not writable");
+ String old(read());
+ create(_path, true);
+ append(data);
+ return old;
+}
+
+void
+File::shred()
+{
+ MutexLocker l(*_mutex);
+ if (!_writable)
+ throw String("not writable");
+ unsigned int len = size();
+ ((fstream*) _pimpl->fs)->seekp(0, ios::beg);
+ check_failed();
+ // should use random source (paranoid)
+ // doesn't work on journaled fss anyways
+ ((fstream*) _pimpl->fs)->write(String(len, 'o').c_str(), len);
+ check_failed();
+}
+
+void
+File::unlink()
+{
+ MutexLocker l(*_mutex);
+ if (::unlink(_path.c_str()))
+ throw String("unlink failed");
+}
+
+File::operator const String () const
+{
+ return read();
+}
+
+void
+File::check_failed() const
+{
+ if (((fstream*) _pimpl->fs)->fail())
+ throw String("IO error");
+}
--- conga/ricci/common/Makefile 2006/06/16 20:44:15 1.6
+++ conga/ricci/common/Makefile 2006/10/23 21:13:20 1.6.2.1
@@ -22,6 +22,7 @@
Thread.o \
Time.o \
utils.o \
+ File.o \
XML.o \
Socket.o \
ServerSocket.o \
--- conga/ricci/common/XML.cpp 2006/10/13 09:36:15 1.6
+++ conga/ricci/common/XML.cpp 2006/10/23 21:13:20 1.6.2.1
@@ -24,12 +24,12 @@
#include "XML.h"
#include "Mutex.h"
#include "utils.h"
+#include "File.h"
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <algorithm>
-#include <fstream>
//#include <iostream>
using namespace std;
@@ -229,25 +229,7 @@
XMLObject
readXML(const String& filename)
{
- char* buff = 0;
- try {
- if (access(filename.c_str(), R_OK))
- throw String("missing ") + filename;
- ifstream is(filename.c_str());
- is.seekg(0, ios::end);
- unsigned int length = is.tellg();
- is.seekg(0, ios::beg);
- buff = new char[length];
- is.read(buff, length);
- String xml_data(buff, length);
-
- XMLObject xml(parseXML(xml_data));
- delete [] buff; buff = 0;
- return xml;
- } catch ( ... ) {
- delete [] buff;
- throw;
- }
+ return parseXML(File::open(filename));
}
--- conga/ricci/common/utils.cpp 2006/08/14 23:55:48 1.6
+++ conga/ricci/common/utils.cpp 2006/10/23 21:13:20 1.6.2.1
@@ -27,8 +27,7 @@
#include <openssl/md5.h>
#include <stdlib.h>
-
-#include <iostream>
+//#include <iostream>
using namespace std;
@@ -298,7 +297,3 @@
std::map<String, exec_cache>
utils::cache;
-
-
-
-
/cvs/cluster/conga/ricci/include/File.h,v --> standard output
revision 1.1.2.1
--- conga/ricci/include/File.h
+++ - 2006-10-23 21:13:26.604399000 +0000
@@ -0,0 +1,84 @@
+/*
+ Copyright Red Hat, Inc. 2006
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+/*
+ * Author: Stanko Kupcevic <kupcevic@redhat.com>
+ */
+
+
+#ifndef File_h
+#define File_h
+
+#include "String.h"
+#include "counting_auto_ptr.h"
+
+
+class File_pimpl
+{
+ public:
+ File_pimpl(void*, bool&);
+ virtual ~File_pimpl();
+ void* const fs;
+ private:
+ File_pimpl(const File_pimpl&);
+ File_pimpl& operator=(const File_pimpl&);
+};
+
+
+class File
+{
+ public:
+
+ // throw if non-existant
+ static File open(const String& filepath,
+ bool rw=false);
+ // same as open, but create if nonexistant
+ static File create(const String& filepath,
+ bool truncate=false);
+ virtual ~File();
+
+ String path() const;
+ long size() const;
+
+ String read() const; // return content
+
+ File& append(const String& data); // append data to the end of file
+
+ String replace(const String& data); // replace content with data, return old content
+
+ void shred();
+ void unlink();
+
+ operator const String () const;
+
+ private:
+ File(counting_auto_ptr<File_pimpl>,
+ const String& path,
+ bool writable);
+
+ counting_auto_ptr<Mutex> _mutex;
+ counting_auto_ptr<File_pimpl> _pimpl;
+ const String _path;
+ const bool _writable;
+
+ void check_failed() const;
+
+};
+
+
+#endif // File_h
--- conga/ricci/modules/cluster/clumon/src/daemon/Monitor.cpp 2006/10/16 19:44:30 1.10
+++ conga/ricci/modules/cluster/clumon/src/daemon/Monitor.cpp 2006/10/23 21:13:21 1.10.2.1
@@ -31,7 +31,6 @@
#include <sys/sysinfo.h>
#include <algorithm>
-#include <fstream>
using namespace ClusterMonitoring;
--- conga/ricci/modules/log/LogParser.cpp 2006/08/15 00:00:10 1.6
+++ conga/ricci/modules/log/LogParser.cpp 2006/10/23 21:13:21 1.6.2.1
@@ -386,6 +386,8 @@
iter != files.end();
iter++) {
ifstream log(iter->c_str());
+ if (!log.is_open())
+ throw String("failed to open ") + *iter;
while (log.good()) {
char buff[1024];
log.getline(buff, sizeof(buff));
--- conga/ricci/modules/rpm/PackageHandler.cpp 2006/10/12 19:13:11 1.9
+++ conga/ricci/modules/rpm/PackageHandler.cpp 2006/10/23 21:13:21 1.9.2.1
@@ -23,9 +23,9 @@
#include "PackageHandler.h"
#include "utils.h"
+#include "File.h"
#include <unistd.h>
-#include <fstream>
using namespace std;
@@ -645,24 +645,7 @@
{
static bool release_set = false;
if (!release_set) {
- char* buff = 0;
- String release;
- try {
- ifstream is("/etc/redhat-release");
- is.seekg(0, ios::end);
- unsigned int length = is.tellg();
- is.seekg(0, ios::beg);
- buff = new char[length];
- is.read(buff, length);
-
- String rel(buff, length);
- delete [] buff; buff = 0;
-
- release = utils::strip(rel);
- } catch ( ... ) {
- delete [] buff;
- throw;
- }
+ String release(utils::strip(File::open("/etc/redhat-release")));
if (release.find("Nahant") != release.npos)
RHEL4 = true;
--- conga/ricci/modules/service/ServiceManager.cpp 2006/10/06 03:10:13 1.5
+++ conga/ricci/modules/service/ServiceManager.cpp 2006/10/23 21:13:21 1.5.2.1
@@ -23,9 +23,9 @@
#include "ServiceManager.h"
#include "utils.h"
+#include "File.h"
#include <vector>
-#include <fstream>
using namespace std;
@@ -110,56 +110,43 @@
String path(INITD_DIR_PATH);
path += name();
- char* buff = 0;
- try {
- ifstream is(path.c_str());
- is.seekg(0, ios::end);
- unsigned int length = is.tellg();
- is.seekg(0, ios::beg);
- buff = new char[length];
- is.read(buff, length);
- String initd(buff, length);
-
- list<String> desc_lines;
-
- vector<String> lines = utils::split(initd, "\n");
- for (vector<String>::const_iterator iter = lines.begin();
- iter != lines.end();
- iter++) {
- String line(utils::strip(*iter));
- if (line.empty())
- continue;
- if (line.find(DESC_SIG) != 0)
- continue;
- desc_lines.push_back(line);
- while (desc_lines.back()[desc_lines.back().size()-1] == '\\' &&
- ++iter != lines.end())
- if (iter->size())
- desc_lines.push_back(*iter);
- else
- break;
- break;
- }
-
- String desc;
- for (list<String>::const_iterator l_iter = desc_lines.begin();
- l_iter != desc_lines.end();
- l_iter++) {
- String s = utils::rstrip(*l_iter, "\\");
- s = utils::lstrip(s, DESC_SIG);
- s = utils::lstrip(s, "#");
- s = utils::lstrip(s);
- desc += s;
- }
-
- _descr = counting_auto_ptr<String>(new String(desc));
-
- delete [] buff;
- } catch ( ... ) {
- delete [] buff;
- throw;
+ String initd(File::open(path));
+
+ list<String> desc_lines;
+
+ vector<String> lines = utils::split(initd, "\n");
+ for (vector<String>::const_iterator iter = lines.begin();
+ iter != lines.end();
+ iter++) {
+ String line(utils::strip(*iter));
+ if (line.empty())
+ continue;
+ if (line.find(DESC_SIG) != 0)
+ continue;
+ desc_lines.push_back(line);
+ while (desc_lines.back()[desc_lines.back().size()-1] == '\\' &&
+ ++iter != lines.end())
+ if (iter->size())
+ desc_lines.push_back(*iter);
+ else
+ break;
+ break;
+ }
+
+ String desc;
+ for (list<String>::const_iterator l_iter = desc_lines.begin();
+ l_iter != desc_lines.end();
+ l_iter++) {
+ String s = utils::rstrip(*l_iter, "\\");
+ s = utils::lstrip(s, DESC_SIG);
+ s = utils::lstrip(s, "#");
+ s = utils::lstrip(s);
+ desc += s;
}
+
+ _descr = counting_auto_ptr<String>(new String(desc));
}
+
return *_descr;
}
@@ -681,26 +668,7 @@
{
static bool release_set = false;
if (!release_set) {
- char* buff = 0;
- String release;
- try {
- ifstream is("/etc/redhat-release");
- is.seekg(0, ios::end);
- unsigned int length = is.tellg();
- is.seekg(0, ios::beg);
- buff = new char[length];
- is.read(buff, length);
-
- String rel(buff, length);
- delete [] buff;
- buff = 0;
-
- release = utils::strip(rel);
- } catch ( ... ) {
- delete [] buff;
- throw;
- }
-
+ String release(utils::strip(File::open("/etc/redhat-release")));
if (release.find("Nahant") != release.npos)
RHEL4 = true;
else if (release.find("Bordeaux") != release.npos)
@@ -709,7 +677,8 @@
release.find("6") != release.npos)
// TODO: detect FC6
FC6 = true;
- // TODO: detect RHEL5
+ else if (release.find("Tikanga") != release.npos)
+ RHEL5 = true;
release_set = true;
}
--- conga/ricci/modules/storage/MountHandler.cpp 2006/10/06 03:10:13 1.5
+++ conga/ricci/modules/storage/MountHandler.cpp 2006/10/23 21:13:22 1.5.2.1
@@ -561,8 +561,8 @@
int ret;
while ((ret = flock(locker_fd, LOCK_UN)) && errno == EINTR)
;
- if (ret)
- throw String("unable to unflock fstab");
+ //if (ret)
+ // throw String("unable to unflock fstab");
while (close(locker_fd) == -1 && errno == EINTR)
;
}
--- conga/ricci/ricci/Ricci.cpp 2006/10/06 03:10:13 1.18
+++ conga/ricci/ricci/Ricci.cpp 2006/10/23 21:13:22 1.18.2.1
@@ -30,6 +30,7 @@
#include "QueueLocker.h"
#include "Time.h"
#include "XML_tags.h"
+#include "File.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -380,22 +381,11 @@
QueueLocker lock;
if (_state != ProcessWorker::st_sched &&
_state != ProcessWorker::st_prog) {
- // shred file:
- // should use random source (paranoid)
- // doesn't work on journaled fss anyways
- ifstream is(_path.c_str());
- is.seekg(0, ios::end);
- unsigned int length = is.tellg();
- is.close();
-
- String t(length + 10, 'o');
- ofstream os(_path.c_str());
- os.seekp(0);
- os << t;
- os.close();
-
- // remove file
- unlink(_path.c_str());
+ try {
+ File f(File::open(_path, true));
+ f.shred();
+ f.unlink();
+ } catch ( ... ) {}
}
}
@@ -487,26 +477,14 @@
pair<String, String>
clusterinfo()
{
- char* buff = 0;
try {
- ifstream is("/etc/cluster/cluster.conf");
- is.seekg(0, ios::end);
- unsigned int length = is.tellg();
- is.seekg(0, ios::beg);
- buff = new char[length];
- is.read(buff, length);
- String cluster_conf(buff, length);
-
- XMLObject xml(parseXML(cluster_conf));
- delete [] buff; buff = 0;
-
+ XMLObject xml(readXML("/etc/cluster/cluster.conf"));
String name = xml.get_attr("name");
String alias = xml.get_attr("alias");
if (utils::strip(alias).empty())
alias = name;
return pair<String, String>(name, alias);
} catch ( ... ) {
- delete [] buff;
return pair<String, String>("", "");
}
}
@@ -514,22 +492,9 @@
String
os_release()
{
- char* buff = 0;
try {
- ifstream is("/etc/redhat-release");
- is.seekg(0, ios::end);
- unsigned int length = is.tellg();
- is.seekg(0, ios::beg);
- buff = new char[length];
- is.read(buff, length);
-
- String rel(buff, length);
- delete [] buff;
- buff = 0;
-
- return utils::strip(rel);
+ return utils::strip(File::open("/etc/redhat-release"));
} catch ( ... ) {
- delete [] buff;
return "";
}
}
--- conga/ricci/ricci/SSLInstance.cpp 2006/08/10 22:53:09 1.5
+++ conga/ricci/ricci/SSLInstance.cpp 2006/10/23 21:13:22 1.5.2.1
@@ -27,6 +27,7 @@
#include "Time.h"
#include "Random.h"
#include "utils.h"
+#include "File.h"
#include <errno.h>
#include <sys/types.h>
@@ -35,7 +36,6 @@
#include <unistd.h>
#include <iostream>
-#include <fstream>
#include <list>
#include <set>
@@ -119,23 +119,11 @@
for (set<String>::const_iterator iter = files.begin();
iter != files.end();
iter++) {
- char* buff = 0;
try {
- ifstream is(iter->c_str());
- is.seekg(0, ios::end);
- streamoff length = is.tellg();
- is.seekg(0, ios::beg);
- if (length > 10 * 1024)
- throw String("file too large to be a certificate");
- buff = new char[length];
- is.read(buff, length);
- String cert(buff, length);
- if (cert.size())
+ String cert(File::open(*iter).read());
+ if (cert.size() && cert.size() < 10 * 1024)
authorized_certs.push_back(file_cert(*iter, cert));
- delete [] buff;
- } catch ( ... ) {
- delete [] buff;
- }
+ } catch ( ... ) {}
}
}
static void
next reply other threads:[~2006-10-23 21:13 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-10-23 21:13 kupcevic [this message]
-- strict thread matches above, loose matches on Subject: below --
2006-10-23 18:43 [Cluster-devel] conga/ricci common/Makefile common/XML.cpp com kupcevic
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20061023211329.14323.qmail@sourceware.org \
--to=kupcevic@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.