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