1 |
douglas |
740 |
// Credit Card Reminder |
2 |
|
|
// |
3 |
|
|
// Douglas Thrift |
4 |
|
|
// |
5 |
|
|
// $Id$ |
6 |
|
|
|
7 |
douglas |
743 |
#include <assert.h> |
8 |
|
|
#include <errno.h> |
9 |
|
|
#include <regex.h> |
10 |
douglas |
742 |
#include <setjmp.h> |
11 |
|
|
#include <stdio.h> |
12 |
douglas |
743 |
#include <sys/stat.h> |
13 |
douglas |
742 |
#include <time.h> |
14 |
|
|
|
15 |
douglas |
740 |
#include <c-client/c-client.h> |
16 |
|
|
|
17 |
douglas |
743 |
static jmp_buf environment; |
18 |
douglas |
742 |
static char *error = NULL; |
19 |
|
|
|
20 |
douglas |
743 |
static int check(int value, jmp_buf environment) |
21 |
douglas |
742 |
{ |
22 |
douglas |
743 |
if (value == -1) |
23 |
|
|
longjmp(environment, 1); |
24 |
|
|
|
25 |
|
|
return value; |
26 |
|
|
} |
27 |
|
|
|
28 |
|
|
static char *fcheck(char *value, FILE *stream, jmp_buf environment) |
29 |
|
|
{ |
30 |
|
|
if (!value) |
31 |
|
|
{ |
32 |
|
|
if (ferror(stream)) |
33 |
|
|
longjmp(environment, 1); |
34 |
|
|
else |
35 |
|
|
{ |
36 |
|
|
clearerr(stdin); |
37 |
|
|
printf("\n"); |
38 |
|
|
|
39 |
|
|
return ""; |
40 |
|
|
} |
41 |
|
|
} |
42 |
|
|
|
43 |
|
|
return value; |
44 |
|
|
} |
45 |
|
|
|
46 |
|
|
static void *mcheck(void *value, jmp_buf environment) |
47 |
|
|
{ |
48 |
douglas |
742 |
if (value == NIL) |
49 |
|
|
longjmp(environment, 2); |
50 |
|
|
|
51 |
|
|
return value; |
52 |
|
|
} |
53 |
|
|
|
54 |
douglas |
743 |
static int regcheck(int value, const regex_t *regex, jmp_buf environment) |
55 |
douglas |
742 |
{ |
56 |
douglas |
743 |
if (value && value != REG_NOMATCH) |
57 |
|
|
{ |
58 |
|
|
char exception[regerror(value, regex, NULL, 0)]; |
59 |
douglas |
742 |
|
60 |
douglas |
743 |
regerror(value, regex, exception, sizeof (exception)); |
61 |
|
|
longjmp(environment, (int)exception); |
62 |
|
|
} |
63 |
douglas |
742 |
|
64 |
douglas |
743 |
return value; |
65 |
|
|
} |
66 |
|
|
|
67 |
|
|
inline static unsigned short mdate(const struct tm *date) |
68 |
|
|
{ |
69 |
|
|
return ((date->tm_year + 1900 - BASEYEAR) << 9) + ((date->tm_mon + 1) << 5) + date->tm_mday; |
70 |
|
|
} |
71 |
|
|
|
72 |
|
|
static void getnext(const struct tm *today, jmp_buf environment) |
73 |
|
|
{ |
74 |
|
|
struct tm date = *today; |
75 |
|
|
|
76 |
douglas |
742 |
--date.tm_mon; |
77 |
|
|
|
78 |
|
|
timegm(&date); |
79 |
|
|
|
80 |
douglas |
743 |
# include <c-client/linkage.c> |
81 |
|
|
|
82 |
|
|
MAILSTREAM *stream = mcheck(mail_open(NIL, "{reptile.douglasthrift.net/novalidate-cert/tls}", OP_READONLY | OP_DEBUG), environment); |
83 |
|
|
STRINGLIST from = { .text = { .data = (unsigned char *)"wellsfargo.com", .size = 14 } }, body = { .text = { .data = (unsigned char *)" is due on ", .size = 11 } }; |
84 |
|
|
SEARCHPGM search = { .from = &from, .body = &body, .since = mdate(&date) }; |
85 |
|
|
SORTPGM sort = { .reverse = 1, .function = SORTARRIVAL }; |
86 |
|
|
unsigned long *messages = mcheck(mail_sort(stream, "ISO-8859-1", &search, &sort, SE_UID), environment); |
87 |
|
|
regex_t due; |
88 |
|
|
|
89 |
|
|
regcheck(regcomp(&due, "^.* is due on ([01][0-9]/[0-3][0-9]/[0-9]{4}).*$", REG_EXTENDED), &due, environment); |
90 |
|
|
|
91 |
|
|
for (unsigned long *message = messages; *message != 0; ++message) |
92 |
|
|
{ |
93 |
|
|
unsigned long size; |
94 |
|
|
char *body = mcheck(mail_fetchbody_full(stream, *message, "1", &size, FT_UID | FT_PEEK), environment); |
95 |
|
|
regmatch_t match[2] = { { .rm_eo = size } }; |
96 |
|
|
|
97 |
|
|
if (!regcheck(regexec(&due, body, 2, match, REG_STARTEND), &due, environment)) |
98 |
|
|
{ |
99 |
|
|
char date[match[1].rm_eo - match[1].rm_so + 1]; |
100 |
|
|
|
101 |
|
|
strlcpy(date, body + match[1].rm_so, sizeof (date)); |
102 |
|
|
assert(sizeof (date) == 11); |
103 |
|
|
|
104 |
|
|
struct tm due; |
105 |
|
|
|
106 |
|
|
strptime(date, "%m/%d/%Y", &due); |
107 |
|
|
strftime(date, sizeof (date), "%F", &due); |
108 |
|
|
|
109 |
|
|
char *path; |
110 |
|
|
|
111 |
|
|
check(asprintf(&path, "%s/.CreditCardReminder.data", getenv("HOME")), environment); |
112 |
|
|
|
113 |
|
|
FILE *file = fopen(path, "w"); |
114 |
|
|
|
115 |
|
|
if (!file) |
116 |
|
|
longjmp(environment, 1); |
117 |
|
|
|
118 |
|
|
free(path); |
119 |
|
|
check(fprintf(file, "%s\n", date), environment); |
120 |
|
|
check(fclose(file), environment); |
121 |
|
|
|
122 |
|
|
break; |
123 |
|
|
} |
124 |
|
|
} |
125 |
|
|
|
126 |
|
|
fs_give((void *)&messages); |
127 |
|
|
mail_close(stream); |
128 |
douglas |
742 |
} |
129 |
|
|
|
130 |
douglas |
740 |
int main(int argc, char *argv[]) |
131 |
|
|
{ |
132 |
douglas |
742 |
int exception; |
133 |
|
|
|
134 |
|
|
switch (exception = setjmp(environment)) |
135 |
|
|
{ |
136 |
|
|
case 0: |
137 |
|
|
break; |
138 |
|
|
case 1: |
139 |
|
|
perror(argv[0]); |
140 |
|
|
|
141 |
|
|
return 1; |
142 |
|
|
case 2: |
143 |
|
|
fprintf(stderr, "%s: %s\n", argv[0], error); |
144 |
|
|
free(error); |
145 |
|
|
|
146 |
|
|
return 1; |
147 |
|
|
default: |
148 |
|
|
fprintf(stderr, "%s: %s\n", argv[0], (char *)exception); |
149 |
|
|
|
150 |
|
|
return 1; |
151 |
|
|
} |
152 |
|
|
|
153 |
douglas |
743 |
umask(0077); |
154 |
douglas |
740 |
|
155 |
douglas |
743 |
struct tm today, next = { .tm_mday = 1, .tm_year = 70 }; |
156 |
douglas |
742 |
|
157 |
douglas |
743 |
{ |
158 |
|
|
time_t now = time(NULL); |
159 |
douglas |
742 |
|
160 |
douglas |
743 |
gmtime_r(&now, &today); |
161 |
douglas |
742 |
|
162 |
douglas |
743 |
today.tm_sec = 0; |
163 |
|
|
today.tm_min = 0; |
164 |
|
|
today.tm_hour = 0; |
165 |
|
|
} |
166 |
|
|
|
167 |
|
|
{ |
168 |
|
|
jmp_buf environment_; |
169 |
|
|
|
170 |
|
|
switch (exception = setjmp(environment_)) |
171 |
|
|
{ |
172 |
|
|
case 0: |
173 |
|
|
break; |
174 |
|
|
case 1: |
175 |
|
|
if (errno == ENOENT) |
176 |
|
|
{ |
177 |
|
|
getnext(&today, environment); |
178 |
|
|
|
179 |
|
|
break; |
180 |
|
|
} |
181 |
|
|
default: |
182 |
|
|
longjmp(environment, exception); |
183 |
|
|
} |
184 |
|
|
|
185 |
|
|
char *path; |
186 |
|
|
|
187 |
|
|
check(asprintf(&path, "%s/.CreditCardReminder.data", getenv("HOME")), environment); |
188 |
|
|
|
189 |
|
|
FILE *file = fopen(path, "r"); |
190 |
|
|
|
191 |
|
|
if (!file) |
192 |
|
|
longjmp(environment_, 1); |
193 |
|
|
|
194 |
|
|
free(path); |
195 |
|
|
|
196 |
|
|
size_t size; |
197 |
|
|
char *line = fcheck(fgetln(file, &size), file, environment); |
198 |
|
|
|
199 |
|
|
line[--size] = '\0'; |
200 |
|
|
|
201 |
|
|
if (!strptime(line, "%F", &next)) |
202 |
|
|
longjmp(environment, 1); |
203 |
|
|
|
204 |
|
|
check(fclose(file), environment); |
205 |
|
|
} |
206 |
|
|
|
207 |
|
|
char date[11]; |
208 |
|
|
|
209 |
|
|
strftime(date, sizeof (date), "%F", &next); |
210 |
|
|
fprintf(stderr, "%s\n", date); |
211 |
|
|
|
212 |
douglas |
740 |
return 0; |
213 |
|
|
} |
214 |
|
|
|
215 |
|
|
void mm_flags(MAILSTREAM *stream, unsigned long number) |
216 |
|
|
{ |
217 |
douglas |
742 |
printf("flags:\n number = %lu\n", number); |
218 |
douglas |
740 |
} |
219 |
|
|
|
220 |
|
|
void mm_status(MAILSTREAM *stream, char *mailbox, MAILSTATUS *status) |
221 |
|
|
{ |
222 |
douglas |
742 |
printf("mailbox %s:\n", mailbox); |
223 |
|
|
|
224 |
|
|
if (status->flags & SA_MESSAGES) |
225 |
|
|
printf(" messages = %lu\n", status->messages); |
226 |
|
|
|
227 |
|
|
if (status->flags & SA_RECENT) |
228 |
|
|
printf(" recent = %lu\n", status->recent); |
229 |
|
|
|
230 |
|
|
if (status->flags & SA_UNSEEN) |
231 |
|
|
printf(" unseen = %lu\n", status->unseen); |
232 |
|
|
|
233 |
|
|
if (status->flags & SA_UIDNEXT) |
234 |
|
|
printf(" uidnext = %lu\n", status->uidnext); |
235 |
|
|
|
236 |
|
|
if (status->flags & SA_UIDVALIDITY) |
237 |
|
|
printf(" uidvalidity = %lu\n", status->uidvalidity); |
238 |
douglas |
740 |
} |
239 |
|
|
|
240 |
|
|
void mm_searched(MAILSTREAM *stream, unsigned long number) |
241 |
|
|
{ |
242 |
douglas |
742 |
printf("searched:\n number = %lu\n", number); |
243 |
douglas |
740 |
} |
244 |
|
|
|
245 |
|
|
void mm_exists(MAILSTREAM *stream, unsigned long number) |
246 |
|
|
{ |
247 |
douglas |
742 |
printf("exists:\n number = %lu\n", number); |
248 |
douglas |
740 |
} |
249 |
|
|
|
250 |
|
|
void mm_expunged(MAILSTREAM *stream, unsigned long number) |
251 |
|
|
{ |
252 |
douglas |
742 |
printf("expunged:\n number = %lu\n", number); |
253 |
douglas |
740 |
} |
254 |
|
|
|
255 |
|
|
void mm_list(MAILSTREAM *stream, int delim, char *name, long attrib) |
256 |
|
|
{ |
257 |
douglas |
742 |
printf("list"); |
258 |
douglas |
740 |
} |
259 |
|
|
|
260 |
|
|
void mm_lsub(MAILSTREAM *stream, int delim, char *name, long attrib) |
261 |
|
|
{ |
262 |
douglas |
742 |
printf("lsub"); |
263 |
douglas |
740 |
} |
264 |
|
|
|
265 |
|
|
void mm_notify(MAILSTREAM *stream, char *string, long errflg) |
266 |
|
|
{ |
267 |
douglas |
742 |
char *flag; |
268 |
|
|
|
269 |
|
|
asprintf(&flag, "%li", errflg); |
270 |
|
|
printf("notify %s:\n string = %s\n", errflg == NIL ? "NIL" : errflg == WARN ? "WARN" : errflg == ERROR ? "ERROR" : flag, string); |
271 |
|
|
free(flag); |
272 |
douglas |
740 |
} |
273 |
|
|
|
274 |
|
|
void mm_log(char *string, long errflg) |
275 |
|
|
{ |
276 |
douglas |
743 |
/* char *flag; |
277 |
douglas |
742 |
|
278 |
|
|
asprintf(&flag, "%li", errflg); |
279 |
|
|
printf("log %s:\n string = %s\n", errflg == NIL ? "NIL" : errflg == PARSE ? "PARSE" : errflg == WARN ? "WARN" : errflg == ERROR ? "ERROR" : flag, string); |
280 |
douglas |
743 |
free(flag);*/ |
281 |
douglas |
742 |
|
282 |
|
|
if (errflg == ERROR) |
283 |
|
|
asprintf(&error, "%s", string); |
284 |
douglas |
740 |
} |
285 |
|
|
|
286 |
|
|
void mm_dlog(char *string) |
287 |
|
|
{ |
288 |
douglas |
743 |
printf("%s\n", string); |
289 |
douglas |
740 |
} |
290 |
|
|
|
291 |
|
|
void mm_login(NETMBX *mb, char *user, char *pwd, long trial) |
292 |
|
|
{ |
293 |
douglas |
742 |
strcpy(user, "douglas"); |
294 |
douglas |
743 |
|
295 |
|
|
char *path; |
296 |
|
|
|
297 |
|
|
check(asprintf(&path, "%s/.CreditCardReminder.pass", getenv("HOME")), environment); |
298 |
|
|
|
299 |
|
|
FILE *file = fopen(path, "r"); |
300 |
|
|
|
301 |
|
|
if (!file) |
302 |
|
|
longjmp(environment, 1); |
303 |
|
|
|
304 |
|
|
free(path); |
305 |
|
|
|
306 |
|
|
size_t size; |
307 |
|
|
char *line = fcheck(fgetln(file, &size), file, environment); |
308 |
|
|
|
309 |
|
|
strlcpy(pwd, line, size); |
310 |
douglas |
740 |
} |
311 |
|
|
|
312 |
|
|
void mm_critical(MAILSTREAM *stream) |
313 |
|
|
{ |
314 |
douglas |
742 |
printf("critical\n"); |
315 |
douglas |
740 |
} |
316 |
|
|
|
317 |
|
|
void mm_nocritical(MAILSTREAM *stream) |
318 |
|
|
{ |
319 |
douglas |
742 |
printf("nocritical\n"); |
320 |
douglas |
740 |
} |
321 |
|
|
|
322 |
|
|
long mm_diskerror(MAILSTREAM *stream, long errcode, long serious) |
323 |
|
|
{ |
324 |
douglas |
742 |
printf("diskerror:\n errcode = %li\n serious = %li\n", errcode, serious); |
325 |
|
|
|
326 |
douglas |
740 |
return 1; |
327 |
|
|
} |
328 |
|
|
|
329 |
|
|
void mm_fatal(char *string) |
330 |
|
|
{ |
331 |
douglas |
742 |
printf("fatal:\n string = %s\n", string); |
332 |
douglas |
740 |
} |