NeoMutt  2019-12-07-60-g0cfa53
Teaching an old dog new tricks
DOXYGEN
from.c
Go to the documentation of this file.
1 
29 #include "config.h"
30 #include <ctype.h>
31 #include <stdbool.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <time.h>
35 #include "mutt/mutt.h"
36 #include "from.h"
37 
50 bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
51 {
52  struct tm tm;
53  int yr;
54 
55  if (path)
56  *path = '\0';
57 
58  if (!mutt_str_startswith(s, "From ", CASE_MATCH))
59  return false;
60 
61  s = mutt_str_next_word(s); /* skip over the From part. */
62  if (!*s)
63  return false;
64 
65  mutt_debug(LL_DEBUG5, "\nis_from(): parsing: %s\n", s);
66 
67  if (!mutt_date_is_day_name(s))
68  {
69  const char *p = NULL;
70  short q = 0;
71 
72  for (p = s; *p && (q || !IS_SPACE(*p)); p++)
73  {
74  if (*p == '\\')
75  {
76  if (*++p == '\0')
77  return false;
78  }
79  else if (*p == '"')
80  {
81  q = !q;
82  }
83  }
84 
85  if (q || !*p)
86  return false;
87 
88  /* pipermail archives have the return_path obscured such as "me at neomutt.org" */
89  size_t plen = mutt_str_startswith(p, " at ", CASE_IGNORE);
90  if (plen != 0)
91  {
92  p = strchr(p + plen, ' ');
93  if (!p)
94  {
96  "error parsing what appears to be a pipermail-style "
97  "obscured return_path: %s\n",
98  s);
99  return false;
100  }
101  }
102 
103  if (path)
104  {
105  size_t len = (size_t)(p - s);
106  if ((len + 1) > pathlen)
107  len = pathlen - 1;
108  memcpy(path, s, len);
109  path[len] = '\0';
110  mutt_debug(LL_DEBUG5, "got return path: %s\n", path);
111  }
112 
113  s = p + 1;
114  SKIPWS(s);
115  if (!*s)
116  return false;
117 
118  if (!mutt_date_is_day_name(s))
119  {
120  mutt_debug(LL_DEBUG1, " expected weekday, got: %s\n", s);
121  return false;
122  }
123  }
124 
125  s = mutt_str_next_word(s);
126  if (!*s)
127  return false;
128 
129  /* do a quick check to make sure that this isn't really the day of the week.
130  * this could happen when receiving mail from a local user whose login name
131  * is the same as a three-letter abbreviation of the day of the week. */
132  if (mutt_date_is_day_name(s))
133  {
134  s = mutt_str_next_word(s);
135  if (!*s)
136  return false;
137  }
138 
139  /* now we should be on the month. */
140  tm.tm_mon = mutt_date_check_month(s);
141  if (tm.tm_mon < 0)
142  return false;
143 
144  /* day */
145  s = mutt_str_next_word(s);
146  if (!*s)
147  return false;
148  if (sscanf(s, "%d", &tm.tm_mday) != 1)
149  return false;
150  if ((tm.tm_mday < 1) || (tm.tm_mday > 31))
151  return false;
152 
153  /* time */
154  s = mutt_str_next_word(s);
155  if (!*s)
156  return false;
157 
158  /* Accept either HH:MM or HH:MM:SS */
159  if (sscanf(s, "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 3)
160  ;
161  else if (sscanf(s, "%d:%d", &tm.tm_hour, &tm.tm_min) == 2)
162  tm.tm_sec = 0;
163  else
164  return false;
165 
166  if ((tm.tm_hour < 0) || (tm.tm_hour > 23) || (tm.tm_min < 0) ||
167  (tm.tm_min > 59) || (tm.tm_sec < 0) || (tm.tm_sec > 60))
168  {
169  return false;
170  }
171 
172  s = mutt_str_next_word(s);
173  if (!*s)
174  return false;
175 
176  /* timezone? */
177  if (isalpha((unsigned char) *s) || (*s == '+') || (*s == '-'))
178  {
179  s = mutt_str_next_word(s);
180  if (!*s)
181  return false;
182 
183  /* some places have two timezone fields after the time, e.g.
184  * From xxxx@yyyyyyy.fr Wed Aug 2 00:39:12 MET DST 1995
185  */
186  if (isalpha((unsigned char) *s))
187  {
188  s = mutt_str_next_word(s);
189  if (!*s)
190  return false;
191  }
192  }
193 
194  /* year */
195  if (sscanf(s, "%d", &yr) != 1)
196  return false;
197  if ((yr < 0) || (yr > 9999))
198  return false;
199  tm.tm_year = (yr > 1900) ? (yr - 1900) : ((yr < 70) ? (yr + 100) : yr);
200 
201  mutt_debug(LL_DEBUG3, "month=%d, day=%d, hr=%d, min=%d, sec=%d, yr=%d\n",
202  tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_year);
203 
204  tm.tm_isdst = -1;
205 
206  if (tp)
207  *tp = mutt_date_make_time(&tm, false);
208  return true;
209 }
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
Match case when comparing strings.
Definition: string2.h:67
#define SKIPWS(ch)
Definition: string2.h:47
Ignore case when comparing strings.
Definition: string2.h:68
size_t mutt_str_startswith(const char *str, const char *prefix, enum CaseSensitivity cs)
Check whether a string starts with a prefix.
Definition: string.c:168
Determine who the email is from.
#define IS_SPACE(ch)
Definition: string2.h:38
Log at debug level 1.
Definition: logging.h:40
int mutt_date_check_month(const char *s)
Is the string a valid month name.
Definition: date.c:398
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a &#39;From&#39; header line?
Definition: from.c:50
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
bool mutt_date_is_day_name(const char *s)
Is the string a valid day name.
Definition: date.c:437
Log at debug level 5.
Definition: logging.h:44
time_t mutt_date_make_time(struct tm *t, bool local)
Convert struct tm to time_t
Definition: date.c:225
Log at debug level 3.
Definition: logging.h:42
const char * mutt_str_next_word(const char *s)
Find the next word in a string.
Definition: string.c:916