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