~ubuntu-branches/ubuntu/oneiric/wmail/oneiric

« back to all changes in this revision

Viewing changes to src/wmail.c

  • Committer: Bazaar Package Importer
  • Author(s): Julien Danjou
  • Date: 2005-02-27 10:34:17 UTC
  • Revision ID: james.westby@ubuntu.com-20050227103417-ns93kxud5nc7yiie
Tags: upstream-2.0
ImportĀ upstreamĀ versionĀ 2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
///////////////////////////////////////////////////////////////////////////////
 
2
// wmail.c
 
3
// email indicator tool designed as docklet for Window Maker
 
4
// main c source-file
 
5
//
 
6
// wmail version 2.0
 
7
//
 
8
// Copyright 2000~2002, Sven Geisenhainer <sveng@informatik.uni-jena.de>.
 
9
// All rights reserved.
 
10
// 
 
11
// Redistribution and use in source and binary forms, with or without
 
12
// modification, are permitted provided that the following conditions
 
13
// are met:
 
14
// 1. Redistributions of source code must retain the above copyright
 
15
//    notice, this list of conditions, and the following disclaimer.
 
16
// 2. Redistributions in binary form must reproduce the above copyright
 
17
//    notice, this list of conditions, and the following disclaimer in the
 
18
//    documentation and/or other materials provided with the distribution.
 
19
// 3. The name of the author may not be used to endorse or promote products
 
20
//    derived from this software without specific prior written permission.
 
21
// 
 
22
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 
23
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
24
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 
25
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 
26
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
27
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
28
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
29
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
30
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
31
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
32
 
 
33
 
 
34
///////////////////////////////////////////////////////////////////////////////
 
35
// includes
 
36
 
 
37
#include <stdio.h>
 
38
#include <stdlib.h>
 
39
#include <stdarg.h>
 
40
#include <string.h>
 
41
#include <signal.h>
 
42
#include <utime.h>
 
43
#include <fnmatch.h>
 
44
#include <sys/time.h>
 
45
#include <sys/stat.h>
 
46
#include <dirent.h>
 
47
#include <X11/Xlib.h>
 
48
#include <dockapp.h>
 
49
#include "common.h"
 
50
#include "config.h"
 
51
 
 
52
// pixmaps
 
53
#ifdef USE_DELT_XPMS
 
54
#include "xpm_delt/main.xpm"
 
55
#include "xpm_delt/symbols.xpm"
 
56
#include "xpm_delt/numbers.xpm"
 
57
#include "xpm_delt/button.xpm"
 
58
#include "xpm_delt/chars.xpm"
 
59
#else
 
60
#include "xpm/main.xpm"
 
61
#include "xpm/symbols.xpm"
 
62
#include "xpm/numbers.xpm"
 
63
#include "xpm/button.xpm"
 
64
#include "xpm/chars.xpm"
 
65
#endif
 
66
 
 
67
 
 
68
///////////////////////////////////////////////////////////////////////////////
 
69
// typedefs
 
70
 
 
71
typedef enum {
 
72
    FLAG_INITIAL = 0,
 
73
    FLAG_READ    = 1
 
74
} flag_t;
 
75
 
 
76
typedef struct _name_t {
 
77
    char *name;
 
78
    unsigned long checksum;
 
79
    flag_t flag;
 
80
        bool visited;
 
81
    struct _name_t *next;
 
82
} name_t;
 
83
 
 
84
typedef enum {
 
85
    STATE_NOMAIL,
 
86
    STATE_NEWMAIL,
 
87
    STATE_READMAIL
 
88
} mail_state_t;
 
89
 
 
90
typedef enum {
 
91
    STATE_ADDRESS,
 
92
    STATE_QUOTED_ADDRESS,
 
93
    STATE_FULLNAME,
 
94
    STATE_QUOTED_FULLNAME,
 
95
    STATE_ENCODED_FULLNAME,
 
96
        STATE_COMMENT
 
97
} parse_state_t;
 
98
 
 
99
 
 
100
///////////////////////////////////////////////////////////////////////////////
 
101
// data
 
102
 
 
103
mail_state_t state = STATE_NOMAIL;
 
104
int numMails = 0;
 
105
bool namesChanged = false;
 
106
bool buttonPressed = false;
 
107
bool readConfigFile = false;
 
108
bool isMaildir = false;
 
109
bool forceRead = false;
 
110
bool forceRedraw = true;
 
111
time_t lastModifySeconds = 0;
 
112
time_t lastAccessSeconds = 0;
 
113
Pixmap mainPixmap;
 
114
Pixmap mainPixmap_mask;
 
115
Pixmap symbolsPixmap;
 
116
Pixmap charsPixmap;
 
117
Pixmap numbersPixmap;
 
118
Pixmap buttonPixmap;
 
119
Pixmap outPixmap;
 
120
GC tickerGC;
 
121
XFontStruct *tickerFS = NULL;
 
122
name_t *names = NULL;
 
123
name_t *curTickerName = NULL;
 
124
 
 
125
static DAProgramOption options[] = {
 
126
    {"-display", NULL, "display to use", DOString, False, {&config.display}},
 
127
    {"-c", "--command", "cmd to run on btn-click (\"xterm -e mail\" is default)",
 
128
     DOString, False, {&config.runCmd} },
 
129
    {"-i", "--intervall",
 
130
     "number of secs between mail-status updates (1 is default)", DONatural,
 
131
     False, {&config.checkInterval} },
 
132
    {"-f", "--familyname", "tickers the family-name if available", DONone,
 
133
     False, {NULL} },
 
134
    {"-fps", "--frames", "ticker frames per second", DONatural,
 
135
     False, {&config.fps} },
 
136
    {"-s", "--shortname", "tickers the nickname (all before the '@')", DONone,
 
137
     False, {NULL} },
 
138
    {"-sc", "--symbolcolor", "symbol color-name",
 
139
     DOString, False, {&config.symbolColor} },
 
140
    {"-fc", "--fontcolor", "ticker-font color-name",
 
141
     DOString, False, {&config.fontColor} },
 
142
    {"-bc", "--backcolor", "backlight color-name",
 
143
     DOString, False, {&config.backColor} },
 
144
    {"-oc", "--offcolor", "off-light color-name",
 
145
     DOString, False, {&config.offlightColor} },
 
146
    {"-bg", "--background", "frame-background for non-shaped window",
 
147
     DOString, False, {&config.backgroundColor} },
 
148
    {"-ns", "--noshape", "make the dockapp non-shaped (combine with -w)",
 
149
     DONone, False, {NULL} },
 
150
    {"-n", "--new", "forces wmail to show new mail exclusively", DONone, False, {NULL} },
 
151
    {"-mb", "--mailbox", "specify another mailbox ($MAIL is default)", DOString, False, {&config.mailBox} },
 
152
    {"-e", "--execute", "command to execute when receiving a new mail", DOString, False, {&config.cmdOnMail} },
 
153
    {"-sf", "--statusfield", "consider the status-field of the mail header to distinguish unread mails", DONone, False, {NULL} },
 
154
        {"-rs", "--readstatus", "status field content that your client uses to mark read mails", DOString, False, {&config.readStatus} },
 
155
        {"-fn", "--tickerfont", "use specified X11 font to draw the ticker", DOString, False, {&config.useX11Font} }
 
156
};
 
157
 
 
158
 
 
159
///////////////////////////////////////////////////////////////////////////////
 
160
// prototypes
 
161
 
 
162
void PreparePixmaps( bool freeThemFirst );
 
163
void TimerHandler( int dummy );
 
164
void CheckMBox();
 
165
void CheckMaildir();
 
166
int TraverseDirectory( const char *name, bool isNewMail );
 
167
name_t *GetMail( unsigned long checksum );
 
168
void UpdatePixmap( bool flashMailSymbol );
 
169
void ParseMBoxFile( struct stat *fileStat );
 
170
void ParseMaildirFile( const char *fileName, unsigned long checksum,
 
171
                                           struct stat *fileStat, bool isNewMail );
 
172
char *ParseFromField( char *buf );
 
173
bool SkipSender( char *address );
 
174
void InsertName( char *name, unsigned long checksum, flag_t flag );
 
175
void RemoveLastName();
 
176
void ClearAllNames();
 
177
void DrawTickerX11Font();
 
178
void DrawTickerBuildinFont();
 
179
void ButtonPressed( int button, int state, int x, int y );
 
180
void ButtonReleased( int button, int state, int x, int y );
 
181
char *XpmColorLine( const char *colorName, char *colorLine, bool disposeLine );
 
182
void ReadChecksumFile();
 
183
void WriteChecksumFile( bool writeAll );
 
184
void UpdateChecksum( unsigned long *checksum, const char *buf );
 
185
void RemoveChecksumFile();
 
186
void SetMailFlags( flag_t flag );
 
187
void MarkName( unsigned long checksum );
 
188
void DetermineState();
 
189
void UpdateConfiguration();
 
190
void CleanupNames();
 
191
char *FileNameConcat( const char *path, const char *fileName );
 
192
bool HasTickerWork();
 
193
 
 
194
 
 
195
///////////////////////////////////////////////////////////////////////////////
 
196
// implementation
 
197
 
 
198
 
 
199
void SetTimer()
 
200
{
 
201
    struct itimerval timerVal;
 
202
 
 
203
    timerVal.it_interval.tv_sec = 0;
 
204
    timerVal.it_interval.tv_usec = 1000000/config.fps;
 
205
    timerVal.it_value.tv_sec = 0;
 
206
    timerVal.it_value.tv_usec = 1000000/config.fps;
 
207
 
 
208
    setitimer( ITIMER_REAL, &timerVal, NULL );
 
209
}
 
210
 
 
211
int main( int argc, char **argv )
 
212
{
 
213
    char *usersHome;
 
214
    struct sigaction sa;
 
215
    int ret;
 
216
        struct stat fileStat;
 
217
        XTextProperty windowName;
 
218
        char *name = argv[0];
 
219
    DACallbacks callbacks = { NULL, &ButtonPressed, &ButtonReleased,
 
220
                                                          NULL, NULL, NULL, NULL };
 
221
 
 
222
    // read the config file and overide the default-settings
 
223
    ReadConfigFile( false );
 
224
 
 
225
    if( config.checksumFileName == NULL ) {
 
226
                if(( usersHome = getenv( "HOME" )) == NULL ) {
 
227
                        WARNING( "HOME environment-variable is not set, placing %s in current directory!\n", WMAIL_CHECKSUM_FILE );
 
228
                        config.checksumFileName = WMAIL_CHECKSUM_FILE;
 
229
                } else 
 
230
                        config.checksumFileName = MakePathName( usersHome, WMAIL_CHECKSUM_FILE );
 
231
    }
 
232
 
 
233
    TRACE( "using checksum-file \"%s\"\n", config.checksumFileName );
 
234
 
 
235
    // parse cmdline-args and overide defaults and cfg-file settings
 
236
    DAParseArguments( argc, argv, options,
 
237
                                          sizeof(options) / sizeof(DAProgramOption),
 
238
                                          WMAIL_NAME, WMAIL_VERSION );
 
239
 
 
240
        if( options[0].used )
 
241
                config.givenOptions |= CL_DISPLAY;
 
242
        if( options[1].used )
 
243
                config.givenOptions |= CL_RUNCMD;
 
244
        if( options[2].used )
 
245
                config.givenOptions |= CL_CHECKINTERVAL;
 
246
        if( options[3].used ) {
 
247
                config.givenOptions |= CL_TICKERMODE;
 
248
                config.tickerMode = TICKER_FAMILYNAME;
 
249
        }
 
250
        if( options[4].used )
 
251
                config.givenOptions |= CL_FPS;
 
252
        if( options[5].used ) {
 
253
                config.givenOptions |= CL_TICKERMODE;
 
254
                config.tickerMode = TICKER_NICKNAME;
 
255
        }
 
256
        if( options[6].used )
 
257
                config.givenOptions |= CL_SYMBOLCOLOR;
 
258
        if( options[7].used )
 
259
                config.givenOptions |= CL_FONTCOLOR;
 
260
        if( options[8].used )
 
261
                config.givenOptions |= CL_BACKCOLOR;
 
262
        if( options[9].used )
 
263
                config.givenOptions |= CL_OFFLIGHTCOLOR;
 
264
        if( options[10].used )
 
265
                config.givenOptions |= CL_BACKGROUNDCOLOR;
 
266
        if( options[11].used ) {
 
267
                config.givenOptions |= CL_NOSHAPE;
 
268
                config.noshape = true;
 
269
        }
 
270
        if( options[12].used ) {
 
271
                config.givenOptions |= CL_NEWMAILONLY;
 
272
                config.newMailsOnly = true;
 
273
        }
 
274
        if( options[13].used )
 
275
                config.givenOptions |= CL_MAILBOX;
 
276
        if( options[14].used )
 
277
                config.givenOptions |= CL_CMDONMAIL;
 
278
    if( options[15].used ) {
 
279
                config.givenOptions |= CL_CONSIDERSTATUSFIELD;
 
280
                config.considerStatusField = true;
 
281
        }
 
282
        if( options[16].used )
 
283
                config.givenOptions |= CL_READSTATUS;
 
284
        if( options[17].used )
 
285
                config.givenOptions |= CL_USEX11FONT;
 
286
 
 
287
    if( config.mailBox == NULL )
 
288
                ABORT( "no mailbox specified - please define at least your $MAIL environment-variable!\n" );
 
289
        else if( stat( config.mailBox, &fileStat ) == 0 )
 
290
                isMaildir = S_ISDIR( fileStat.st_mode ) != 0;
 
291
 
 
292
        TRACE( "mailbox is of type %s\n", isMaildir ? "maildir" : "mbox" );
 
293
 
 
294
    // dockapp size hard wired - sorry...
 
295
    DAInitialize( config.display, "wmail", 64, 64, argc, argv );
 
296
 
 
297
        outPixmap = DAMakePixmap();
 
298
    PreparePixmaps( false );
 
299
 
 
300
    DASetCallbacks( &callbacks );
 
301
    DASetTimeout( -1 );
 
302
 
 
303
    sa.sa_handler = TimerHandler;
 
304
    sigemptyset( &sa.sa_mask );
 
305
    sa.sa_flags = SA_RESTART;
 
306
    ret = sigaction( SIGALRM, &sa, 0 );
 
307
 
 
308
    if( ret ) {
 
309
        perror( "wmail error: sigaction" );
 
310
        exit( 1 );
 
311
    }
 
312
 
 
313
        XStringListToTextProperty( &name, 1, &windowName );
 
314
        XSetWMName( DADisplay, DAWindow, &windowName );
 
315
    
 
316
    UpdatePixmap( false );
 
317
    DAShow();
 
318
    SetTimer();
 
319
 
 
320
    DAEventLoop();
 
321
 
 
322
    return 0;
 
323
}
 
324
 
 
325
void PreparePixmaps( bool freeMem )
 
326
{
 
327
    // simple recoloring of the raw xpms befor creating Pixmaps of them
 
328
    // this works as long as you don't "touch" the images...
 
329
 
 
330
    unsigned dummy;
 
331
        XGCValues values;
 
332
 
 
333
    if( config.symbolColor != NULL ) { // symbol color ?
 
334
                symbols_xpm[2] = XpmColorLine( config.symbolColor, symbols_xpm[2],
 
335
                                                                           freeMem && ( config.colorsUsed & SYM_COLOR ));
 
336
                config.colorsUsed |= SYM_COLOR;
 
337
    } else {
 
338
                symbols_xpm[2] = XpmColorLine( "#20B2AA", symbols_xpm[2],
 
339
                                                                           freeMem && ( config.colorsUsed & SYM_COLOR ));
 
340
                config.colorsUsed |= SYM_COLOR;
 
341
        }
 
342
 
 
343
    if( config.fontColor != NULL ) { // font color ?
 
344
                chars_xpm[3] = XpmColorLine( config.fontColor, chars_xpm[3],
 
345
                                                                         freeMem && ( config.colorsUsed & FNT_COLOR ));
 
346
                numbers_xpm[3] = XpmColorLine( config.fontColor, numbers_xpm[3],
 
347
                                                                           freeMem && ( config.colorsUsed & FNT_COLOR ));
 
348
                config.colorsUsed |= FNT_COLOR;
 
349
    } else {
 
350
                chars_xpm[3] = XpmColorLine( "#D3D3D3", chars_xpm[3],
 
351
                                                                         freeMem && ( config.colorsUsed & FNT_COLOR ));
 
352
                numbers_xpm[3] = XpmColorLine( "#D3D3D3", numbers_xpm[3],
 
353
                                                                           freeMem && ( config.colorsUsed & FNT_COLOR ));
 
354
                config.colorsUsed |= FNT_COLOR;
 
355
        }
 
356
 
 
357
    if( config.backColor != NULL ) { // backlight color ?
 
358
                main_xpm[3] = XpmColorLine( config.backColor, main_xpm[3],
 
359
                                                                        freeMem && ( config.colorsUsed & BCK_COLOR ));
 
360
                symbols_xpm[3] = XpmColorLine( config.backColor, symbols_xpm[3],
 
361
                                                                           freeMem && ( config.colorsUsed & BCK_COLOR ));
 
362
                chars_xpm[2] = XpmColorLine( config.backColor, chars_xpm[2],
 
363
                                                                         freeMem && ( config.colorsUsed & BCK_COLOR ));
 
364
                numbers_xpm[2] = XpmColorLine( config.backColor, numbers_xpm[2],
 
365
                                                                           freeMem && ( config.colorsUsed & BCK_COLOR ));
 
366
                config.colorsUsed |= BCK_COLOR;
 
367
    } else {
 
368
                main_xpm[3] = XpmColorLine( "#282828", main_xpm[3],
 
369
                                                                        freeMem && ( config.colorsUsed & BCK_COLOR ));
 
370
                symbols_xpm[3] = XpmColorLine( "#282828", symbols_xpm[3],
 
371
                                                                           freeMem && ( config.colorsUsed & BCK_COLOR ));
 
372
                chars_xpm[2] = XpmColorLine( "#282828", chars_xpm[2],
 
373
                                                                         freeMem && ( config.colorsUsed & BCK_COLOR ));
 
374
                numbers_xpm[2] = XpmColorLine( "#282828", numbers_xpm[2],
 
375
                                                                           freeMem && ( config.colorsUsed & BCK_COLOR ));
 
376
                config.colorsUsed |= BCK_COLOR;
 
377
        }
 
378
 
 
379
    if( config.offlightColor != NULL ) { // off-light color ?
 
380
                main_xpm[2] = XpmColorLine( config.offlightColor, main_xpm[2],
 
381
                                                                        freeMem && ( config.colorsUsed & OFF_COLOR ));
 
382
                numbers_xpm[4] = XpmColorLine( config.offlightColor, numbers_xpm[4],
 
383
                                                                           freeMem && ( config.colorsUsed & OFF_COLOR ));
 
384
                config.colorsUsed |= OFF_COLOR;
 
385
    } else {
 
386
                main_xpm[2] = XpmColorLine( "#000000", main_xpm[2],
 
387
                                                                        freeMem && ( config.colorsUsed & OFF_COLOR ));
 
388
                numbers_xpm[4] = XpmColorLine( "#000000", numbers_xpm[4],
 
389
                                                                           freeMem && ( config.colorsUsed & OFF_COLOR ));
 
390
                config.colorsUsed |= OFF_COLOR;
 
391
        }
 
392
 
 
393
    if( config.backgroundColor != NULL ) { // window-frame background (only seen if nonshaped) ?
 
394
                main_xpm[1] = XpmColorLine( config.backgroundColor, main_xpm[1],
 
395
                                                                        freeMem && ( config.colorsUsed & BGR_COLOR ));
 
396
                config.colorsUsed |= BGR_COLOR;
 
397
    }
 
398
 
 
399
    if( freeMem )
 
400
        {
 
401
                XFreePixmap( DADisplay, mainPixmap );
 
402
                XFreePixmap( DADisplay, mainPixmap_mask );
 
403
                XFreePixmap( DADisplay, symbolsPixmap );
 
404
                XFreePixmap( DADisplay, charsPixmap );
 
405
                XFreePixmap( DADisplay, numbersPixmap );
 
406
                XFreePixmap( DADisplay, buttonPixmap );
 
407
 
 
408
                if( tickerGC != NULL )
 
409
                {
 
410
                        XFreeGC( DADisplay, tickerGC );
 
411
                        tickerGC = NULL;
 
412
                        if( tickerFS != NULL ) {
 
413
                                XFreeFont( DADisplay, tickerFS );
 
414
                                tickerFS = NULL;
 
415
                        }
 
416
                }
 
417
    }
 
418
 
 
419
    DAMakePixmapFromData( main_xpm, &mainPixmap, &mainPixmap_mask, &dummy,  &dummy );
 
420
    DAMakePixmapFromData( symbols_xpm, &symbolsPixmap, NULL, &dummy, &dummy );
 
421
    DAMakePixmapFromData( chars_xpm, &charsPixmap, NULL, &dummy, &dummy );
 
422
    DAMakePixmapFromData( numbers_xpm, &numbersPixmap, NULL, &dummy, &dummy );
 
423
    DAMakePixmapFromData( button_xpm, &buttonPixmap, NULL, &dummy, &dummy );
 
424
  
 
425
        if( config.useX11Font != NULL )
 
426
        {
 
427
                XRectangle clipRect;
 
428
 
 
429
                if( config.fontColor != NULL )
 
430
                        values.foreground = DAGetColor( config.fontColor );
 
431
                else
 
432
                        values.foreground = DAGetColor( "#D3D3D3" );
 
433
 
 
434
                tickerFS = XLoadQueryFont( DADisplay, config.useX11Font );
 
435
                if( tickerFS == NULL )
 
436
                        ABORT( "Cannot load font \"%s\"", config.useX11Font );
 
437
 
 
438
                values.font = tickerFS->fid;
 
439
                tickerGC = XCreateGC( DADisplay, DAWindow, GCForeground | GCFont,
 
440
                                                          &values );
 
441
                clipRect.x = 6;
 
442
                clipRect.y = 19;
 
443
                clipRect.width = 52;
 
444
                clipRect.height = 23;
 
445
 
 
446
                XSetClipRectangles( DADisplay, tickerGC, 0, 0, &clipRect, 1, Unsorted );
 
447
        }
 
448
  
 
449
    if( config.noshape ) // non-shaped dockapp ?
 
450
                DASetShape( None );
 
451
    else
 
452
                DASetShape( mainPixmap_mask );
 
453
}
 
454
 
 
455
void MarkName( unsigned long checksum )
 
456
{
 
457
    name_t *name;
 
458
    
 
459
    for( name = names; name != NULL; name = name->next ) {
 
460
                if( name->checksum == checksum ) {
 
461
                        name->flag |= FLAG_READ;
 
462
                        if( config.newMailsOnly )
 
463
                                numMails--;
 
464
                        break;
 
465
                }
 
466
    }
 
467
}
 
468
 
 
469
void DetermineState()
 
470
{
 
471
    name_t *name;
 
472
    
 
473
    for( name = names; name != NULL; name = name->next )
 
474
                if(!( name->flag & FLAG_READ )) {
 
475
                        state = STATE_NEWMAIL;
 
476
 
 
477
                        if( config.cmdOnMail != NULL ) {
 
478
                                int ret = system( config.cmdOnMail );
 
479
        
 
480
                                if( ret == 127 || ret == -1 )
 
481
                                        WARNING( "execution of command \"%s\" failed.\n", config.cmdOnMail );
 
482
                        }
 
483
                
 
484
                        break;
 
485
                }
 
486
}
 
487
 
 
488
void ReadChecksumFile()
 
489
{
 
490
    FILE *f = fopen( config.checksumFileName, "rb" );
 
491
    if( f != NULL ) while( !feof( f )) {
 
492
                unsigned long checksum;
 
493
                if( fread( &checksum, sizeof(long), 1, f ) != 1 )
 
494
                        continue;
 
495
 
 
496
                MarkName( checksum );
 
497
    } else
 
498
                return;
 
499
    
 
500
    fclose( f );
 
501
}
 
502
 
 
503
void WriteChecksumFile( bool writeAll )
 
504
{
 
505
    FILE *f;
 
506
    TRACE( "writing checksums:" );
 
507
 
 
508
    if(( f = fopen( config.checksumFileName, "wb" )) != NULL ) {
 
509
                name_t *name;
 
510
                for( name = names; name != NULL; name = name->next ) {
 
511
                        if( writeAll || (name->flag & FLAG_READ)) {
 
512
                                fwrite( &name->checksum, sizeof(long), 1, f );
 
513
                                TRACE( " %X", name->checksum );
 
514
                        }
 
515
                }
 
516
    } else
 
517
                return;
 
518
 
 
519
    TRACE( "\n" );
 
520
    
 
521
    fclose( f );    
 
522
}
 
523
 
 
524
void UpdateChecksum( unsigned long *checksum, const char *buf )
 
525
{
 
526
    if( buf != NULL ) {
 
527
                unsigned int i, len = strlen( buf );
 
528
        
 
529
                for( i = 0; i < len; ++i )
 
530
                        *checksum += buf[i] << (( i % sizeof(long) ) * 8 );
 
531
    }
 
532
}
 
533
 
 
534
void RemoveChecksumFile()
 
535
{
 
536
    TRACE( "removing checksum-file\n" );
 
537
    remove( config.checksumFileName );
 
538
}
 
539
 
 
540
// dummy needed because this func is a signal-handler
 
541
void TimerHandler( int dummy )
 
542
{
 
543
    static int checkMail = 0;
 
544
 
 
545
    if( readConfigFile ) {
 
546
                readConfigFile = false;
 
547
                UpdateConfiguration();
 
548
                checkMail = 0;
 
549
                forceRead = true;
 
550
    }
 
551
 
 
552
    if( checkMail == 0 )
 
553
        {
 
554
                TRACE( "checking for new mail...\n" );
 
555
 
 
556
                if( isMaildir )
 
557
                        CheckMaildir();
 
558
                else
 
559
                        CheckMBox();
 
560
        }
 
561
    
 
562
    UpdatePixmap( checkMail % config.fps < config.fps/2 );
 
563
 
 
564
    if( ++checkMail >= config.fps * config.checkInterval )
 
565
                checkMail = 0;
 
566
}
 
567
 
 
568
void CheckMBox()
 
569
{
 
570
        struct stat fileStat;
 
571
 
 
572
        // error retrieving file-stats -> no/zero-size file and no new/read mails
 
573
        // available
 
574
        if( stat( config.mailBox, &fileStat ) == -1 || fileStat.st_size == 0 ) {
 
575
                if( state != STATE_NOMAIL ) {
 
576
                        state = STATE_NOMAIL;
 
577
                        ClearAllNames();
 
578
                        RemoveChecksumFile();
 
579
                        forceRedraw = true;
 
580
                }
 
581
        } else {
 
582
                // file has changed -> new mails arrived or some mails removed
 
583
                if( lastModifySeconds != fileStat.st_mtime || forceRead ) {
 
584
                        forceRead = false;
 
585
                        ParseMBoxFile( &fileStat );
 
586
                        stat( config.mailBox, &fileStat );
 
587
                        forceRedraw = true;
 
588
                        // file has accessed (read) -> mark all mails as "read", because
 
589
                        // it cannot be decided which mails the user has read...
 
590
                } else if( lastAccessSeconds != fileStat.st_atime ) {
 
591
                        state = STATE_READMAIL;
 
592
                        WriteChecksumFile( true );
 
593
                        if( config.newMailsOnly ) {
 
594
                                numMails = 0;
 
595
                                SetMailFlags( FLAG_READ );
 
596
                        }
 
597
                        forceRedraw = true;
 
598
                }
 
599
 
 
600
                lastModifySeconds = fileStat.st_mtime;
 
601
                lastAccessSeconds = fileStat.st_atime;
 
602
        }
 
603
}
 
604
 
 
605
void CheckMaildir()
 
606
{
 
607
        DIR *dir = NULL;
 
608
        int lastState = state;
 
609
        int lastMailCount = numMails;
 
610
 
 
611
        if( forceRead ) {
 
612
                forceRead = false;
 
613
                ClearAllNames();
 
614
                TRACE( "all names cleared\n" );
 
615
        }
 
616
 
 
617
        state = STATE_NOMAIL;
 
618
 
 
619
        if(( dir = opendir( config.mailBox )) != NULL )
 
620
        {
 
621
                struct dirent *dirEnt = NULL;
 
622
                //bool writeChecksums = false;
 
623
                name_t *name;
 
624
 
 
625
                for( name = names; name != NULL; name = name->next )
 
626
                        name->visited = false;
 
627
 
 
628
                while(( dirEnt = readdir( dir )) != NULL )
 
629
                {
 
630
                        char *fullName = FileNameConcat( config.mailBox, dirEnt->d_name );
 
631
                        struct stat fileStat;
 
632
 
 
633
                        if( !stat( fullName, &fileStat ) == 0 ) {
 
634
                                WARNING( "Can't stat file/path \"%s\"\n", fullName );
 
635
                                free( fullName );
 
636
                                continue;
 
637
                        }
 
638
 
 
639
                        if(     S_ISDIR( fileStat.st_mode ))
 
640
                        {
 
641
                                if( strcmp( dirEnt->d_name, "new" ) == 0 ) {
 
642
                                        if( TraverseDirectory( fullName, true ) > 0 )
 
643
                                                state = STATE_NEWMAIL;
 
644
                                }
 
645
                                else if( strcmp( dirEnt->d_name, "cur" ) == 0 ) {
 
646
                                        if( TraverseDirectory( fullName, false ) > 0 )
 
647
                                                if( state != STATE_NEWMAIL )
 
648
                                                        state = STATE_READMAIL;
 
649
                                }
 
650
                                // directories ".", ".." and "tmp" discarded
 
651
                        }
 
652
                }
 
653
                closedir( dir );
 
654
                CleanupNames();
 
655
        } else
 
656
                WARNING( "can't open directory \"%s\"\n", config.mailBox );
 
657
 
 
658
        if( lastState != state || lastMailCount != numMails )
 
659
                forceRedraw = true;
 
660
}
 
661
 
 
662
int TraverseDirectory( const char *name, bool isNewMail )
 
663
{
 
664
        DIR *dir = NULL;
 
665
        int mails = 0;
 
666
 
 
667
        if(( dir = opendir( name )) != NULL )
 
668
        {
 
669
                struct dirent *dirEnt = NULL;
 
670
 
 
671
                while(( dirEnt = readdir( dir )) != NULL )
 
672
                {
 
673
                        char *fullName = FileNameConcat( name, dirEnt->d_name );
 
674
                        struct stat fileStat;
 
675
                        unsigned long checksum = 0;
 
676
                        name_t *name;
 
677
 
 
678
                        if( !stat( fullName, &fileStat ) == 0 ) {
 
679
                                WARNING( "Can't stat file/path \"%s\"\n", fullName );
 
680
                                free( fullName );
 
681
                                continue;
 
682
                        }
 
683
 
 
684
                        if(     !S_ISDIR( fileStat.st_mode ))
 
685
                        {
 
686
                                TRACE( "found email-file \"%s\"\n", fullName );
 
687
                                UpdateChecksum( &checksum, dirEnt->d_name );
 
688
 
 
689
                                if(( name = GetMail( checksum )) == NULL )
 
690
                                {
 
691
                                        TRACE( "-> new file - parsing it\n" );
 
692
                                        ParseMaildirFile( fullName, checksum, &fileStat, isNewMail );
 
693
                                }
 
694
                                else {
 
695
                                        name->flag = isNewMail ? FLAG_INITIAL : FLAG_READ;
 
696
                                        name->visited = true;
 
697
                                }
 
698
                                ++mails;
 
699
                        }
 
700
                }
 
701
        }
 
702
 
 
703
        closedir( dir );
 
704
 
 
705
        return mails;
 
706
}
 
707
 
 
708
char *FileNameConcat( const char *path, const char *fileName )
 
709
{
 
710
        int len1 = strlen( path );
 
711
        int len2 = strlen( fileName );
 
712
        char *buf;
 
713
 
 
714
        if( path[len1-1] == '/' )
 
715
                --len1;
 
716
 
 
717
        buf = (char *)malloc( len1 + len2 + 2 );
 
718
 
 
719
        memcpy( buf, path, len1 );
 
720
        buf[len1] = '/';
 
721
        memcpy( &buf[len1+1], fileName, len2 );
 
722
        buf[len1+len2+1] = '\0';
 
723
 
 
724
        return buf;
 
725
}
 
726
 
 
727
name_t *GetMail( unsigned long checksum )
 
728
{
 
729
        name_t *name;
 
730
    
 
731
    for( name = names; name != NULL; name = name->next )
 
732
                if( name->checksum == checksum )
 
733
                        return name;
 
734
 
 
735
    return NULL;
 
736
}
 
737
 
 
738
void UpdatePixmap( bool flashMailSymbol )
 
739
{
 
740
    int drawCount, i;
 
741
 
 
742
        if( !forceRedraw && !HasTickerWork() )
 
743
                return;
 
744
 
 
745
        forceRedraw = false;
 
746
 
 
747
    XCopyArea( DADisplay, mainPixmap, outPixmap, DAGC,
 
748
                           0, 0, 64, 64, 0, 0 );
 
749
    
 
750
    if( numMails > 999 )
 
751
        {
 
752
                XCopyArea( DADisplay, numbersPixmap, outPixmap, DAGC,
 
753
                                   50, 0, 5, 9, 6, 49 );
 
754
                drawCount = 999;
 
755
    } else
 
756
                drawCount = numMails;
 
757
 
 
758
    for( i = 0; i < 3; ++i, drawCount /= 10 )
 
759
        {
 
760
                XCopyArea( DADisplay, numbersPixmap, outPixmap, DAGC,
 
761
                                   (drawCount%10)*5, 0, 5, 9, 24-i*6, 49 );
 
762
                if( drawCount <= 9 )
 
763
                        break;
 
764
    }
 
765
 
 
766
    if( buttonPressed )
 
767
        {
 
768
                XCopyArea( DADisplay, buttonPixmap, outPixmap, DAGC,
 
769
                                   0, 0, 23, 11, 36, 48 );
 
770
        }
 
771
 
 
772
    switch( state ) {
 
773
    case STATE_NEWMAIL:
 
774
                if( flashMailSymbol )
 
775
                        XCopyArea( DADisplay, symbolsPixmap, outPixmap, DAGC,
 
776
                                           13, 0, 37, 12, 20, 7 );
 
777
    case STATE_READMAIL:
 
778
                XCopyArea( DADisplay, symbolsPixmap, outPixmap, DAGC,
 
779
                                   0, 0, 13, 12, 7, 7 );
 
780
 
 
781
                if( config.useX11Font == NULL )
 
782
                        DrawTickerBuildinFont();
 
783
                else
 
784
                        DrawTickerX11Font();
 
785
    default: // make compiler happy
 
786
                ;
 
787
    }
 
788
 
 
789
    DASetPixmap( outPixmap );
 
790
}
 
791
 
 
792
void ParseMBoxFile( struct stat *fileStat )
 
793
{
 
794
    char buf[1024];
 
795
    struct utimbuf timeStruct;
 
796
    int fromFound = 0;
 
797
    FILE *f = fopen( config.mailBox, "rt" );
 
798
    unsigned long checksum;
 
799
 
 
800
    state = STATE_READMAIL;
 
801
    ClearAllNames();
 
802
 
 
803
    numMails = 0;
 
804
 
 
805
    if( f == NULL ) {
 
806
                WARNING( "can't open mbox \"%s\"\n", config.mailBox );
 
807
                return;
 
808
    }
 
809
 
 
810
    while( fgets( buf, 1024, f ) != NULL )
 
811
        {
 
812
                if( strncmp( buf, "From ", 5 ) == 0 ) {
 
813
                        fromFound = 1;
 
814
                        checksum = 0;
 
815
                        continue;
 
816
                }
 
817
 
 
818
                if( fromFound )
 
819
                        UpdateChecksum( &checksum, buf );
 
820
 
 
821
                if( fromFound && strncasecmp( buf, "from: ", 6 ) == 0 )
 
822
                {
 
823
                        if( SkipSender( buf+6 ))
 
824
                                goto NEXTMAIL;
 
825
 
 
826
                        InsertName( ParseFromField( buf+6 ), checksum, FLAG_INITIAL );
 
827
 
 
828
                        ++numMails;
 
829
                        fromFound = 0;
 
830
                        checksum = 0;
 
831
                } else if( config.considerStatusField && strncasecmp( buf, "status: ", 8 ) == 0 &&
 
832
                                   strstr( buf+8, config.readStatus ) == NULL )
 
833
                {
 
834
                        RemoveLastName();
 
835
                        --numMails;
 
836
                }
 
837
    NEXTMAIL:
 
838
                ;
 
839
    }
 
840
 
 
841
    fclose( f );
 
842
    ReadChecksumFile();
 
843
 
 
844
    DetermineState();
 
845
 
 
846
    timeStruct.actime = fileStat->st_atime;
 
847
    timeStruct.modtime = fileStat->st_mtime;
 
848
    utime( config.mailBox, &timeStruct );
 
849
}
 
850
 
 
851
void ParseMaildirFile( const char *fileName, unsigned long checksum,
 
852
                                           struct stat *fileStat, bool isNewMail )
 
853
{
 
854
        char buf[1024];
 
855
        struct utimbuf timeStruct;
 
856
        FILE *f = fopen( fileName, "rt" );
 
857
 
 
858
        if( f == NULL )
 
859
        {
 
860
                WARNING( "can't open maildir file \"%s\"\n", fileName );
 
861
                return;
 
862
        }
 
863
 
 
864
        while( fgets( buf, 1024, f ) != NULL )
 
865
        {
 
866
                if( strncasecmp( buf, "from: ", 6 ) == 0 )
 
867
                {
 
868
                        if( SkipSender( buf+6 ))
 
869
                                break;
 
870
 
 
871
                        InsertName( ParseFromField( buf+6 ), checksum,
 
872
                                                isNewMail ? FLAG_INITIAL : FLAG_READ );
 
873
 
 
874
                        //++numMails;
 
875
                }
 
876
        }
 
877
 
 
878
        fclose( f );
 
879
 
 
880
        timeStruct.actime = fileStat->st_atime;
 
881
    timeStruct.modtime = fileStat->st_mtime;
 
882
    utime( fileName, &timeStruct );     
 
883
}
 
884
 
 
885
char *ParseFromField( char *buf )
 
886
{
 
887
        parse_state_t state = STATE_FULLNAME;
 
888
        int fullNameEncoded = 0;
 
889
        int saveAtCharPos = -1;
 
890
        char *fullName;
 
891
        char *addressName;
 
892
        char *atChar = NULL;
 
893
        char *c;
 
894
        int maxLen = strlen( buf ) + 1;
 
895
        char *comment;
 
896
 
 
897
        // FIXME: Uhm, those mallocs might fail...
 
898
 
 
899
        fullName = malloc( maxLen );
 
900
        addressName = malloc( maxLen );
 
901
        comment = malloc( maxLen );
 
902
 
 
903
        memset( fullName, '\0', maxLen );
 
904
        memset( addressName, '\0', maxLen );
 
905
        memset( comment, '\0', maxLen );
 
906
 
 
907
        // FIXME: Don't do that "encoded" dance.  It's not intended by
 
908
        // RFC2047, and it's better to just do it in the end.
 
909
        // Cleaner.
 
910
 
 
911
        for( c = buf; *c != '\0'; ++c )
 
912
        {
 
913
                switch( state ) {
 
914
                case STATE_FULLNAME:
 
915
 
 
916
                        switch( *c ) {
 
917
                        case '"':
 
918
                                state = STATE_QUOTED_FULLNAME;
 
919
                                continue;
 
920
                        case '<':
 
921
                                if( fullName[0] != '\0' && 
 
922
                                        fullName[ strlen( fullName ) - 1 ] == ' ' )
 
923
                                        fullName[ strlen( fullName ) - 1 ] = '\0';
 
924
                                state = STATE_ADDRESS;
 
925
                                continue;
 
926
                        case '@':
 
927
                                saveAtCharPos = strlen( fullName );
 
928
                                fullName[ saveAtCharPos ] = *c;
 
929
                                continue;
 
930
                        case '(':
 
931
                                state = STATE_COMMENT;
 
932
                                continue;
 
933
                        case '=':
 
934
                                if( *(c+1) == '?' ) {
 
935
                                        ++c;
 
936
                                        fullNameEncoded = 1;
 
937
                                        state = STATE_ENCODED_FULLNAME;
 
938
                                        continue;
 
939
                                } // else do the default action
 
940
                        default:
 
941
                                fullName[ strlen( fullName ) ] = *c;
 
942
                        }
 
943
                        continue;
 
944
                        
 
945
                case STATE_QUOTED_FULLNAME:
 
946
                    
 
947
                        switch( *c ) {
 
948
                        case '\\':
 
949
                                fullName[ strlen( fullName ) ] = *(++c);
 
950
                                continue;
 
951
                        case '"':
 
952
                                state = STATE_FULLNAME;
 
953
                                continue;
 
954
                        default:
 
955
                                fullName[ strlen( fullName ) ] = *c;
 
956
                        }
 
957
                        continue;
 
958
 
 
959
                case STATE_ENCODED_FULLNAME:
 
960
 
 
961
                        switch( *c ) {
 
962
                        case '?':
 
963
                                if( *(c+1) == '=' ) {
 
964
                                        ++c;
 
965
                                        state = STATE_FULLNAME;
 
966
                                        continue;
 
967
                                }
 
968
                        default:
 
969
                                ; // do nothing... COMING SOON: decode at least latin1
 
970
                        }
 
971
                        continue;
 
972
 
 
973
                case STATE_ADDRESS:
 
974
 
 
975
                        switch( *c ) {
 
976
                        case '"':
 
977
                                state = STATE_QUOTED_ADDRESS;
 
978
                        case '>':
 
979
                        case '\n':
 
980
                                // FIXME: Shouldn't it break here?
 
981
                                // Since the address is finished?
 
982
                                continue;
 
983
                        case '@':
 
984
                                atChar = &addressName[ strlen( addressName ) ];
 
985
                                *atChar = *c;
 
986
                                continue;
 
987
                        default:
 
988
                                addressName[ strlen( addressName ) ] = *c;
 
989
                        }
 
990
                        continue;
 
991
 
 
992
                case STATE_QUOTED_ADDRESS:
 
993
 
 
994
                        switch( *c ) {
 
995
                        case '"':
 
996
                                state = STATE_ADDRESS;
 
997
                                continue;
 
998
                        case '\\':
 
999
                                addressName[ strlen( addressName ) ] = *(++c);
 
1000
                                continue;
 
1001
                        default:
 
1002
                                addressName[ strlen( addressName ) ] = *c;
 
1003
                        }
 
1004
                        continue;
 
1005
                        case STATE_COMMENT:
 
1006
                                switch( *c ) {
 
1007
                                case ')':
 
1008
                                        state = STATE_FULLNAME;
 
1009
                                        continue;
 
1010
                                default:
 
1011
                                        comment[ strlen( comment ) ] = *c;
 
1012
                                        continue;
 
1013
                                }
 
1014
                                continue;
 
1015
                }
 
1016
        }
 
1017
 
 
1018
        if (*comment) {
 
1019
                //WARNING("Comment seen: %s\nIn: %s\nFullname: %s\nAddress: %s\n", comment, buf, fullName, addressName);
 
1020
                // Comment seen: if there's an address, append to
 
1021
                // fullname.  If no address, copy fullname to address
 
1022
                // and comment to fullname.
 
1023
                if (*addressName) {
 
1024
                        strcat(fullName, "(");
 
1025
                        strcat(fullName, comment);
 
1026
                        strcat(fullName, ")");
 
1027
                } else {
 
1028
                        strcpy(addressName, fullName);
 
1029
                        strcpy(fullName, comment);
 
1030
                }
 
1031
        }
 
1032
 
 
1033
        //WARNING("Fullname: %s\nAddress: %s\n", fullName, addressName);
 
1034
 
 
1035
        // what name should be tickered
 
1036
        if( config.tickerMode == TICKER_FAMILYNAME && fullName[0] != '\0' && !fullNameEncoded ) {
 
1037
                free( addressName );
 
1038
                return fullName;
 
1039
        } else {
 
1040
                if( state == STATE_FULLNAME ) {
 
1041
                        strcpy( addressName, fullName );
 
1042
                        if( saveAtCharPos != -1 )
 
1043
                                atChar = &addressName[saveAtCharPos];
 
1044
                }
 
1045
                if( config.tickerMode == TICKER_NICKNAME ) {
 
1046
                        if( atChar != NULL )
 
1047
                                *atChar = '\0';
 
1048
                }
 
1049
                free( fullName );
 
1050
                return addressName;
 
1051
        }
 
1052
}
 
1053
 
 
1054
bool SkipSender( char *address )
 
1055
{
 
1056
        char **skipName;
 
1057
        int len = strlen( address );
 
1058
 
 
1059
        // remove trailing '\n' got from fgets
 
1060
        if( address[len-1] == '\n' )
 
1061
                address[len-1] = '\0';
 
1062
 
 
1063
        for( skipName = config.skipNames;
 
1064
                 skipName != NULL && *skipName != NULL; skipName++ )
 
1065
        {
 
1066
                TRACE( "comparing \"%s\" and \"%s\"\n", *skipName, address );
 
1067
                        
 
1068
                // call libc-fnmatch (wildcard-match :-) !
 
1069
                if( !fnmatch( *skipName, address, 0 )) {
 
1070
                        TRACE( "skipping sender \"%s\"\n", *skipName );
 
1071
                        return true;
 
1072
                }
 
1073
        }
 
1074
 
 
1075
        return false;
 
1076
}
 
1077
 
 
1078
void InsertName( char *name, unsigned long checksum, flag_t flag )
 
1079
{
 
1080
    name_t *item;
 
1081
 
 
1082
    TRACE( "insertName: %X, \"%s\"\n", checksum, name );
 
1083
    item = (name_t *)malloc( sizeof( name_t ));
 
1084
    item->name = name; /*strdup( name );*/
 
1085
    item->checksum = checksum;
 
1086
    item->flag = flag;
 
1087
        item->visited = true;
 
1088
    item->next = names;
 
1089
    names = item;
 
1090
 
 
1091
    namesChanged = true;
 
1092
}
 
1093
 
 
1094
void RemoveLastName()
 
1095
{
 
1096
    if( names != NULL ) {
 
1097
                name_t *name = names;
 
1098
                names = names->next;
 
1099
                free( name );
 
1100
    }
 
1101
}
 
1102
 
 
1103
void ClearAllNames()
 
1104
{
 
1105
    name_t *name, *nextName;
 
1106
    
 
1107
    for( name = names; name != NULL; name = nextName ) {
 
1108
                nextName = name->next;
 
1109
 
 
1110
                free( name->name );
 
1111
                free( name );
 
1112
    }
 
1113
 
 
1114
    names = NULL;
 
1115
    numMails = 0;
 
1116
 
 
1117
    namesChanged = true;
 
1118
}
 
1119
 
 
1120
void SetMailFlags( flag_t flag )
 
1121
{
 
1122
    name_t *name;
 
1123
    
 
1124
    for( name = names; name != NULL; name = name->next )
 
1125
                name->flag |= flag;
 
1126
}
 
1127
 
 
1128
void DrawTickerX11Font()
 
1129
{
 
1130
        // 49x21+7+20 out-drawable size
 
1131
 
 
1132
        static int insertAt;
 
1133
 
 
1134
        if( curTickerName == NULL || namesChanged )
 
1135
        {
 
1136
                for( curTickerName = names;
 
1137
                         curTickerName != NULL && config.newMailsOnly && ( curTickerName->flag & FLAG_READ );
 
1138
                         curTickerName = curTickerName->next );
 
1139
 
 
1140
                if( curTickerName == NULL )
 
1141
                        return;
 
1142
 
 
1143
                namesChanged = false;
 
1144
                insertAt = 54;
 
1145
    }
 
1146
 
 
1147
        XDrawString( DADisplay, outPixmap, tickerGC, insertAt,
 
1148
                                 41-tickerFS->max_bounds.descent,
 
1149
                                 curTickerName->name, strlen( curTickerName->name ));
 
1150
 
 
1151
        --insertAt;
 
1152
 
 
1153
        if( insertAt < -XTextWidth( tickerFS, curTickerName->name,
 
1154
                                                                  strlen( curTickerName->name )) + 6 )
 
1155
        {
 
1156
                do {
 
1157
                        curTickerName = curTickerName->next;
 
1158
                } while( curTickerName != NULL && config.newMailsOnly && ( curTickerName->flag & FLAG_READ ));
 
1159
 
 
1160
                if( curTickerName != NULL ) {
 
1161
                        insertAt = 54;
 
1162
                }
 
1163
        }
 
1164
}
 
1165
 
 
1166
void DrawTickerBuildinFont()
 
1167
{
 
1168
    // 49x21+7+20 out-drawable size
 
1169
    // 14x21 font-character size
 
1170
 
 
1171
    static int insertAt;
 
1172
    static int takeItFrom;
 
1173
 
 
1174
    int leftSpace;
 
1175
    int drawTo;
 
1176
    unsigned char *currentChar;
 
1177
    
 
1178
    if( names == NULL )
 
1179
                return;
 
1180
 
 
1181
    if( curTickerName == NULL || namesChanged ) {
 
1182
 
 
1183
                for( curTickerName = names;
 
1184
                         curTickerName != NULL && config.newMailsOnly && ( curTickerName->flag & FLAG_READ );
 
1185
                         curTickerName = curTickerName->next );
 
1186
 
 
1187
                if( curTickerName == NULL )
 
1188
                        return;
 
1189
 
 
1190
                insertAt = 57;
 
1191
                takeItFrom = 0;
 
1192
                namesChanged = false;
 
1193
    }
 
1194
 
 
1195
    leftSpace = takeItFrom % 14;
 
1196
 
 
1197
    for( currentChar = (unsigned char *)&curTickerName->name[takeItFrom/14],
 
1198
                         drawTo = insertAt; *currentChar != '\0'; ++currentChar )
 
1199
    {
 
1200
 
 
1201
                int outChar = (*currentChar < 32 || *currentChar >= 128) ? '?' :
 
1202
                        *currentChar;
 
1203
                int charWidth = 57-drawTo >= 14 ? 14 - leftSpace : 57-drawTo;
 
1204
        
 
1205
                XCopyArea( DADisplay, charsPixmap, outPixmap, DAGC,
 
1206
                                   (outChar-32)*14+leftSpace, 0, charWidth, 21, drawTo, 20 );
 
1207
 
 
1208
        leftSpace = 0;
 
1209
        drawTo += charWidth;
 
1210
        
 
1211
        if( drawTo > 57 )
 
1212
            break;
 
1213
    }
 
1214
 
 
1215
    if( --insertAt < 7 ) {
 
1216
                insertAt = 7;
 
1217
                takeItFrom++;
 
1218
 
 
1219
                if( takeItFrom/14 >= strlen( curTickerName->name )) {
 
1220
 
 
1221
                        do {
 
1222
                                curTickerName = curTickerName->next;
 
1223
                        } while( curTickerName != NULL && config.newMailsOnly && ( curTickerName->flag & FLAG_READ ));
 
1224
 
 
1225
                        if( curTickerName != NULL ) {
 
1226
                                takeItFrom = 0;
 
1227
                                insertAt = 57;
 
1228
                        }
 
1229
                }
 
1230
    }
 
1231
}
 
1232
        
 
1233
void ButtonPressed( int button, int state, int x, int y )
 
1234
{
 
1235
    if( x >= 35 && x <= 59 && y >= 47 && y <= 59 ) {
 
1236
                buttonPressed = true;
 
1237
                forceRedraw = true;
 
1238
    } else
 
1239
                // reread the config file
 
1240
                readConfigFile = true;
 
1241
}
 
1242
 
 
1243
void ButtonReleased( int button, int state, int x, int y )
 
1244
{
 
1245
    buttonPressed = false;
 
1246
        forceRedraw = true;
 
1247
 
 
1248
    if( x >= 35 && x <= 59 && y >= 47 && y <= 59 ) {
 
1249
                int ret = system( config.runCmd );
 
1250
        
 
1251
                if( ret == 127 || ret == -1 )
 
1252
                        WARNING( "execution of command \"%s\" failed.\n", config.runCmd );
 
1253
    }
 
1254
}
 
1255
 
 
1256
void GetHexColorString( const char *colorName, char *xpmLine )
 
1257
{
 
1258
    XColor color;
 
1259
 
 
1260
    if( XParseColor( DADisplay,
 
1261
                                         DefaultColormap( DADisplay, DefaultScreen( DADisplay )),
 
1262
                                         colorName, &color ))
 
1263
    {
 
1264
                sprintf( xpmLine, "%02X%02X%02X", color.red>>8, color.green>>8,
 
1265
                                  color.blue>>8 );
 
1266
    } else
 
1267
                WARNING( "unknown colorname: \"%s\"\n", colorName );
 
1268
}
 
1269
 
 
1270
char *XpmColorLine( const char *colorName, char *colorLine, bool disposeLine )
 
1271
{
 
1272
    char *newLine = strdup( colorLine );
 
1273
    char *from = strrchr( newLine, '#' );
 
1274
    
 
1275
    if( from == NULL && !strcasecmp( &colorLine[ strlen( colorLine ) - 4 ], "none" ))  {
 
1276
                // if no # found, it should be a None-color line
 
1277
                free( newLine );
 
1278
                newLine = malloc( 12 );
 
1279
                strcpy( newLine, " \tc #" );
 
1280
                newLine[11] = '\0';
 
1281
                from = newLine + 4;
 
1282
    }
 
1283
 
 
1284
    if( disposeLine )
 
1285
                free( colorLine );
 
1286
    
 
1287
    GetHexColorString( colorName, from+1 );
 
1288
    
 
1289
    return newLine;
 
1290
}
 
1291
 
 
1292
void UpdateConfiguration()
 
1293
{
 
1294
        struct stat fileStat;
 
1295
 
 
1296
    TRACE( "reading configuration file...\n" );
 
1297
 
 
1298
    ReadConfigFile( true );
 
1299
 
 
1300
        // if no path/name to an mbox or maildir inbox directory was given,
 
1301
        // use the environment
 
1302
    if( config.mailBox == NULL )
 
1303
                config.mailBox = getenv( "MAIL" );
 
1304
 
 
1305
        // mbox or maildir ?
 
1306
        if( config.mailBox != NULL && stat( config.mailBox, &fileStat ) == 0 )
 
1307
                isMaildir = S_ISDIR( fileStat.st_mode ) != 0;
 
1308
        else
 
1309
                isMaildir = false;
 
1310
 
 
1311
        TRACE( "mailbox is of type %s\n", isMaildir ? "maildir" : "mbox" );
 
1312
 
 
1313
    PreparePixmaps( true );
 
1314
 
 
1315
    SetTimer();
 
1316
}
 
1317
 
 
1318
void CleanupNames()
 
1319
{
 
1320
        name_t *name, *last = NULL, *nextName;
 
1321
 
 
1322
        numMails = 0;
 
1323
 
 
1324
        for( name = names; name != NULL; name = nextName )
 
1325
        {
 
1326
                nextName = name->next;
 
1327
 
 
1328
                if( !name->visited ) {
 
1329
                        if( last == NULL )
 
1330
                                names = name->next;
 
1331
                        else
 
1332
                                last->next = name->next;
 
1333
 
 
1334
                        free( name );
 
1335
                } else {
 
1336
                        last = name;
 
1337
 
 
1338
                        if( !config.newMailsOnly || (name->flag & FLAG_READ) == 0 )
 
1339
                                ++numMails;
 
1340
                }
 
1341
        }
 
1342
}
 
1343
 
 
1344
bool HasTickerWork()
 
1345
{
 
1346
        name_t *nextTickerName;
 
1347
 
 
1348
        if( names == NULL )
 
1349
                return false;
 
1350
 
 
1351
        if( curTickerName == NULL || namesChanged ) {
 
1352
 
 
1353
                for( nextTickerName = names;
 
1354
                         nextTickerName != NULL && ( config.newMailsOnly && ( nextTickerName->flag & FLAG_READ ));
 
1355
                         nextTickerName = nextTickerName->next );
 
1356
 
 
1357
                if( nextTickerName == NULL )
 
1358
                        return false;
 
1359
        }
 
1360
 
 
1361
        return true;
 
1362
}