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 |
douglas |
744 |
#include <sys/types.h> |
10 |
douglas |
743 |
#include <regex.h> |
11 |
douglas |
742 |
#include <setjmp.h> |
12 |
|
|
#include <stdio.h> |
13 |
douglas |
743 |
#include <sys/stat.h> |
14 |
douglas |
742 |
#include <time.h> |
15 |
|
|
|
16 |
douglas |
740 |
#include <c-client/c-client.h> |
17 |
|
|
|
18 |
douglas |
743 |
static jmp_buf environment; |
19 |
douglas |
742 |
static char *error = NULL; |
20 |
|
|
|
21 |
douglas |
743 |
static int check(int value, jmp_buf environment) |
22 |
douglas |
742 |
{ |
23 |
douglas |
743 |
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 |
douglas |
742 |
if (value == NIL) |
50 |
|
|
longjmp(environment, 2); |
51 |
|
|
|
52 |
|
|
return value; |
53 |
|
|
} |
54 |
|
|
|
55 |
douglas |
743 |
static int regcheck(int value, const regex_t *regex, jmp_buf environment) |
56 |
douglas |
742 |
{ |
57 |
douglas |
743 |
if (value && value != REG_NOMATCH) |
58 |
|
|
{ |
59 |
|
|
char exception[regerror(value, regex, NULL, 0)]; |
60 |
douglas |
742 |
|
61 |
douglas |
743 |
regerror(value, regex, exception, sizeof (exception)); |
62 |
|
|
longjmp(environment, (int)exception); |
63 |
|
|
} |
64 |
douglas |
742 |
|
65 |
douglas |
743 |
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 |
douglas |
742 |
--date.tm_mon; |
78 |
|
|
|
79 |
|
|
timegm(&date); |
80 |
|
|
|
81 |
douglas |
743 |
# 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 |
douglas |
742 |
} |
130 |
|
|
|
131 |
douglas |
740 |
int main(int argc, char *argv[]) |
132 |
|
|
{ |
133 |
douglas |
742 |
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 |
douglas |
743 |
umask(0077); |
155 |
douglas |
740 |
|
156 |
douglas |
743 |
struct tm today, next = { .tm_mday = 1, .tm_year = 70 }; |
157 |
douglas |
742 |
|
158 |
douglas |
743 |
{ |
159 |
|
|
time_t now = time(NULL); |
160 |
douglas |
742 |
|
161 |
douglas |
743 |
gmtime_r(&now, &today); |
162 |
douglas |
742 |
|
163 |
douglas |
743 |
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 |
douglas |
740 |
return 0; |
214 |
|
|
} |
215 |
|
|
|
216 |
|
|
void mm_flags(MAILSTREAM *stream, unsigned long number) |
217 |
|
|
{ |
218 |
douglas |
742 |
printf("flags:\n number = %lu\n", number); |
219 |
douglas |
740 |
} |
220 |
|
|
|
221 |
|
|
void mm_status(MAILSTREAM *stream, char *mailbox, MAILSTATUS *status) |
222 |
|
|
{ |
223 |
douglas |
742 |
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 |
douglas |
740 |
} |
240 |
|
|
|
241 |
|
|
void mm_searched(MAILSTREAM *stream, unsigned long number) |
242 |
|
|
{ |
243 |
douglas |
742 |
printf("searched:\n number = %lu\n", number); |
244 |
douglas |
740 |
} |
245 |
|
|
|
246 |
|
|
void mm_exists(MAILSTREAM *stream, unsigned long number) |
247 |
|
|
{ |
248 |
douglas |
742 |
printf("exists:\n number = %lu\n", number); |
249 |
douglas |
740 |
} |
250 |
|
|
|
251 |
|
|
void mm_expunged(MAILSTREAM *stream, unsigned long number) |
252 |
|
|
{ |
253 |
douglas |
742 |
printf("expunged:\n number = %lu\n", number); |
254 |
douglas |
740 |
} |
255 |
|
|
|
256 |
|
|
void mm_list(MAILSTREAM *stream, int delim, char *name, long attrib) |
257 |
|
|
{ |
258 |
douglas |
742 |
printf("list"); |
259 |
douglas |
740 |
} |
260 |
|
|
|
261 |
|
|
void mm_lsub(MAILSTREAM *stream, int delim, char *name, long attrib) |
262 |
|
|
{ |
263 |
douglas |
742 |
printf("lsub"); |
264 |
douglas |
740 |
} |
265 |
|
|
|
266 |
|
|
void mm_notify(MAILSTREAM *stream, char *string, long errflg) |
267 |
|
|
{ |
268 |
douglas |
742 |
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 |
douglas |
740 |
} |
274 |
|
|
|
275 |
|
|
void mm_log(char *string, long errflg) |
276 |
|
|
{ |
277 |
douglas |
743 |
/* char *flag; |
278 |
douglas |
742 |
|
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 |
douglas |
743 |
free(flag);*/ |
282 |
douglas |
742 |
|
283 |
|
|
if (errflg == ERROR) |
284 |
|
|
asprintf(&error, "%s", string); |
285 |
douglas |
740 |
} |
286 |
|
|
|
287 |
|
|
void mm_dlog(char *string) |
288 |
|
|
{ |
289 |
douglas |
743 |
printf("%s\n", string); |
290 |
douglas |
740 |
} |
291 |
|
|
|
292 |
|
|
void mm_login(NETMBX *mb, char *user, char *pwd, long trial) |
293 |
|
|
{ |
294 |
douglas |
742 |
strcpy(user, "douglas"); |
295 |
douglas |
743 |
|
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 |
douglas |
740 |
} |
312 |
|
|
|
313 |
|
|
void mm_critical(MAILSTREAM *stream) |
314 |
|
|
{ |
315 |
douglas |
742 |
printf("critical\n"); |
316 |
douglas |
740 |
} |
317 |
|
|
|
318 |
|
|
void mm_nocritical(MAILSTREAM *stream) |
319 |
|
|
{ |
320 |
douglas |
742 |
printf("nocritical\n"); |
321 |
douglas |
740 |
} |
322 |
|
|
|
323 |
|
|
long mm_diskerror(MAILSTREAM *stream, long errcode, long serious) |
324 |
|
|
{ |
325 |
douglas |
742 |
printf("diskerror:\n errcode = %li\n serious = %li\n", errcode, serious); |
326 |
|
|
|
327 |
douglas |
740 |
return 1; |
328 |
|
|
} |
329 |
|
|
|
330 |
|
|
void mm_fatal(char *string) |
331 |
|
|
{ |
332 |
douglas |
742 |
printf("fatal:\n string = %s\n", string); |
333 |
douglas |
740 |
} |