Use own code to parse rfc822 dates, strptime() cant do

This commit is contained in:
dockes
2006-09-15 16:50:44 +00:00
parent de7a312051
commit cfe1dd5d9f
5 changed files with 559 additions and 188 deletions
+171 -3
View File
@@ -1,5 +1,5 @@
#ifndef lint
static char rcsid[] = "@(#$Id: mimeparse.cpp,v 1.12 2006-09-06 09:14:43 dockes Exp $ (C) 2004 J.F.Dockes";
static char rcsid[] = "@(#$Id: mimeparse.cpp,v 1.13 2006-09-15 16:50:44 dockes Exp $ (C) 2004 J.F.Dockes";
#endif
/*
* This program is free software; you can redistribute it and/or modify
@@ -26,6 +26,7 @@ static char rcsid[] = "@(#$Id: mimeparse.cpp,v 1.12 2006-09-06 09:14:43 dockes E
#include <ctype.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include "mimeparse.h"
#include "base64.h"
@@ -578,8 +579,159 @@ bool rfc2047_decode(const std::string& in, std::string &out)
return true;
}
#define DEBUGDATE 1
#if DEBUGDATE
#define DATEDEB(X) fprintf X
#else
#define DATEDEB(X)
#endif
// Convert rfc822 date to unix time. A date string normally looks like:
// Mon, 3 Jul 2006 09:51:58 +0200
// But there are many common variations
//
time_t rfc2822DateToUxTime(const string& dt)
{
// Strip everything up to first comma if any, we don't need weekday,
// then break into tokens
list<string> toks;
string::size_type idx;
if ((idx = dt.find_first_of(",")) != string::npos) {
if (idx == dt.length() - 1) {
DATEDEB((stderr, "Bad rfc822 date format (short1): [%s]\n",
dt.c_str()));
return (time_t)-1;
}
string date = dt.substr(idx+1);
stringToTokens(date, toks, " \t:");
} else {
stringToTokens(dt, toks, " \t:");
}
#if DEBUGDATE
for (list<string>::iterator it = toks.begin(); it != toks.end(); it++) {
DATEDEB((stderr, "[%s] ", it->c_str()));
}
DATEDEB((stderr, "\n"));
#endif
if (toks.size() == 6) {
// Probably no timezone, sometimes happens
toks.push_back("+0000");
}
if (toks.size() < 7) {
DATEDEB((stderr, "Bad rfc822 date format (toks cnt): [%s]\n",
dt.c_str()));
return (time_t)-1;
}
struct tm tm;
memset(&tm, 0, sizeof(tm));
// Load struct tm with appropriate tokens, possibly converting
// when needed
list<string>::iterator it = toks.begin();
// Day of month: no conversion needed
tm.tm_mday = atoi(it->c_str());
it++;
// Month. Only Jan-Dec are legal. January, February do happen
// though. Convert to 0-11
if (*it == "Jan" || *it == "January") tm.tm_mon = 0; else if
(*it == "Feb" || *it == "February") tm.tm_mon = 1; else if
(*it == "Mar" || *it == "March") tm.tm_mon = 2; else if
(*it == "Apr" || *it == "April") tm.tm_mon = 3; else if
(*it == "May") tm.tm_mon = 4; else if
(*it == "Jun" || *it == "June") tm.tm_mon = 5; else if
(*it == "Jul" || *it == "July") tm.tm_mon = 6; else if
(*it == "Aug" || *it == "August") tm.tm_mon = 7; else if
(*it == "Sep" || *it == "September") tm.tm_mon = 8; else if
(*it == "Oct" || *it == "October") tm.tm_mon = 9; else if
(*it == "Nov" || *it == "November") tm.tm_mon = 10; else if
(*it == "Dec" || *it == "December") tm.tm_mon = 11; else {
DATEDEB((stderr, "Bad rfc822 date format (month): [%s]\n",
dt.c_str()));
return (time_t)-1;
}
it++;
// Year. Struct tm counts from 1900
tm.tm_year = atoi(it->c_str());
if (tm.tm_year > 1900)
tm.tm_year -= 1900;
it++;
// Hour minute second need no adjustments
tm.tm_hour = atoi(it->c_str()); it++;
tm.tm_min = atoi(it->c_str()); it++;
tm.tm_sec = atoi(it->c_str()); it++;
// Timezone is supposed to be either +-XYZT or a zone name
int zonesecs = 0;
if (it->length() < 1) {
DATEDEB((stderr, "Bad rfc822 date format (zlen): [%s]\n", dt.c_str()));
return (time_t)-1;
}
if (it->at(0) == '-' || it->at(0) == '+') {
// Note that +xy:zt (instead of +xyzt) sometimes happen, we
// may want to process it one day
if (it->length() < 5) {
DATEDEB((stderr, "Bad rfc822 date format (zlen1): [%s]\n",
dt.c_str()));
goto nozone;
}
zonesecs = 3600*((it->at(1)-'0') * 10 + it->at(2)-'0')+
(it->at(3)-'0')*10 + it->at(4)-'0';
zonesecs = it->at(0) == '+' ? -1 * zonesecs : zonesecs;
} else {
int hours;
if (*it == "A") hours= 1; else if (*it == "B") hours= 2;
else if (*it == "C") hours= 3; else if (*it == "D") hours= 4;
else if (*it == "E") hours= 5; else if (*it == "F") hours= 6;
else if (*it == "G") hours= 7; else if (*it == "H") hours= 8;
else if (*it == "I") hours= 9; else if (*it == "K") hours= 10;
else if (*it == "L") hours= 11; else if (*it == "M") hours= 12;
else if (*it == "N") hours= -1; else if (*it == "O") hours= -2;
else if (*it == "P") hours= -3; else if (*it == "Q") hours= -4;
else if (*it == "R") hours= -5; else if (*it == "S") hours= -6;
else if (*it == "T") hours= -7; else if (*it == "U") hours= -8;
else if (*it == "V") hours= -9; else if (*it == "W") hours= -10;
else if (*it == "X") hours= -11; else if (*it == "Y") hours= -12;
else if (*it == "Z") hours= 0; else if (*it == "UT") hours= 0;
else if (*it == "GMT") hours= 0; else if (*it == "EST") hours= 5;
else if (*it == "EDT") hours= 4; else if (*it == "CST") hours= 6;
else if (*it == "CDT") hours= 5; else if (*it == "MST") hours= 7;
else if (*it == "MDT") hours= 6; else if (*it == "PST") hours= 8;
else if (*it == "PDT") hours= 7;
// Non standard names
// Standard Time (or Irish Summer Time?) is actually +5.5
else if (*it == "CET") hours= -1; else if (*it == "JST") hours= -9;
else if (*it == "IST") hours= -5; else if (*it == "WET") hours= 0;
else if (*it == "MET") hours= -1;
else {
DATEDEB((stderr, "Bad rfc822 date format (zname): [%s]\n",
dt.c_str()));
// Forget tz
goto nozone;
}
zonesecs = 3600 * hours;
}
DATEDEB((stderr, "Tz: [%s] -> %d\n", it->c_str(), zonesecs));
nozone:
time_t tim = mktime(&tm);
tim += zonesecs;
DATEDEB((stderr, "Date: %s uxtime %ld \n", ctime(&tim), tim));
return tim;
}
#else
#include <time.h>
#include <string>
#include "mimeparse.h"
@@ -588,6 +740,7 @@ bool rfc2047_decode(const std::string& in, std::string &out)
using namespace std;
extern bool rfc2231_decode(const string& in, string& out, string& charset);
extern time_t rfc2822DateToUxTime(const string& date);
int
main(int argc, const char **argv)
@@ -641,7 +794,7 @@ main(int argc, const char **argv)
exit(1);
}
printf("Decoded: '%s'\n", out.c_str());
#elif 1
#elif 0
char line [1024];
string out;
bool res;
@@ -675,7 +828,22 @@ main(int argc, const char **argv)
exit(1);
}
printf("Decoded: [%s]\n", decoded.c_str());
#elif 1
{
time_t t;
const char *dates[] = {
" Wed, 13 Sep 2006 11:40:26 -0700 (PDT)",
" Mon, 3 Jul 2006 09:51:58 +0200",
" Wed, 13 Sep 2006 08:19:48 GMT-07:00",
" Wed, 13 Sep 2006 11:40:26 -0700 (PDT)",
" Sat, 23 Dec 89 19:27:12 EST",
" 13 Jan 90 08:23:29 GMT"};
for (unsigned int i = 0; i <sizeof(dates) / sizeof(char *); i++) {
t = rfc2822DateToUxTime(dates[i]);
}
}
#endif
}
+20 -6
View File
@@ -16,18 +16,24 @@
*/
#ifndef _MIME_H_INCLUDED_
#define _MIME_H_INCLUDED_
/* @(#$Id: mimeparse.h,v 1.7 2006-09-06 09:14:43 dockes Exp $ (C) 2004 J.F.Dockes */
/* @(#$Id: mimeparse.h,v 1.8 2006-09-15 16:50:44 dockes Exp $ (C) 2004 J.F.Dockes */
#include <time.h>
#include <string>
#include <map>
#include "base64.h"
#ifndef NO_NAMESPACES
using std::string;
#endif
/** A class to represent a MIME header value with parameters */
class MimeHeaderValue {
public:
std::string value;
std::map<std::string, std::string> params;
string value;
std::map<string, string> params;
};
/**
@@ -36,10 +42,10 @@ class MimeHeaderValue {
* @param in the input string should be like: value; pn1=pv1; pn2=pv2.
* Example: text/plain; charset="iso-8859-1"
*/
extern bool parseMimeHeaderValue(const std::string& in, MimeHeaderValue& psd);
extern bool parseMimeHeaderValue(const string& in, MimeHeaderValue& psd);
/** Quoted printable decoding. Doubles up as rfc2231 decoder, hence the esc */
extern bool qp_decode(const std::string& in, std::string &out,
extern bool qp_decode(const string& in, string &out,
char esc = '=');
/** Decode an Internet mail field value encoded according to rfc2047
@@ -53,6 +59,14 @@ extern bool qp_decode(const std::string& in, std::string &out,
* @param in input string, ascii with rfc2047 markup
* @return out output string encoded in utf-8
*/
extern bool rfc2047_decode(const std::string& in, std::string &out);
extern bool rfc2047_decode(const string& in, string &out);
/** Decode RFC2822 date to unix time (gmt secs from 1970
*
* @param dt date string (the part after Date: )
* @return unix time
*/
time_t rfc2822DateToUxTime(const string& dt);
#endif /* _MIME_H_INCLUDED_ */