summaryrefslogtreecommitdiffstats
path: root/libmail/imapacl.C
diff options
context:
space:
mode:
Diffstat (limited to 'libmail/imapacl.C')
-rw-r--r--libmail/imapacl.C925
1 files changed, 925 insertions, 0 deletions
diff --git a/libmail/imapacl.C b/libmail/imapacl.C
new file mode 100644
index 0000000..c257bab
--- /dev/null
+++ b/libmail/imapacl.C
@@ -0,0 +1,925 @@
+/*
+** Copyright 2004, Double Precision Inc.
+**
+** See COPYING for distribution information.
+*/
+#include "libmail_config.h"
+#include "addmessage.H"
+#include "imap.H"
+#include "misc.H"
+#include "imapacl.H"
+#include "imapfolder.H"
+#include "imapfolders.H"
+#include "imaphandler.H"
+#include "imaplisthandler.H"
+#include "maildir/maildiraclt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sstream>
+#include <algorithm>
+
+using namespace std;
+
+
+LIBMAIL_START
+
+/*
+** ACL1 MYRIGHTS response.
+*/
+
+class imapGetMyRights::parseMyRights : public imapHandlerStructured {
+
+ string folder;
+ string &rights;
+
+ string curfolder;
+ void (parseMyRights::*next_func)(imap &, Token);
+
+public:
+ parseMyRights(string folderArg, string &rightsArg);
+ ~parseMyRights();
+ void installed(imap &imapAccount);
+
+private:
+ const char *getName();
+ void timedOut(const char *);
+ void process(imap &imapAccount, Token t);
+
+ void getFolder(imap &imapAccount, Token t);
+ void getRights(imap &imapAccount, Token t);
+
+};
+
+
+/*
+** ACL2 LIST(MYRIGHTS) handler.
+*/
+
+class imapGetMyRights::listMyRights : private vector<imapFolder>,
+ public imapLIST {
+
+ string folderName;
+ string &rights;
+
+public:
+ listMyRights(string folderNameArg,
+ string &rightsArg);
+ ~listMyRights();
+
+ void processExtendedAttributes(imapFolder &);
+};
+
+LIBMAIL_END
+
+mail::imapGetMyRights::imapGetMyRights(string folderNameArg, string &rightsArg,
+ mail::callback &callbackArg)
+ : folderName(folderNameArg),
+ rights(rightsArg),
+ callback(callbackArg)
+{
+}
+
+mail::imapGetMyRights::~imapGetMyRights()
+{
+}
+
+void mail::imapGetMyRights::installed(imap &imapAccount)
+{
+ if (imapAccount.getCapability("ACL") == "ACL")
+ {
+ /* ACL 1 */
+
+ imapAccount.imapcmd("MYRIGHTS",
+ "MYRIGHTS "
+ + imapAccount.quoteSimple(folderName)
+ + "\r\n");
+ }
+ else
+ {
+ imapAccount.imapcmd("MYRIGHTS",
+ "LIST (MYRIGHTS) \"\" "
+ + imapAccount.quoteSimple(folderName)
+ + "\r\n");
+ }
+}
+
+const char *mail::imapGetMyRights::getName()
+{
+ return "MYRIGHTS";
+}
+
+void mail::imapGetMyRights::timedOut(const char *errmsg)
+{
+ callbackTimedOut(callback, errmsg);
+}
+
+bool mail::imapGetMyRights::untaggedMessage(imap &imapAccount,
+ string msgname)
+{
+ if (msgname == "MYRIGHTS")
+ {
+ imapAccount
+ .installBackgroundTask(new parseMyRights(folderName,
+ rights));
+ return true;
+ }
+
+ if (msgname == "LIST")
+ {
+ imapAccount
+ .installBackgroundTask(new listMyRights(folderName,
+ rights));
+ return true;
+ }
+ return false;
+}
+
+bool mail::imapGetMyRights::taggedMessage(imap &imapAccount, string msgname,
+ string message,
+ bool okfail, string errmsg)
+{
+ if (!okfail)
+ {
+ callback.fail(errmsg);
+ imapAccount.uninstallHandler(this);
+ return true;
+ }
+ callback.success(message);
+ imapAccount.uninstallHandler(this);
+ return true;
+}
+
+/* ---- Parse ACL1 MYRIGHTS response ----- */
+
+mail::imapGetMyRights::parseMyRights::parseMyRights(string folderArg,
+ string &rightsArg)
+ : folder(folderArg), rights(rightsArg),
+ next_func(&mail::imapGetMyRights::parseMyRights::getFolder)
+{
+ rights="";
+}
+
+mail::imapGetMyRights::parseMyRights::~parseMyRights()
+{
+}
+
+void mail::imapGetMyRights::parseMyRights::installed(imap &imapAccount)
+{
+}
+
+const char *mail::imapGetMyRights::parseMyRights::getName()
+{
+ return "LISTMYRIGHTS_FOLDER";
+}
+
+void mail::imapGetMyRights::parseMyRights::timedOut(const char *dummy)
+{
+}
+
+void mail::imapGetMyRights::parseMyRights::process(imap &imapAccount, Token t)
+{
+ (this->*next_func)(imapAccount, t);
+}
+
+void mail::imapGetMyRights::parseMyRights::getFolder(imap &imapAcct, Token t)
+{
+ if (t != ATOM && t != STRING)
+ {
+ error(imapAcct);
+ return;
+ }
+ curfolder=t.text;
+ next_func=&mail::imapGetMyRights::parseMyRights::getRights;
+}
+
+void mail::imapGetMyRights::parseMyRights::getRights(imap &imapAcct, Token t)
+{
+ if (t == EOL)
+ {
+ done();
+ return;
+ }
+
+ if (t != ATOM && t != STRING)
+ {
+ error(imapAcct);
+ return;
+ }
+
+ string s=t.text;
+
+ size_t p=s.find(ACL_DELETE_SPECIAL[0]);
+
+ if (p != std::string::npos)
+ s.replace(s.begin()+p, s.begin()+p+1,
+ ACL_DELETEMSGS ACL_DELETEFOLDER ACL_EXPUNGE);
+ sort(s.begin(), s.end());
+
+ if (curfolder == folder)
+ rights=s;
+}
+
+/* ---- Parse ACL2 LIST(MYRIGHTS) response ----- */
+
+mail::imapGetMyRights::listMyRights::listMyRights(string folderNameArg,
+ string &rightsArg)
+ : imapLIST( *this, 0), folderName(folderNameArg),
+ rights(rightsArg)
+{
+}
+
+mail::imapGetMyRights::listMyRights::~listMyRights()
+{
+}
+
+void mail::imapGetMyRights::listMyRights
+::processExtendedAttributes(imapFolder &f)
+{
+ if (f.getPath() != folderName)
+ return; /* Sanity check */
+
+ vector<mail::imapparsefmt *>::iterator b=xattributes.children.begin(),
+ e=xattributes.children.end();
+
+ while (b != e)
+ {
+ mail::imapparsefmt *node= *b++;
+
+ if (node->children.size() < 2)
+ continue;
+
+ string name=(*node->children.begin())->value;
+
+ mail::upper(name);
+
+ if (name != "MYRIGHTS")
+ continue;
+
+ rights=node->children.begin()[1]->value;
+
+ size_t p=rights.find(ACL_DELETE_SPECIAL[0]);
+
+ if (p != std::string::npos)
+ rights.replace(rights.begin()+p, rights.begin()+p+1,
+ ACL_DELETEMSGS ACL_DELETEFOLDER ACL_EXPUNGE);
+
+ sort(rights.begin(), rights.end());
+ break;
+ }
+
+}
+
+/***** GET RIGHTS *****/
+
+LIBMAIL_START
+
+/*
+** ACL1 "ACL" response.
+*/
+
+class imapGetRights::parseGetRights : public imapHandlerStructured {
+
+ string folder;
+ list<pair<string,string> > &rights;
+
+ string curfolder;
+ string curidentifier;
+ void (parseGetRights::*next_func)(imap &, Token);
+
+public:
+ parseGetRights(string folderArg, list<pair<string,string> >&rightsArg);
+ ~parseGetRights();
+ void installed(imap &imapAccount);
+
+private:
+ const char *getName();
+ void timedOut(const char *);
+ void process(imap &imapAccount, Token t);
+
+ void getFolder(imap &imapAccount, Token t);
+ void getIdentifier(imap &imapAccount, Token t);
+ void getRights(imap &imapAccount, Token t);
+
+};
+
+LIBMAIL_END
+
+/*
+** ACL2 LIST(ACL) handler.
+*/
+
+class mail::imapGetRights::listGetRights : private vector<mail::imapFolder>,
+ public mail::imapLIST {
+
+ string folderName;
+ list< pair< string, string> > &rights;
+
+public:
+ listGetRights(string folderNameArg,
+ list<pair< string, string> > &rightsArg);
+ ~listGetRights();
+
+ void processExtendedAttributes(mail::imapFolder &);
+};
+
+mail::imapGetRights::imapGetRights(string folderNameArg,
+ list<pair<string,string> >&rightsArg,
+ mail::callback &callbackArg)
+ : folderName(folderNameArg), rights(rightsArg),
+ callback(callbackArg)
+{
+}
+
+mail::imapGetRights::~imapGetRights()
+{
+}
+
+void mail::imapGetRights::installed(imap &imapAccount)
+{
+ if (imapAccount.getCapability("ACL") == "ACL")
+ {
+ /* ACL 1 */
+
+ imapAccount.imapcmd("GETACL",
+ "GETACL "
+ + imapAccount.quoteSimple(folderName)
+ + "\r\n");
+ }
+ else
+ {
+ imapAccount.imapcmd("GETACL",
+ "LIST (ACL) \"\" "
+ + imapAccount.quoteSimple(folderName)
+ + "\r\n");
+ }
+
+}
+
+const char *mail::imapGetRights::getName()
+{
+ return "GETACL";
+}
+
+void mail::imapGetRights::timedOut(const char *errmsg)
+{
+ callbackTimedOut(callback, errmsg);
+}
+
+bool mail::imapGetRights::untaggedMessage(imap &imapAccount, string msgname)
+{
+ if (msgname == "LIST")
+ {
+ imapAccount
+ .installBackgroundTask(new listGetRights(folderName,
+ rights));
+ return true;
+ }
+
+ if (msgname == "ACL")
+ {
+ imapAccount
+ .installBackgroundTask(new parseGetRights(folderName,
+ rights));
+ return true;
+ }
+ return false;
+}
+
+bool mail::imapGetRights::taggedMessage(imap &imapAccount, string msgname,
+ string message,
+ bool okfail, string errmsg)
+{
+ if (!okfail)
+ {
+ callback.fail(errmsg);
+ imapAccount.uninstallHandler(this);
+ return true;
+ }
+ callback.success(message);
+ imapAccount.uninstallHandler(this);
+ return true;
+}
+
+/* --- Process ACL1 "ACL" response --- */
+
+mail::imapGetRights::parseGetRights::parseGetRights(string folderArg,
+ list<pair<string,string> >
+ &rightsArg)
+ : folder(folderArg), rights(rightsArg),
+ next_func(&mail::imapGetRights::parseGetRights::getFolder)
+{
+ rights.clear();
+}
+
+mail::imapGetRights::parseGetRights::~parseGetRights()
+{
+}
+
+void mail::imapGetRights::parseGetRights::installed(imap &imapAccount)
+{
+}
+
+const char *mail::imapGetRights::parseGetRights::getName()
+{
+ return "GETACL_FOLDER";
+}
+
+void mail::imapGetRights::parseGetRights::timedOut(const char *dummy)
+{
+}
+
+void mail::imapGetRights::parseGetRights::process(imap &imapAccount, Token t)
+{
+ (this->*next_func)(imapAccount, t);
+}
+
+void mail::imapGetRights::parseGetRights::getFolder(imap &imapAcct, Token t)
+{
+ if (t != ATOM && t != STRING)
+ {
+ error(imapAcct);
+ return;
+ }
+ curfolder=t.text;
+ next_func=&mail::imapGetRights::parseGetRights::getIdentifier;
+}
+
+void mail::imapGetRights::parseGetRights::getIdentifier(imap &imapAcct,Token t)
+{
+ if (t == EOL)
+ {
+ done();
+ return;
+ }
+
+ if (t != ATOM && t != STRING)
+ {
+ error(imapAcct);
+ return;
+ }
+
+ curidentifier=t.text;
+
+ /* Fudge ACL 1 identifiers into ACL 2 identifiers */
+
+ if (curidentifier != "anyone" &&
+ curidentifier != "anonymous" &&
+ curidentifier != "authuser" &&
+ curidentifier != "owner" &&
+ curidentifier != "administrators")
+ curidentifier="user=" + curidentifier;
+
+ next_func=&mail::imapGetRights::parseGetRights::getRights;
+}
+
+void mail::imapGetRights::parseGetRights::getRights(imap &imapAcct, Token t)
+{
+ if (t == EOL)
+ {
+ done();
+ return;
+ }
+
+ if (t != ATOM && t != STRING)
+ {
+ error(imapAcct);
+ return;
+ }
+
+ string s=t.text;
+
+ size_t p=s.find(ACL_DELETE_SPECIAL[0]);
+
+ if (p != std::string::npos)
+ s.replace(s.begin()+p, s.begin()+p+1,
+ ACL_DELETEMSGS ACL_DELETEFOLDER ACL_EXPUNGE);
+ sort(s.begin(), s.end());
+
+ if (curfolder == folder)
+ rights.push_back(make_pair(curidentifier, s));
+ next_func=&mail::imapGetRights::parseGetRights::getIdentifier;
+}
+
+/* --- Process ACL2 LIST(ACL) response --- */
+
+mail::imapGetRights
+::listGetRights::listGetRights(string folderNameArg,
+ list<pair<string,string> > &rightsArg)
+ : imapLIST(*this, 0), folderName(folderNameArg),
+ rights(rightsArg)
+{
+}
+
+mail::imapGetRights::listGetRights::~listGetRights()
+{
+}
+
+void mail::imapGetRights::listGetRights
+::processExtendedAttributes(imapFolder &f)
+{
+ if (f.getPath() != folderName)
+ return; /* Sanity check */
+
+ vector<mail::imapparsefmt *>::iterator b=xattributes.children.begin(),
+ e=xattributes.children.end();
+
+ while (b != e)
+ {
+ mail::imapparsefmt *node= *b++;
+
+ if (node->children.size() < 2)
+ continue;
+
+ string name=(*node->children.begin())->value;
+
+ mail::upper(name);
+
+ if (name != "ACL")
+ continue;
+
+ mail::imapparsefmt *rights_list=node->children.begin()[1];
+
+ vector<mail::imapparsefmt *>::iterator
+ bb=rights_list->children.begin(),
+ ee=rights_list->children.end();
+
+ while (bb != ee)
+ {
+ mail::imapparsefmt *node= *bb++;
+
+ if (node->children.size() < 2)
+ continue;
+
+ string identifier=node->children.begin()[0]->value;
+ string r=node->children.begin()[1]->value;
+
+ size_t p=r.find(ACL_DELETE_SPECIAL[0]);
+
+ if (p != std::string::npos)
+ r.replace(r.begin()+p, r.begin()+p+1,
+ ACL_DELETEMSGS ACL_DELETEFOLDER
+ ACL_EXPUNGE);
+
+ sort(r.begin(), r.end());
+ rights.push_back(make_pair(identifier, r));
+ }
+ break;
+ }
+}
+
+/* --- DELETE or SET ACL rights */
+
+
+LIBMAIL_START
+
+/* ACL2 RIGHTS-INFO and ACLFAILED untagged messages */
+
+class imapSetRights::parseRightsInfo : public imapHandlerStructured {
+
+ string &folderName;
+ string &identifier;
+ vector<string> &rights;
+
+ void (parseRightsInfo::*next_func)(imap &, Token);
+
+public:
+ parseRightsInfo(string &folderNameArg,
+ string &identifierArg,
+ vector<string> &rightsArg);
+ ~parseRightsInfo();
+
+ void installed(imap &imapAccount);
+ const char *getName();
+ void timedOut(const char *);
+ void process(imap &imapAccount, Token t);
+
+ void get_folder(imap &imapAccount, Token t);
+ void get_identifier(imap &imapAccount, Token t);
+ void get_rights(imap &imapAccount, Token t);
+
+};
+
+class imapSetRights::parseAclFailed : public imapHandlerStructured {
+
+ string &folderName, &errmsg;
+ bool wantErrorMessage;
+ void (parseAclFailed::*next_func)(imap &, Token);
+public:
+ parseAclFailed(string &folderNameArg, string &errmsgArg);
+ ~parseAclFailed();
+
+ void installed(imap &imapAccount);
+ const char *getName();
+ void timedOut(const char *);
+ void process(imap &imapAccount, Token t);
+
+private:
+ int process(imap &imapAccount, std::string &buffer);
+};
+LIBMAIL_END
+
+mail::imapSetRights::imapSetRights(string folderNameArg,
+ string identifierArg,
+ string rightsArg,
+ bool delIdentifierArg,
+ string &errorIdentifierArg,
+ vector<string> &errorRightsArg,
+ mail::callback &callbackArg)
+ : folderName(folderNameArg),
+ identifier(identifierArg),
+ rights(rightsArg),
+ delIdentifier(delIdentifierArg),
+ errorIdentifier(errorIdentifierArg),
+ errorRights(errorRightsArg),
+ callback(callbackArg)
+{
+}
+
+mail::imapSetRights::~imapSetRights()
+{
+}
+
+void mail::imapSetRights::installed(imap &imapAccount)
+{
+ if (imapAccount.getCapability("ACL") == "ACL")
+ {
+ /* ACL 1 */
+
+ string n=identifier;
+
+ if (n != "anyone" &&
+ n != "anonymous" &&
+ n != "authuser" &&
+ n != "owner" &&
+ n != "administrators")
+ {
+ if (strncmp(n.c_str(), "user=", 5))
+ {
+ callback.fail("Invalid ACL identifier.");
+ imapAccount.uninstallHandler(this);
+ return;
+ }
+
+ n=n.substr(5);
+ }
+
+ // If all three "etx" were originally set, it would've been
+ // replaced with a "d". If we still have one or more of them
+ // it means that not all three were specified.
+
+ if (rights.find(ACL_DELETEMSGS[0]) != std::string::npos ||
+ rights.find(ACL_DELETEFOLDER[0]) != std::string::npos ||
+ rights.find(ACL_EXPUNGE[0]) != std::string::npos)
+ {
+ errorIdentifier=identifier;
+ errorRights.push_back("");
+ errorRights.push_back(ACL_DELETEMSGS
+ ACL_EXPUNGE
+ ACL_DELETEFOLDER);
+ errorRights.push_back(ACL_LOOKUP);
+ errorRights.push_back(ACL_READ);
+ errorRights.push_back(ACL_SEEN);
+ errorRights.push_back(ACL_WRITE);
+ errorRights.push_back(ACL_INSERT);
+ errorRights.push_back(ACL_POST);
+ errorRights.push_back(ACL_CREATE);
+ errorRights.push_back(ACL_ADMINISTER);
+ callback.fail("Invalid access right combination.");
+ imapAccount.uninstallHandler(this);
+ return;
+ }
+
+ imapAccount.imapcmd("SETACL",
+ delIdentifier ?
+ "DELETEACL "
+ + imapAccount.quoteSimple(folderName)
+ + " "
+ + imapAccount.quoteSimple(n)
+ + "\r\n"
+ :
+ "SETACL "
+ + imapAccount.quoteSimple(folderName)
+ + " "
+ + imapAccount.quoteSimple(n)
+ + " "
+ + imapAccount.quoteSimple(rights)
+ + "\r\n");
+ }
+ else
+ {
+ imapAccount.imapcmd("SETACL",
+ delIdentifier ?
+ "ACL DELETE "
+ + imapAccount.quoteSimple(folderName)
+ + " "
+ + imapAccount.quoteSimple(identifier)
+ + "\r\n"
+ :
+ "ACL STORE "
+ + imapAccount.quoteSimple(folderName)
+ + " "
+ + imapAccount.quoteSimple(identifier)
+ + " "
+ + imapAccount.quoteSimple(rights)
+ + "\r\n");
+ }
+}
+
+const char *mail::imapSetRights::getName()
+{
+ return "SETACL";
+}
+
+void mail::imapSetRights::timedOut(const char *errmsg)
+{
+ callbackTimedOut(callback, errmsg);
+}
+
+bool mail::imapSetRights::untaggedMessage(imap &imapAccount, string msgname)
+{
+ if (msgname == "LIST")
+ {
+ dummy_rights.clear();
+ imapAccount
+ .installBackgroundTask(new imapGetRights
+ ::listGetRights(folderName,
+ dummy_rights));
+ return true;
+ }
+
+ if (msgname == "ACLFAILED")
+ {
+ imapAccount
+ .installBackgroundTask(new parseAclFailed(dummy,
+ dummy));
+ return true;
+ }
+
+ if (msgname == "RIGHTS-INFO")
+ {
+ imapAccount
+ .installBackgroundTask(new
+ parseRightsInfo(dummy,
+ errorIdentifier,
+ errorRights));
+ return true;
+ }
+
+ return false;
+}
+
+bool mail::imapSetRights::taggedMessage(imap &imapAccount, string msgname,
+ string message,
+ bool okfail, string errmsg)
+{
+ if (okfail && errorIdentifier.size() > 0)
+ {
+ okfail=false;
+ message="Invalid access rights.";
+ }
+
+ if (!okfail)
+ {
+ callback.fail(errmsg);
+ imapAccount.uninstallHandler(this);
+ return true;
+ }
+ callback.success(message);
+ imapAccount.uninstallHandler(this);
+ return true;
+}
+
+
+mail::imapSetRights::parseRightsInfo::parseRightsInfo(string &folderNameArg,
+ string &identifierArg,
+ vector<string> &rightsArg)
+ : folderName(folderNameArg),
+ identifier(identifierArg),
+ rights(rightsArg), next_func(&mail::imapSetRights::parseRightsInfo
+ ::get_folder)
+{
+}
+
+mail::imapSetRights::parseRightsInfo::~parseRightsInfo()
+{
+}
+
+void mail::imapSetRights::parseRightsInfo::installed(imap &imapAccount)
+{
+}
+
+const char *mail::imapSetRights::parseRightsInfo::getName()
+{
+ return "RIGHTS-INFO";
+}
+
+void mail::imapSetRights::parseRightsInfo::timedOut(const char *dummy)
+{
+}
+
+void mail::imapSetRights::parseRightsInfo::process(imap &imapAccount, Token t)
+{
+ (this->*next_func)(imapAccount, t);
+}
+
+void mail::imapSetRights::parseRightsInfo::get_folder(imap &imapAccount,
+ Token t)
+{
+ if (t != ATOM && t != STRING)
+ {
+ error(imapAccount);
+ return;
+ }
+ folderName=t.text;
+ next_func= &mail::imapSetRights::parseRightsInfo::get_identifier;
+}
+
+void mail::imapSetRights::parseRightsInfo::get_identifier(imap &imapAccount,
+ Token t)
+{
+ if (t != ATOM && t != STRING)
+ {
+ error(imapAccount);
+ return;
+ }
+ identifier=t.text;
+ next_func= &mail::imapSetRights::parseRightsInfo::get_rights;
+}
+
+void mail::imapSetRights::parseRightsInfo::get_rights(imap &imapAccount,
+ Token t)
+{
+ if (t == EOL)
+ {
+ done();
+ return;
+ }
+ if (t != ATOM && t != STRING)
+ {
+ error(imapAccount);
+ return;
+ }
+ rights.push_back(t.text);
+}
+
+mail::imapSetRights::parseAclFailed::parseAclFailed(string &folderNameArg,
+ string &errmsgArg)
+ : folderName(folderNameArg),
+ errmsg(errmsgArg),
+ wantErrorMessage(false)
+{
+}
+
+mail::imapSetRights::parseAclFailed::~parseAclFailed()
+{
+}
+
+void mail::imapSetRights::parseAclFailed::installed(imap &imapAccount)
+{
+}
+
+const char *mail::imapSetRights::parseAclFailed::getName()
+{
+ return "ACLFAILED";
+}
+
+void mail::imapSetRights::parseAclFailed::timedOut(const char *dummy)
+{
+}
+
+void mail::imapSetRights::parseAclFailed::process(imap &imapAccount,
+ Token t)
+{
+ if (t != ATOM && t != STRING)
+ {
+ error(imapAccount);
+ return;
+ }
+
+ folderName=t.text;
+ wantErrorMessage=true;
+}
+
+int mail::imapSetRights::parseAclFailed::process(imap &imapAccount,
+ string &buffer)
+{
+ if (!wantErrorMessage)
+ return imapHandlerStructured::process(imapAccount, buffer);
+
+ size_t p=buffer.find('\n');
+
+ if (p == std::string::npos)
+ return 0;
+
+ string msg=buffer.substr(0, p);
+
+ size_t cr=msg.find('\r');
+ if (cr != std::string::npos)
+ msg=msg.substr(0, cr);
+ errmsg=msg;
+ done();
+ return (int)p;
+}