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