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