e2e3cfab43d1086fbe7b216d0183dc74fcc300c5
[monky] / src / mboxscan.c
1 /* Conky, a system monitor, based on torsmo
2  *
3  * Any original torsmo code is licensed under the BSD license
4  *
5  * All code written since the fork of torsmo is licensed under the GPL
6  *
7  * Please see COPYING for details
8  *
9  * Copyright (c) 2006 Marco Candrian <mac@calmar.ws>
10  * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
11  *      (see AUTHORS)
12  * All rights reserved.
13  *
14  * This program is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation, either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * You should have received a copy of the GNU General Public License
24  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
25  *
26  */
27
28 #include "conky.h"
29 #include "logging.h"
30 #include "mail.h"
31 #include <sys/stat.h>
32 #include <sys/time.h>
33 #include <errno.h>
34 #include "mboxscan.h"
35
36 #define FROM_WIDTH 10
37 #define SUBJECT_WIDTH 22
38 #define PRINT_MAILS 5
39 #define TIME_DELAY 5
40
41 struct ring_list {
42         char *from;
43         char *subject;
44         struct ring_list *previous;
45         struct ring_list *next;
46 };
47
48 static time_t last_ctime;       /* needed for mutt at least */
49 static time_t last_mtime;       /* not sure what to test: testing both now */
50 static double last_update;
51
52 static int args_ok = 0;
53 static int from_width;
54 static int subject_width;
55 static int print_mails;
56 static int time_delay;
57
58 static char mbox_mail_spool[DEFAULT_TEXT_BUFFER_SIZE];
59
60 void mbox_scan(char *args, char *output, size_t max_len)
61 {
62         int i, u, flag;
63         int force_rescan = 0;
64         char buf[text_buffer_size];
65         struct stat statbuf;
66         struct ring_list *curr = 0, *prev = 0, *startlist = 0;
67         FILE *fp;
68
69         /* output was set to 1 after malloc'ing in conky.c */
70         /* -> beeing able to test it here for catching SIGUSR1 */
71         if (output[0] == 1) {
72                 force_rescan = 1;
73                 output[0] = '\0';
74         }
75
76         if (!args_ok || force_rescan) {
77
78                 char *substr = strstr(args, "-n");
79
80                 if (substr) {
81                         if (sscanf(substr, "-n %i", &print_mails) != 1) {
82                                 print_mails = PRINT_MAILS;
83                         }
84                 } else {
85                         print_mails = PRINT_MAILS;
86                 }
87                 if (print_mails < 1) {
88                         print_mails = 1;
89                 }
90
91                 substr = strstr(args, "-t");
92                 if (substr) {
93                         if (sscanf(substr, "-t %i", &time_delay) != 1) {
94                                 time_delay = TIME_DELAY;
95                         }
96                 } else {
97                         time_delay = TIME_DELAY;
98                 }
99
100                 substr = strstr(args, "-fw");
101                 if (substr) {
102                         if (sscanf(substr, "-fw %i", &from_width) != 1) {
103                                 from_width = FROM_WIDTH;
104                         }
105                 } else {
106                         from_width = FROM_WIDTH;
107                 }
108
109                 substr = strstr(args, "-sw");
110                 if (substr) {
111                         if (sscanf(substr, "-sw %i", &subject_width) != 1) {
112                                 subject_width = SUBJECT_WIDTH;
113                         }
114                 } else {
115                         subject_width = SUBJECT_WIDTH;
116                 }
117                 /* encapsulated with "'s find first occurrence of " */
118                 if (args[strlen(args) - 1] == '"') {
119                         char *start;
120                         strncpy(mbox_mail_spool, args, DEFAULT_TEXT_BUFFER_SIZE);
121                         start = strchr(mbox_mail_spool, '"') + 1;
122
123                         start[(long) (strrchr(mbox_mail_spool, '"') - start)] = '\0';
124                         strncpy(mbox_mail_spool, start, DEFAULT_TEXT_BUFFER_SIZE);
125                 } else {
126                         char *copy_args = strndup(args, text_buffer_size);
127                         char *tmp = strtok(copy_args, " ");
128                         char *start = tmp;
129
130                         while (tmp) {
131                                 tmp = strtok(NULL, " ");
132                                 if (tmp) {
133                                         start = tmp;
134                                 }
135                         }
136                         strncpy(mbox_mail_spool, start, DEFAULT_TEXT_BUFFER_SIZE);
137                         free(copy_args);
138                 }
139                 if (strlen(mbox_mail_spool) < 1) {
140                         CRIT_ERR(NULL, NULL, "Usage: ${mboxscan [-n <number of messages to print>] "
141                                 "[-fw <from width>] [-sw <subject width>] "
142                                 "[-t <delay in sec> mbox]}");
143                 }
144
145                 /* allowing $MAIL in the config */
146                 if (!strcmp(mbox_mail_spool, "$MAIL")) {
147                         strcpy(mbox_mail_spool, current_mail_spool);
148                 }
149
150                 if (stat(mbox_mail_spool, &statbuf)) {
151                         CRIT_ERR(NULL, NULL, "can't stat %s: %s", mbox_mail_spool, strerror(errno));
152                 }
153                 args_ok = 1;    /* args-computing necessary only once */
154         }
155
156         /* if time_delay not yet reached, then return */
157         if (current_update_time - last_update < time_delay && !force_rescan) {
158                 return;
159         }
160
161         last_update = current_update_time;
162
163         /* mbox still exists? and get stat-infos */
164         if (stat(mbox_mail_spool, &statbuf)) {
165                 ERR("can't stat %s: %s", mbox_mail_spool, strerror(errno));
166                 output[0] = '\0';       /* delete any output */
167                 return;
168         }
169
170         /* modification time has not changed, so skip scanning the box */
171         if (statbuf.st_ctime == last_ctime && statbuf.st_mtime == last_mtime
172                         && !force_rescan) {
173                 return;
174         }
175
176         last_ctime = statbuf.st_ctime;
177         last_mtime = statbuf.st_mtime;
178
179         /* build up double-linked ring-list to hold data, while scanning down the
180          * mbox */
181         for (i = 0; i < print_mails; i++) {
182                 curr = (struct ring_list *) malloc(sizeof(struct ring_list));
183                 curr->from = (char *) malloc(sizeof(char[from_width + 1]));
184                 curr->subject = (char *) malloc(sizeof(char[subject_width + 1]));
185                 curr->from[0] = '\0';
186                 curr->subject[0] = '\0';
187
188                 if (i == 0) {
189                         startlist = curr;
190                 }
191                 if (i > 0) {
192                         curr->previous = prev;
193                         prev->next = curr;
194                 }
195                 prev = curr;
196         }
197
198         /* connect end to start for an endless loop-ring */
199         startlist->previous = curr;
200         curr->next = startlist;
201
202         /* mbox */
203         fp = fopen(mbox_mail_spool, "r");
204         if (!fp) {
205                 return;
206         }
207
208         /* first find a "From " to set it to 0 for header-sarchings */
209         flag = 1;
210         while (!feof(fp)) {
211                 if (fgets(buf, text_buffer_size, fp) == NULL) {
212                         break;
213                 }
214
215                 if (strncmp(buf, "From ", 5) == 0) {
216                         curr = curr->next;
217
218                         /* skip until \n */
219                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
220                                 fgets(buf, text_buffer_size, fp);
221                         }
222
223                         flag = 0;       /* in the headers now */
224                         continue;
225                 }
226
227                 if (flag == 1) {        /* in the body, so skip */
228                         continue;
229                 }
230
231                 if (buf[0] == '\n') {
232                         /* beyond the headers now (empty line), skip until \n */
233                         /* then search for new mail ("From ") */
234
235                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
236                                 fgets(buf, text_buffer_size, fp);
237                         }
238                         flag = 1;       /* in the body now */
239                         continue;
240                 }
241
242                 if ((strncmp(buf, "X-Status: ", 10) == 0)
243                                 || (strncmp(buf, "Status: R", 9) == 0)) {
244
245                         /* Mail was read or something, so skip that message */
246                         flag = 1;       /* search for next From */
247                         curr->subject[0] = '\0';
248                         curr->from[0] = '\0';
249                         /* (will get current again on new 'From ' finding) */
250                         curr = curr->previous;
251                         /* Skip until \n */
252                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
253                                 fgets(buf, text_buffer_size, fp);
254                         }
255                         continue;
256                 }
257
258                 /* that covers ^From: and ^from: ^From:<tab> */
259                 if (strncmp(buf + 1, "rom:", 4) == 0) {
260
261                         i = 0;
262                         u = 6;  /* no "From: " string needed, so skip */
263                         while (1) {
264
265                                 if (buf[u] == '"') {    /* no quotes around names */
266                                         u++;
267                                         continue;
268                                 }
269
270                                 /* some are: From: <foo@bar.com> */
271                                 if (buf[u] == '<' && i > 1) {
272
273                                         curr->from[i] = '\0';
274                                         /* skip until \n */
275                                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
276                                                 fgets(buf, text_buffer_size, fp);
277                                         }
278                                         break;
279                                 }
280
281                                 if (buf[u] == '\n') {
282                                         curr->from[i] = '\0';
283                                         break;
284                                 }
285
286                                 if (buf[u] == '\0') {
287                                         curr->from[i] = '\0';
288                                         break;
289                                 }
290
291                                 if (i >= from_width) {
292                                         curr->from[i] = '\0';
293                                         /* skip until \n */
294                                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
295                                                 fgets(buf, text_buffer_size, fp);
296                                         }
297                                         break;
298                                 }
299
300                                 /* nothing special so just set it */
301                                 curr->from[i++] = buf[u++];
302                         }
303                 }
304
305                 /* that covers ^Subject: and ^subject: and ^Subjec:<tab> */
306                 if (strncmp(buf + 1, "ubject:", 7) == 0) {
307
308                         i = 0;
309                         u = 9;  /* no "Subject: " string needed, so skip */
310                         while (1) {
311
312                                 if (buf[u] == '\n') {
313                                         curr->subject[i] = '\0';
314                                         break;
315                                 }
316                                 if (buf[u] == '\0') {
317                                         curr->subject[i] = '\0';
318                                         break;
319                                 }
320                                 if (i >= subject_width) {
321                                         curr->subject[i] = '\0';
322
323                                         /* skip until \n */
324                                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
325                                                 fgets(buf, text_buffer_size, fp);
326                                         }
327                                         break;
328                                 }
329
330                                 /* nothing special so just set it */
331                                 curr->subject[i++] = buf[u++];
332                         }
333                 }
334         }
335
336         fclose(fp);
337
338         output[0] = '\0';
339
340         i = print_mails;
341         while (i) {
342                 struct ring_list *tmp;
343                 if (curr->from[0] != '\0') {
344                         if (i != print_mails) {
345                                 snprintf(buf, text_buffer_size, "\nF: %-*s S: %-*s", from_width,
346                                         curr->from, subject_width, curr->subject);
347                         } else {        /* first time - no \n in front */
348                                 snprintf(buf, text_buffer_size, "F: %-*s S: %-*s", from_width,
349                                         curr->from, subject_width, curr->subject);
350                         }
351                 } else {
352                         snprintf(buf, text_buffer_size, "\n");
353                 }
354                 strncat(output, buf, max_len - strlen(output));
355
356                 tmp = curr;
357                 curr = curr->previous;
358                 free(tmp->from);
359                 free(tmp->subject);
360                 free(tmp);
361
362                 i--;
363         }
364 }